我的代码使用来自SSPI dll(security.dll)的方法通过P / Invoke,它在所有测试平台(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,以及是否有任何方法可以修复类返回方法,以避免相当丑陋的显式编组。
答案 0 :(得分:3)
使用Marshal.PtrToStructure
是正确的要做的事情。这个曾经在XP / Server 2003上运行的事实纯属巧合,因为代码一直都是错误的。
答案 1 :(得分:3)
编辑:我没有意识到它是一个API函数。您不能声明返回结构的函数。 P / Invoke marshaller将使用CoTaskMemFree()为结构释放内存。不起作用,它没有用CoTaskMemAlloc()分配。
XP和2003中的堆管理器是宽容的,它只是忽略了错误的释放请求。但不是Vista和2008中的那个,它轰炸了程序,因为它显然使用了错误的内存。重要的是因为这些记忆诡计是安全问题。
是的,通过将返回类型声明为IntPtr,可以避免P / Invoke编组器释放内存。哪个合适,API函数确实 返回指针,而不是结构。 Marshal.PtrToStructure需要将指向的结构封送到托管结构。
安全API通常会像这样麻烦。将指针返回到内部内核安全结构的安全API的概念确实让人头脑发昏......