【问题标题】:Strange crash when loading a C DLL compiled with GCC in cygwin into a C#.NET application将 cygwin 中使用 GCC 编译的 C DLL 加载到 C#.NET 应用程序中时发生奇怪的崩溃
【发布时间】:2010-09-24 08:09:48
【问题描述】:

我尝试将在 cygwin 中使用 GCC 编译的简单 DLL 加载到 C#.NET 应用程序中。 DLL 看起来像这样

#ifndef __FOO_H
#define __FOO_H

#if _WIN32
  #define EXPORT extern "C" __declspec(dllexport)
#else //__GNUC__ >= 4
  #define EXPORT extern "C" __attribute__((visibility("default")))
#endif

EXPORT int bar();

#endif  // __FOO_H

函数 bar() 只返回 42。

我编译并链接了 DLL

g++ -shared -o foo.dll foo.cpp

现在我想将这个超级简单的 DLL 加载到 C# WinForms 应用程序中。

public partial class Form1 : Form
{
  [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 IntPtr Action2();

  unsafe public Form1()
  {
    InitializeComponent();

    IntPtr pcygwin = LoadLibrary("cygwin1.dll");
    IntPtr pcyginit = GetProcAddress(pcygwin, "cygwin_dll_init");
    Action init = (Action)Marshal.GetDelegateForFunctionPointer(pcyginit, typeof(Action));
    init();
  }

  unsafe private void button1_Click(object sender, EventArgs e)
  {
    IntPtr foo = LoadLibrary("foo.dll");         // CRASH ... sometimes
    IntPtr barProc = GetProcAddress(foo, "bar");
    Action2 barAction = (Action2)Marshal.GetDelegateForFunctionPointer(barProc, typeof(Action2));
    IntPtr inst = barAction();
  }
}

现在奇怪的是:有时有效,有时无效。当它不工作时,它会在加载 foo.dll 时崩溃。我在调试模式下运行它,但我什至没有得到异常。调试器就像我自己停止一样停止!

我还尝试在加载 cygwin1.dll 的同一堆栈框架中加载 foo.dll。一样的!

任何提示为什么会发生这种情况以及我可以做些什么来使它起作用?

更新 1:我们使用最新的 cygwin 和 Visual Studio 2010。

更新 2:假设它必须与时间和垃圾收集有关。在我看来,加载 cygwin1.dll 和加载 foo.dll 之间的时间很重要。两次 LoadLibrary 调用之间的时间越短,它似乎就越有可能起作用。

更新 3:如果加载 foo.dll 第一次成功,它总是在会话期间成功。我可以随意点击 button1。

注意: LoadLibrary("foo.dll") 不仅无法加载 foo.dll。那会很好。我崩溃并且调试器停止工作。甚至没有抛出异常。 并且它并不总是崩溃。有时它会起作用!

【问题讨论】:

    标签: c# .net dll cygwin loadlibrary


    【解决方案1】:

    查看我的old answer 中关于关闭问题的“更新”部分。我建议您使用 MinGW 工具而不是 CygWin 工具来编译您的 DLL。如果自那时以来没有任何变化,“确保堆栈底部有 4K 的暂存空间”使 CygWin DLL 与 .NET 不兼容。我不知道如何在 .NET 应用程序中实现需求。

    【讨论】:

    • 我们必须使用cygwin。我们想在 Windows 上编译一个 Hadoop ZooKeeper 客户端。 ZooKeeper C 接口依赖于 Windows 中的 cygwin。
    • @Fair Dinkum Thinkum:那么在您的情况下,编写 EXE 而不是 DLL 并就任何已知方法与 EXE 进行通信会更容易(推荐一种方法,我需要了解更多信息来自你)。在我看来,尝试解决“堆栈底部有 4K 暂存空间”要求的问题要容易得多。
    • @Fair Dinkum Thinkum:我不知道如何控制 .NET 应用程序的堆栈。堆栈不是您的应用程序的一部分。它属于线程。托管线程将控制一些其他的非托管线程。 CygWin 对在当前线程中运行的非托管代码的要求似乎对托管应用程序来说太难了。有时堆栈可以是免费的,并且您的 DLL 调用可以工作,但我不知道允许控制 CygWin 的堆栈要求的 .NET 互操作属性(选项)。
    • @Oleg,您需要什么信息?我们必须将 C# 模块连接到 ZooKeeper 集群。它需要对 ZooKeeper 的读写权限。实际上我们现在的解决方案是一个可执行文件。 exe 将信息写入 C# 模块读取的文件。但是这个解决方案似乎是“错误的”并且容易出错。另外,它不考虑将数据从 C# 模块写入 ZooKeeper。我们也不想为那个方向使用文件。
    • @Fair Dinkum Thinkum:我离 ZooKeeper 还很远。所以它对我说的不多。您可以在 CygWin 中使用哪些 IPC 方式?可以使用 COM/DCOM 吗?共享内存、命名管道、套接字、窗口消息传递?调用 Web 服务更容易吗?您可以使用任何 IPC(进程间通信)方法将信息发送到 .NET 应用程序并接收返回的响应。在实现通信方式上,应该选择最可靠、最简单的方式。
    【解决方案2】:

    您应该尝试使用 msft 的进程监视器。这很可能是由于加载依赖 dll 失败引起的。进程监视器会告诉你什么 dll 以及为什么它没有加载。

    【讨论】:

    • 我首先想到的是缺少依赖项。我已经检查过是否缺少 DLL。为什么它有时会起作用?如果缺少 DLL,它不应该总是失败吗?
    • DLL 不应该在它们的 DLLMain 中做任何实际的工作,所以通常的做法是产生一个线程来做某事。如果下一个 dll 依赖于正在做的事情,那么加载将失败。在这种情况下,第一个 dll 可能正在更新或修改路径以包含 cygwin dll。如果是这种情况,那么您应该能够通过自己修改路径来修复崩溃。您可以通过在工作和失败案例上使用进程监视器并比较使用的搜索路径来判断是否是这种情况。
    【解决方案3】:

    试试下面...

      [DllImport("kernel32", CharSet=CharSet.Unicode)]
      static extern IntPtr LoadLibrary(string lpLibFileName);
    

    甚至可以使用

      [DllImport("kernel32", CharSet=CharSet.Unicode, SetLastError=true)]
    

    并在尝试使用之前检查调用 LoadLibrary 和 GetProcAddress 的返回值。

    【讨论】:

    • 如果加载 foo.dll 崩溃,则没有返回值。有时是对 LoadLibrary("foo.dll") 的调用导致崩溃。有时它会起作用。
    • @Fair,我的建议背后的想法是,如果 PInvoke 签名和数据不正常,第一个 LoadLibrary 可能是罪魁祸首。此外,是否需要“安全”?尽可能避免安全。
    • 我刚刚检查了你的假设。 cygwin init 部分每次都成功。我还删除了 unsafe 关键字。没有效果。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-28
    相关资源
    最近更新 更多