仅在 Vista/Windows Server 2008 上出现 P/Invoke 错误
我的代码通过 P/Invoke 使用 SSPI dll (security.dll) 中的方法,该代码在每个测试平台(Windows XP、Windows Server 2003 x86 和 x64)上都能完美运行,但是我们正在迁移到Windows Server 2008,并发现 P/Invoke 调用导致进程崩溃。
我整理了以下重现代码:
using System;
using System.Runtime.InteropServices;
namespace TestPInvoke
{
class Program
{
static void Main(string[] args)
{
try
{
// The following code works on all platforms tested (Windows Server 2003 x86/x64, Windows Server 2008 x64, Windows XP, Windows Vista)
var table1 = (SecurityFunctionTable)Marshal.PtrToStructure(InitReturningPtr(), typeof(SecurityFunctionTable));
Console.WriteLine(table1.dwVersion);
Console.WriteLine(table1.EnumerateSecurityPackages.ToInt64().ToString("x16"));
Console.ReadLine();
// This call crashes only on Windows Server 2008 and Windows Vista (but works fine on XP and 2K3)
var table2 = InitReturningClass();
Console.WriteLine(table2.dwVersion);
Console.WriteLine(table2.EnumerateSecurityPackages.ToInt64().ToString("x16"));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
Console.ReadLine();
}
[DllImport("security.dll", EntryPoint = "InitSecurityInterfaceW")]
public static extern IntPtr InitReturningPtr();
[DllImport("security.dll", EntryPoint = "InitSecurityInterfaceW")]
public static extern SecurityFunctionTable InitReturningClass();
[StructLayout(LayoutKind.Sequential)]
public class SecurityFunctionTable
{
public uint dwVersion;
public IntPtr EnumerateSecurityPackages;
public IntPtr QueryCredentialsAttributes;
// ...omitted for brevity
}
}
}
问题也不是孤立于这个特定的 DLL 或函数,而是我尝试过的任何 P/Invoke 调用,其中本机函数的返回值是指向隐式编组的结构的指针进入一个班级展示这个问题。
由于我有一个功能解决方法(使用 Marshal.PtrToStructure),这不是一个主要问题,但我很好奇为什么它在 XP 和 2k3 上没有(明显的)问题,但在 Vista 和 2k8 上却没有问题,以及是否有任何修复类返回方法以避免相当丑陋的显式编组的方法。
I have code that uses methods from the SSPI dll (security.dll) via P/Invoke, which has worked perfectly on every platform tested (Windows XP, Windows Server 2003 both x86 and x64), however we are in the process of migrating to Windows Server 2008, and have found that the P/Invoke calls are crashing the process.
I have put together the following reproduce code:
using System;
using System.Runtime.InteropServices;
namespace TestPInvoke
{
class Program
{
static void Main(string[] args)
{
try
{
// The following code works on all platforms tested (Windows Server 2003 x86/x64, Windows Server 2008 x64, Windows XP, Windows Vista)
var table1 = (SecurityFunctionTable)Marshal.PtrToStructure(InitReturningPtr(), typeof(SecurityFunctionTable));
Console.WriteLine(table1.dwVersion);
Console.WriteLine(table1.EnumerateSecurityPackages.ToInt64().ToString("x16"));
Console.ReadLine();
// This call crashes only on Windows Server 2008 and Windows Vista (but works fine on XP and 2K3)
var table2 = InitReturningClass();
Console.WriteLine(table2.dwVersion);
Console.WriteLine(table2.EnumerateSecurityPackages.ToInt64().ToString("x16"));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.StackTrace);
}
Console.ReadLine();
}
[DllImport("security.dll", EntryPoint = "InitSecurityInterfaceW")]
public static extern IntPtr InitReturningPtr();
[DllImport("security.dll", EntryPoint = "InitSecurityInterfaceW")]
public static extern SecurityFunctionTable InitReturningClass();
[StructLayout(LayoutKind.Sequential)]
public class SecurityFunctionTable
{
public uint dwVersion;
public IntPtr EnumerateSecurityPackages;
public IntPtr QueryCredentialsAttributes;
// ...omitted for brevity
}
}
}
The problem is not isolated to this particular DLL or function either, but any P/Invoke call I've tried where the return value of the native function is a pointer to a structure which is implicitly marshalled into a class exhibits the issue.
Since I have a functional workaround (using Marshal.PtrToStructure) this isn't a major problem, but I am curious to know why it works with no (apparent) issues on XP and 2k3, but not Vista and 2k8, and whether there is any way to fix the class-returning method to avoid the rather uglier explicit marshalling.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用
Marshal.PtrToStructure
是正确的做法。事实上,这在 XP/Server 2003 上运行纯属巧合,因为代码总是有错误。Using
Marshal.PtrToStructure
is the correct thing to do. The fact that this ever worked on XP/Server 2003 is pure coincidence, as the code has always been buggy.编辑:我没有意识到这是一个API函数。您不能声明返回结构的函数。 P/Invoke 编组器将使用 CoTaskMemFree() 释放该结构的内存。不起作用,它不是用 CoTaskMemAlloc() 分配的。
XP和2003中的堆管理器是宽容的,它只是忽略错误的释放请求。但不是 Vista 和 2008 中的那个,它会轰炸程序,因为它显然不正确地使用内存。重要的是,这些内存恶作剧是安全问题。
是的,通过将返回类型声明为 IntPtr,可以避免 P/Invoke 编组器释放内存。这是合适的,API 函数确实返回一个指针,而不是一个结构。需要 Marshal.PtrToStructure 将指向的结构编组为托管结构。
安全API一般都是这样麻烦的。安全 API 返回指向内部内核安全结构的指针的概念确实有点令人难以置信......
Edit: I didn't realize it is an API function. You cannot declare the function returning a structure. The P/Invoke marshaller will deallocate the memory for the structure with CoTaskMemFree(). Doesn't work, it wasn't allocated with CoTaskMemAlloc().
The heap manager in XP and 2003 is forgiving, it just ignores the faulty release request. But not the one in Vista and 2008, it bombs the program because it obviously is using memory incorrectly. Important because these kind of memory shenanigans are security problems.
Yes, by declaring the return type as IntPtr, you avoid the P/Invoke marshaller releasing the memory. Which is appropriate, the API function really does return a pointer, not a structure. Marshal.PtrToStructure is required to marshal the pointed-to structure to a managed struct.
The security APIs are in general troublesome like this. The notion of a security API returning a pointer into internal kernel security structure does boggle the mind a bit...