Jump to content

dll - help getting string from returned dll structure (DHCP_GetSubnetInfo)


Recommended Posts

Hi,

This is my first post, although I've been using the forums as a fantastic source of inspiration and help for a couple of years. I'm trying to get the SubnetName and SubnetComments back from a dll call to dhcpsapi, but seem to just get pointers to returned strings, and can't work out how to get the actual strings themselves. The function returns a dll structure, which includes the strings inside. I've looked at some other posts which have just made me more confused. Can anyone help? The code takes a string with the dhcp server's ip address, and another string with the subnetID. This is the code I've cobbled together so far, modifying the DHCP.au3 from amel27 It also uses a couple of functions I found on the forums to translate the $subnet string to a binary value, and then to it's int value:

Func _DHCP_GetSubnetInfo($sDHCP, $subnet)
Local $tEnumSubnetsParms = DllStructCreate("ptr SubnetInfoPtr;DWORD Subnet")
Local $infoArray[4]=[0, 0, 0, 0], $tSubnetInfoStr, $tAddress, $aRet, $temp, $tempArray, $tSubnetInfo
Local $returnedString

; Translate the $subnet string to a string of 32 bits
$tempArray = StringSplit($subnet, ".")

For $i = 1 To UBound($tempArray) -1
$temp = $temp & _binary($tempArray[$i])
Next

; Now turn this into an int value to pass to the DHCP server
$subnet = _BinToInt($temp)

$aRet = DllCall("Dhcpsapi.dll", "int", "DhcpGetSubnetInfo", _
"wstr", $sDHCP, _
"int", $subnet, _
"ptr", DllStructGetPtr($tEnumSubnetsParms, "SubnetInfoPtr") )
If $aRet[0] Then Return 2; SetError(2, $aRet[0]) ; ERR: Any runtime errors
 
; Debug
_ArrayDisplay($aRet)


If DllStructGetData($tEnumSubnetsParms,"SubnetInfoPtr")=0 Then Return 1; SetError(1, 0, $infoArray) ; ERR: Not Found
$tSubnetInfoStr = DllStructCreate("ptr SubnetAddress;ptr SubnetMask;ptr SubnetName;ptr SubnetComment;ptr PrimaryHost;ptr SubnetState", DllStructGetData($tEnumSubnetsParms,"SubnetInfoPtr"))

$tSubnetInfo = DllStructCreate("char[" & DllStructGetData($tSubnetInfoStr, 3) & "]");  , DllStructGetData($tEnumSubnetsParms, "SubnetInfoPtr"))

; Debug
If $tSubnetInfoStr = 0 Then
MsgBox(0, "E", @error)
EndIf

; Let's see what we have
MsgBox(0, "G", DllStructGetData($tSubnetInfoStr, 1))

; Freeing  memory
DllCall("Dhcpsapi.dll", "none", "DhcpRpcFreeMemory", "ptr", DllStructGetData($tEnumSubnetsParms,"EnumInfoPtr"))

Return $infoArray
EndFunc

Thanks so much for any help anyone can give!

Link to comment
Share on other sites

I'm certainly no expert, but in lieu of an answer I'll just make a few observations, and ask a few questions which might clear up a few things for someone who is able to help you.

How certain are you that $sDHCP is a unicode string?

Param 2 (DHCP_IP_ADDRESS) of DhcpGetSubnetInfo wants a DWORD where you are passing type int (according to msdn page.

MSDN says the ID you can pass can be decimal (not integer) so DWORD might be the best option.

If param 3 does indeed take a pointer, then that is also the out value which should be a structure of which element should be your SubnetName and element 4 be your SubnetComment, both strings (wstr) to autoit.

To be honest I cannot really figure out what you are doing in that function, for example.

$tSubnetInfoStr = DllStructCreate(.....)

Here you seem to be trying to create your own structure similar to what should have already been returned in param 3 of your dllcall.

Like I said, I'm only posting this in lieu of an answer. Hope you get something from it.

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

Hi JohnOne,

Thanks so much for your reply. I'm pretty certain that the $sDHCP string is correct, as this was copied directly out of the DHCP_EnumSubnets in the DHCP.au3 that amel27. I'm also fairly confident that passing the the ip address as an int is OK, as initially I was getting an error message returned from the DHCP server stating that the subnet was not found. After a bit of tinkering, I am now getting a success message (return code 0), plus the ip address of the server, the subnetID, plus a pointer to something else. From this "something else" (which I believe to be a DHCP_subnetInfo structure), I am managing to extract pointers to other elements, which are different to the pointer that is originally returned by the dllCall. But where I'm getting lost (and probably going around in circles), is how to extract the actual elements from the returned DHCP_subnetInfo structure. There's something there, but I just can't read it properly! As I'm trying to read from a structure, I thought I would have to create another structure to read the elements into, and then extract the subnetName etc from that. But now I'm just confused as neither reading direct from the returned structure, or moving the elements into another one, seem to work.

It's my first time trying to use dllcall - maybe I just need to spend some more time using it in other (simpler!) ways to get my head around it...

Link to comment
Share on other sites

What do you get if you just read the struct element directly?

$aRet = DllCall("Dhcpsapi.dll", "int", "DhcpGetSubnetInfo", _

"wstr", $sDHCP, _

"int", $subnet, _

"ptr", DllStructGetPtr($tEnumSubnetsParms, "SubnetInfoPtr") )

If $aRet[0] Then Return 2; SetError(2, $aRet[0]) ; ERR: Any runtime errors

If Not IsDllStruct($tEnumSubnetsParms) Then

MsgBox(0,0,"Not DllStruct")

Exit

EndIf

MsgBox(0,0,DllStructGetData($tEnumSubnetsParms,3))

Edited by JohnOne

AutoIt Absolute Beginners    Require a serial    Pause Script    Video Tutorials by Morthawt   ipify 

Monkey's are, like, natures humans.

Link to comment
Share on other sites

Hmmm...interesting - I just get a message box with a "0" in the title and in the text, so it IS a structure, but it's value can't be displayed that way. Now, that made me wonder if I'm wrong about the subnetInfo being a dll structure...so, I tried adding a test to that, ie after:

$tSubnetInfoStr = DllStructCreate("ptr SubnetAddress;ptr SubnetMask;ptr SubnetName;ptr SubnetComment;ptr PrimaryHost;ptr SubnetState", DllStructGetData($tEnumSubnetsParms,"SubnetInfoPtr"))

I added this:

If IsDllStruct(DllStructGetData($tSubnetInfoStr, 3)) Then

MsgBox(0, "", "Yup, it's a structure")

Else

MsgBox(0, "", "Not a structure")

EndIf

which came back saying that it wasn't a structure - but then the help file for isDllStruct says:

Returns 0 if expression is not DllStruct type as return by DllStructCreate.

As this has come back from the dhcp server, then it's been created remotely, rather than in AutoIT. Maybe AutoIT can't handle remotely-generated dll structures?

Link to comment
Share on other sites

OK, I've made some progress. I'm now getting the first character of the subnetName, but I'm not sure how to get the rest of the string. Does anyone know what I need to do from here? I tried using DllStructGetSize and a For loop in the hope that would give me the length of the string that the pointer points to, but that only gave me the same character twice. How would I get the length of the string in memory? Thanks for any help!

Func _DHCP_GetSubnetInfo1($sDHCP, $subnet)
Local $tEnumSubnetsParms = DllStructCreate("ptr SubnetInfoPtr;DWORD Subnet")
Local $infoArray[4]=[0, 0, 0, 0], $tSubnetInfoStr, $tAddress, $aRet, $temp, $tempArray, $tSubnetInfo
Local $returnedString

; Set the $subnet to correct structure
$tempArray = StringSplit($subnet, ".")


If @error Then
MsgBox(0, @error, "Error creating dll structure")
EndIf

For $i = 1 To UBound($tempArray) -1
$temp = $temp & _binary($tempArray[$i])
Next
$subnet = _BinToInt($temp)

$aRet = DllCall("Dhcpsapi.dll", "HANDLE", "DhcpGetSubnetInfo", _
"wstr", $sDHCP, _
"int", $subnet, _
"ptr", DllStructGetPtr($tEnumSubnetsParms, "SubnetInfoPtr") )
If $aRet[0] Then Return 2; SetError(2, $aRet[0]) ; ERR: Any runtime errors

If DllStructGetData($tEnumSubnetsParms,"SubnetInfoPtr")=0 Then Return 1; ERR: Not Found

$tSubnetInfoStr = DllStructCreate("ptr SubnetAddress;ptr SubnetMask;ptr SubnetName;ptr SubnetComment;ptr PrimaryHost;ptr SubnetState", DllStructGetData($tEnumSubnetsParms,"SubnetInfoPtr"))

; This gives me the first character of the string only - eg "M"
$returnedString = DllStructCreate('wchar', DllStructGetData($tSubnetInfoStr, 3))
MsgBox(0, "", DllStructGetData($returnedString, 1))

; This gives me the first character of the string twice eg "MM"
$temp = DllStructCreate('wchar', DllStructGetData($tSubnetInfoStr, 3))
        For $i = 1 To DllStructGetSize($temp)
            $returnedString = $returnedString & DllStructGetData($temp, 1)
        Next


; Freeing memory
DllCall("Dhcpsapi.dll", "none", "DhcpRpcFreeMemory", "ptr", DllStructGetData($tEnumSubnetsParms,"EnumInfoPtr"))
MsgBox(0, "1", "placeholder")
Return $infoArray
EndFunc
Link to comment
Share on other sites

Sorted! This is what I did, in case anyone needs it in the future...

Thanks to the following:

_binToInt function from Spiff59 taken from

_binary function from LimeSeed taken from

The function takes a string value for the dhcp server's IP address and a string value for the subnetID, and returns a string with the subnet name from the DHCP scope:

Func _DHCP_GetSubnetInfo($sDHCP, $subnetID)
Local $tEnumSubnetsParms = DllStructCreate("ptr SubnetInfoPtr;DWORD Subnet")
Local $infoArray[4]=[0, 0, 0, 0], $tSubnetInfoStr, $tAddress, $aRet, $temp, $tempArray, $tSubnetInfo
Local $returnedString, $stringSize

$stringSize = 32

; Change the subnetID into binary
$tempArray = StringSplit($subnetID, ".")
For $i = 1 To UBound($tempArray) -1
$temp = $temp & _binary($tempArray[$i])
Next
$subnetID = _BinToInt($temp)

; Make the dllCall
$aRet = DllCall("Dhcpsapi.dll", "int", "DhcpGetSubnetInfo", _
"wstr", $sDHCP, _
"int", $subnetID, _
"ptr", DllStructGetPtr($tEnumSubnetsParms, "SubnetInfoPtr") )
If $aRet[0] Then Return 0; ERR: Any runtime errors

If DllStructGetData($tEnumSubnetsParms,"SubnetInfoPtr")=0 Then Return -1; ERR: Not Found

; Create a new structure to hold all the elements returned in the DHCP_subnet_info structure
$tSubnetInfoStr = DllStructCreate("ptr SubnetAddress;ptr SubnetMask;ptr SubnetName;ptr SubnetComment;ptr PrimaryHost;ptr SubnetState", DllStructGetData($tEnumSubnetsParms,"SubnetInfoPtr"))
If @error Then
MsgBox(0, @error, "Error creating dll structure")
EndIf

; Now, pull out the element that we want - SubnetName is the only one implemented here for now
; (1 = SubnetAddress (DHCP_IP_ADDRESS), 2 = SubnetMask(DHCP_IP_MASK), 3 = SubnetName (LPWSTR), 4 = SubnetComment (LPWSTR) , 5 = PrimaryHost(DHCP_HOST_INFO)
; 6 = SubnetState(DHCP_SUBNET_STATE)
$temp = DllStructCreate('wchar[' & $stringSize & ']', DllStructGetData($tSubnetInfoStr, 3))
If @error Then
MsgBox(0, @error, "Error creating dll structure")
EndIf

; Get the data
For $i = 1 To $stringSize
$returnedString = $returnedString & DllStructGetData($temp, 1, $i)
Next

; Freeing memory
DllCall("Dhcpsapi.dll", "none", "DhcpRpcFreeMemory", "ptr", DllStructGetData($tEnumSubnetsParms,"EnumInfoPtr"))
Return $returnedString
EndFunc

Func _binary($num) ;converts a number to binary!

Local $string
dim $string = ""
Do
$num = $num/2
if(floor($num) = $num) then
$string = $string & "0"
Else
$string = $string & "1"
$num -= .5
EndIf
Until ($num = 0)
While StringLen($string) < 8
$string = $string & "0"
WEnd
Return _stringreverse($string)
EndFunc

Func _BinToInt($sValue)
Local $iOut = 0, $aValue = StringSplit($sValue, "")
For $i = 1 To $aValue[0]
$aValue[0] -= 1
If $aValue[$i] = "1" Then $iOut += 2 ^ ($aValue[0])
Next
Return Int($iOut)
EndFunc
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...