草庐IT

c# - 从 C#/NET 引用针对 Cygwin 在 GCC 中构建的 GNU C (POSIX) DLL

coder 2024-05-27 原文

这就是我想要的:我有一个为 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),就像它无法加载它或找不到导出的函数一样。

我确信,如果我让这个基本案例正常工作,我可以通过这种方式引用我的代码库的其余部分。任何建议或指示,或者有人知道我想要的东西是否可能吗?谢谢。

编辑: 使用 Dependency Walker(很棒的工具,谢谢)检查了我的 DLL,它似乎正确地导出了函数。问题:我应该引用它作为 Dependency 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 建议在我的 dll 上运行 dumpbin.exe,这是输出:

Dump of file helloworld.dll

File Type: DLL

Section contains the following exports for 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

Summary

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

感谢大家的回答~~

最佳答案

您遇到的主要问题如下。在您可以使用您的 helloworld.dll 之前,必须初始化一个 cygwin 环境(参见 http://cygwin.com/faq/faq.programming.html#faq.programming.msvs-mingw)。因此, native 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 环境变量中(在计算机上单击鼠标右键,选择属性然后“高级系统设置”,“环境变量...”,然后选择系统变量 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)”启动命令提示符以设置所有 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 文档中描述的标准方法,并且有效(参见我第一篇文章中的示例)。

附言如果您在最后选择的其中一种或另一种方式中取得成功,请在您的问题文本中写下简短的内容。独立于声誉和赏金对我来说很有趣。

关于c# - 从 C#/NET 引用针对 Cygwin 在 GCC 中构建的 GNU C (POSIX) DLL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2710465/

有关c# - 从 C#/NET 引用针对 Cygwin 在 GCC 中构建的 GNU C (POSIX) DLL的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  3. ruby-on-rails - 无法在centos上安装therubyracer(V8和GCC出错) - 2

    我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e

  4. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  5. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  6. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  7. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  8. ruby - 在 Ruby 中构建长字符串的简洁方法 - 2

    在编写Ruby(客户端脚本)时,我看到了三种构建更长字符串的方法,包括行尾,所有这些对我来说“闻起来”有点难看。有没有更干净、更好的方法?变量递增。ifrender_quote?quote="NowthatthereistheTec-9,acrappyspraygunfromSouthMiami."quote+="ThisgunisadvertisedasthemostpopularguninAmericancrime.Doyoubelievethatshit?"quote+="Itactuallysaysthatinthelittlebookthatcomeswithit:themo

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. Get https://registry-1.docker.io/v2/: net/http: request canceled while waiting - 2

    1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里

随机推荐