Вот что я хочу: у меня есть огромная устаревшая кодовая база C/C++, написанная для POSIX, включая некоторые очень специфичные для POSIX вещи, такие как pthreads. Это можно скомпилировать в Cygwin/GCC и запустить как исполняемый файл под Windows вместе с Cygwin DLL.
То, что я хотел бы сделать, это встроить саму кодовую базу в Windows DLL, на которую я могу затем ссылаться из С#, и написать обертку вокруг нее для программного доступа к некоторым ее частям.
Я попробовал этот подход на очень простом примере "hello world" на http://www.cygwin.com/cygwin-ug-net/dll.html, и он, похоже, не работает.
#include <stdio.h>
extern "C" __declspec(dllexport) int hello();
int hello()
{
printf ("Hello World!\n");
return 42;
}
Я считаю, что я должен иметь возможность ссылаться на DLL, созданную с помощью приведенного выше кода в С#, используя что-то вроде:
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32.dll")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32.dll")]
public static extern bool FreeLibrary(IntPtr hModule);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int hello();
static void Main(string[] args)
{
var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "helloworld.dll");
IntPtr pDll = LoadLibrary(path);
IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, "hello");
hello hello = (hello)Marshal.GetDelegateForFunctionPointer(
pAddressOfFunctionToCall,
typeof(hello));
int theResult = hello();
Console.WriteLine(theResult.ToString());
bool result = FreeLibrary(pDll);
Console.ReadKey();
}
Но этот подход, похоже, не работает. LoadLibrary возвращает ноль. Он может найти DLL (helloworld.dll), точно так же, как он не может загрузить ее или найти экспортированную функцию.
Я уверен, что если я получу этот базовый пример, я смогу ссылаться на остальную часть моей кодовой базы таким образом. Любые предложения или указатели, или кто-нибудь знает, возможно ли то, что я хочу? Благодарю.
Изменить: Изучил мою DLL с Dependency Walker (отличный инструмент, спасибо), и, кажется, правильно экспортировать функцию. Вопрос: я должен ссылаться на него, так как имя функции Dependency Walker, кажется, находит (_Z5hellov)?
Edit2: просто чтобы показать вам, что я пробовал это, связывая непосредственно с DLL по относительному или абсолютному пути (то есть не используя LoadLibrary):
[DllImport(@"C:\.....\helloworld.dll")]
public static extern int hello();
static void Main(string[] args)
{
int theResult = hello();
Console.WriteLine(theResult.ToString());
Console.ReadKey();
}
Это приводит к ошибке: "Невозможно загрузить DLL" C:.....\helloworld.dll ": Неверный доступ к расположению памяти. (Исключение из HRESULT: 0x800703E6)
***** Редактировать 3: ***** Олег предложил запустить dumpbin.exe на моей DLL, это вывод:
Дамп файла helloworld.dll
Тип файла: DLL
Раздел содержит следующие экспорты для helloworld.dll
00000000 characteristics 4BD5037F time date stamp Mon Apr 26 15:07:43 2010 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 000010F0 hello
Резюме
1000 .bss 1000 .data 1000 .debug_abbrev 1000 .debug_info 1000 .debug_line 1000 .debug_pubnames 1000 .edata 1000 .eh_frame 1000 .idata 1000 .reloc 1000 .text
Редактировать 4 Спасибо всем за помощь, мне удалось заставить его работать. Ответ Олега дал мне информацию, необходимую для выяснения, что я делаю не так.
Есть 2 способа сделать это. Один из них - это сборка с флагом компилятора gcc -mno-cygwin, который собирает dll без dll cygwin, в основном, как если бы вы создали его в MingW. Создавая его таким образом, я получил пример с моим привет миром! Однако MingW не имеет всех библиотек, которые есть у cygwin в установщике, поэтому, если ваш код POSIX имеет зависимости от этих библиотек (у меня были кучи), вы не сможете сделать это таким образом. И если в вашем коде POSIX не было этих зависимостей, почему бы просто не начать сборку для Win32 с самого начала. Так что это не сильно поможет, если вы не хотите тратить время на правильную настройку MingW.
Другим вариантом является сборка с помощью Cygwin DLL. Cygwin DLL требует вызова функции инициализации init(), прежде чем ее можно будет использовать. Вот почему мой код не работал раньше. Код ниже загружает и запускает мой пример "Привет, мир!"
//[DllImport(@"hello.dll", EntryPoint = "#1",SetLastError = true)]
//static extern int helloworld(); //don't do this! cygwin needs to be init first
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibrary(string lpFileName);
public delegate int MyFunction();
static void Main(string[] args)
{
//load cygwin dll
IntPtr pcygwin = LoadLibrary("cygwin1.dll");
IntPtr pcyginit = GetProcAddress(pcygwin, "cygwin_dll_init");
Action init = (Action)Marshal.GetDelegateForFunctionPointer(pcyginit, typeof(Action));
init();
IntPtr phello = LoadLibrary("hello.dll");
IntPtr pfn = GetProcAddress(phello, "helloworld");
MyFunction helloworld = (MyFunction)Marshal.GetDelegateForFunctionPointer(pfn, typeof(MyFunction));
Console.WriteLine(helloworld());
Console.ReadKey();
}
Спасибо всем, кто ответил ~~