P / вызывать ошибки только в Vista / Windows Server 2008 - PullRequest
3 голосов
/ 19 июля 2010

У меня есть код, который использует методы из библиотеки SSPI (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 ]

3 голосов
/ 20 июля 2010

Редактировать: я не понял, что это функция API. Вы не можете объявить функцию, возвращающую структуру. Маршаллер P / Invoke освобождает память для структуры с помощью CoTaskMemFree (). Не работает, он не был выделен с помощью CoTaskMemAlloc ().

Менеджер кучи в XP и 2003 простит, он просто игнорирует ошибочный запрос на выпуск. Но не та, что в Vista и 2008, она бомбит программу, потому что она явно использует память неправильно. Важно, потому что такого рода махинации с памятью являются проблемами безопасности.

Да, объявив тип возвращаемого значения как IntPtr, вы избежите, чтобы маршаллер P / Invoke освободил память. Что уместно, функция API действительно действительно возвращает указатель, а не структуру. Marshal.PtrToStructure требуется для преобразования указанной структуры в управляемую структуру.

API безопасности, как правило, такие неприятные. Идея API-интерфейса безопасности, возвращающего указатель на внутреннюю структуру безопасности ядра, немного сбивает с толку ...

3 голосов
/ 19 июля 2010

Использование Marshal.PtrToStructure - это правильный выбор .Тот факт, что это когда-либо работало на XP / Server 2003, является простым совпадением, так как код всегда был с ошибками.

...