使用GetModuleFileName(x64平台)后FreeLibrary抛出AccessViolationException?

时间:2015-08-22 08:44:58

标签: c# winapi dll 64-bit pinvoke

我完全不知道这是怎么发生的。我正在尝试使用cmd.exe获取可执行文件的实际全名(GetModuleFileName)。调试显示GetModuleFileName输出正确的路径,但在调用FreeLibrary时,它会抛出异常AccessViolationException

  

尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

以下是代码:

[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string path);
[DllImport("kernel32")]
public static extern int FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", SetLastError = true)]
[PreserveSig]
public static extern uint GetModuleFileName([In] IntPtr hModule,[Out] StringBuilder lpFilename,[In] [MarshalAs(UnmanagedType.U4)] int nSize);

var hm = LoadLibrary("cmd.exe");
if (hm != IntPtr.Zero) {
     var s = new StringBuilder();
     GetModuleFileName(hm, s, 255);//at here s contains the correct path
     FreeLibrary(hm);//the exception throws at here.
}

通过一些试验,我发现它仅适用于x64(或AnyCPU)平台。如果可执行文件(找到它的全名)是一个32位文件,平台应该是x86然后它工作正常(虽然我的Windows是64位但是代码也可以在尝试查找"cmd.exe"时使用x86目标平台)。但是,如果可执行文件是64位,那么平台应该是x64,但代码将不起作用并抛出我提到的异常。

问题是x86平台内置32位文件,但内置的x64平台不适用于64位文件。所以很奇怪。至少构建的x86是无bug的(因为它可以预期),而x64构建看起来像马车。

我想知道这是否是预期的行为?您可以使用提供的代码以及我所描述的内容轻松地重现问题。

谢谢!

1 个答案:

答案 0 :(得分:1)

您没有在字符串构建器对象中分配空间。因此错误。在调用函数之前设置容量。

var sb = new StringBuilder(260);

然后将sb.Capacity传递给GetModuleFileName

您还应该检查错误。不要忽略返回值。

您不应该使用LoadLibrary将可执行文件加载到您的进程中,只能使用DLL。如果可执行文件是您的进程,那么将LoadLibrary与可执行文件一起使用是有意义的。可执行文件,虽然那会毫无意义。

使用DLL搜索顺序找不到可执行文件。可执行文件的搜索过程取决于它们的启动方式。使用CreateProcessShellExecuteEx。请查阅那里的文档。