Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 674
  • Last Modified:

How to call and extract data from netapi32.dll API call?

I am trying to make an API call to netapi32.dll to get domain info (for legacy workstations).

The call to the .dll appears successful, but I am unable to extract the data from the memory location. The CopyMemory call does not work. I pasted the orig vb6 code and what I have thus far with .Net. Any help would be appreciated.

I have made various attempts at using the Marshal methods with pointers, etc, but can't seem to get the data I need (always end up with 0 as the value). IE: create a pointer to the lpbuffer, attempt to convert pointer into structure always ends up with blank values.
'VB6 code
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pTo As Any, uFrom As Any, ByVal lSize As Long)
Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Long) As Long
 
Private Const NO_ERROR = 0&
 
Private Declare Function DsGetDcNameAWin5 Lib "netapi32.dll" Alias "DsGetDcNameA" ( _
   ByVal ComputerName As String, _
   ByVal DomainName As String, _
   DomainGuid As Any, _
   ByVal SiteName As String, _
   ByVal Flags As Long, _
   lpDomainControllerInfo As Long) As Long
 
Private Type GUID
   Data1 As Long
   Data2 As Integer
   Data3 As Integer
   Data4(0 To 7) As Byte
End Type
 
Private Type DOMAIN_CONTROLLER_INFO
   DomainControllerName As Long
   DomainControllerAddress As Long
   DomainControllerAddressType As Long
   DomainGuid As GUID
   DomainName As Long
   DnsForestName As Long
   Flags As Long
   DcSiteName As Long
   ClientSiteName As Long
End Type
 
Private Function SetDcNameA(Optional ByVal strDomain As String = vbNullString, Optional ByVal strComputer As String = vbNullString) As Boolean
    Dim lpBuffer As Long
    Dim dci As DOMAIN_CONTROLLER_INFO
 
    m_LastError = DsGetDcNameAWin5(strComputer, strDomain, ByVal 0&, vbNullString, m_Flags, lpBuffer)
    
    If m_LastError <> NO_ERROR Then
         GoTo ErrorHandler
    End If
   
    ' Recover structure into one we can deal with in VB
    Call CopyMemory(dci, ByVal lpBuffer, Len(dci))
    
    ' Transfer contents to member structure.
    m_DCI.DomainControllerName = PointerToStringA(dci.DomainControllerName)
        m_DCI.DomainControllerAddress = PointerToStringA(dci.DomainControllerAddress)
        m_DCI.DomainControllerAddressType = dci.DomainControllerAddressType
        m_DCI.DomainGuid = dci.DomainGuid
        m_DCI.DomainName = PointerToStringA(dci.DomainName)
        m_DCI.DnsForestName = PointerToStringA(dci.DnsForestName)
End Name
 
Private Function PointerToStringA(ByVal lpStringA As Long) As String
   Dim Buffer() As Byte
   Dim nLen As Long
   
On Error GoTo ErrorHandler
 
   If lpStringA Then
      nLen = lstrlenA(ByVal lpStringA)
      If nLen Then
         ReDim Buffer(0 To (nLen - 1)) As Byte
         CopyMemory Buffer(0), ByVal lpStringA, nLen
         PointerToStringA = StrConv(Buffer, vbUnicode)
      End If
   End If
   Exit Function
   
ErrorHandler:
    PointerToStringA = ""
End Function
 
 
'.Net code
Private Const NO_ERROR As Short = 0
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByRef pTo As Integer, ByRef uFrom As Integer, ByVal lSize As Integer)
Private Declare Function lstrlenA Lib "kernel32" (ByVal lpString As Integer) As Integer
Private Declare Function DsGetDcNameAWin5 Lib "netapi32.dll" Alias "DsGetDcNameA" (ByVal ComputerName As String, ByVal DomainName As String, ByRef DomainGuid As GUID, ByVal SiteName As String, ByVal Flags As Integer, ByRef lpDomainControllerInfo As Integer) As Integer
 
    Private Structure GUID
        Dim Data1 As Integer
        Dim Data2 As Short
        Dim Data3 As Short
        <VBFixedArray(7)> Dim Data4() As Byte
 
        Public Sub Initialize()
            ReDim Data4(7)
        End Sub
    End Structure
 
    Private Structure DOMAIN_CONTROLLER_INFO
        Dim DomainControllerName As Integer
        Dim DomainControllerAddress As Integer
        Dim DomainControllerAddressType As Integer
        Dim DomainGuid As GUID
        Dim DomainName As Integer
        Dim DnsForestName As Integer
        Dim Flags As Integer
        Dim DcSiteName As Integer
        Dim ClientSiteName As Integer
 
        Public Sub Initialize()
            DomainGuid.Initialize()
        End Sub
    End Structure
 
    Private Function SetDcNameA(Optional ByVal strDomain As String = vbNullString, Optional ByVal strComputer As String = vbNullString) As Boolean
        Dim lpBuffer As Integer
        Dim dci As DOMAIN_CONTROLLER_INFO
        dci.DomainGuid.Initialize()
 
        m_LastError = DsGetDcNameAWin5(strComputer, strDomain, dci.DomainGuid, vbNullString, m_Flags, lpBuffer)
 
        If m_LastError <> NO_ERROR Then
            GoTo ErrorHandler
        End If
 
        ' Recover structure into one we can deal with in VB
        Call CopyMemory(dci, lpBuffer, Len(dci)) 'Doesn't compile
 
        ' Transfer contents to member structure.
        m_DCI.DomainControllerName = PointerToStringA(dci.DomainControllerName)
        m_DCI.DomainControllerAddress = PointerToStringA(dci.DomainControllerAddress)
        m_DCI.DomainControllerAddressType = dci.DomainControllerAddressType
        m_DCI.DomainGuid = dci.DomainGuid
        m_DCI.DomainName = PointerToStringA(dci.DomainName)
        m_DCI.DnsForestName = PointerToStringA(dci.DnsForestName)
End Sub
 
    Private Function PointerToStringA(ByVal lpStringA As Integer) As String
        Dim Buffer() As Byte
        Dim nLen As Integer
 
        On Error GoTo ErrorHandler
 
        If CBool(lpStringA) Then
            nLen = lstrlenA(lpStringA)
            If CBool(nLen) Then
                ReDim Buffer((nLen - 1))
                CopyMemory(Buffer(0), lpStringA, nLen)
                PointerToStringA = StrConv(System.Text.UnicodeEncoding.Unicode.GetString(Buffer), VbStrConv.None)
            End If
        End If
        Exit Function
 
ErrorHandler:
        PointerToStringA = ""
    End Function

Open in new window

0
awp5379
Asked:
awp5379
  • 3
1 Solution
 
GHCS_MarkCommented:
Question, why are you making a direct NetAPI32 call when you can using the Environment object to get the information you need?
Dim strDomainName as String = Environment.UserDomianName.ToString()
Dim strUserName as String = Environment.UserName.ToString()

Open in new window

0
 
awp5379Author Commented:
Because the environment namespace doesn't provide: DomainControllerName, DomainControllerAddress, DomainControllerAddressType, DNSForestName, DCSiteName, and ClientSiteName.

DirectoryServices doesn't work on older OSs like Win98, hence api32 is still needed.
0
 
GHCS_MarkCommented:
Give the following a try... I am a bit rusty on whether the LONG's should be LONG's or Integer's, but I've assumed LONG's for now.
    <StructLayout(LayoutKind.Sequential)> _
    Structure DOMAIN_CONTROLLER_INFO
        <MarshalAs(UnmanagedType.LPTStr)> Public DomainControllerName As String
        <MarshalAs(UnmanagedType.LPTStr)> Public DomainControllerAddress As String
        Public DomainControllerAddressType As Long
        Public DomainGuid As Guid
        <MarshalAs(UnmanagedType.LPTStr)> _
        Public DomainName As String
        <MarshalAs(UnmanagedType.LPTStr)> _
        Public DnsForestName As String
        Public Flags As Long
        <MarshalAs(UnmanagedType.LPTStr)> _
        Public DcSiteName As String
        <MarshalAs(UnmanagedType.LPTStr)> _
        Public ClientSiteName As String
    End Structure
 
    Public Declare Function DsGetDcName Lib "NetApi32" Alias "DsGetDcNameA" ( _
    <MarshalAs(UnmanagedType.LPTStr)> ByVal _
    ComputerName As String, _
    <MarshalAs(UnmanagedType.LPTStr)> ByVal _
    DomainName As String, _
    ByVal DomainGuid As Long, _
    <MarshalAs(UnmanagedType.LPTStr)> ByVal _
    SiteName As String, _
    ByVal Flags As Long, _
    ByRef pDOMAIN_CONTROLLER_INFO As IntPtr) As Integer
 
    Public Declare Function NetApiBufferFree Lib "NetApi32.dll" Alias "NetApiBufferFree" (ByVal Buffer As IntPtr) As Long
 
    Public Enum DSGETDCNAME_FLAGS As Long
        DS_FORCE_REDISCOVERY = &H1
        DS_DIRECTORY_SERVICE_REQUIRED = &H10
        DS_DIRECTORY_SERVICE_PREFERRED = &H20
        DS_GC_SERVER_REQUIRED = &H40
        DS_PDC_REQUIRED = &H80
        DS_BACKGROUND_ONLY = &H100
        DS_IP_REQUIRED = &H200
        DS_KDC_REQUIRED = &H400
        DS_TIMESERV_REQUIRED = &H800
        DS_WRITABLE_REQUIRED = &H1000
        DS_GOOD_TIMESERV_PREFERRED = &H2000
        DS_AVOID_SELF = &H4000
        DS_ONLY_LDAP_NEEDED = &H8000
        DS_IS_FLAT_NAME = &H10000
        DS_IS_DNS_NAME = &H20000
        DS_RETURN_DNS_NAME = &H40000000
        'DS_RETURN_FLAT_NAME = &H80000000
    End Enum
 
    Public Function GetDomainInfo() As DOMAIN_CONTROLLER_INFO
        Dim domainInfo As DOMAIN_CONTROLLER_INFO
        Const ERROR_SUCCESS As Integer = 0
 
        Dim pDCI As IntPtr = IntPtr.Zero
        Try    
            Dim retval As Integer = DsGetDcName("", "", 0, "", _
                Convert.ToUInt32(DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED Or _
                    DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME Or _
                    DSGETDCNAME_FLAGS.DS_IP_REQUIRED), pDCI)
            'check return value for error
            If (ERROR_SUCCESS = retval) Then
                domainInfo = CType(Marshal.PtrToStructure(pDCI, GetType(DOMAIN_CONTROLLER_INFO)), DOMAIN_CONTROLLER_INFO)
 
                Dim msg As String = "Forest : " + domainInfo.DnsForestName + vbCrLf
                msg += "DC-Site: " + domainInfo.DomainControllerName + vbCrLf
                msg += " Client: " + domainInfo.ClientSiteName + vbCrLf
                System.Windows.Forms.MessageBox.Show(msg)        
            Else
                Throw New System.ComponentModel.Win32Exception(retval)
            End If
        Finally
            NetApiBufferFree(pDCI)
        End Try
        Return domainInfo
    End Function

Open in new window

0
 
GHCS_MarkCommented:
I commented out the 'DS_RETURN_FLAT_NAME = &H80000000 as it was failing when the type was ULong, but can be uncommented if you leave them as Long's in the example I've pasted.

Once you have the example working, you can modify your existing code to match.
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now