最近在编译某开源项目时需要在c#工程里面调用非托管c艹dll,结果不管怎么搞都是DllNotFoundException。然后作者用TravisCi倒是运行的好好地。折腾了2天现在来总结一下:
不是真正的DllNotFound
找不到函数入口签名等等都算在内,统称NotFound。检查方法也很简单,dumpbin看一下就好了。注意要用VS自带的Tool Command Prompt:
由于c艹导出的时候extern C了,所以无需指定入口,dll里面各函数名字也是正确的。
dll缺少依赖
dll依赖的dll也要一并复制到bin下面,然而有时候缺少的是某些系统的库文件,不知道是哪个漏了,这就尴尬了。好在有Dependency Walker
少了个zlib,如果由于项目是使用命令行自动编译的,懒得多写一个copy,而且万一以后有依赖了什么dll又要copy太烦了。干脆一起打包进一个dll:
在C艹项目中:使用静态链接编译dll(项目属性->配置属性->C/C++->代码生成->运行时库), 将MD选项改为MT. 使用MT选项生成的dll文件大小更大。
改了以后在使用上述软件Zlib不显示了,然而问题还是没有解决。
真的NotFound
找不到dll也分为两种:找不到文件和文件名不对。MSDN上对这两者的说明并不多,最后看Mono文档的时候发现了问题所在。我就直接摘录文档了:
Windows DLL Search Path
- The directory from which the application loaded.
- The current directory
- The system directory. Use the GetSystemDirectory() function to get the path of this directory.
- The 16-bit system directory.
- The Windows directory. Use the GetWindowsDirectory() function to get the path of this directory.
- The directories that are listed in the
PATH
environment variable.
dll名字
文档中对dll名字的说明就是在[DllImport(“”)]中写入的字符串是文件名去除平台后缀+平台前缀的剩余部分。譬如一个工程名为 cocoshit_2.0.0,在win下就是cocoshit_2.0.0.dll,linux下为libcocoshit_2.0.0.so, mac下是 libcocoshit_2.0.0.dylib,而写进代码中只要[DllImport(“cocoshit”)]即可,DllImport为自动添加前后缀。
道理我也知道啊!代码里面也是这么写的,就指望这个跨平台呢!
然而我注意了文档后面还有一段话:
Note: Windows will not automatically append a
.dll
extension to library names that already have a period (.) in their name, such aslibgtk-win32-2.0-0.dll
. If you try to uselibgtk-win32-2.0-0
as the library name, Windows won’t automatically append.dll
, resulting in a DllNotFoundException. Consequently you should either avoid periods in library names or always use the full filename (including the.dll
extension) and rely on Mono’s <dllmap/> mechanism.
???
你是说在有 点 的情况下windows平台下就不会自动添加.dll后缀?什么?这也太智障了吧,项目加点命名成项目.子项目这种情况太常见了吧,所以遇到这要就一定要填入后缀全称?然后用#if之流分平台多写几行恶心的代码?或者如Mono文档中给出的解决方案写一个dllmap?WTF。
于是通篇加.dll测试一下问题解决,就是不加后缀这个问题。之后估计写个issue向作者反映一下这个问题或者干脆在linux下部署持续集成运行项目绕过这个问题算了。