Вот то, что я хочу: у Меня есть огромная кодовая база C/C++ прежней версии, записанная для POSIX, включая некоторых очень POSIX определенный материал как pthreads. Это может быть скомпилировано на Cygwin/GCC и выполнено как исполняемый файл в соответствии с Windows с DLL Cygwin.
То, что я хотел бы сделать, создать саму кодовую базу в Windows DLL, на который я могу затем сослаться от C# и записать обертку вокруг этого для доступа к некоторым частям ее программно.
Я попробовал этот подход очень простым "привет мировой" пример по http://www.cygwin.com/cygwin-ug-net/dll.html, и это, кажется, не работает.
#include
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 с Зависимостью Walker (большой инструмент, спасибо) и это, кажется, экспортирует функцию правильно. Вопрос: я должен ссылаться на него как на Зависимость от имени функции, которую 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: ***** Oleg предложил выполнить 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 Спасибо все для справки, мне удалось получить ее работа. Ответ Oleg дал мне информацию, я должен был узнать то, что я делал неправильно.
Существует 2 способа сделать это. Нужно создать с gcc-mno-cygwin флаг компилятора, который создает dll без cygwin dll, в основном как будто Вы создали его в MingW. При создании его этот путь получил мою привет мировую работу в качестве примера! Однако MingW не имеет всех библиотек, которые cygwin имеет в установщике, поэтому если Ваш код POSIX имеет зависимости от этих библиотек (шахта имела "кучу"), Вы не можете сделать этого пути. И если Ваш код POSIX не имел тех зависимостей, почему не только создают для Win32 с начала. Таким образом, это не много справки, если Вы не хотите провести время, создавая MingW правильно.
Другая опция состоит в том, чтобы создать с DLL Cygwin. DLL Cygwin нужна функция инициализации 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();
}
Благодаря всем, которые ответили на ~~
Основная проблема, с которой вы столкнулись, заключается в следующем. Прежде чем вы сможете использовать ваш helloworld.dll, окружение cygwin должно быть инициализировано (см. http://cygwin.com/faq/faq.programming.html#faq.programming.msvs-mingw). Поэтому следующий код на родном C++ будет работать:
#include <windows.h>
typedef int (*PFN_HELLO)();
typedef void (*PFN_CYGWIN_DLL_INIT)();
int main()
{
PFN_HELLO fnHello;
HMODULE hLib, h = LoadLibrary(TEXT("cygwin1.dll"));
PFN_CYGWIN_DLL_INIT init = (PFN_CYGWIN_DLL_INIT) GetProcAddress(h,"cygwin_dll_init");
init();
hLib = LoadLibrary (TEXT("C:\\cygwin\\home\\Oleg\\mydll.dll"));
fnHello = (PFN_HELLO) GetProcAddress (hLib, "hello");
return fnHello();
}
Конечно, путь к cygwin1.dll должен быть найден. Вы можете установить C:\cygwin\bin в качестве текущего каталога, используя функцию SetDllDirectory
или просто включить C:\cygwin\bin в глобальную переменную окружения PATH (нажмите правую кнопку мыши на Computer, выберите Properties, затем "Advanced System Settings", "Environment variables...", затем выберите системную переменную PATH и добавьте к ней ";C:\cygwin\bin").
Далее, если вы компилируете DLL, лучше использовать DEF-файл, чтобы определить базовый адрес DLL во время компиляции и сделать все имена функций, которые вы экспортировали, более понятными для чтения (см. http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/gnu-linker/win32.html)
Вы можете проверить результаты с помощью dumpbin.exe mydll.dll /exports
, если у вас установлена Visual Studio. (не забудьте запустить command promt из "Visual Studio Command Prompt (2010)", чтобы все Visual Studio были установлены).
UPDATED: Поскольку вы не пишете об успехе, я думаю, что существуют некоторые проблемы. В мире Win32/Win64 (неуправляемый мир) это работает. Код, который я выложил, я протестировал. Загрузка CygWin DLL в .NET может иметь некоторые проблемы. В http://cygwin.com/faq/faq.programming.html#faq.programming.msvs-mingw можно прочитать: "Убедитесь, что у вас есть 4K места для скретча в нижней части стека". Это требование может быть неверным в .NET. Стек - это часть потока, а не процесса. Поэтому вы можете попробовать использовать библиотеки CygWin DLL в новом потоке .NET. Начиная с .NET 2.0 можно определить максимальный размер стека для потока. Еще один способ - попытаться понять http://cygwin.com/cgi-bin/cvsweb.cgi/~checkout~/src/winsup/cygwin/how-cygtls-works.txt?rev=1.1&content-type=text/plain&cvsroot=src и код, описанный в http://old.nabble.com/Cygwin-dll-from-C--Application-td18616035.html#a18616996. Но действительно интересными мне показались два способа без всяких ухищрений:
P.S. Пожалуйста, напишите кратко в тексте вашего вопроса, если у вас есть успех в одном из этих или в другом способе, который вы выберете в конце. Для меня интересна независимость от репутации и щедрости.
Стандартный компилятор MS C поддерживает большинство интерфейсов POSIX, включая потоки pthread. Иногда как отдельные реализации, но обычно как макросы, преобразующие синтаксис POSIX к вызовам библиотеки Windows.
Если в вашем коде C не так много "гнуизмов", вы сможете скомпилировать его с помощью стандартного компилятора Visual C.
Написанный код не будет работать, имя экспортированной функции оформлено компилятором C ++ и больше не похоже на "привет". Обычно вы объявляете функцию extern «C», чтобы подавить украшение.
Но вы еще не зашли так далеко. Вызов LoadLibrary () завершается ошибкой Windows 998, ERROR_NOACCESS, «Недопустимый доступ к ячейке памяти». Чаще всего это происходит, когда точка входа DllMain () в одной из библиотек DLL зависит от бомб с аппаратным исключением AccessViolation. Это должно быть видно в отладчике (окно вывода в Visual Studio), вы должны увидеть «Первое случайное исключение» с кодом исключения 0xc0000005.
Это, вероятно, будет неприятно диагностировать, у вас есть несколько DLL, которые являются кандидатами и чьи отладочные символы, вероятно, плохо подходят для используемого вами отладчика. Попробуйте изолировать это, написав небольшую тестовую программу на C, которая вызывает LoadLibrary. Настройте отладчик так, чтобы он останавливался при первом исключении. В Visual Studio вы бы сделали это с помощью флажка Debug + Exceptions, Thrown. Удачи с этим!
Вы должны иметь возможность ссылаться на DLL, созданную для Cygwin, без необходимости создавать новую DLL. Единственное требование - убедиться, что и «cygwin.dll», и DLL, которую вы пытаетесь загрузить, находятся в соответствующих путях. Возможно, вам потребуется использовать SetDllDirectory до вызова «LoadLibrary».
Сначала вы должны попробовать запустить свой простой пример hello world. Нет особого смысла пытаться работать с огромной унаследованной кодовой базой C/C++, написанной для POSIX и зависящей от Cygwin, если вы даже не сможете правильно выполнить первое. Обратите внимание, что если вы подключаете Cygwin, вы должны иметь GPL-лицензию на свою библиотеку.
Для этого посмотрите документацию (например, вам нужно явно указать в примере hello world, используете ли вы Cdecl (чего вы сейчас не делаете)): Потребление неуправляемых функций DLL
На родной стороне вы должны инициализировать Cygwin (см. winsup/cygwin/how-cygtls-works.txt)
Используйте P/Invoke к вашей библиотеке напрямую. Нет никакого смысла в PInvoking в библиотеки Win32, например LoadLibrary, чтобы затем вызывать свои библиотеки. Это только добавляет еще один слой для ошибок и ничего не дает.
Убедитесь, что вы правильно выбрали архитектуру (приложения .Net по умолчанию работают на 64-разрядных машинах). Поэтому убедитесь, что ваши dll соответствуют/поддерживают эту архитектуру, или ограничьте .Net до 32 бит.
Если это сработает, попробуйте заставить работать другую библиотеку. (И вам придется немного повезти, если вы рассчитываете использовать функции, смешивающие две совершенно разные модели потоков)