WinAPI CredEnumerate Marshal.PtrToStructure异常

时间:2017-05-10 20:38:52

标签: vb.net winapi

我在使用WinAPI函数从VB.Net应用程序枚举Windows凭据时遇到问题。我的代码如下。

<DllImport("advapi32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Private Shared Function CredEnumerate(filter As String, flag As Integer, ByRef count As Integer, ByRef pCredentials As IntPtr) As Boolean
End Function

Public Enum CRED_PERSIST As UInteger
    SESSION = 1
    LOCAL_MACHINE = 2
    ENTERPRISE = 3
End Enum

Public Enum CRED_TYPE As UInteger
    GENERIC = 1
    DOMAIN_PASSWORD = 2
    DOMAIN_CERTIFICATE = 3
    DOMAIN_VISIBLE_PASSWORD = 4
    GENERIC_CERTIFICATE = 5
    DOMAIN_EXTENDED = 6
    MAXIMUM = 7
    ' Maximum supported cred type
    MAXIMUM_EX = (MAXIMUM + 1000)
    ' Allow new applications to run on old OSes
End Enum

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
Public Structure CREDENTIAL_ATTRIBUTE
    Private Keyword As String
    Private Flags As UInteger
    Private ValueSize As UInteger
    Private Value As IntPtr
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Private Class Credential
    Public Flags As UInt32
    Public Type As CRED_TYPE
    Public TargetName As String
    Public Comment As String
    Public LastWritten As ComTypes.FILETIME
    Public CredentialBlobSize As UInt32
    Public CredentialBlob As IntPtr
    Public Persist As CRED_PERSIST
    Public AttributeCount As UInt32
    Public Attributes As IntPtr
    Public TargetAlias As String
    Public UserName As String
End Class


Private Function GetCredentials() As Credential()
    Dim count As Integer = 0
    Dim pCredentials As IntPtr = IntPtr.Zero
    Dim credentials As List(Of Credential) = New List(Of Credential)
    Dim ret As Boolean = CredEnumerate(Nothing, 0, count, pCredentials)
    If ret <> False Then
        Dim p As IntPtr = pCredentials
        For n As Integer = 0 To count - 1
            If Marshal.SizeOf(p) = 4 Then
                p = New IntPtr(p.ToInt32() + n)
            Else
                p = New IntPtr(p.ToInt64() + n)
            End If
            credentials.Add(Marshal.PtrToStructure(Marshal.ReadIntPtr(p), GetType(Credential)))
        Next
    End If
    Return credentials.ToArray
End Function

Marshal.PtrToStructure函数会抛出System.ExecetionEngineException而没有任何有用的信息。我怀疑凭证结构错误但对我来说似乎是对的。如果您对错误有任何疑问,我会等待您的回答。 感谢

编辑:感谢@Zaggler这里是我更正的功能,现在它为数组添加了凭据,但整个结构是空的。 这是新功能。

Private Function GetCredentials() As Credential()
    Dim count As Integer = 0
    Dim pCredentials As IntPtr = IntPtr.Zero
    Dim credentials As List(Of Credential) = New List(Of Credential)

    Dim ret As Boolean = CredEnumerate(Nothing, 0, count, pCredentials)
    If ret <> False Then
        Dim p As IntPtr = pCredentials
        For n As Integer = 0 To count - 1
            Dim cred As Credential = New Credential
            Dim pnt As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(cred))
            Try

                If Marshal.SizeOf(p) = 4 Then
                    p = New IntPtr(p.ToInt32() + n)
                Else
                    p = New IntPtr(p.ToInt64() + n)
                End If
                Marshal.StructureToPtr(cred, pnt, False)
                credentials.Add(Marshal.PtrToStructure(pnt, GetType(Credential)))

            Finally
                Marshal.FreeHGlobal(pnt)
            End Try
        Next
    End If
    Return credentials.ToArray
End Function

1 个答案:

答案 0 :(得分:1)

你的第一次尝试更好。不要使用AllocHGlobal和FreeHGlobal。 Winapi正在分配内存。 Lookup CredFree释放已分配的内存(在编组结构后)。

指针算术有误。你必须用指针大小递增,所以试试:

...
Dim p As IntPtr = pCredentials
For n As Integer = 0 To count - 1
  credentials.Add(Marshal.PtrToStructure(Marshal.ReadIntPtr(p), GetType(Credential)))
  p = p + IntPtr.Size
Next
...

UInt32和UInteger是相同的,所以要保持一致并选择一个。

您可以尝试将Charset.Auto用于所有内容,如果这不起作用,请尝试使用Charset.Unicode并使用CredEnumerateW函数。