未托管的 DLL 在使用 DLLImport C# 时无法加载

3

我可能漏掉了什么,让自己有点疯狂.. 我正在使用VS2010 C#,用C#开发Windows窗体应用程序。在同一个解决方案中,我有一个c++ dll项目,它将它的DLL输出到与C#可执行文件相同的“/bin/”文件夹中。我查看了文件夹,文件肯定在那里。我甚至使用了以下代码来确保它在运行时存在:

// yep.. it's there...
string s = System.IO.Directory.GetCurrentDirectory() + @"\Foo.dll";
System.Diagnostics.Debug.Assert(System.IO.File.Exists(s));

我的DLL里有这个:

extern "C" __declspec(dllexport) int test_func();

我通过 DLL explorer 工具确认了其内部名称正确的函数。该函数本身没有任何作用,只是“返回 4;”。

C# 代码如下:

[DllImport("Foo.dll")]
private static extern int test_func();

我遇到了这个错误:

类型为“System.DllNotFoundException”的未经处理异常发生在 .exe 中

其他信息: 无法加载 DLL 'Foo.dll':对内存位置的访问无效。 (HRESULT 异常:0x800703E6)


你的应用程序是32位还是64位? - Ahmed ilyas
32位。或者我应该说我没有明确定义一个64位的应用程序。 - Ben
2个回答

2
错误发生在框架调用LoadLibrary时。这可以从错误消息中推断出来:

无法加载DLL 'Foo.dll'

报告的错误代码是一个COM错误代码,它包装了Win32错误代码998,也称为ERROR_NOACCESS。导致调用LoadLibrary抛出ERROR_NOACCESS的情况在此处描述:http://support.microsoft.com/kb/196069
症状: 在尝试将指定模块映射到调用进程的地址空间时,LoadLibrary() API有时可能会遇到访问冲突。在这种情况下,LoadLibrary()返回一个值为NULL,并且GetLastError()返回错误代码998(ERROR_NOACCESS)。
原因: Windows NT状态码STATUS_ACCESS_VIOLATION被映射到Win32错误代码ERROR_NOACCESS。因此,如果操作系统加载程序在映射指定的DLL文件映像或执行启动代码时遇到访问冲突(异常C0000005),则加载程序将设置最后的错误代码为998(ERROR_NOACCESS),并且LoadLibrary()函数将失败并返回一个值为NULL。
更多信息: 当启动代码中发生访问违规时,异常分派程序会检测遇到此异常的进程是否正在进行调试。如果是,则会向调试器发送此第一次机会异常。
要解决LoadLibrary()失败问题,请在调试器下运行应用程序并为C0000005访问违规异常启用第一次机会异常处理。如果在调用LoadLibrary()函数时发生访问违规,则应用程序将进入调试器。然后可以使用调试器的调用堆栈来跟踪异常发生的位置。堆栈跟踪应该有助于缩小与遇到的异常相关的实际问题。
有关如何为C0000005访问违规异常启用第一次机会异常处理的信息,请参阅调试器文档。
本质上,您的DLL在其启动代码中产生了访问冲突。许多事情可能会导致这种情况,您需要对DLL进行一些调试。我建议您尝试从本地C++主机调试此问题。这样调试DLL可能更容易。
另外,您的函数使用 cdecl 调用约定,一旦您解决当前的障碍,您需要修改C# p/invoke以指定 CallingConvention.Cdecl 。

我在我的C#应用程序中使用了DllImport[]来调用LoadLibrary,并且它返回了“0”。?!我使用了与File.Exists()相同的路径,因此我认为这必须是某种权限问题。 - Ben
LoadLibrary失败时,它会返回0。这表示出现了错误。如果在LoadLibrary的PInvoke中包含SetLastError=true,则可以调用Marshal.GetLastWin32Error,它将返回998。这不是权限问题。您的DLL的DllMain引发了访问冲突。您的下一步是按照我说的做。使用本机主机进行调试。或者找出如何从您的C#主机启用非托管调试。 - David Heffernan
哦,天啊,我没想到会出现这种情况...一个全局静态类正在访问它自己的“static std::map<>”成员。但是似乎在成员定义之前就被访问了。我猜可能是它没有及时初始化自己的值以供构造函数使用。我甚至没想到这是可能的!我重新排列了代码,现在它可以工作了...太疯狂了。 - Ben
是的。静态初始化。容易出错!! - David Heffernan

0

假设您已经看到了此链接

您是否使用depends.exe打开了“foo.dll”,以确保您已满足所有依赖项?


这个DLL什么也没做。它只依赖于一些STL模板头文件。我运行了depends.exe,只得到了以下信息:错误:至少有一个模块由于在隐式依赖模块中缺少导出函数而具有未解决的导入。 错误:发现不同CPU类型的模块。我猜测它抱怨是因为这个DLL是32位的,而我正在运行Windows 7 x64。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接