c #Pinvoke with StringBuilder为同一方法返回不同的结果

时间:2016-05-05 08:58:51

标签: c# encoding pinvoke stringbuilder

所以我注意到一些非常奇怪的事情,我没有解决。我确定这只是一个愚蠢的编码错误,但尝试对结果进行不同的编码并不能帮助我。

问题:

当我Pinvoke函数" PathYetAnotherMakeUniqueName"从Shell32.dll通过DLLImport

[DllImport("shell32.dll", EntryPoint = "PathYetAnotherMakeUniqueName", CharSet = CharSet.Unicode)]
internal static extern bool PathYetAnotherMakeUniqueName2(
                System.Text.StringBuilder pszUniqueName,
                string pszPath,
                string pszShort,
                string pszFileSpec);

[...snip...]

System.Text.StringBuilder pszUniqueName = new System.Text.StringBuilder();
PathYetAnotherMakeUniqueName2(pszUniqueName, "Foo", "Bar", "Test");
Console.WriteLine(pszUniqueName); // Output : Foo\Bar

或通过LoadLibrary方法

internal delegate bool dPathYetAnotherMakeUniqueName([MarshalAs(UnmanagedType.LPWStr)]System.Text.StringBuilder pszUniqueName, string pszPath, string pszShort, string pszFileSpec);
static dPathYetAnotherMakeUniqueName PathYetAnotherMakeUniqueName;

[...snip...]

hCurModule = LoadLibrary("shell32.dll");
IntPtr pFunction = GetProcAddress(hCurModule, "PathYetAnotherMakeUniqueName");
PathYetAnotherMakeUniqueName = (dPathYetAnotherMakeUniqueName)Marshal.GetDelegateForFunctionPointer(pFunction, 
            typeof(dPathYetAnotherMakeUniqueName));

System.Text.StringBuilder pszUniqueName = new System.Text.StringBuilder ();
PathYetAnotherMakeUniqueName(pszUniqueName, "Foo", "Bar", "Test");
Console.WriteLine(pszUniqueName); // output: "?o\?r???o

正如你所看到的,我只得到了两个模糊相似的输出。我尝试更改MarshalAs属性或编码stringbuilders' ASCII Unicode或UTF7,8,32等内容,但输出没有变化。 不使用MarshalAs(它似乎适用于其他一些需要StringBuilder的方法)我得到了一个堆栈不平衡异常。

以前有人遇到过这个问题吗? 非常感谢任何帮助。

度过美好的一天,并提前致谢

1 个答案:

答案 0 :(得分:2)

您的输入字符串似乎正在编组为LPStr。字符串的default marshallingUmanagedType.LPTStr,与平台相关,并已解析为LPStrLPWStr。在您的平台上,它被解析为LPStr,我假设它是因为默认CharSetCharSet.ANSI,并且您没有为手动加载的函数指定不同的({1}}欢迎权威确认)。

将输入参数明确指定为[MarshalAs(UnmanagedType.LPWStr)]可以解决问题。

internal delegate bool dPathYetAnotherMakeUniqueName(
    [MarshalAs(UnmanagedType.LPWStr)]System.Text.StringBuilder pszUniqueName, 
    [MarshalAs(UnmanagedType.LPWStr)]string pszPath,
    [MarshalAs(UnmanagedType.LPWStr)]string pszShort, 
    [MarshalAs(UnmanagedType.LPWStr)]string pszFileSpec);

CharSet = CharSet.Unicode属性中的DllImport字段会为您处理此问题,因此存在差异。

更直接的方法是在声明您的委托时使用UnmanagedFunctionPointer属性,并指定正确的CharSet。这更符合您的DllImport声明。

[UnmanagedFunctionPointer(CallingConvention.Winapi, CharSet=CharSet.Unicode)]
internal delegate bool dPathYetAnotherMakeUniqueName(
    System.Text.StringBuilder pszUniqueName, 
    string pszPath,
    string pszShort,
    string pszFileSpec);

更重要的是,在这种情况下似乎没有任何理由使用GetProcAddress。您事先已经知道了dll的名称,您不需要指定其完整路径,并且您可以保证将找到该DLL并且该函数将在其中找到。DLLImport已经处理了情况完美。