Jump to content

Understanding DllCall and data types


Recommended Posts

1/ You really need some C and WinAPI basics befor fully understanding DllCall

2/ In windows, always prefere using 'Wide' functions.

Nearly all functions that involves strings in/out have 2 variants. See GetUserNameEx, at the end of the msdn page, you have GetUserNameExW (Unicode) and GetUserNameExA (ANSI). The real functions are those 2, and you must know that in modern windows OS (I think XP+) the ...A variant is only a wrapper arround the ...W variant.

If you look at the C headers of the WinAPI, you'll see that GetUserNameEx is not really an exported function, but only a preprocessor define that is calling the ...W version when UNICODE is unabled (at compilation time). (And I was very surprised that Calling GetUserNameEx without A or W works fine in AutoIt, I learned something today :P )

In the same way, about strings data types. TCHAR and TSTR are only defines that are WCHAR (wchar_t) and WSTR (wchar_t*) when UNICODE is unabled, and CHAR (char) and STR (char*) when it is not. (Note: le LP is for long pointer, and the C is for const, sor for example: LPCWSTR = const wchar_t*)

I know that all what I say can be very obscure when not knowing C, but remember just to always use ...W functions with W strings.

So, the GetUserName function will be:

MsgBox(0, "", _GetUserName())

Func _GetUserName()
    Local $aRet = DllCall("advapi32.dll", "int", "GetUserNameW", "wstr", "", "dword*", 256)
    If @error Then Return SetError(@error, 0, 0)
    ; ---
    ; because the second parameter is a pointer, it is modified by the function
    ConsoleWrite("User name = " & $aRet[1] & " (" & $aRet[2] & " chars long)" & @CRLF)
    Return $aRet[1]
EndFunc

PS: I forgot to mention the main befit of using ...W functions : you will have non-english caracters returned normally.

Edited by matwachich
Link to comment
Share on other sites

Hi matwachich ! it's been long time since we did not cross (on the french forum)

Sorry, I didn't see your post. Thanks for the great informations, clear enough.

For now, I manage to create the following functions : _WinAPI_CreateProfile, _WinAPI_DeleteProfile, _WinAPI_GetAllUsersProfileDirectory, _WinAPI_GetDefaultUserProfileDirectory, _WinAPI_GetProfilesDirectory and _WinAPI_GetProfileType :

; http://msdn.microsoft.com/en-us/library/windows/desktop/bb762271%28v=vs.85%29.aspx
Func _WinAPI_CreateProfile($sUserSid, $sUserName)
    Local $ret = DllCall("Userenv.dll", "long", "CreateProfile", "wstr", $sUserSid, "wstr", $sUserName, "wstr", "", "dword", 255)
    If @error Then Return SetError(1, @extended, @error)
    
    Return $ret[3]
EndFunc


; http://msdn.microsoft.com/en-us/library/windows/desktop/bb762273%28v=vs.85%29.aspx
Func _WinAPI_DeleteProfile($sSidString, $sProfilePath, $sComputerName = Null)
    Local $ret = DllCall("Userenv.dll", "bool", "DeleteProfileW", "wstr", $sSidString, "wstr", $sProfilePath, "wstr", $sComputerName)
    If @error Then Return SetError(1, @extended, @error)
    
    Return $ret[0]
EndFunc


; http://msdn.microsoft.com/en-us/library/windows/desktop/bb762276%28v=vs.85%29.aspx
Func _WinAPI_GetAllUsersProfileDirectory()
    Local $ret = DllCall("Userenv.dll", "bool", "GetAllUsersProfileDirectoryW", "wstr", "", "dword*", 255)
    If @error Then Return SetError(1, @extended, @error)
    
    Return $ret[1]
EndFunc


; http://msdn.microsoft.com/en-us/library/windows/desktop/bb762277%28v=vs.85%29.aspx
Func _WinAPI_GetDefaultUserProfileDirectory()
    Local $ret = DllCall("Userenv.dll", "bool", "GetDefaultUserProfileDirectoryW", "wstr", "", "dword*", 255)
    If @error Then Return SetError(1, @extended, @error)
    
    Return $ret[1]
EndFunc


; http://msdn.microsoft.com/en-us/library/windows/desktop/bb762278%28v=vs.85%29.aspx
Func _WinAPI_GetProfilesDirectory()
    Local $ret = DllCall("Userenv.dll", "bool", "GetProfilesDirectoryW", "wstr", "", "dword*", 255)
    If @error Then Return SetError(1, @extended, @error)
    
    Return $ret[1]
EndFunc


; http://msdn.microsoft.com/en-us/library/windows/desktop/bb762279%28v=vs.85%29.aspx
Func _WinAPI_GetProfileType()
    Local $tpdwFlags = DllStructCreate("dword pdwFlags")
    Local $ppdwFlags = DllStructGetPtr($tpdwFlags)
    Local $ret = DllCall("Userenv.dll", "bool", "GetProfileType", "ptr", $ppdwFlags )
    If @error Then Return SetError(1, @extended, @error)
    
    Return DllStructGetData($tpdwFlags, "pdwFlags")
EndFunc

It seems to be good, but not really sure for _WinAPI_GetProfileType (if someone can give his point of view ?)

I have still some questions : the GetProfileType MSDN page says that the returned value can be PT_MANDATORY, PT_ROAMING or PT_TEMPORARY. I cannot find the corresponding values for these 3 possibilities.

I had a look one the Shell Constants, Enumerations, and Flags, but they are not described. Where can we find the official information ?

 

Edit : I know these functions are part of AutoIt UDF, but, I wrote this for training (without watching in the UDF source)

Edited by jguinch
Link to comment
Share on other sites

Those constants are probably located in Userenv.h header.

EDIT:

They are

#define PT_TEMPORARY         0x00000001      // A profile has been allocated that will be deleted at logoff.
#define PT_ROAMING           0x00000002      // The loaded profile is a roaming profile.
#define PT_MANDATORY         0x00000004      // The loaded profile is mandatory.

 

 

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

Good.

I thought about an official page, but it seems the #define keyword was what I need.

I found the Userenv.h file with CodeBlocks (installed on my computer but not worn out :) )

Thx !

Link to comment
Share on other sites

For enumerations and defines, usually an int value type is good.

When you don't know/find the value of a constant, google is always you're best friend (as pointed by trancexx).

In your DeleteProfile function, last parameter should by wstr. But don't forget to read this from msdn: Note  As of Windows Vista, this parameter must be NULL. If it is not, this function fails with the error code ERROR_INVALID_PARAMETER.

Last function, I don't understand what you wanted to do :P

It's simple:

Const $PT_MANDATORY = 0x00000004
Const $PT_ROAMING = 0x00000002
Const $PT_TEMPORARY = 0x00000001

Func _WinAPI_GetProfileType()
    Local $ret = DllCall("Userenv.dll", "bool", "GetProfileType", "dword*", 0)
    If @error Then Return SetError(1, @extended, @error)

    If $ret[0] Then
        Return $ret[1]
    Else
        Return 0
    EndIf
End Func

PS: Yes, it's been a long time... studies, exams, and soon work!

Link to comment
Share on other sites

OK, I just edited _WinAPI_DeleteProfile (it's a missing : after reading your post about unicode, I changed functions to use unicode, but forgot this parameter).

For the last function, I don't know myself what I wanted do to.. :shifty:

In fact, I just get lost with MSDN when it says Pointer to a variable that receives the profile type... Sometime I need to use a pointer, sometime not, even if MSDN talk about pointer...

It seems to not want to get into my head ! :geek:

Link to comment
Share on other sites

OK, I just edited _WinAPI_DeleteProfile (it's a missing : after reading your post about unicode, I changed functions to use unicode, but forgot this parameter).

For the last function, I don't know myself what I wanted do to.. :shifty:

In fact, I just get lost with MSDN when it says Pointer to a variable that receives the profile type... Sometime I need to use a pointer, sometime not, even if MSDN talk about pointer...

It seems to not want to get into my head ! :geek:

It's because AutoIt work diferent. (to be more easy)

msdn say that, because you should pass  a variable by reference  &Yourvariable (C/C++ way) . it's means your're passing its pointer.

So DllCall AutoIt says: Add * to the end of another type to pass it by reference. For example "int*" passes a pointer to an "int" type.

AutoIt Does not supports pointers to variables. For that is Dllstructcreate. You can Simulate a variable that can have a reference pointer.

Look:

Const $PT_MANDATORY = 0x00000004
Const $PT_ROAMING = 0x00000002
Const $PT_TEMPORARY = 0x00000001

Func _WinAPI_GetProfileType()
    Local $tdword=DllStructCreate("dword") ;Your dword variable
    Local $pdword=DllStructGetPtr($tdword) ;Your pointer to a dword variable
    Local $ret = DllCall("Userenv.dll", "bool", "GetProfileType", "ptr", $pdword)
    If @error Then Return SetError(1, @extended, @error)
    If $ret[0] Then
        Return DllStructGetData($tdword,1)
    Else
        Return 0
    EndIf
EndFunc

but obviously it's easier, just add *.

Saludos

Edited by Danyfirex
Link to comment
Share on other sites

OK. So my code was good, Danyfirex, no ? :thumbsup:

matwachich, you're absolutely right : I need some base concepts of C.

I will try to find a good website to learn these unavoidable fundamentals.

Again, thank you all !

Soon for more questions!
Link to comment
Share on other sites

  • 4 years later...

I know this is an old post, but I couldn't find anything close to topic more recent.

When using DLLCall() The Help Document use CAPS the same as Windows API Types, but all examples of DLLCall() seam to use Lower Case.

My question is can I use the standard WinAPI Capitols convention for readability, or is there a preference for Lower Case, and if a basic reasoning?

Any guidance would be appreciated :-)

"Writing code to carry out an intended set of tasks is relatively easy.
Writing code to carry out ONLY an intended set of tasks, well that can be a little more challenging."

Alex Maddern

Link to comment
Share on other sites

11 minutes ago, Nine said:

A convention in AutoIt programming is to put all constants in CAPS.  That's only a question of readability.

DllCall("user32.dll", "LRESULT", "SendMessageW", "HWND", $idLabel1, "UINT", $iMsg, "WPARAM", 0, "LPARAM", 0)

DllCall("user32.dll", "lresult", "SendMessageW", "hwnd", $idLabel1, "uint", $iMsg, "wparam", 0, "lparam", 0)

The top is how I would go following WIN API Types, The lower seams to the only method in the UDFs.

I'll stay with Windows convention as its easier to read. Thanks much for the reply =)

"Writing code to carry out an intended set of tasks is relatively easy.
Writing code to carry out ONLY an intended set of tasks, well that can be a little more challenging."

Alex Maddern

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

×
×
  • Create New...