DHCP with P/Invoke

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Richard

    DHCP with P/Invoke

    Hello,

    I'm working on an application to allow our network team to use a small
    application to make DHCP reservations on our Microsoft DHCP Server.

    The problem is you have to use P/Invoke to do it, and from what I've
    found on the web and in this newsgroup is that it's not easy, however I
    believe it can/has been done.

    At the moment I'm just trying to find out information about an existing
    reservation but I'm getting stuck. I read the newsgroup article
    'P/Invoke with DHCP management API' which got me started but the last
    comment made on 2006-01-01 didn't make sense to me and I can't get the
    damn thing to work, in my test console app.

    Here is the code I am using, could someone point me in the right
    direction please?:

    using System;
    using System.Collecti ons.Generic;
    using System.Text;
    using System.Runtime. InteropServices ;

    namespace DHCP
    {
    class Program
    {
    static void Main(string[] args)
    {
    String ServerIpAddress = "192.168.0.250" ;

    UInt32 DHCPResult = 0;
    try
    {
    DhcpApi.DHCP_SE ARCH_INFO searchInfo = new
    DhcpApi.DHCP_SE ARCH_INFO();
    DhcpApi.DHCP_SE ARCH_INFO_TYPE searchInfoType =
    DhcpApi.DHCP_SE ARCH_INFO_TYPE. DhcpClientIpAdd ress;

    searchInfo.Sear chType = searchInfoType;
    searchInfo.Clie ntIpAddress =
    DhcpApi.Convert IPAddress("192. 168.0.5");

    DhcpApi.DHCP_CL IENT_INFO clientInfo = new
    DhcpApi.DHCP_CL IENT_INFO();

    DHCPResult = DhcpApi.DhcpGet ClientInfo(Serv erIpAddress,
    ref searchInfo, ref clientInfo);
    Console.WriteLi ne(DHCPResult.T oString() + " Client
    Info: " + clientInfo.Clie ntName);
    }
    catch (Exception ex)
    {

    Console.WriteLi ne("*********** *************** *************** *************** *************** *****");
    Console.WriteLi ne("ERROR: \t" + DHCPResult.ToSt ring());
    Console.WriteLi ne("MESSAGE: \t" + ex.Message);
    Console.WriteLi ne();
    Console.WriteLi ne("FULL DETAILS:");
    Console.WriteLi ne(ex.ToString( ));

    Console.WriteLi ne("*********** *************** *************** *************** *************** *****");
    }

    Console.ReadKey (true);
    }
    }

    public class DhcpApi
    {
    /// <summary>
    /// The DhcpGetClientIn fo function returns information about a
    specific DHCP client.
    /// </summary>
    /// <param name="ServerIpA ddress">[in] Unicode string that
    specifies the IP address of the DHCP server.</param>
    /// <param name="SearchInf o">[in] DHCP_SEARCH_INF O structure
    that contains the parameters for the search. </param>
    /// <param name="ClientInf o">[out] Pointer to a
    DHCP_CLIENT_INF O structure that contains information describing the DHCP
    client that most closely matches the provided search parameters. If no
    client is found, this parameter will be null.</param>
    /// <returns>This function returns ERROR_SUCCESS upon a
    successful call. Otherwise, it returns one of the DHCP Server Management
    API Error Codes.</returns>
    [DllImport("dhcp sapi.dll", SetLastError = true,CharSet =
    CharSet.Unicode )]
    public static extern UInt32
    DhcpGetClientIn fo([MarshalAs(Unman agedType.LPWStr )]String
    ServerIpAddress , ref DHCP_SEARCH_INF O SearchInfo, ref DHCP_CLIENT_INF O
    ClientInfo);

    /// <summary>
    /// The DHCP_SEARCH_INF O_TYPE enumeration defines the set of
    possible
    /// attributes used to search DHCP client information records.
    /// </summary>
    public enum DHCP_SEARCH_INF O_TYPE : int
    {
    /// <summary>
    /// The search will be performed against the assigned DHCP
    client IP address, represented as a 32-bit unsigned integer value.
    /// </summary>
    DhcpClientIpAdd ress = 0,
    /// <summary>
    /// The search will be performed against the MAC address of
    the DHCP client network interface device, represented as a
    DHCP_BINARY_DAT A structure.
    /// </summary>
    DhcpClientHardw areAddress = 1,
    /// <summary>
    /// The search will be performed again the DHCP client's
    network name, represented as a Unicode string.
    /// </summary>
    DhcpClientName = 2
    }

    /// <summary>
    /// The DHCP_SEARCH_INF O structure defines the DHCP client record
    /// data used to search against for particular server operations.
    /// </summary>
    [StructLayout(La youtKind.Sequen tial, CharSet = CharSet.Unicode )]
    public struct DHCP_SEARCH_INF O
    {
    // typedef struct _DHCP_CLIENT_SE ARCH_INFO {
    // DHCP_SEARCH_INF O_TYPE SearchType;
    // union {
    // DHCP_IP_ADDRESS ClientIpAddress ;
    // DHCP_CLIENT_UID ClientHardwareA ddress;
    // LPWSTR ClientName;
    // } SearchInfo;
    //} DHCP_SEARCH_INF O, *LPDHCP_SEARCH_ INFO;

    public DHCP_SEARCH_INF O_TYPE SearchType;
    public UInt32 ClientIpAddress ;
    }
    /// <summary>
    /// The DHCP_CLIENT_INF O structure defines a client information
    record
    /// used by the DHCP server.
    /// </summary>
    [StructLayout(La youtKind.Sequen tial, CharSet = CharSet.Unicode )]
    public struct DHCP_CLIENT_INF O
    {
    //
    *************** *************** *************** *************** *************** *
    // DHCP_IP_ADDRESS Specifies an IP address, with the first
    byte containing
    // the first number in the address, the second byte
    containing the second
    // number, the third byte containing the third number, and
    the last byte
    // containing the last number in the address. For example,
    the address
    // 192.1.1.10 is represented as 11000000 00000001 00000001
    00001010 (binary),
    // or 3221291274 (decimal).
    //
    *************** *************** *************** *************** *************** *
    public UInt32 ClientIpAddress ;
    public UInt32 SubnetMask;
    public DHCP_BINARY_DAT A ClientHardwareA ddress;
    [MarshalAs(Unman agedType.LPWStr )]
    public String ClientName;
    [MarshalAs(Unman agedType.LPWStr )]
    public String ClientComment;
    public DATE_TIME ClientLeaseExpi res;
    public DHCP_HOST_INFO OwnerHost;
    }
    }
    }


    I'm also hoping to implement the following API functions in the future,
    if anyone has any pointers on these as well! ;o):

    DhcpGetClientIn fo
    DhcpDeleteClien tInfo
    DhcpCreateClien tInfo
    DhcpSetClientIn fo
    DhcpEnumSubnetC lients

    Cheers,

    Richard W.
    --
    Google First. Ask Later.
  • Mattias Sjögren

    #2
    Re: DHCP with P/Invoke

    Richard,
    >At the moment I'm just trying to find out information about an existing
    >reservation but I'm getting stuck. I read the newsgroup article
    >'P/Invoke with DHCP management API' which got me started but the last
    >comment made on 2006-01-01 didn't make sense to me and I can't get the
    >damn thing to work, in my test console app.
    What he's saying is that you have to change the method signature to

    public static extern UInt32 DhcpGetClientIn fo(String ServerIpAddress ,
    ref DHCP_SEARCH_INF O SearchInfo, out IntPtr ClientInfo);

    and then make the calling code

    IntPtr pClientInfo;
    DHCPResult = DhcpApi.DhcpGet ClientInfo(Serv erIpAddress, ref
    searchInfo, out pClientInfo);
    if (DHCPResult == 0 && pClientInfo != IntPtr.Zero)
    {
    DHCP_CLIENT_INF O clientInfo =
    (DHCP_CLIENT_IN FO)Marshal.PtrT oStructure(pCli entInfo,
    typeof(DHCP_CLI ENT_INFO));
    ...
    }



    Mattias

    --
    Mattias Sjögren [C# MVP] mattias @ mvps.org
    http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
    Please reply only to the newsgroup.

    Comment

    • Richard

      #3
      Re: DHCP with P/Invoke

      Mattias,
      What he's saying is that you have to change the method signature to
      >
      public static extern UInt32 DhcpGetClientIn fo(String ServerIpAddress ,
      ref DHCP_SEARCH_INF O SearchInfo, out IntPtr ClientInfo);
      >
      and then make the calling code
      >
      IntPtr pClientInfo;
      DHCPResult = DhcpApi.DhcpGet ClientInfo(Serv erIpAddress, ref
      searchInfo, out pClientInfo);
      if (DHCPResult == 0 && pClientInfo != IntPtr.Zero)
      {
      DHCP_CLIENT_INF O clientInfo =
      (DHCP_CLIENT_IN FO)Marshal.PtrT oStructure(pCli entInfo,
      typeof(DHCP_CLI ENT_INFO));
      ...
      }
      Thank you very much, that's got it working a treat. :o)

      I can move on now, I'm sure I'll have more questions about this how
      topic so watch this thread people!! ;o)

      Cheers,

      Richard

      Comment

      • Richard

        #4
        Re: DHCP with P/Invoke

        Mattias,

        You mention in the thread 'Problem Creating Structures with P/Invoke'
        that when using the struct DHCP_IP_ARRAY that you should dereference the
        pointer to retrieve the addresses with Marshal.Copy().

        I've been trying all morning to do it, but can't seem to get my head
        round it. Could you offer some assistance again please? I get the code
        below working but I'm trying to retrieve a list of the subnets returned
        not just the number.

        ....
        {
        DHCP_IP_ARRAY ips = new DHCP_IP_ARRAY() ;
        uint nr = 0, total = 0, resumeHandle = 0;

        DhcpEnumSubnets ("192.168.0.250 ", ref resumeHandle, 1000, ref ips,
        ref nr, ref total);

        Console.WriteLi ne("Elements read {0} of total {1}", nr, total);
        }

        [StructLayout(La youtKind.Sequen tial)]
        public struct DHCP_IP_ARRAY
        {
        public uint NumElements;
        public IntPtr IPAddresses;
        }

        [DllImport("dhcp sapi.dll", SetLastError = true, CharSet = CharSet.Unicode )]
        public static extern uint DhcpEnumSubnets (
        string ServerIP,
        ref uint resumeHandle,
        uint PerferedMax,
        ref DHCP_IP_ARRAY ipAddresses,
        ref uint ElementsRead,
        ref uint ElementsTotal);

        Cheers,

        Richard

        Comment

        • Mattias Sjögren

          #5
          Re: DHCP with P/Invoke

          Richard,
          >You mention in the thread 'Problem Creating Structures with P/Invoke'
          >that when using the struct DHCP_IP_ARRAY that you should dereference the
          >pointer to retrieve the addresses with Marshal.Copy().
          >
          >I've been trying all morning to do it, but can't seem to get my head
          >round it. Could you offer some assistance again please?
          Looks like it's pretty much the same issue as in your last post. The
          parameter is actually a pointer to a pointer to the struct. So try it
          like this:
          >[DllImport("dhcp sapi.dll", SetLastError = true, CharSet = CharSet.Unicode )]
          >public static extern uint DhcpEnumSubnets (
          string ServerIP,
          ref uint resumeHandle,
          uint PerferedMax,
          out IntPtr ipAddresses,
          ref uint ElementsRead,
          ref uint ElementsTotal);
          If the argument comes back non-null, you can use
          Marshal.PtrToSt ructure or Marshal.Copy to retrieve the DHCP_IP_ARRAY
          data.


          Mattias

          --
          Mattias Sjögren [C# MVP] mattias @ mvps.org
          http://www.msjogren.net/dotnet/ | http://www.dotnetinterop.com
          Please reply only to the newsgroup.

          Comment

          • Richard

            #6
            Re: DHCP with P/Invoke

            Mattias,
            Looks like it's pretty much the same issue as in your last post. The
            parameter is actually a pointer to a pointer to the struct. So try it
            like this:
            >
            >[DllImport("dhcp sapi.dll", SetLastError = true, CharSet = CharSet.Unicode )]
            >public static extern uint DhcpEnumSubnets (
            > string ServerIP,
            > ref uint resumeHandle,
            > uint PerferedMax,
            out IntPtr ipAddresses,
            > ref uint ElementsRead,
            > ref uint ElementsTotal);
            >
            If the argument comes back non-null, you can use
            Marshal.PtrToSt ructure or Marshal.Copy to retrieve the DHCP_IP_ARRAY
            data.
            Thank you, I've got this bit working, although I had to work hard to get
            it back into a decent format.

            If there are any glaring mistakes then let me know, but the code below
            does work for me. Also if any one has a better (more efficient method)
            for converting the UInt32 IP Address back in to a dot notation IP
            Address then please post here! ;o)

            Now onto the next bit. Anyone got some pointers on searching for a
            MAC/Hardware Address on the DHCP server using DhcpGetClientIn fo? I can
            retrieve the ClientInfo structure back if I search for the IP Address
            but not if I use the hardware address or client name.

            Code for others who search this thread, I'll stick it up on the
            pinvoke.net (wiki) when I get a chance:

            static void Main()
            {
            String ServerIpAddress = "192.168.0.250" ;
            UInt32 DHCPResult = 0;

            IntPtr ips;
            uint nr = 0;
            uint total = 0;
            uint resumeHandle = 0;

            if (DHCPResult == 0)
            {
            DHCPResult = DhcpEnumSubnets (ServerIpAddres s, ref resumeHandle,
            1000, out ips, ref nr, ref total);

            DHCP_IP_ARRAY ipArray =
            (DHCP_IP_ARRAY) Marshal.PtrToSt ructure(ips, typeof(DHCP_IP_ ARRAY));

            int size = (int)ipArray.Nu mElements;
            IntPtr outArray = ipArray.IPAddre sses;
            DHCP_IP_ADDRESS[] ipAddressArray = new DHCP_IP_ADDRESS[size];
            IntPtr current = outArray;
            for (int i = 0; i < size; i++)
            {
            ipAddressArray[i] = new DHCP_IP_ADDRESS ();
            Marshal.PtrToSt ructure(current , ipAddressArray[i]);
            Marshal.Destroy Structure(curre nt, typeof(DHCP_IP_ ADDRESS));
            current = (IntPtr)((int)c urrent +
            Marshal.SizeOf( ipAddressArray[i]));

            Console.WriteLi ne("{0}",
            UInt32IPAddress ToString(ipAddr essArray[i].IPAddress));
            }
            Marshal.FreeCoT askMem(outArray );

            Console.WriteLi ne("Elements read {0} of total {1}", nr, total);

            }
            else
            {
            int code = 0;
            unchecked
            {
            code = (int)DHCPResult ;
            }
            Win32Exception winex = new Win32Exception( code);
            Console.WriteLi ne(winex.Native ErrorCode + " : " + winex.Message);
            }
            }

            [DllImport("dhcp sapi.dll", SetLastError = true, CharSet = CharSet.Unicode )]
            public static extern uint DhcpEnumSubnets (
            string ServerIpAddress ,
            ref uint ResumeHandle,
            uint PreferredMaximu m,
            out IntPtr EnumInfo,
            ref uint ElementsRead,
            ref uint ElementsTotal
            );

            [StructLayout(La youtKind.Sequen tial)]
            public struct DHCP_IP_ARRAY
            {
            public uint NumElements;
            public IntPtr IPAddresses;
            }

            /// This is a custom type/class
            [StructLayout(La youtKind.Sequen tial, CharSet = CharSet.Ansi)]
            public class DHCP_IP_ADDRESS
            {
            public UInt32 IPAddress;
            }

            public static string UInt32IPAddress ToString(UInt32 ipAddress)
            {
            IPAddress ipA = new IPAddress(ipAdd ress);
            string[] sIp = ipA.ToString(). Split('.');

            return sIp[3] + "." + sIp[2] + "." + sIp[1] + "." + sIp[0];
            }

            Regards,

            Richard

            Comment

            Working...