Jump to content

Assigning COM array to DllStruct


PaulIA
 Share

Recommended Posts

I have been working for the last couple of days on this and I've reached the point where I need assistance from somebody with better COM skills than I have. The problem appears when I try to convert a byte array returned from a COM call. When I write a VBS wrapper to convert the array, it returns the value correctly. I have included the script below that shows the problem. Any assistance as to what I'm doing wrong would be appreciated.

Opt("MustDeclareVars", 1)

; =================================================================================================
Global $oError, $aSid, $sUserName

; =================================================================================================
$oError    = ObjEvent("AutoIt.Error", "ComError")
$sUserName = InputBox("Username", "Please enter a username:", "Administrator")
if @Error then Exit
$aSid = GetUserSid($sUserName)
if @Error then
  ConsoleWrite($sUserName & " does not exist on local machine")
  Exit
EndIf

; =================================================================================================

GetSidAIT($aSid) ; <-- These two calls should produce the same output?
GetSidVBS($aSid) ; <--

; =================================================================================================
Func GetSidAIT($aSid)
  Local $rSid, $sSid

  $rSid = DllStructCreate("byte[28]")
  DllStructSetData($rSid, 1, $aSID)
  for $iI = 1 to 28
    $sSid = $sSid & Hex(DllStructGetData($rSid, 1, $iI), 2)
  next
  ConsoleWrite("AutoIT Sid ...: " & $sSid & @CR)
EndFunc

; =================================================================================================
Func GetSidVBS($aSid)
  Local $sCode, $oVBS

  $sCode = "Function OctetToHexStr(Octet)"
  $sCode = $sCode & @CR & "Dim I"
  $sCode = $sCode & @CR & "For I = 1 To LenB(Octet)"
  $sCode = $sCode & @CR & "  OctetToHexStr = OctetToHexStr & Right(""0"" & Hex(Ascb(Midb(Octet, I, 1))), 2)"
  $sCode = $sCode & @CR & "Next"
  $sCode = $sCode & @CR & "end Function"

  $oVBS = ObjCreate("ScriptControl")
  $oVBS.Language = "vbscript"
  $oVBS.AddCode($sCode)

  ConsoleWrite("VBS Sid ......: " & $oVBS.Run("OctetToHexStr", $aSID) & @CR)
EndFunc

; =================================================================================================
Func GetUserSid($sUserName)
  Local $sCode, $oVBS

  $sCode = "Function GetUserSid(UserName)"
  $sCode = $sCode & @CR & 'Set oSid = GetObject("WinNT://./' & $sUserName & '")'
  $sCode = $sCode & @CR & 'GetUserSid = oSid.objectSID'
  $sCode = $sCode & @CR & 'end Function'

  $oVBS = ObjCreate("ScriptControl")
  $oVBS.Language = "vbscript"
  $oVBS.AddCode($sCode)
  Return $oVBS.Run("GetUserSid", $sUserName)
EndFunc

; =================================================================================================
Func ComError()
  if IsObj($oError) then
    SetError(Hex($oError.Number))
  else
    SetError(1)
  endif
EndFunc

Edit: Provided example that doesn't require Active Directory

Edited by PaulIA
Auto3Lib: A library of over 1200 functions for AutoIt
Link to comment
Share on other sites

An example of what you get and what you expect would be useful.

The demo actually prints these values for you. The GetSidVBS prints what I expect and the GetSidAIT prints what I get when I attempt to duplicate the VBS function in AutoIt. I'm sure I'm just screwing up the transfer of the byte array from the COM object to the AutoIt structure, but I'm tapped as to what I'm doing wrong.

Here is the console print out when I run this on my machine:

AutoIT Sid ...: 407E4500000000000000000000000000000000000000000000000000
VBS Sid ......: 010500000000000515000000456C8711DC5727E9FEE12033F4010000

Edit: Added example printout values

Edited by PaulIA
Auto3Lib: A library of over 1200 functions for AutoIt
Link to comment
Share on other sites

I get the "Username does not exist" error regardless of whether the user actually exists or not.

Ditto. Is your code supposed to handle a user who is not authenticated via a domain controller?

Reading the help file before you post... Not only will it make you look smarter, it will make you smarter.

Link to comment
Share on other sites

Ditto. Is your code supposed to handle a user who is not authenticated via a domain controller?

No, it's only suppose to work for AD. I thought all of the references to the Active Directory constants would be a dead give away. ;)
Auto3Lib: A library of over 1200 functions for AutoIt
Link to comment
Share on other sites

It is a giveaway, but it does hurt your chances of getting an answer. Presumably you want a developer to help (based on the forum you're posting in). But when you filter down the number of developers with COM knowledge and then further filter that down to those with AD access and finally you take into account that some of us are less active than others, well, that doesn't leave too many people.

Link to comment
Share on other sites

It is a giveaway, but it does hurt your chances of getting an answer. Presumably you want a developer to help (based on the forum you're posting in). But when you filter down the number of developers with COM knowledge and then further filter that down to those with AD access and finally you take into account that some of us are less active than others, well, that doesn't leave too many people.

True. Maybe I can find another COM call that exhibits the same behavior. I'll look around and see if I can find something that returns a byte array and will run on a generic PC.
Auto3Lib: A library of over 1200 functions for AutoIt
Link to comment
Share on other sites

If I understand the code (AutoIt's, not yours) correctly, quite simply, SAFEARRAYs are not implemented. It's implemented for just a couple types but not for bytes. The reason I think VBS works is because while AutoIt doesn't put the SAFEARRAY into a format you can use, it does keep it intact. Thus when you later pass it back to VBS, VBS sees a perfectly valid VARIANT containing a SAFEARRAY of BYTEs.

I'll need to get with SvenP to see where his thinking was on this... limitation. It looks somewhat trivial to me to convert a SAFEARRAY into an array in AutoIt (I believe that AutoIt -> SAFEARRAY works already). I'm not sure why Sven didn't go ahead and do this.

Link to comment
Share on other sites

I rewrote the example so that it will return a byte array when used on a local machine. Any help would be appreciated.

on my system the AutoIT SID always contains the same value (B7D9420....), no matter which user I select. Looks like a memory location to me.....

Cheers

Kurt

__________________________________________________________(l)user: Hey admin slave, how can I recover my deleted files?admin: No problem, there is a nice tool. It's called rm, like recovery method. Make sure to call it with the "recover fast" option like this: rm -rf *

Link to comment
Share on other sites

I think that's just garbage. I'm not quite sure what AutoIt is returning but whatever it is, it's actually quite lucky it doesn't cause crash. It's CC a bunch of times under the debugger.

Could well be garbage. I used "RtlMoveMemory" to copy the memory content of that "pointer" to a DllStruct. But it returns just garbage...

Cheers

Kurt

__________________________________________________________(l)user: Hey admin slave, how can I recover my deleted files?admin: No problem, there is a nice tool. It's called rm, like recovery method. Make sure to call it with the "recover fast" option like this: rm -rf *

Link to comment
Share on other sites

i've this problem too

you know i work a lot on AD function

but "garbage" is returned :/

it would be really useful to make it work because SID are really important (migration, deleted accounts, etc...)

i've the same workaround as PaulIA (look in my tokengroup function)

=> using vbs

it's some binary data to retrieve, then to convert into string

it's really over my knowledge, and i didn't think it was a bug of autoit, but more an autoit implementation that could be too hard to implement, so i didn't tell anything

but, i you implement it, it would be just too great

thx for support

-- Arck System _ Soon -- Ideas make everything

"La critique est facile, l'art est difficile"

Projects :

[list] [*]Au3Service : Run your exe as service V3 / Updated 29/07/2013 Get it Here [/list]
Link to comment
Share on other sites

@PaulIA

A solution to the problem is to embed VbScript in AutoIT using the ObjCreate("ScriptControl").

This is a workaround for the time being.

Example script :

; Here's a function to emulate vbscript;
; I assume you can find the high , low values mentioned; here is example using vbscript object!
; I don't know if native AutoIt can do it!
#include<Date.au3>
#include<Array.au3>

Func _StampDateAdd($lngHigh,$lngLow,$format = "")
    
Local $s_Quotes='"'

;""
$code= "Function Integer8Date(lngHigh,lngLow)"
$code = $code & @CRLF & "Dim WshShell, lngBiasKey,lngTZBias"
$code = $code & @CRLF & 'Set WshShell = CreateObject("WScript.Shell")'
$code = $code & @CRLF & "lngBiasKey=WshShell.RegRead(""HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias"")"
$code = $code & @CRLF & "If UCase(TypeName(lngBiasKey)) = ""LONG"" Then"
$code = $code & @CRLF & "lngTZBias = lngBiasKey"
$code = $code & @CRLF & "ElseIf UCase(TypeName(lngBiasKey)) = ""VARIANT()"" Then"
$code = $code & @CRLF & "lngTZBias = 0"
$code = $code & @CRLF & "For k = 0 To UBound(lngBiasKey)"
$code = $code & @CRLF & "lngTZBias = lngTZBias + (lngBiasKey(k) * 256^k)"
$code = $code & @CRLF & "Next"
$code = $code & @CRLF & "End If"
$code = $code & @CRLF & "lngDate = #1/1/1601# + (((lngHigh * (2 ^ 32)) + lngLow) / 600000000 - lngTZBias) / 1440"
$code = $code & @CRLF & "Integer8Date = CDate(lngDate)"
$code =  $code & @CRLF & "end Function"

$vbs = ObjCreate("ScriptControl")
$vbs.language="vbscript"
$vbs.addcode($code)
$retour = $vbs.run("Integer8Date",$lngHigh,$lngLow)
$vbs=""
$Year=StringLeft($retour,4)
$Month=StringMid($retour,5,2)
$Day=StringMid($retour,7,2)
$Hour=StringMid($retour,9,2)
$Minute=StringMid($retour,11,2)
$Second=StringMid($retour,13,2)
;ConsoleWrite($retour)
if $format = "date" then
$retourstring = $Day&"/"&$Month&"/"&$Year&" "&$Hour&":"&$Minute&":"&$Second
Else
$retourstring=$Year&"/"&$Month&"/"&$Day&" "&$Hour&":"&$Minute&":"&$Second
EndIf

return $retourstring
EndFunc;==>_StampDateAdd


;$retour=_datetointeger8("03/01/2006 09:02:16")
;msgbox(0,"",$retour)
;$splithigh=StringMid($retour,1,8)
;$splitlow=StringMid($retour,9,8)
;$retour=_StampDateAdd($splithigh,$splitlow,"date")
;msgbox(0,"YEEEES",$retour)


Func _datetointeger8($date)
    
;Obtain local Time Zone bias from machine registry.
Local $s_Quotes='"' 
$code = "function datetointeger8(date)"

;$code = $code & @CRLF & "Option Explicit"

$code = $code & @CRLF & "Dim dtmDateValue, dtmAdjusted, lngSeconds"
$code = $code & @CRLF & "Dim WshShell, lngBiasKey,lngTZBias"
$code = $code & @CRLF & 'Set WshShell = CreateObject("WScript.Shell")'
$code = $code & @CRLF & "dtmDateValue = CDate(date)"
$code = $code & @CRLF & "lngBiasKey=WshShell.RegRead(""HKLM\System\CurrentControlSet\Control\TimeZoneInformation\ActiveTimeBias"")"
$code = $code & @CRLF & "If UCase(TypeName(lngBiasKey)) = ""LONG"" Then"
$code = $code & @CRLF & "lngTZBias = lngBiasKey"
$code = $code & @CRLF & "ElseIf UCase(TypeName(lngBiasKey)) = ""VARIANT()"" Then"
$code = $code & @CRLF & "lngTZBias = 0"
$code = $code & @CRLF & "For k = 0 To UBound(lngBiasKey)"
$code = $code & @CRLF & "lngTZBias = lngTZBias + (lngBiasKey(k) * 256^k)"
$code = $code & @CRLF & "Next"
$code = $code & @CRLF & "End If"

; Convert datetime value to UTC.
$code = $code & @CRLF & "dtmAdjusted = DateAdd(""n"", lngBias, dtmDateValue)"

;' Find number of seconds since 1/1/1601.
$code = $code & @CRLF & "lngSeconds = DateDiff(""s"", #1/1/1601#, dtmAdjusted)"

;' Convert the number of seconds to a string
;' and convert to 100-nanosecond intervals.
$code = $code & @CRLF & "datetointeger8 = CStr(lngSeconds) & ""0000000"""
$code = $code & @CRLF & "end Function"
msgbox(0,"",$code)
$vbs = ObjCreate("ScriptControl")
$vbs.language="vbscript"
$vbs.addcode($code)
$retour = $vbs.run("datetointeger8",$date)  
return $retour

$vbs=""
EndFunc



func _getinfotime($champ,$cheminldap)
    Local $s_Quotes='"'
    $code= "Function Getinfo(champ,cheminldap)"
    $code = $code & @CRLF & "Dim objConnection, objChild , string"
    $code = $code & @CRLF & "Set objEntry = GetObject(""LDAP://"" & cheminldap)"
    $code = $code & @CRLF & 'proplist = array(champ) '
    $code = $code & @CRLF & 'objEntry.getinfoex proplist, 0'
    $code = $code & @CRLF &  'string = objEntry.Get(champ)'
    $code = $code & @CRLF &  'Getinfo = string'
    $code = $code & @CRLF & "end Function"
;msgbox(0,"",$code)
$vbs = ObjCreate("ScriptControl")
$vbs.language="vbscript"
$vbs.addcode($code)
$retour = $vbs.run("Getinfo",$champ,$cheminldap)    
$Year=StringLeft($retour,4)
$Month=StringMid($retour,5,2)
$Day=StringMid($retour,7,2)
$Hour=StringMid($retour,9,2)
$Minute=StringMid($retour,11,2)
$Second=StringMid($retour,13,2)
$retour = $Day&"/"&$Month&"/"&$Year&" "&$Hour&":"&$Minute&":"&$Second
return $retour
$vbs=""
endfunc

I hope this helps

regards

ptrex

Link to comment
Share on other sites

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...