P /仅在Vista / Windows Server 2008上调用错误

时间:2010-07-19 19:40:17

标签: c# windows-server-2008 pinvoke

我的代码使用来自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,以及是否有任何方法可以修复类返回方法,以避免相当丑陋的显式编组。

2 个答案:

答案 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的概念确实让人头脑发昏......