代码之家  ›  专栏  ›  技术社区  ›  Iridium

仅在Vista/Windows Server 2008上P/Invoke错误

  •  3
  • Iridium  · 技术社区  · 14 年前

    我有通过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上没有(明显的)问题,以及是否有任何方法修复类返回方法以避免更糟糕的显式编组。

    2 回复  |  直到 14 年前
        1
  •  3
  •   JSBÕ±Õ¸Õ£Õ¹    14 年前

    使用 Marshal.PtrToStructure 对的 要做的事。事实上,这曾经在xp/server 2003上工作过,这完全是巧合,因为代码总是有问题的。

        2
  •  3
  •   Hans Passant    14 年前

    编辑:我没有意识到这是一个API函数。不能声明返回结构的函数。p/invoke封送拆收器将使用cotaskmemfree()释放结构的内存。不起作用,它没有用cotaskmemalloc()分配。

    XP和2003中的堆管理器可以原谅,它只是忽略了错误的发布请求。但不是Vista和2008中的程序,它会轰炸程序,因为它显然错误地使用了内存。重要的是,这些记忆恶作剧都是安全问题。

    是的,通过将返回类型声明为intptr,可以避免p/invoke封送拆收器释放内存。这是适当的,API函数真的 返回指针,而不是结构。Marshal.ptrtoStructure需要将指向结构封送到托管结构。

    安全API一般都是这样麻烦的。安全API将指针返回到内部内核安全结构的概念确实让人有点困惑…