2

I totally have no idea how this could happen. I'm trying to get the actual full name of an executable file (the cmd.exe) using GetModuleFileName. Debugging shows that GetModuleFileName outputs the correct path but right at the call to FreeLibrary, it throws the exception AccessViolationException:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Here is the code:

[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.
}

With some trial, I recognized that it occurs only for x64 (or AnyCPU) platform. If the executable file (to find its full name) is a 32 bit file, the platform should be x86 and then it works OK (although my Windows is 64 bit but the code also works when trying to find "cmd.exe" with the x86 target platform). However if the executable file is a 64 bit one, the platform should be x64 but the code won't work and throw the exception I mentioned.

The problem is x86 platform built works for 32 bit file but x64 platform built does not work for 64 bit file. So it is very strange. At least x86 built is bug free (because it works expectedly) while x64 built looks like buggy.

I would like to know if this is an expected behavior? You can surely reproduce the problem easily with the provided code together with what I described.

Thank you!

Hopeless
  • 4,397
  • 5
  • 37
  • 64
  • How about `var s = Environment.GetEnvironmentVariable( "ComSpec" );`? – IInspectable Aug 22 '15 at 12:00
  • @IInspectable not sure what you meant but I don't mean to get any path from the EnvironmentVariable, the code example is just an example where I want it to get full path of `cmd.exe`. Any other dlls or exe files can be used instead. – Hopeless Aug 22 '15 at 13:01
  • Since you didn't explicitly state, that *cmd.exe* is just an example, I offered a solution that returns the fully qualified pathname to the default command line interpreter. – IInspectable Aug 22 '15 at 13:08

1 Answers1

1

You did not allocate space in the string builder object. Hence the error. Set the capacity before calling the function.

var sb = new StringBuilder(260);

Then pass sb.Capacity to GetModuleFileName.

You should check for errors also. Don't ignore the return value.

You should not use LoadLibrary to load an executable file into your process, only with a DLL. It only makes sense to use LoadLibrary with an executable file if that executable file is your process' executable, although that would be rather pointless.

Executable files are not located using the DLL search order. The search process for executable files depends on how they are started. Either with CreateProcess or ShellExecuteEx. Consult the documentation there.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Does all this apply to just x64 platform? Because as I said when building the app as x86, it seems to work OK. I've made some simple tests and looks like `LoadLibrary` can load exe files OK and in the order expected. In fact the process is not started, I just want to it to help me find the exe full path (currently saved on disk) not its path after executed. Thanks for your answer, I did not think it was so important to init the capacity of StringBuilder (it's strange that x86 works any way). – Hopeless Aug 22 '15 at 10:53
  • 1
    Yeah, the code is broken on all platforms. If it seems to work then that's by accident. LoadLibrary won't give you reliable results for an exe. – David Heffernan Aug 22 '15 at 10:59