Reputation: 9
Below is my function for getting universal paths. It has been through many iterations as I find various conflicting posts on the subject, but this function works - usually. However about 5% of the time it causes a crash of the application. The error message indicates that it is possibly corrupted memory. I can find no reason for that. The problem never happens when running the c# application under the debugger, but if I attach to the process after the error then I can view the data returned by WNetGetUniversalName, and it looks exactly the same as when there is no crash. I need some ideas of what might be causing this.
public static string GetUncPath (string localPath)
{
if (localPath.IndexOf ('\\')== 0)
return localPath; // already a unc path
int bufferSize = 8000;
IntPtr buffer = Marshal.AllocHGlobal (bufferSize);
int ret = WNetGetUniversalName (localPath, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref bufferSize);
if (ret == 2250)
return localPath;
string result = "";
if (ret == 0) {
IntPtr ptr = Marshal.ReadIntPtr (buffer);
result = Marshal.PtrToStringAuto (ptr, bufferSize);
result = result.Substring (0, result.IndexOf ('\0'));
}
Marshal.FreeHGlobal (buffer);
return result;
}
[DllImport ("mpr.dll", EntryPoint="WNetGetUniversalName",
CharSet=CharSet.Unicode, SetLastError=false)]
[return: MarshalAs (UnmanagedType.U4)]
static extern int WNetGetUniversalName (string lpLocalPath,
[MarshalAs (UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs (UnmanagedType.U4)] ref int lpBufferSize);
The crash always happens in the call to Marshal.PtrToStringAuto. Again, the data in the buffer looks absolutely correct when the crash has happened.
Notice that I have set up a buffer of 8000 bytes. I started with 2000, thinking that would be plenty since the strings being returned are all less that 200 bytes. But using 8000 seems to result in fewer crashes. This is somewhat subjective since the crashes are totally random, sometimes not occurring for a long time and other times happening nearly every time the application is started.
This is a 64 bit application. The errors happen with Windows 7 Pro and with Windows 10.
Any Ideas? Thanks, Russ
Ok, thanks to Ben I have the answer. Here is my final working code:
public static string GetUncPath (string localPath)
{
if (localPath.IndexOf ('\\')== 0)
return localPath; // already a unc path
int bufferSize = 512;
IntPtr buffer = Marshal.AllocCoTaskMem (bufferSize);
int ret = WNetGetUniversalName (localPath, UNIVERSAL_NAME_INFO_LEVEL,
buffer, ref bufferSize);
if (ret == 2250) { // if localpath is on the local computer
Marshal.FreeCoTaskMem (buffer);
return localPath;
}
string result = "";
if (ret == 0) {
IntPtr ptr = Marshal.ReadIntPtr (buffer);
result = Marshal.PtrToStringAuto (ptr);
}
Marshal.FreeCoTaskMem (buffer);
return result;
}
Upvotes: 0
Views: 1287
Reputation: 283684
There are three errors in the following line:
result = Marshal.PtrToStringAuto (ptr, bufferSize);
One was pointed out in the comments by @tolanj -- on return bufferSize
is the total number of bytes written, including the size of the _UNIVERSAL_NAME_INFOW
structure itself. Not the number of bytes in the string.
Second is that Marshal.PtrToStringAuto
's second argument is the number of characters to read from the specified pointer, but you are passing the number of bytes.
Third is that you imported the function with CharSet.Unicode
, so you know you are always getting WNetGetUniversalNameW
, _UNIVERSAL_NAME_INFOW
, and UTF-16 string data. So you should use PtrToStringUni
not PtrToStringAuto
1. Both the above concerns also apply to PtrToStringUni
.
PtrToStringAuto
behavior is documented as
On Unicode platforms, this method calls
PtrToStringUni
; on ANSI platforms, it callsPtrToStringAnsi
. No transformations are done before these methods are called.
You don't want sometimes one, sometimes the other. The p/invoke declaration is forcing Unicode on all platforms.
Upvotes: 1