Разница между dllimport и getProcAddress

Во-первых, я знаю, что сравнивать напрямую атрибут dllimport и функцию getProcAddress не имеет смысла. Скорее, меня интересует сравнение двух фрагментов кода, которые достигают в основном одного и того же — вызова функции в dll — либо путем импорта функции с атрибутом dllimport, либо с функцией getProcAddress. В частности, я пишу приложение C#, которое использует некоторую функцию в dll, которую я написал. Сначала я получил доступ к своей функции dll с помощью следующего фрагмента кода:

class DllAccess
{
    [DllImport("kernel32.dll", SetLastError = true)]
    private extern IntPtr LoadLibrary(String DllName);

    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    private delegate Bool BarType(Byte arg); // return value indicates whether function call went well or not.

    Bool Bar(Byte arg)
    {
        Bool ok = false;
        IntPtr pDll= LoadLibrary("foo.dll");
        if (pDll != IntPtr.Zero)
        {
            IntPtr pfunc = GetProcAddress(pDll, "bar");
            if (pFunc != IntPtr.Zero)
            {
                BarType bar = (BarType)Marshal.GetDelegateForFunctionPointer(pFunc, typeof(BarType));
                ok = bar(arg);
            }
            FreeLibrary(pDll);
        }
        return ok;
    }
}

Однако позже мне нужно было получить значение lastError, если оно было установлено во время вызова dll, поэтому я изменил свой код на это:

class DllAccess
{
    [DllImport("foo.dll", EntryPoint = "bar", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    private extern Bool DllBar(Byte arg); // return value indicates whether function call went well or not.

    Bool Bar(Byte arg)
    {
        return DllBar(arg);
    }
}

Это, конечно, намного аккуратнее и, как уже упоминалось, устанавливает код lastError. Очевидно, что мой первый кусок кода дает мне возможность изменить dll и вызов функции во время выполнения, но на данный момент это не требуется. Итак, мой вопрос: есть ли причины использовать первую формулировку, если я уверен, что не буду использовать другую dll или другую функцию?


person Boris    schedule 13.07.2010    source источник


Ответы (2)


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

Однако второй вариант предоставляет вам огромное количество преимуществ. В дополнение к тому, что он "аккуратнее", он также выполняет большую часть маршалинга типов данных для вас, что становится очень важным для некоторых API.

При этом, если вы используете метод, который вы указали первым, вы также должны обязательно выгрузить все. Прямо сейчас вы в основном пропускаете адреса каждый раз, когда вызываете Bar()... Подробнее см. FreeLibrary.

person Reed Copsey    schedule 13.07.2010
comment
Спасибо за ваш ответ. В моем коде я фактически загрузил библиотеку в конструкторе, освободил библиотеку в деструкторе и использовал все это как синглтон, так как я не знал, вызовет ли это проблемы с загрузкой библиотеки более одного раза. В интересах правильности я отредактировал свой пост, чтобы также освободить библиотеку. - person Boris; 14.07.2010
comment
@Boris: LoadLibrary и FreeLibrary выполняют подсчет ссылок, поэтому несколько вызовов, как правило, работают нормально. - person Ben Voigt; 14.07.2010
comment
Что вы подразумеваете под утечкой адресов? DLL загружается только один раз, в памяти существует только одна копия функции, а делегат удаляется сборщиком мусора. - person Ben Voigt; 14.07.2010
comment
@Ben: на самом деле он хранит в памяти дополнительные счетчики указателей - это не имеет большого значения, поскольку библиотека загружается 1 раз только из-за подсчета ссылок, но вы действительно пропускаете указатель, и, вероятно, лучше выгрузить библиотеку, если вы все равно пользоваться не будем... - person Reed Copsey; 14.07.2010

Вероятно, самым большим преимуществом GetProcAddress является то, что он позволяет вам контролировать путь поиска DLL. Например, вы можете автоматически загрузить 32-разрядную или 64-разрядную версию собственной библиотеки DLL. С DllImportAttribute это невозможно.

person Ben Voigt    schedule 13.07.2010
comment
Бен: Просто к сведению. Вы можете сделать это с помощью [DllImport], разместив библиотеку по отдельному пути и изменив пути поиска во время выполнения. - person Reed Copsey; 14.07.2010
comment
Вы имеете в виду возиться с переменной окружения PATH? Не рекомендуется. blogs.msdn.com/b/oldnewthing/archive/ 2008/12/11/9193695.aspx - person Ben Voigt; 14.07.2010