Dal*_*ale 12 c# dll gcc cygwin posix
这就是我想要的:我为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;
}
Run Code Online (Sandbox Code Playgroud)
我相信我应该能够使用以下内容引用在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();
}
Run Code Online (Sandbox Code Playgroud)
但这种方法似乎不起作用.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();
}
Run Code Online (Sandbox Code Playgroud)
这失败了: "无法加载DLL'C:.....\helloworld.dll':对内存位置的访问无效.(HRESULT异常:0x800703E6)
*****编辑3:*****Oleg建议在我的dll上运行dumpbin.exe,这是输出:
转储文件helloworld.dll
文件类型:DLL
该部分包含helloworld.dll的以下导出
Run Code Online (Sandbox Code Playgroud)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
摘要
Run Code Online (Sandbox Code Playgroud)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();
}
Run Code Online (Sandbox Code Playgroud)
感谢大家回答~~
你遇到的主要问题是如何.在使用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();
}
Run Code Online (Sandbox Code Playgroud)
因为必须找到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)
dumpbin.exe mydll.dll /exports
如果安装了Visual Studio,则可以使用验证结果.(不要忘记从"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&cvsroot = src和http://old.nabble.com/Cygwin-dll-from-C--Application-td18616035.html#a18616996中描述的代码.但真正有趣的是我找到两种没有任何技巧的方法:
PS如果您最终选择了其中一种或另一种方式,请在您的问题文本中写下简短的内容.独立于声誉和赏金,这对我来说很有意思.