c# Pinvoke with StringBuilder returns 同一方法的不同结果

c# Pinvoke with StringBuilder returns different results for the same method

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

问题:

当我通过 DLLImport

从 Shell32.dll 调用函数 "PathYetAnotherMakeUniqueName" 时
[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 属性或将 stringbuilder 的内容编码为 ASCII Unicode 或 UTF7、8、32 等,但输出没有改变。 不使用 MarshalAs(这似乎适用于其他一些需要 StringBuilder 的方法),我得到一个堆栈不平衡异常。

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

祝你有愉快的一天,提前致谢

您的输入字符串似乎被编组为 LPStr。字符串的 default marshallingUmanagedType.LPTStr,它依赖于平台并解析为 LPStrLPWStr。在你的平台上,它被解析为 LPStr,我假设这是因为默认的 CharSetCharSet.ANSI,并且你没有为手动加载的函数指定一个不同的函数(会欢迎权威确认)。

将输入参数显式指定为 [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);

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

更直接的方法 是在声明委托时使用 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会被找到,函数会在in里面找到。DLLImport已经处理了情况完美。