Solved

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

Posted on 2008-06-24
4
609 Views
Last Modified: 2008-07-01
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
Comment
Question by:awp5379
  • 3
4 Comments
 
LVL 3

Expert Comment

by:GHCS_Mark
ID: 21858576
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
 

Author Comment

by:awp5379
ID: 21858635
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
 
LVL 3

Expert Comment

by:GHCS_Mark
ID: 21859056
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
 
LVL 3

Accepted Solution

by:
GHCS_Mark earned 500 total points
ID: 21859076
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

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

When trying to find the cause of a problem in VBA or VB6 it's often valuable to know what procedures were executed prior to the error. You can use the Call Stack for that but it is often inadequate because it may show procedures you aren't intereste…
Since upgrading to Office 2013 or higher installing the Smart Indenter addin will fail. This article will explain how to install it so it will work regardless of the Office version installed.
Get people started with the process of using Access VBA to control Excel using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Excel. Using automation, an Access application can laun…
This lesson covers basic error handling code in Microsoft Excel using VBA. This is the first lesson in a 3-part series that uses code to loop through an Excel spreadsheet in VBA and then fix errors, taking advantage of error handling code. This l…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now