Jump to content

Problems calling ShellExecuteEx API in AutoIt


Recommended Posts

I use following code to call ShellExecuteEx and it works fine.

Local $ShExecInfo = DllStructCreate('STRUCT; DWORD cbSize; ULONG fMask; HWND hwnd; PTR lpVerb; PTR lpFile; PTR lpParameters; PTR lpDirectory; INT nShow; HANDLE hInstApp; PTR lpIDList; PTR lpClass; HANDLE hkeyClass; DWORD dwHotKey; HANDLE hIcon; HANDLE hProcess; ENDSTRUCT')

Local $lpVerb = DllStructCreate('WCHAR[15];WCHAR')
Local $lpFile = DllStructCreate('WCHAR[255];WCHAR')
Local $lpParameters = DllStructCreate('WCHAR[255];WCHAR')
Local $lpDirectory = DllStructCreate('WCHAR[255];WCHAR')

DllStructSetData($lpVerb, 1, '')
DllStructSetData($lpFile, 1, 'C:\Windows\Notepad.exe')
DllStructSetData($lpParameters, 1, '')
DllStructSetData($lpDirectory, 1, '')

DllStructSetData($ShExecInfo, 'cbSize', DllStructGetSize($ShExecInfo))
DllStructSetData($ShExecInfo, 'fMask', BitOR(0xC, 0x40, 0x400))
DllStructSetData($ShExecInfo, 'hwnd', NULL)
DllStructSetData($ShExecInfo, 'lpVerb', DllStructGetPtr($lpVerb))
DllStructSetData($ShExecInfo, 'lpFile', DllStructGetPtr($lpFile))
DllStructSetData($ShExecInfo, 'lpParameters',DllStructGetPtr($lpParameters))
DllStructSetData($ShExecInfo, 'lpDirectory', DllStructGetPtr($lpDirectory))
DllStructSetData($ShExecInfo, 'nShow', @SW_SHOW)

Local $Result = DllCall('Shell32.dll', 'BOOL', 'ShellExecuteExW', 'PTR', DllStructGetPtr($ShExecInfo))[0]

ConsoleWriteError('ShellExecuteEx: ' & String($Result) & '    ' & String(_WinAPI_GetLastErrorMessage()) & @CRLF)
Exit 1

But the problem is I can't declare some items in struct such as lpFile, lpDirectory (whose items have String data type) in AutoIt.

If I declare they in String type like:

Local $ShExecInfo = DllStructCreate('STRUCT; DWORD cbSize; ULONG fMask; HWND hwnd; WSTR lpVerb; WSTR lpFile; WSTR lpParameters; WSTR lpDirectory; INT nShow; HANDLE hInstApp; PTR lpIDList; WSTR lpClass; HANDLE hkeyClass; DWORD dwHotKey; HANDLE hIcon; HANDLE hProcess; ENDSTRUCT')

And use it like:

DllStructSetData($ShExecInfo, 'lpVerb', '')
DllStructSetData($ShExecInfo, 'lpFile', 'C:\Windows\Notepad.exe')
DllStructSetData($ShExecInfo, 'lpParameters', '')
DllStructSetData($ShExecInfo, 'lpDirectory', '')

AutoIt crashes end script doesn't execute.

But I can't figure why it works when I declare them as PTR type.

Doing so also increase number of code lines, I want to declare they as WSTR anyway to reduce number of code lines.

Any help is greatly appreciated to know what goes wrong with WideString type here.

Link to comment
Share on other sites

Welcome to the AutoIt forums. :)

7 hours ago, GTAVLover said:

I want to declare they as WSTR anyway to reduce number of code lines.

You can't, that's not how it works.:no: When you use a dynamic link library that you haven't written yourself (such as shell32), you have to play by its rules, just as when you use an AutoIt UDF, you have to parse the (types of) arguments it expects, and in the right order, or else the call will likely fail. In this case, shell32 expects pointers to work buffers to be pushed onto the stack (i.e., 4/8 bytes each), not the buffers themselves (which take up much more space than the pointers to them would, e.g., here some are 255 bytes each). It can then perform operations on the indicated memory regions. It would be incredibly inefficient to parse those buffers themselves back and forth every time (I write dlls that handle buffers that are GBs in size; can you imagine (how long it would take to be) shuffling those around for each call? You don't want to be re-allocating, copying everything across, performing an action there, copying everything back, and de-allocating, when all you really have to do is tell the dll: "you need to do X on some data that live in location Y"). So unless you're willing to write your own dlls that behave the way you want them to, you'll have to conform to their specifications, or else you don't get to play.

Edited by RTFC
Link to comment
Share on other sites

2 minutes ago, mikell said:

LPCTSTR = L‌ong P‌ointer to a C‌onst T‌CHAR STR‌ing   

Not very clear here where MS says  :  lpVerb = Type: LPCTSTR   "A string, referred to as a verb" BUT  lpFile = Type: LPCTSTR  "The address of a null-terminated string"
Thanks Microsoft   :)

Ahh okay......That's why it won't work with WideString........Thanks guys ........ :) And does AutoIt suport Unions in such API functions? I think it isn't.

Link to comment
Share on other sites

But, I can't figure why even there are ShellExecute and many other Windows API functions those require LPCTSTR as parameters, work with normal String type instead Pointers? I tested and worked, can't figure why this happened only with ShellExecuteEx. B)

Link to comment
Share on other sites

Hello. The issue is that AutoIt structures does not support wstr LPCSTR,LPSTRSTR LPCWSTR,LPWSTRWSTR as C/C++. For that reason you need to create an structure.

If dllstructure allow you wstr and str data types it was correct did the way you tried.

using something like this  WSTR lpFile (Of couse AutoIt does not support that kind of structure data type)

DllStructSetData($ShExecInfo, 'lpFile', 'C:\Windows\Notepad.exe')

But as I said if was possible if AutoIt allow you that data type in dll structures but it does not, So it will not work.

 

So for that reason you need to create the strings structures.

 

;~ If you'll use  UNICODE unicode
Local $lpVerb = DllStructCreate('WCHAR[15];') 
Local $lpFile = DllStructCreate('WCHAR[255];')
Local $lpParameters = DllStructCreate('WCHAR[255];')
Local $lpDirectory = DllStructCreate('WCHAR[255];')


;~ If you'll use  ANSI version
Local $lpVerb = DllStructCreate('CHAR[15];') 
Local $lpFile = DllStructCreate('CHAR[255];')
Local $lpParameters = DllStructCreate('CHAR[255];')
Local $lpDirectory = DllStructCreate('CHAR[255];')

;pd: of course the size could be diferent 

Sorry for post code without correct tag my connections is unable to load the popup window for put the code.

 

MSDN is clear enough I think when it says:

lpFile

Type: LPCTSTR

The address of a null-terminated string that specifies the name of the file or object on which ShellExecuteEx will perform the action specified by the lpVerb parameter. 

 

So what is the address?

$MyAddress=DllStructGetPtr($lpFile)

the we put it in our HELLEXECUTEINFO structure

DllStructSetData($ShExecInfo, 'lpFile',$MyAddress)

 

that's all.

 

Saludos

 

 

Link to comment
Share on other sites

Then, for other API functions such as ShellExecute, I don't create string structures and just pass strings as parameters (Thay also require LPCTSTR parameters).......But they works fine without those string structures in AutoIt.......Why this?

Link to comment
Share on other sites

ShellExecute uses literal params (no struct) while ShellExecuteEx uses a pointer to SHELLEXECUTEINFO only

You might have even more fun by looking into the "WinAPIShellEx.au3" UDF to the _WinAPI_ShellExecute func and see how AutoIt handles the thingy
Dazing: in this func, wstr is used in the call if the parameter value is defined, but if not then ptr is used with the value 0 (implied: pointer to default)
Nice  :) 

Link to comment
Share on other sites

12 hours ago, GTAVLover said:

Then, for other API functions such as ShellExecute, I don't create string structures and just pass strings as parameters (Thay also require LPCTSTR parameters).......But they works fine without those string structures in AutoIt.......Why this?

That happends because AutoIt include the nice parameter type wstr and str that allow you to wrap all structutures stuffs just using wstr,str. why? this simply happeds because AutoIt does no support string Pointer directry as many other programing lenguages.  

 

But you here is an example of using structures in common dllcalls.

Local $sFilePath="notepad.exe"
Local $tFilePath=DllStructCreate("WCHAR Data[" & StringLen($sFilePath) & "]")
$tFilePath.Data=$sFilePath
Local $pFilePath=DllStructGetPtr($tFilePath,1)


;this is the prefered correct way
DllCall('shell32.dll', 'ULONG_PTR', 'ShellExecuteW', 'hwnd', 0, "wstr", Null, 'wstr', $sFilePath, _
            "wstr", Null, "wstr", Null, 'int', 10)

;this is also correct (but please  use wstr/str instead)
;~ DllCall('shell32.dll', 'ULONG_PTR', 'ShellExecuteW', 'hwnd', 0, "wstr", Null, 'ptr', $pFilePath, _
;~          "wstr", Null, "wstr", Null, 'int', 10)



;even this can be correct (but please  use wstr/str instead)
;~ DllCall('shell32.dll', 'ULONG_PTR', 'ShellExecuteW', 'hwnd', 0, "wstr", Null, 'struct*', $tFilePath, _
;~          "wstr", Null, "wstr", Null, 'int', 10)

 

8 hours ago, mikell said:

ShellExecute uses literal params (no struct) while ShellExecuteEx uses a pointer to SHELLEXECUTEINFO only

You might have even more fun by looking into the "WinAPIShellEx.au3" UDF to the _WinAPI_ShellExecute func and see how AutoIt handles the thingy
Dazing: in this func, wstr is used in the call if the parameter value is defined, but if not then ptr is used with the value 0 (implied: pointer to default)
Nice  :) 

You also can used Null without change the parameter type to ptr and pass value 0.

"wstr",Null

Null was created for that.

 

Saludos

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