Сошлитесь на GNU C (POSIX) DLL, созданный в GCC против Cygwin от C#/NET

Вот то, что я хочу: у Меня есть огромная кодовая база 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)? Dependency Walker Output

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();
    }

Благодаря всем, которые ответили на ~~

12
задан Glorfindel 24 July 2019 в 11:11
поделиться

5 ответов

Основная проблема, с которой вы столкнулись, заключается в следующем. Прежде чем вы сможете использовать ваш 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. Но действительно интересными мне показались два способа без всяких ухищрений:

  1. Компиляция DLL с использованием инструментов MinGW вместо инструментов CygWin. MinGW производит код, который гораздо более совместим с Windows. Я сам не использую CygWin или MinGW, поэтому я не уверен, что вы сможете скомпилировать весь существующий код, использующий POSIX функции в MinGW. Если это возможно, то этот способ может иметь больший успех. Например, вы можете посмотреть на http://www.adp-gmbh.ch/csharp/call_dll.html, чтобы увидеть, что MinGW DLL может быть вызвана из C# точно так же, как и Windows DLL.
  2. Использование CygWin DLL внутри неуправляемого процесса или неуправляемого потока. Это стандартный способ, описанный в документации CygWin, и он работает (см. пример из моего первого сообщения).

P.S. Пожалуйста, напишите кратко в тексте вашего вопроса, если у вас есть успех в одном из этих или в другом способе, который вы выберете в конце. Для меня интересна независимость от репутации и щедрости.

9
ответ дан 2 December 2019 в 21:02
поделиться

Стандартный компилятор MS C поддерживает большинство интерфейсов POSIX, включая потоки pthread. Иногда как отдельные реализации, но обычно как макросы, преобразующие синтаксис POSIX к вызовам библиотеки Windows.

Если в вашем коде C не так много "гнуизмов", вы сможете скомпилировать его с помощью стандартного компилятора Visual C.

1
ответ дан 2 December 2019 в 21:02
поделиться

Написанный код не будет работать, имя экспортированной функции оформлено компилятором C ++ и больше не похоже на "привет". Обычно вы объявляете функцию extern «C», чтобы подавить украшение.

Но вы еще не зашли так далеко. Вызов LoadLibrary () завершается ошибкой Windows 998, ERROR_NOACCESS, «Недопустимый доступ к ячейке памяти». Чаще всего это происходит, когда точка входа DllMain () в одной из библиотек DLL зависит от бомб с аппаратным исключением AccessViolation. Это должно быть видно в отладчике (окно вывода в Visual Studio), вы должны увидеть «Первое случайное исключение» с кодом исключения 0xc0000005.

Это, вероятно, будет неприятно диагностировать, у вас есть несколько DLL, которые являются кандидатами и чьи отладочные символы, вероятно, плохо подходят для используемого вами отладчика. Попробуйте изолировать это, написав небольшую тестовую программу на C, которая вызывает LoadLibrary. Настройте отладчик так, чтобы он останавливался при первом исключении. В Visual Studio вы бы сделали это с помощью флажка Debug + Exceptions, Thrown. Удачи с этим!

1
ответ дан 2 December 2019 в 21:02
поделиться

Вы должны иметь возможность ссылаться на DLL, созданную для Cygwin, без необходимости создавать новую DLL. Единственное требование - убедиться, что и «cygwin.dll», и DLL, которую вы пытаетесь загрузить, находятся в соответствующих путях. Возможно, вам потребуется использовать SetDllDirectory до вызова «LoadLibrary».

1
ответ дан 2 December 2019 в 21:02
поделиться

Сначала вы должны попробовать запустить свой простой пример 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 бит.

Если это сработает, попробуйте заставить работать другую библиотеку. (И вам придется немного повезти, если вы рассчитываете использовать функции, смешивающие две совершенно разные модели потоков)

2
ответ дан 2 December 2019 в 21:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: