DllImport с разными точками входа (разные DLL для одного и того же импорта в разных проектах)
В качестве продолжения моего недавнего вопроса об отладке.NET Compact Framework в настоящее время я пытаюсь использовать OpenGL ES как из.NET Compact Framework, так и из приложения.NET Framework. Я использую эту обертку, которая была создана для OpenGL ES и импортирует из libGLES_CM.dll.
Чтобы упростить отладку, я создал приложение.NET Framework, пересоздал проект импорта для OpenGL ES и EGL с теми же файлами (просто сборка для Desktop Framework), создал константы для имен DLL, чтобы они импортировались из libGLESv2.dll и libEGL.dll в Windows и из libGLES_CM.dll в CF. Библиотеки DLL взяты из PowerVR OpenGL ES Emulation SDK (целевое устройство имеет PowerVR SGX) и являются просто оболочкой OpenGL ES для реальной реализации OpenGL. И тут возникает проблема:
В библиотеке-обертке функции OpenGL находятся в двух статических классах (gl и egl) и имеют обычное имя, но без префикса gl/egl, поэтому их вызов будет egl.GetDisplay()
вместо egl.eglGetDisplay()
, Они импортируются так:
[DllImport(DllName, EntryPoint = "eglGetDisplay")]
static extern IntPtr GetDisplay(EGLNativeDisplayType display_id);
Это отлично работает на Compact Framework. В настольном проекте выдается исключение EntryPointNotFoundException - потому что функции называются как _eglGetDisplay@4
(примечание: ОМУ ловит Alt-Gr+Q для кавычек, который является символом at на немецких раскладках клавиатуры. Я должен был вставить его.) согласно Dependency Walker.
Мне удалось добавить подчеркивание к имени функции для настольного проекта, но не для CF, с помощью условной установки строковой константы для пустой строки или "_" и конкатенации ее и имени точки входа, чтобы она выглядела как этот:
[DllImport(DllName, EntryPoint = FunctionPrefix + "eglGetDisplay")]
Здесь нет проблем. Но функция все еще не найдена, потому что @4 (что это такое?) Отсутствует. Если я добавлю @4, это сработает, но, поскольку все функции имеют разные значения, я должен был сделать это вручную, и, вероятно, числа будут неправильными для версии CF. Вот странная часть:
Если я просто не укажу точку входа и вместо этого назову функцию так, как она должна быть названа, то импорт работает нормально! Теперь это уродливо из-за двойного префикса (статического имени класса и имени функции), хотя я мог бы обойти это, просто добавив обертку для этого. Поскольку я не буду сильно полагаться на эти функции (нужен только довольно простой 2D-движок), это не будет проблемой, но это просто не правильно.
Почему не работает при указании точки входа? Что я могу сделать, чтобы он работал так, как должен?
3 ответа
Если у CF и настольных API разные точки входа, вам нужно работать с этим. Это означает, что вам нужны разные определения DllImport.
Проще всего может быть наличие двух классов-оболочек, реализующих все внутренние (.NET) имена, которые вызывают их импорт, а затем создание экземпляра правильного во время выполнения в зависимости от платформы. Затем получите доступ к API через общий интерфейс.
Interface IGLImports {
IntPtr GetDisplay(EGLNativeDisplayType display_id);
}
static class CFRawImports {
[DllImport(DllName, EntryPoint = "eglGetDisplay")]
static extern IntPtr GetDisplay(EGLNativeDisplayType display_id);
}
static class DeskRawImports {
[DllImport(DllName, EntryPoint = "_eglGetDisplay@4")]
static extern IntPtr GetDisplay(EGLNativeDisplayType display_id);
}
class DesktopImports : IGLImports {
public IntPtr GetDisplay(EGLNativeDisplayType display_id) {
return DeskRawImports.GetDisplay(display_id);
}
}
class CFImports : IGLImports {
public IntPtr GetDisplay(EGLNativeDisplayType display_id) {
return CFRawImports.GetDisplay(display_id);
}
}
static class ImportLoader {
public static IGLImports GetImports() {
if (isCF) {
return new CFImports();
} else {
return new DesktopImports();
}
}
}
class MyApp {
private static IGLIMports gl = ImportLoader.GetImports();
// In code use gl.GetDesktop(...)
РЕДАКТИРОВАТЬ: интерфейс и четыре класса должны быть созданы с небольшим количеством кода. Входной файл, содержащий Имя DesktopImport CFImport (возможно, добавление имен DLL, если они различаются). Было бы оправданием для изучения шаблонов VS T4...
Декорированное имя - это строка, созданная компилятором во время компиляции определения функции или прототипа. "@4" в имени означает, что длина его параметра составляет 4 байта (32-разрядное целое число?).
Вы можете использовать dumpbin.exe, чтобы получить украшенные имена от вас.dll.
Используйте #define yourdllname_API extern "C" __declspec (dllexport) для предоставления методов в вашей dll, избегая декорирования функций таким образом, как вы не получите вышеупомянутое исключение, например:
DLL:
#ifdef DEPLOYHOOK_EXPORTS
#define DEPLOYHOOK_API extern "C" __declspec(dllexport)
#else
#define DEPLOYHOOK_API __declspec(dllimport)
#endif
// This class is exported from the DeployHook.dll
DEPLOYHOOK_API int nDeployHook;
DEPLOYHOOK_API bool InstallHook(void);
DEPLOYHOOK_API bool UnInstallHook(void);
Вызов проекта / exe:
[DllImport("DeployHook.dll",EntryPoint = "InstallHook",CharSet = CharSet::Auto, SetLastError = true)] extern bool InstallHook(void);
// EntryPointNotFoundException избегается