Search code examples
c#pinvokesspi

C# p/invoke Secur32.dll problems


I'm trying to wrap the Secur32.dll's EnumerateSecurityPackages function which is declared below:

SECURITY_STATUS SEC_Entry EnumerateSecurityPackages(
  __in  PULONG pcPackages,
  __in  PSecPkgInfo *ppPackageInfo
);

I have the following C# code, but I get an AccessViolationException when I try to run it. In the debugger the pcPackages variable does get set correctly, but I think I'm doing something wrong with the array of SecPkgInfos.

[StructLayout(LayoutKind.Sequential)]
public struct SecPkgInfo
{
    public ulong fCapabilities;
    public ushort wVersion;
    public ushort wRPCID;
    public ulong cbMaxToken;
    public string Name;
    public string Comment;
}

[DllImport("Secur32.dll")]
public extern static int EnumerateSecurityPackages(
    ref ulong pcPackages,
    ref SecPkgInfo[] ppPackageInfo
);

///Calling code
ulong count = 0;
SecPkgInfo[] buffer = new SecPkgInfo[256];
EnumerateSecurityPackages(ref count, ref buffer);

Any ideas what I'm doing wrong?


Solution

  • Try this code, its converted from VB.Net (my native language) but runs fine for me in C#. Just call Call_EnumerateSecurityPackages() and it will return a list for you.

        public static List<SecPkgInfo> Call_EnumerateSecurityPackages()
        {
            //Will hold the number of security packages found
            UInt32 count = 0;
    
            //Will hold a pointer to our array
            IntPtr SourcePoint = IntPtr.Zero;
    
            //Call function
            int MSG = EnumerateSecurityPackages(ref count, ref SourcePoint);
            //See if there was an error
            if (MSG == 0)
            {
                //Create a copy of our pointer so that we can clear it later
                IntPtr ArrayPtr = new IntPtr(SourcePoint.ToInt32());
                //The type of our structure
                Type T = typeof(SecPkgInfo);
                //The size of our structure
                int ObjSize = Marshal.SizeOf(T);
                //We'll store our information in a standard list object
                List<SecPkgInfo> SecPackages = new List<SecPkgInfo>();
    
                //Create a loop and increment our pointer by the size of the SecPkgInfo structure, effectively walking the array
                for (ulong I = 0; I <= (count - 1); I++)
                {
                    //This converts the current bytes at the pointer to the given structure
                    SecPackages.Add((SecPkgInfo)Marshal.PtrToStructure(ArrayPtr, T));
    
                    //Increment our pointer by the size of the structure
                    ArrayPtr = IntPtr.Add(ArrayPtr, ObjSize);
                }
    
                //Cleanup our pointer
                MSG = FreeContextBuffer(ref SourcePoint);
    
                //Make sure cleanup worked
                if (MSG == 0)
                {
                    //Return our values
                    return SecPackages;
                }
                else
                {
                    //Do something better with the error code here
                    throw new ApplicationException("Error cleaning up pointer");
                }
            }
            else
            {
                //Do something better with the error code here
                throw new ApplicationException("Error calling native function");
            }
    
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct SecPkgInfo
        {
            //ulong is 32 bit so we need to use a 32 bit int
            public UInt32 fCapabilities;
            //ushort is 16 bit 
            public UInt16 wVersion;
            public UInt16 wRPCID;
            public UInt32 cbMaxToken;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
            public string Name;
            [MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]
            public string Comment;
        }
    
        [DllImport("Secur32.dll")]
        public static extern int EnumerateSecurityPackages(ref UInt32 pcPackages, ref IntPtr ppPackageInfo);
    
        [DllImport("Secur32.dll")]
        public static extern int FreeContextBuffer(ref IntPtr pvContextBuffer);
    

    And for future use, here's the VB version:

    Public Shared Function Call_EnumerateSecurityPackages() As List(Of SecPkgInfo)
        ''//Will hold the number of security packages found
        Dim count As UInt32 = 0
    
        ''//Will hold a pointer to our array
        Dim SourcePoint As IntPtr
    
        ''//Call function
        Dim MSG = EnumerateSecurityPackages(count, SourcePoint)
        ''//See if there was an error
        If MSG = 0 Then
            ''//Create a copy of our pointer so that we can clear it later
            Dim ArrayPtr As New IntPtr(SourcePoint.ToInt32())
            ''//The type of our structure
            Dim T = GetType(SecPkgInfo)
            ''//The size of our structure
            Dim ObjSize = Marshal.SizeOf(T)
            ''//We will store our information in a standard list object
            Dim SecPackages As New List(Of SecPkgInfo)
    
            ''//Create a loop and increment our pointer by the size of the SecPkgInfo structure, effectively walking the array
            For I = 0 To (count - 1)
                ''//This converts the current bytes at the pointer to the given structure
                SecPackages.Add(CType(Marshal.PtrToStructure(ArrayPtr, T), SecPkgInfo))
                ''//Increment our pointer by the size of the structure
                ArrayPtr = IntPtr.Add(ArrayPtr, ObjSize)
            Next
    
            ''//Cleanup our pointer
            MSG = FreeContextBuffer(SourcePoint)
    
            ''//Make sure cleanup worked
            If MSG = 0 Then
                ''//Return our values
                Return SecPackages
            Else
                ''//Do something better with the error code here
                Throw New ApplicationException("Error cleaning up pointer")
            End If
        Else
            ''//Do something better with the error code here
            Throw New ApplicationException("Error calling native function")
        End If
    
    End Function
    
    <StructLayout(LayoutKind.Sequential)>
    Public Structure SecPkgInfo
        Public fCapabilities As UInt32 ''//ulong is 32 bit so we need to use a 32 bit int
        Public wVersion As UInt16 ''//ushort is 16 bit 
        Public wRPCID As UInt16
        Public cbMaxToken As UInt32
        <MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)> Public Name As String
        <MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)> Public Comment As String
    End Structure
    
    <DllImport("Secur32.dll")>
    Public Shared Function EnumerateSecurityPackages(ByRef pcPackages As UInt32, ByRef ppPackageInfo As IntPtr) As Integer
    End Function
    
    <DllImport("Secur32.dll")>
    Public Shared Function FreeContextBuffer(ByRef pvContextBuffer As IntPtr) As Integer
    End Function