这就是我想要的:我为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编译器进行编译。