从C#/ NET引用针对Cygwin的GCC内置的GNU C(POSIX)DLL

Dal*_*ale 12 c# dll gcc cygwin posix

这就是我想要的:我为POSIX编写了一个巨大的遗留C/C++代码库,包括一些非常类似POSIX的东西,比如pthreads.这可以在Cygwin/GCC上编译,并在Windows下使用Cygwin DLL作为可执行文件运行.

我想要做的是将代码库本身构建到Windows DLL中,然后我可以从C#中引用它并在其周围编写一个包装器以编程方式访问它的某些部分.

我在http://www.cygwin.com/cygwin-ug-net/dll.html上使用非常简单的"hello world"示例尝试了这种方法,它似乎不起作用.

#include <stdio.h>
extern "C" __declspec(dllexport) int hello();

int hello()
{
  printf ("Hello World!\n");
 return 42;
}
Run Code Online (Sandbox Code Playgroud)

我相信我应该能够使用以下内容引用在C#中使用上述代码构建的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();
}
Run Code Online (Sandbox Code Playgroud)

但这种方法似乎不起作用.LoadLibrary返回null.它可以找到DLL(helloworld.dll),就像它无法加载它或找到导出的函数一样.

我确信,如果我将这个基本案例工作,我可以用这种方式引用我的代码库的其余部分.任何建议或指示,或者有人知道我想要的是甚至可能吗?谢谢.

编辑:检查我的DLL与Dependency Walker(伟大的工具,谢谢),它似乎正确导出功能.问题:我应该引用它作为Dependency Walker似乎找到的函数名称(_Z5hellov)吗? Dependency 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();
    }
Run Code Online (Sandbox Code Playgroud)

这失败了: "无法加载DLL'C:.....\helloworld.dll':对内存位置的访问无效.(HRESULT异常:0x800703E6)

*****编辑3:*****Oleg建议在我的dll上运行dumpbin.exe,这是输出:

转储文件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
Run Code Online (Sandbox Code Playgroud)

摘要

    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
Run Code Online (Sandbox Code Playgroud)





编辑4感谢大家的帮助,我设法让它工作.奥列格的回答给了我需要的信息,以找出我做错了什么.

有两种方法可以做到这一点.一个是使用gcc -mno-cygwin编译器标志构建,它在没有cygwin dll的情况下构建dll,基本上就像你在MingW中构建它一样.以这种方式构建它让我的hello world示例正常工作!但是,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();
    }
Run Code Online (Sandbox Code Playgroud)

感谢大家回答~~

Ole*_*leg 9

你遇到的主要问题是如何.在使用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();
}
Run Code Online (Sandbox Code Playgroud)

因为必须找到cygwin1.dll的路径.您可以将C:\ cygwin\bin设置为当前目录,使用SetDllDirectory函数或在全局PATH环境变量中轻松包含C:\ cygwin\bin(在计算机上单击鼠标右键,然后选择属性,然后选择"高级系统设置","环境变量......",然后选择系统变量PATH并将其附加到"; C:\ cygwin\bin").

接下来,如果您编译DLL,最好使用DEF文件在编译期间定义DLL的BASE地址,并使您导出的所有函数名称更清晰可读(请参阅http://www.redhat.com/docs/manuals /enterprise/RHEL-4-Manual/gnu-linker/win32.html)

dumpbin.exe mydll.dll /exports如果安装了Visual Studio,则可以使用验证结果.(不要忘记从"Visual Studio命令提示符(2010)"启动命令promt以设置所有Visual Studio).

更新:因为你没有写成功,我认为存在一些问题.在Win32/Win64世界(非托管世界)它的工作原理.我发布的代码我测试过.在.NET中加载CygWin DLL可能会遇到一些问题.在http://cygwin.com/faq/faq.programming.html#faq.programming.msvs-mingw中可以读到" 确保堆栈底部有4K的临时空间 ".这个要求在.NET中可能是错误的.堆栈是线程的一部分而不是进程.因此,您可以尝试在新的.NET线程中使用CygWin DLL.从.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 = srchttp://old.nabble.com/Cygwin-dll-from-C--Application-td18616035.html#a18616996中描述的代码.但真正有趣的是我找到两种没有任何技巧的方法:

  1. 使用MinGW工具而不是CygWin工具编译DLL.MinGW生成的代码与Windows兼容得多.我自己没有使用CygWin或MinGW,所以我不确定,您将能够在MinGW中编译所有现有的使用POSIX函数的代码.如果可行的话,这种方式可以取得更大的成功.您可以查看http://www.adp-gmbh.ch/csharp/call_dll.html,例如,看看,可以从C#调用MinGW DLL,就像Windows DLL一样.
  2. 在非托管进程或非托管线程中使用CygWin DLL.这是CygWin文档中描述的标准方法,它可以工作(请参阅我的第一篇文章中的示例).

PS如果您最终选择了其中一种或另一种方式,请在您的问题文本中写下简短的内容.独立于声誉和赏金,这对我来说很有意思.