I was working on a program where once again I needed a list of files to work on.
Normally I would break out the FindFirst/FindNext stuff, but for this quicky, I thought I would just execute a DIR command.
So, I did this, which works. The question is, is there some smaller, simpler way to get a list of files to use?
include \masm32\include\masm32rt.inc
.data
open db "open",0
cmd db "cmd",0
parm db "/c dir *.htm /s/w/b/a-d > " ; continued on next line
dest db "C:\allhtm.txt",0
ddir db "C:\helpfiles",0
hanx dd 0
hres dd 0
.code
program:
invoke ShellExecute,0,addr open,addr cmd,addr parm,addr ddir,SW_HIDE
.if eax>32
mov hanx,eax
.repeat
invoke Sleep,100
invoke GetExitCodeProcess,hanx,addr hres
.until hres != STILL_ACTIVE
; the rest is just testing stuff
.data
errx db "error number "
buff db 15 dup (0)
answ dd 0
alen dd 0
aline dd 170 dup (0)
spos dd 0
.code
invoke read_disk_file,addr dest,addr answ,addr alen
invoke readline,answ,addr aline,spos
mov spos,eax
invoke MessageBox,0,addr aline,0,0
invoke GlobalFree,answ
.else
invoke dwtoa,eax,addr buff
invoke MessageBox,0,addr errx,0,0
.endif
invoke ExitProcess,eax
end program
Opps. I just checked and I'm getting 0 as a return from GetExitCodeProcess. This must not be the correct api to use to see if shellexecute is done, what is?
Well, it appears that the number that ShellExecute returns is worthless unless it's an error code < 33. So no way to tell when it's done.
The only alternatives I can think of are ShellExecuteEX and CreateProcess, both of which are much bigger and more complicated and generally yucky. Not simpler and smaller as per my original request. So much for quickies.
Oops - I had already scratched my head wondering how I possible could have overlooked, in all those years, such an elegant way to launch a synchronous process; without all that "yucky" stuff... if somebody gives me the name of the guy at M$ who ordered to "forget" the wait parameter in WinExec & friends, he should watch out for his b***s.
Back to serious work: Would you consider the LB_DIR Message "simple and elegant"? Another option is the DlgDirList (http://msdn.microsoft.com/en-us/library/bb761366(VS.85).aspx) function.
Personally, I would have to say Nothing about a listbox is simpler or smaller than anything. Probably my least favorite control to hassle with.
But... WinExec is certainly smaller and simpler. Thanks.
.data
cmd2 db "cmd /c dir C:\helpfile\*.htm /s/w/b/a-d > c:allhtm.txt",0
.code
invoke WinExec,addr cmd2,0
Then read output and try again if you get a read error ??
WinExec was an almost perfect function. "Almost" because it still had that completely useless second para that nobody ever used. Together with GetModuleUsage, you would have been able to do what you want. Then, M$ decided that it was not a good idea if amateur programmers managed to do these things; so they "deprecated" WinExec and abolished GetModuleUsage. So now we are stuck with that rubbish sequence of CreateProcessWithTenNullParams+WaitForBloodyObject...
The correct procedure is mentioned here on M$ support (http://support.microsoft.com/kb/129796/EN-US/).
How To Use a 32-Bit Application to Determine When a Shelled Process Ends
The Win32 API has integrated functionality that enables an application to wait until a shelled process has completed. To use these functions, you need a handle to the shelled process. The easiest way to achieve this is to use the CreateProcess() API function to launch your shelled program rather than Visual Basic's Shell() function.
Creating the Shelled Process
In a 32-bit application, you need to create an addressable process. To do this, use the CreateProcess() function to start your shelled application. The CreateProcess() function gives your program the process handle of the shelled process through one of its passed parameters.
Waiting for the Shelled Process to Terminate
Having used CreateProcess() to get a process handle, pass that handle to the WaitForSingleObject() function. This causes your Visual Basic application to suspend execution until the shelled process terminates.
Getting the Exit Code from the Shelled Application
It was common for a DOS application to return an exit code indicating the status of the completed application. While Windows provides other ways to convey the same information, some applications only provide exit codes. Passing the process handle to the GetExitCodeProcess() API allows you to retrieve this information.
If you are going to the trouble of ShellExecute/WinExec then CreateProcess can't be any harder can it?
Besides, it gives you access to a handle that you can use with WaitForSingleObject, and
know when it's finished.
Quote from: jj2007 on June 05, 2008, 08:46:12 AMCreateProcessWithTenNullParams
As bad as the 12 params (48 bytes) passed
on the stack to CreateWindowEx - 8 params which can be 0/NULL/zero (ffs)
BTW, can we leave off the "M$" thing please? Too bad Apple (or Mac or Linux) doesn't have an "s" in its name... ::)
Lin$ux ?
Here is the absolute minimum. If M$ had the user's interest in mind, there would be a function called ExecWait with one para:
invoke ExecWait, addr PathAndCmd
Full example:
include \masm32\include\masm32rt.inc
ExecWait PROTO:DWORD
.data?
sinfo STARTUPINFO <?>
pinfo PROCESS_INFORMATION <?>
.code
PathAndCmd db "C:\WINDOWS\notepad.exe C:\WINDOWS\SYSTEM.INI", 0
AppName db "ExecWait:", 0
start:
invoke ExecWait, addr PathAndCmd
.if eax
invoke MessageBox, 0, chr$("We are done!"), addr AppName, MB_OK
.else
invoke MessageBox, 0, addr PathAndCmd, chr$("Something went wrong:"), MB_OK
.endif
invoke ExitProcess, 0
ExecWait proc cmdline:DWORD
mov sinfo.cb, sizeof sinfo ; Windows needs to know the STARTUPINFO version
mov sinfo.dwFlags, STARTF_USESHOWWINDOW ; these two are
mov sinfo.wShowWindow, SW_MAXIMIZE ; optional but useful
invoke CreateProcess, 0, ; no pathname
cmdline, ; the only bit that REALLY counts...!
0, 0, 0, ; lpProcessAttributes, lpThreadAttributes, bInheritHandles
IDLE_PRIORITY_CLASS,
0, 0, ; lpEnvironment, lpCurrentDirectory
addr sinfo, addr pinfo
mov eax, pinfo.hProcess
.if eax
push eax
invoke WaitForSingleObject, eax, INFINITE ; you may specify milliseconds here
pop eax
.endif
ret
ExecWait endp
end start
See, how hard was that (no, don't answer). One thing, the handles returned in pinfo can cause memory leaks (if my memory serves me well).
You need a CloseHandle for the 2 handles (even though they will address a no-longer-existing process). Hutch had quite a spirited discussion
about this a couple of years ago I think :bdg
You could also redirect the output of the console to one of your handles (e.g. stdout -> file) and wait on that...
Your ExecWait is just a wrapper (hey, just like 'C' or 'C Double Plus Good' likes to do).
Quote from: sinsi on June 05, 2008, 10:25:53 AM
handles returned in pinfo can cause memory leaks (if my memory serves me well).
You need a CloseHandle for the 2 handles (even though they will address a no-longer-existing process).
I wonder what happens if you use the timeout (new option below): Close the handles of running processes? My test show no effect.
So here is the full "wrapper", as you like to call it. Tip: Interpret "CreateProcess" as a "wrapper" for the newbies and HL freaks, then let Olly disassemble the whole CreateProcess call starting at 7C802367, and use the listing for your own proggies. Why use a wrapper if you can make it complicated, eh?
include \masm32\include\masm32rt.inc
ExecWait PROTO:DWORD, :DWORD
.data?
sinfo STARTUPINFO <?>
pinfo PROCESS_INFORMATION <?>
.code
PathAndCmd db "C:\WINDOWS\notepad.exe C:\WINDOWS\SYSTEM.INI", 0
AppName db "ExecWait:", 0
start:
invoke ExecWait, addr PathAndCmd, 0 ; try 2000 as timeout
.if eax
invoke MessageBox, 0, chr$("We are done!"), addr AppName, MB_OK
.else
invoke MessageBox, 0, addr PathAndCmd, chr$("Something went wrong:"), MB_OK
.endif
invoke ExitProcess, 0
ExecWait proc cmdline:DWORD, timeout:DWORD
mov sinfo.cb, sizeof sinfo ; Windows needs to know the STARTUPINFO version
mov sinfo.dwFlags, STARTF_USESHOWWINDOW ; these two are
mov sinfo.wShowWindow, SW_MAXIMIZE ; optional but useful
invoke CreateProcess, 0, ; no pathname
cmdline, ; the only bit that REALLY counts...!
0, 0, 0, ; lpProcessAttributes, lpThreadAttributes, bInheritHandles
IDLE_PRIORITY_CLASS,
0, 0, ; lpEnvironment, lpCurrentDirectory
addr sinfo, addr pinfo
mov eax, pinfo.hProcess
.if eax
push eax
.if timeout==0 ; for the lazybones who don't want to type INFINITE
mov timeout, INFINITE
.endif
invoke WaitForSingleObject, eax, timeout ; specify milliseconds here
invoke CloseHandle, pinfo.hProcess ; close the two handles
invoke CloseHandle, pinfo.hThread
pop eax
.endif
ret
ExecWait endp
end start
For what you want 'timeout' is a waste of time since you *must* wait until the process is finished for you to get the output.
Quote from: jj2007 on June 05, 2008, 10:47:05 AM
So here is the full "wrapper", as you like to call it. Tip: Interpret "CreateProcess" as a "wrapper" for the newbies and HL freaks, then let Olly disassemble the whole CreateProcess call starting at 7C802367, and use the listing for your own proggies. Why use a wrapper if you can make it complicated, eh?
If it's not a 'wrapper' then what is?
I ain't a 'newbie' or a 'HL freak' so what's your problem? (but thanks for the 'tip' buddy)
Sure, man, it's always gonna be at 7C802367 isn't it, and 'Olly' isn't a teddy-bear i guess (I use windbg).
All this to get a f*cking directory listing, sheesh... ::)
Quote from: sinsi on June 05, 2008, 11:04:58 AM
If it's not a 'wrapper' then what is?
I ain't a 'newbie' or a 'HL freak' so what's your problem? (but thanks for the 'tip' buddy)
I guess I should mark irony in red. I didn't imply that you are a newbie. I just wanted to make the point that ExecWait should not be a 'wrapper', but rather a standard API call. Remember batch files?
echo Now we start
notepad
echo Now we are done
Compare that to the absolutely ridiculous efforts to produce the same result with CreateProcess & WaitForSingleObject. By the way, CreateProcess would not even know The Path, so you have to specify the full C:\WINDOWS\notepad.exe, hoping that it will still sit there after the next service pack. And no, %windir% won't work with CreateProcess.
Holy crap.
Quote from: jj2007 on June 05, 2008, 12:33:53 PMI guess I should mark irony in red.
A bit like the colour of my face...to be fair to me, I couldn't see your body language in your post. And now I've sobered up a bit I 'shore are embrassed'.
Especially since my incs are full of 'wrappers'...
As for batch files, ah, yes, the day they brought in "ECHO." was a special day.
Quote from: sinsi on June 05, 2008, 01:49:13 PM
the day they brought in "ECHO." was a special day.
Don't remember when I discovered that dot, many years ago, but yes indeed I remember it was a special day.
Re the timeout, I think it is an important option for cases when you might get stuck; e.g. launch Outlook or Word or some of these monsters, it gets blocked, your proggie is blocked, and you still have a file with valuable stuff open... that's when a 60 seconds timeout comes in handy. Even ml.exe hangs from time to time, e.g. when you specify a lot of memory in the .data? section (eventually it comes back, though).
And back to the original question, if I'm going to use masmlib anyway, this works for me-
.data
cmd2 db 'cmd /c dir "C:\help new\*.htm" /s/w/b/a-d > '
dest db "C:allhtm.txt",0
.code
invoke wshell,addr cmd2
can't get much simpler than that.
The GeneSys project provides a shell function :
.486 ; create 32 bit code
.model flat, stdcall ; 32 bit memory model
option casemap :none ; case sensitive
include \GeneSys\include\windows.inc
include \GeneSys\include\kernel32.inc
.code
;----------------------------------------------------------------------------;
; This is a translation of a shell script originally written by Edgar Hansen ;
; in GoASM code. Translation by Paul E. Brennick <pebrennick@verizon.net> ;
; ;
; Shell: This function will run an executable and wait until it has ;
; finished before continuing. The function provides for a time ;
; out for a maximum wait period. ;
; Parameters: lpfilename = The fully qualified path to an executable to run ;
; dwTimeOut = The amount of time in milliseconds to wait, -1 for ;
; no timeout ;
; Returns: 0 if successful, STATUS_TIMEOUT if the timeout has elapsed ;
; -1 if there was an error creating the process ;
; ;
;----------------------------------------------------------------------------;
Shell PROC lpfilename:DWORD, dwTimeOut:DWORD
LOCAL Sh_st_info :STARTUPINFO
LOCAL Sh_pr_info :PROCESS_INFORMATION
mov DWORD PTR [Sh_st_info.cb], SIZEOF STARTUPINFO
invoke GetStartupInfoA, ADDR Sh_st_info
mov DWORD PTR [Sh_st_info.lpReserved], 0
invoke CreateProcessA, 0, [lpfilename], 0, 0, 0, 0, 0, 0, \
ADDR Sh_st_info, ADDR Sh_pr_info
test eax, eax
jz ERROR
invoke WaitForSingleObject, [Sh_pr_info.hProcess], [dwTimeOut]
push eax
invoke CloseHandle, [Sh_pr_info.hThread]
invoke CloseHandle, [Sh_pr_info.hProcess]
pop eax
test eax, eax
RET
ERROR:
xor eax, eax
sub eax, 1
RET
Shell ENDP
end
Looks good, Vortex. Main difference to my version is that you call GetStartupInfo (which in turn allows to declare the STARTUPINFO locally). How much is the added value? MSDN has some pretty original remarks on this function:
This function does not return a value. (if you find one, throw it away!)
If an error occurs, the ANSI version of this function (GetStartupInfoA) can raise an exception (but we won't tell you when, haha!). The Unicode version (GetStartupInfoW) does not fail. (never! promised!)
For all that code to shell out to a cmd prompt, why not this code
allspec dd '*'
updir dd '..'
search_dir proc uses ebx esi
local wfd:WIN32_FIND_DATA
lea esi,wfd
invoke FindFirstFile,offset allspec,esi
cmp eax,-1
jz done
mov ebx,eax
next: call found
test wfd.dwFileAttributes,FILE_ATTRIBUTE_DIRECTORY
jz over
cmp wfd.cFileName,'.'
jz over
invoke SetCurrentDirectory,addr wfd.cFileName
test eax,eax
jz fini
call search_dir
invoke SetCurrentDirectory,offset updir
over:
invoke FindNextFile,ebx,esi
test eax,eax
jnz next
fini: invoke FindClose,ebx
done: ret
search_dir endp
The only other code needed is the 'found' proc called (oh, and some error checking)
I am inclided to agree with sinsi here, for the amount of code to shell out to CMD.EXE or command.com, you may as well get the data yourself.
Here is a version that returns success in eax plus the exit code in ecx. It seems the topic has moved to "what is the best version of SHELL" ;-)
include \masm32\include\masm32rt.inc
ShellWait PROTO: DWORD, :SDWORD
.code
PathAndCmd db "C:\WINDOWS\notepad.exe C:\WINDOWS\SYSTEM.INI", 0
AppName db "ExecWait:", 0
start: invoke ShellWait, addr PathAndCmd, 0 ; try 2000 as timeout
.if eax
invoke MessageBox, 0, str$(ecx), chr$("We are done with this exit code:"), MB_OK
.else
invoke MessageBox, 0, addr PathAndCmd, chr$("Something went wrong:"), MB_OK
.endif
invoke ExitProcess, 0
ShellWait proc cmdline:DWORD, timeout:SDWORD
LOCAL ExCode:DWORD ; exit code, returned in ecx
LOCAL sinfo:STARTUPINFO
LOCAL pinfo:PROCESS_INFORMATION
mov sinfo.cb, sizeof sinfo ; Windows needs to know the STARTUPINFO version
mov sinfo.dwFlags, STARTF_USESHOWWINDOW ; these two are
mov sinfo.wShowWindow, SW_MAXIMIZE ; optional but useful
invoke GetStartupInfo, addr sinfo ; fill the structure
xor ecx, ecx ; using ecx as NULL saves some bytes
mov ExCode, ecx ; valid only if eax!=0
invoke CreateProcess, ecx, ; no pathname
cmdline, ; the only bit that REALLY counts...!
ecx, ecx, ecx, ; lpProcessAttributes, lpThreadAttributes, bInheritHandles
ecx, ecx, ecx, ; xx_PRIORITY_CLASS, lpEnvironment, lpCurrentDirectory
addr sinfo, addr pinfo
mov eax, pinfo.hProcess
.if eax
push eax
.if timeout<=0 ; for those lazybones who don't want to type INFINITE
mov timeout, INFINITE ; -1
.endif
invoke WaitForSingleObject, eax, timeout ; specify milliseconds here
invoke GetExitCodeProcess, pinfo.hProcess, addr ExCode
invoke CloseHandle, pinfo.hProcess
invoke CloseHandle, pinfo.hThread
pop eax ; hProcess
.endif
mov ecx, ExCode
ret
ShellWait endp ; <-- Edit
end start
mov sinfo.dwFlags, STARTF_USESHOWWINDOW ; these two are
mov sinfo.wShowWindow, SW_MAXIMIZE ; optional but useful
I could swear these flags worked on my office puter with XP SP2. But now it's weekend, and on XP SP1 the flags are being happily ignored by Notepad. When started from a Desktop shortcut, they work, though. Windows mysteries...
Quote from: jj2007 on June 07, 2008, 12:03:35 AM
mov sinfo.dwFlags, STARTF_USESHOWWINDOW ; these two are
mov sinfo.wShowWindow, SW_MAXIMIZE ; optional but useful
I could swear these flags worked on my office puter with XP SP2. But now it's weekend, and on XP SP1 the flags are being happily ignored by Notepad. When started from a Desktop shortcut, they work, though. Windows mysteries...
No Windows mysteries: jj lacked sleep.
Old:
LOCAL pinfo:PROCESS_INFORMATION
mov sinfo.cb, sizeof sinfo ; Windows needs to know the STARTUPINFO version
mov sinfo.dwFlags, STARTF_USESHOWWINDOW ; these two are
mov sinfo.wShowWindow, SW_MAXIMIZE ; optional but useful
invoke GetStartupInfo, addr sinfo ; fill the structure
New:
LOCAL pinfo:PROCESS_INFORMATION
invoke GetStartupInfo, addr sinfo ; fill the structure, then change the two members:
; NO mov sinfo.cb, sizeof sinfo ; Windows hopefully knows the STARTUPINFO version
mov sinfo.dwFlags, STARTF_USESHOWWINDOW ; these two are
mov sinfo.wShowWindow, SW_MAXIMIZE ; optional but useful
After all, I found "invoke ShellWait, addr PathAndCmd, 0" far too clumsy, so I added a macro, see attachment. Usage:
.data
PathAndCmd db "C:\WINDOWS\notepad.exe C:\WINDOWS\SYSTEM.INI", 0
.code
start:
Launch PathAndCmd ; plain launch
Launch PathAndCmd, SW_MAXIMIZE ; gimme a full screen
Launch PathAndCmd, SW_HIDE, 2000 ; do it hidden, and come back soon
[attachment deleted by admin]
The simplest, laziest way is wshell-
.data
cmd2 db 'cmd /c dir "C:\help new\*.htm" /s/w/b/a-d > ' ; or whatever options you want
dest db "C:allhtm.txt",0
.code
invoke wshell,addr cmd2
Quote from: Jimg on December 28, 2010, 03:33:58 PM
The simplest, laziest way is wshell-
Jim,
You realise this thread is a bit old, do you? :wink
But it works indeed. The strange thing is that MSDN is denying the existence of wshell...