Статические библиотеки с проблемой управляемого кода

StackOverflow https://stackoverflow.com/questions/72769

  •  09-06-2019
  •  | 
  •  

Вопрос

Проблема (упрощенная, чтобы было понятнее):

    1.существует один статически связанный файл static.lib, который имеет функцию, увеличивающую:
    
        extern int CallCount = 0;
        int TheFunction()
        {
            void *p = &CallCount;
            printf("Function called");
            return CallCount++;
        }
    
    2.static.lib связан с управляемой библиотекой C ++ / CLI.библиотека DLL, которая обертывает метод функции:
    
        int Managed::CallLibFunc()
        {
            return TheFunction();
        }
    
    3.Тестовое приложение имеет ссылку на управляемое.dll и создает несколько доменов, которые вызывают оболочку C ++ / CLI:
    
        static void Main(string[] args)
        {
            Managed c1 = new Managed();
            int val1 = c1.CallLibFunc();
            // value is zero
    
            AppDomain ad = AppDomain.CreateDomain("NewDomain");
            Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
            int val2 = c.CallLibFunc();
            // value is one 
        }
    

Вопрос:

Основываясь на том , что я прочитал в Essential .NET Vol1 В окне CLR by Don я бы ожидал, что val2 будет равен нулю, поскольку при вызове CreateInstanceAndUnwrap загружается совершенно новая копия managed.dll/static.lib.Я неправильно понимаю, что происходит?Статическая библиотека, похоже, не соблюдает границы appdomain, поскольку это неуправляемый код.Есть ли способ обойти эту проблему, кроме как создав совершенно новый процесс для создания управляемого экземпляра?

Всем вам огромное спасибо!

Это было полезно?

Решение

Моя догадка заключалась в том, что, как вы и подозревали, неуправляемые библиотеки DLL загружаются в контексте процесса, а не в контексте AppDomain, поэтому любые статические данные в неуправляемом коде являются общими для AppDomain.

Эта ссылка показывает кого-то с той же проблемой, что и у вас, все еще не проверено на 100%, но, вероятно, это так.

Эта ссылка речь идет о создании обратного вызова из неуправляемого кода в AppDomain с использованием трюка thunking.Я не уверен, что это может вам помочь, но, возможно, вы найдете это полезным для создания какого-то обходного пути.

Другие советы

После того, как ты позвонишь

Managed c1 = new Managed(); 

Ваша управляемая.оболочка dll будет загружена в домен основного приложения вашего приложения.До тех пор, пока он не появится, неуправляемые файлы домена из static.lib будут использоваться совместно с другими доменами.Вместо того чтобы создавать отдельный процесс, вам просто нужно быть уверенным (перед каждым вызовом), что удалось.библиотека DLL не загружается ни в один домен приложения.

Сравните с этим

static void Main(string[] args)
{

    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  Value is zero

               AppDomain.Unload(ad)
    }
    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  I think value is zero

               AppDomain.Unload(ad)
    }


}
`

ВАЖНЫЙ и :Если вы добавите только одну строку, JIT-компилятор загрузится управляемо.dll- и волшебство исчезает.

static void Main(string[] args)
{

    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  Value is zero 

               AppDomain.Unload(ad)
    }
    {       
                AppDomain ad = AppDomain.CreateDomain("NewDomain");
                Managed c = ad.CreateInstanceAndUnwrap(a.FullName, typeof(Managed).FullName) as Managed;
                int val2 = c.CallLibFunc();
                //  I think value is one

               AppDomain.Unload(ad)
    }
Managed c1 = new Managed(); 


}

Если вы не хотите зависеть от таких строк, вы можете создать еще одну оболочку ManagedIsolated.dll, которая будет ссылаться на managed.dll и будет выполнять каждый вызов в отдельном домене с выгрузкой домена сразу после вызова.Основное приложение будет зависеть только от ManagedIsolated.типы DLL и управление ими.библиотека DLL не будет загружена в основной домен приложения.

Это выглядит как уловка, но, возможно, кому-то она пригодится.`

Короче говоря, может быть.Домены приложений - это чисто управляемая концепция.Когда создается экземпляр AppDomain, он не отображается в новых копиях базовых библиотек DLL, он может повторно использовать код, уже находящийся в памяти (например, вы же не ожидаете, что он загрузит новые копии всех сборок System. *, верно?)

В управляемом мире все статические переменные ограничены AppDomain, но, как вы указали, это не применимо в неуправляемом мире.

Вы могли бы сделать что-то сложное, что принудительно загружает уникальную управляемую библиотеку DLL.для каждого домена приложения, что привело бы к появлению новой версии статической библиотеки lib.Например, возможно, с помощью Assembly.Загрузка с помощью массива байтов сработала бы, но я не знаю, как CLR попытается справиться с коллизией в типах, если одна и та же сборка загружается дважды.

Я не думаю, что мы подходим здесь к самому главному вопросу - смотрите эту статью DDJ.

Значением по умолчанию атрибута оптимизации загрузчика является SingleDomain, который "заставляет AppDomain загружать закрытую копию кода каждой необходимой сборки".Даже если бы это было одно из многодоменных значений "каждый AppDomain всегда поддерживает отдельную копию статических полей".

'managed.dll' - это (как следует из ее названия) управляемая сборка.Код в static.lib был статически скомпилирован (как IL-код) в 'managed.dll', поэтому я ожидал бы того же поведения, что и Lenik....

...если только static.lib не является статической библиотекой экспорта для неуправляемой библиотеки DLL.Леник говорит, что это не тот случай, поэтому я все еще не уверен, что здесь происходит.

Вы пробовали работать в отдельных процессах?Статическая библиотека не должна совместно использовать экземпляры памяти вне своего собственного процесса.

Я знаю, справиться с этим может быть непросто.Однако я не уверен, какие у вас были бы другие варианты в этом случае.

Редактировать:Немного осмотревшись, я думаю, вы могли бы сделать все, что вам было нужно, с помощью Система.Диагностика.Процесс класс.На данный момент у вас было бы много вариантов общения, но Удаленное использование .NET или WCF, вероятно, был бы хорошим и простым выбором.

Это две лучшие статьи, которые я нашел на эту тему

Важной частью является:

Статические поля на основе RVA являются глобальными для процесса.Они ограничены скалярами и типами значений, потому что мы не хотим, чтобы объекты выходили за границы домена приложения.Это вызвало бы всевозможные проблемы, особенно во время выгрузки домена приложения.Некоторые языки, такие как ILASM и MC ++, позволяют удобно определять статические поля на основе RVA.В большинстве языков этого нет.

Хорошо, итак, если вы управляете кодом в .lib, я бы попробовал

class CallCountHolder {
   public:
     CallCountHolder(int i) : count(i) {}
     int count;
};

static CallCountHolder cc(0);
int TheFunction()
{
    printf("Function called");
    return cc.count++;
}

Поскольку он сказал, что статические поля на основе RVA ограничены скалярами и типами значений.Массив int также может работать.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top