从C#/ NET引用GCC中构建的GNU C(POSIX)DLL对抗Cygwin

这就是我想要的:我为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;
}

我相信我应该能够使用以下内容引用在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();
}

但这种方法似乎不起作用。 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();
    }

这失败了: “无法加载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

     摘要

    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编译器标志构建,它在没有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();
    }

感谢大家回答~~     

2019-04-17 6 条评论

分享

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

函数或在全局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) 如果安装了Visual Studio,则可以使用

dumpbin.exe mydll.dll /exports

验证结果。 (不要忘记从“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&amp; cvsroot = src和http://old.nabble.com/Cygwin-dll-from-C--Application-td18616035.html#a18616996中描述的代码。但真正有趣的是我找到两种没有任何技巧的方法: 使用MinGW工具而不是CygWin工具编译DLL。 MinGW生成的代码与Windows兼容得多。我自己没有使用CygWin或MinGW,所以我不确定,您将能够在MinGW中编译所有现有的使用POSIX函数的代码。如果可行的话,这种方式可以取得更大的成功。您可以查看http://www.adp-gmbh.ch/csharp/call_dll.html,例如,看看,可以从C#调用MinGW DLL,就像Windows DLL一样。 在非托管进程或非托管线程中使用CygWin DLL。这是CygWin文档中描述的标准方法,它可以工作(请参阅我的第一篇文章中的示例)。 附:如果您最终选择了其中一种或另一种方式,请在问题文本中写下简短的内容。独立于声誉和赏金,这对我来说很有意思。     

2019-04-17 0 0

济畦刨

你应该首先尝试让你的简单hello world样本运行。尝试使用为POSIX编写的庞大的遗留C / C ++代码库,根据Cygwin,如果你甚至没有得到前者,那么没有多大意义。请注意,如果您要连接Cygwin,则需要GPL许可您的库。 为此,请查看文档(例如,如果您使用的是Cdecl(您目前不做的事情),则需要在hello world示例中明确指定): 使用非托管DLL函数 在本机方面,你必须初始化Cygwin(参见winsup / cygwin / how-cygtls-works.txt) 直接使用P / Invoke到您的库。 PInvoking到LoadLibrary之类的Win32库中然后调用你的库是没有意义的。这只是为错误添加了另一层,并且几乎没有任何东西。 确保您正确使用该架构(.Net应用程序默认在64位计算机上运行64位)。因此,请确保您的dll匹配/支持或将.Net限制为32位。 如果可行则尝试让您的其他库工作。 (如果你希望使用混合两种完全不同的线程模型的函数,你将不得不运气好)     

2019-04-17 0 0

琳娘

您应该能够引用针对Cygwin构建的DLL,而无需创建新的DLL。唯一的要求是您需要确保“cygwin.dll”和您尝试加载的DLL都在适当的路径中。您可能需要在调用“LoadLibrary”之前使用SetDllDirectory。     

2019-04-17 0 0

勘掸府迫路

写入的代码不起作用,导出的函数名称由C ++编译器修饰,不再类似于“hello”。您通常会声明函数extern“C”来抑制装饰。 但是,你还没有那么远。 LoadLibrary()调用失败,出现Windows错误998,ERROR_NOACCESS,“对内存位置的访问无效”。这通常发生在其中一个DLL中的DllMain()入口点依赖于具有AccessViolation硬件异常的炸弹时。这应该在调试器(Visual Studio中的输出窗口)中可见,您应该看到“第一次机会异常”,异常代码为0xc0000005。 这可能是令人不愉快的诊断,你有几个候选DLL,其调试符号可能与你使用的调试器不匹配。尝试通过在C中编写一个调用LoadLibrary的小测试程序来隔离它。配置调试器以在第一次机会异常时停止。在Visual Studio中,您可以使用Debug + Exceptions,Thrown复选框执行此操作。祝你好运!     

2019-04-17 0 0

徘廷

标准MS C编译器支持大多数POSIX接口,包括pthread。 有时作为单独的实现,但通常作为宏将POSIX语法转换为Windows库调用。 如果您的C代码没有太多的“gnuisms”,您应该能够使用标准的Visual C编译器进行编译。     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值