Статические библиотеки с проблемой управляемого кода - PullRequest
4 голосов
/ 16 сентября 2008

Задача (упрощена, чтобы прояснить ситуацию):

Вопрос:

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

Большое спасибо всем!

Ответы [ 6 ]

3 голосов
/ 16 сентября 2008

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

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

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

1 голос
/ 28 марта 2009

После звонка

Managed c1 = new Managed(); 

Ваша управляемая оболочка.dll будет загружена в основной домен приложения. Пока это не будет, неуправляемые вещи домена из static.lib будут делиться с другими доменами. Вместо того, чтобы создавать отдельный процесс, вам просто нужно быть уверенным (перед каждым вызовом), что managed.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-компилятор загрузит managed.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, и Managed.dll не будет загружаться в основной домен приложения.

Это похоже на трюк, но может быть, это будет полезно для кого-то. `

0 голосов
/ 01 октября 2008

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

Важная часть:

Статические поля на основе RVA являются глобальными для процессов. Они ограничены скалярами и типами значений, потому что мы не хотим, чтобы объекты проходили через границы AppDomain. Это может вызвать всевозможные проблемы, особенно во время выгрузки AppDomain. В некоторых языках, таких как 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 также может работать.

0 голосов
/ 18 сентября 2008

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

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

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

0 голосов
/ 16 сентября 2008

Я не думаю, что мы подходим к актуальной проблеме - см. Эту статью DDJ .

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

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

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

0 голосов
/ 16 сентября 2008

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...