Вот что я хочу: у меня есть огромная устаревшая кодовая база C / C ++, написанная для POSIX, включая некоторые очень специфичные для POSIX вещи, такие как pthreads. Это можно скомпилировать в Cygwin / GCC и запустить как исполняемый файл под Windows с помощью библиотеки Cygwin DLL.
Что я хотел бы сделать, так это встроить саму кодовую базу в DLL-библиотеку Windows, на которую я могу затем ссылаться из C #, и написать обертку вокруг нее для программного доступа к некоторым ее частям.
Я попробовал этот подход на очень простом примере "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, созданную с помощью приведенного выше кода на C #, используя что-то вроде:
[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)?
Вывод зависимостей Walker http://i44.tinypic.com/1yagqv.png
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 Спасибо всем за помощь, мне удалось заставить ее работать. Ответ Олега дал мне информацию, необходимую для выяснения, что я делаю неправильно.
Есть два способа сделать это. Один из них - построить с флагом компилятора gcc -mno-cygwin, который создает dll без dll cygwin, в основном, как если бы вы создали его в MingW. Создавая его таким образом, я получил пример с моим привет миром! Однако MingW не имеет всех библиотек, которые есть у cygwin в установщике, поэтому, если ваш код POSIX имеет зависимости от этих библиотек (у меня были кучи), вы не сможете сделать это таким образом. И если в вашем коде POSIX не было этих зависимостей, почему бы просто не начать сборку для Win32 с самого начала. Так что это не сильно поможет, если вы не хотите тратить время на правильную настройку MingW.
Другой вариант - сборка с помощью Cygwin DLL. Cygwin DLL требует вызова функции инициализации init (), прежде чем ее можно будет использовать. Вот почему мой код не работал раньше. Приведенный ниже код загружает и запускает мой пример Hello World.
//[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();
}
Спасибо всем, кто ответил ~~