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 marshalling 是 UmanagedType.LPTStr
,它依赖于平台并解析为 LPStr
或 LPWStr
。在你的平台上,它被解析为 LPStr
,我假设这是因为默认的 CharSet
是 CharSet.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
已经处理了情况完美。
所以我注意到一些很奇怪的事情我没能解决。我确定这只是一个愚蠢的编码错误,但尝试对结果进行不同的编码对我也没有帮助。
问题:
当我通过 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 marshalling 是 UmanagedType.LPTStr
,它依赖于平台并解析为 LPStr
或 LPWStr
。在你的平台上,它被解析为 LPStr
,我假设这是因为默认的 CharSet
是 CharSet.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
已经处理了情况完美。