【发布时间】:2011-02-12 05:16:19
【问题描述】:
这就是我想要的:我有一个为 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,这是输出:
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();
}
感谢所有回答的人~~
【问题讨论】:
-
Dependency Walker (dependencywalker.com) 工具可用于确保 DLL 可加载并查看它们的依赖关系。
-
您确定虽然 helloworld.dll 在路径中并且可以找到,但 cyg*.dll 文件也在您运行 c# 程序集的位置的路径中。如果您使用的是 Visual Studio,请进入项目属性并确保将所有必需的路径放入“环境变量”属性中。其他设置路径的方法见stackoverflow.com/questions/428085/…
-
我没有低估你为什么要在所有实验中从 C# 调用你的 cygwin DLL。与使用非托管 C++ 相比,您在实用方面有哪些优势?您需要已经加载了 POSIX 子系统。为什么需要在同一个进程中加载 .NET 运行时?可以在 Win32 进程和用 C# 编写的托管进程之间进行通信,但它们必须不能共享相同的地址空间,就像您现在尝试做的那样。
-
尝试查看系统或应用程序事件日志,看看在您看到该错误消息时是否有任何相关消息到达。