News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Getting a "DIR"

Started by Jimg, June 04, 2008, 07:14:56 PM

Previous topic - Next topic

Jimg

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

Jimg

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?

Jimg

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.

jj2007

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

Jimg

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

jj2007

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.

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.

sinsi

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... ::)
Light travels faster than sound, that's why some people seem bright until you hear them.

MichaelW

eschew obfuscation

jj2007

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

sinsi

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).
Light travels faster than sound, that's why some people seem bright until you hear them.

jj2007

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

sinsi

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... ::)
Light travels faster than sound, that's why some people seem bright until you hear them.

jj2007

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.

sinsi

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.
Light travels faster than sound, that's why some people seem bright until you hear them.

jj2007

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