If you really want to make the #COFF_xxx statements superfluous and analyse "proc" and "endp" directly please be careful about public and private procs. private procs in separate modules would be useless. So your tool would have to analyse which public procs need which private procs. Possibly libm has to analyse "option proc:xxx" as well then.

Just to remind you :)

Posted on 2003-01-05 02:12:17 by japheth
NaN, thanks for the solution. I think i have to try more to get the exact behavior :) ( I just jumped in when I found problem he..he.. that's me :grin: )
But now I can''t try it anymore. The new one crash on my 2K sp 3 (yes, libm 1.5 crashed :( ). The old one has been deleted, and i can't download it again cause the attachment for your old one has gone. :confused:

About using "Cmd.exe" or "" it is up to what you like. W2K has both file, cmd.exe is for 32bit dos and for 16bit. I prefer to use a 32bit dos, which is also the system default.

For the reason i use prefixed is, I want to run the cmd.exe directly plus i want to give additional parameter ( /C ), to tell cmd.exe to run anything that pass to it as a command line until finish and then terminates itself, the result is a clean system ( no winoldapp left ).

There is a big different between cmd.exe for 2K and for 9X. Cmd.exe only take one single string parameter as command line. This single string can contain the filename to run plus the argument for that file. Cmd.exe will take care of it including long filename. If you want to pass more than one command line, you have to concatenate using symbol "&&"
But on 9X, treat the argument an array of string (separated by spaces) this is why you have to call rundos differently for long filename in 9X (i mentioned this earliar)

in W2K -> RunDOS notepad readme first.txt
in W9X -> RunDOS notepad "readme first.txt"

Look at this code

; NT
invoke lstrcat, Addr lc_Params, SAddr("Cmd.exe /C """)
.While (ln_Count > 0)
invoke lstrcat, Addr lc_Params, CL_argv[esi]
.If (ln_Count > 1)
invoke lstrcat, Addr lc_Params, SAddr(" ")
add esi, 4
dec ln_Count
invoke lstrcat, Addr lc_Params, SAddr("""")

If i run the program like this -> rundos notepad readme file.txt

The lc_Params will be form like this -> Cmd.exe /C "notepad readme file.txt"

invoke lstrcat, Addr lc_Params, SAddr("Command.Com /C ")

.While (ln_Count > 0)
invoke lstrcat, Addr lc_Params, SAddr("""")
invoke lstrcat, Addr lc_Params, CL_argv[esi]
invoke lstrcat, Addr lc_Params, SAddr("""")
.If (ln_Count > 1)
invoke lstrcat, Addr lc_Params, SAddr(" ")
add esi, 4
dec ln_Count

If i run the program like this -> rundos notepad readme file.txt

The lc_Params will be form like this -> /C "notepad" "readme" "file.txt"

So -> RunDOS notepad "readme first.txt"

will be form -> /C "notepad" "readme file.txt"

Well, that's it I guess :grin:
I am sorry if it takes too long, cause my english is not good and i can't explain it in a proper word

Regards :alright:
Posted on 2003-01-05 03:37:24 by HermanT

The "shell" proc works fine and is a part of the end of the installation in MASM32. It is not specifically designed for multithreading but thats hardly a problem when you want to synchronously stop one app while it runs another.

If you wanted to use LOCAL memory for the structures, all you need to do is get the length of both structures and use a REP STOSD or similar to initialise them to zero.

It is by design a simple and unproblematic procedure that works well in practice. You don't need to run ExitProcess() as the application that is started is shut down to restart the calling app.

GetExitCodeProcess is a published API function designed to work with CreateProcess so it is not a problem.

Posted on 2003-01-05 03:39:04 by hutch--
Ok.. im confused...

On one hand, i read that i have to do special consideration for WinNT/2000/XP users (big surprise :rolleyes: ), and on the other hand i have Hutch defending the Shell saying it worked in the Masm32... (and i *know* NT users arnt complaining about it).

So Why is HermanT getting crashes??

HermanT, please post the Error info (EIP, EBP, ESP,..... etc. etc.) so that i can look at where your getting this problem.

Anywho.. here is the latest version. It nolonger requires PROTO and COFF lables. It parses and extracts the PROTO info from the PROC line.

It looks for '<Space/tab>PROC' as a start point, and '<Space/tab>ENDP' to end the function. You only need headers tags now. The files generated are based on the first 8 chars of the file name. As well, the PROC/ENDP is not case sensitive in the search ;)

Example File:

.model flat,stdcall
option casemap:none

include D:\MASM32\INCLUDE\


a2dw proc uses ecx edi edx esi String:DWORD

; Convert decimal string into dword value
; return value in eax

xor ecx, ecx
mov edi, String
invoke lstrlen, String

.while eax != 0
xor edx, edx
mov dl, byte ptr [edi]
sub dl, "0" ; subtrack each digit with "0" to convert it to hex value
mov esi, eax
dec esi
push eax
mov eax, edx
push ebx
mov ebx, 10
.while esi > 0
mul ebx
dec esi
pop ebx
add ecx, eax
pop eax
inc edi
dec eax

mov eax, ecx

a2dw endp

atodw proc String:DWORD

; ----------------------------------------
; Convert decimal string into dword value
; return value in eax
; ----------------------------------------

push esi
push edi

xor eax, eax
mov esi, [String]
xor ecx, ecx
xor edx, edx
mov al, [esi]
inc esi
cmp al, 2D
jne proceed
mov al, byte ptr [esi]
not edx
inc esi
jmp proceed

sub al, 30h
lea ecx, dword ptr [ecx+4*ecx]
lea ecx, dword ptr [eax+2*ecx]
mov al, byte ptr [esi]
inc esi

or al, al
jne @B
lea eax, dword ptr [edx+ecx]
xor eax, edx

pop edi
pop esi


atodw endp


If you have function specific .data to include it must be added within the proc itself! This is no big deal:

Mydata dd 0

rest of proc

This version has no changes regarding this NT crash business... Pleae let me know if it crashes or not, your platform, and debug info if you can..

Otherwise, it works pretty good on Win98SE...
Posted on 2003-01-05 04:50:48 by NaN
For everyone to debate.. here is the code im using to run processes:
   xor eax, eax

lea edx, buffer
mov [edx], eax
invoke szCatStr, addr buffer, CStr("ML.EXE /c /coff ")
invoke szCatStr, addr buffer, addr tempDir
invoke szCatStr, addr buffer, CStr("*.ASM")
invoke ExecuteApp, addr buffer

And the Function:
ExecuteApp PROC lpfilename:DWORD


invoke RtlZeroMemory, addr st_info, sizeof STARTUPINFO
invoke RtlZeroMemory, addr pr_info, sizeof PROCESS_INFORMATION
mov st_info.cb, sizeof STARTUPINFO
mov st_info.dwFlags, STARTF_USESHOWWINDOW
mov st_info.wShowWindow, SW_HIDE

invoke CreateProcess,NULL,lpfilename,NULL,NULL,
ADDR st_info,
ADDR pr_info
invoke WaitForSingleObject, pr_info.hProcess, 300
cmp eax, WAIT_OBJECT_0
jne @B

invoke CloseHandle, pr_info.hThread
invoke CloseHandle, pr_info.hProcess
invoke TerminateProcess, pr_info.hProcess, NULL
ExecuteApp ENDP

Please feel free to point out any problems...
Posted on 2003-01-05 04:58:37 by NaN
NaN: It crashes on my system too (w2k sp3), it appears you call GetCurrentDirectory with the parameters in the wrong order; the first parameter is the buffer size, the second the buffer pointer. On one of the calls (the one before SetCurrentDirectory and StdOut("Compiling Functions...")), you have 100h as the buffer pointer and the address of some local buffer as the buffer size :).


edit: Your tool seems to work fine after I fixed the bug, except for that the lib is called 'test' without an extension.
Posted on 2003-01-05 05:29:53 by Thomas

I think you don't have to use TerminateProcess cause it's already terminated when eax == WAIT_OBJECT_0, unless you have something in the process you created. Also you can actually use INFINITE for the time out

; wait for the interval to elapse
invoke WaitForSingleObject, pr_info.hProcess, 300
cmp eax, WAIT_OBJECT_0
jne @B

; --- replace with
; wait until the process terminated
invoke WaitForSingleObject, pr_info.hProcess, INFINITE

Btw, here is the crashed log

Microsoft (R) Windows 2000 (TM) Version 5.00 DrWtsn32
Copyright (C) 1985-1999 Microsoft Corp. All rights reserved.

Application exception occurred:
App: (pid=1252)
When: 05-01-2003 @ 14:58:48.514
Exception number: c0000005 (access violation)

*----> Task List <----*
0 Idle.exe
8 System.exe
156 SMSS.exe
180 CSRSS.exe
200 WINLOGON.exe
228 SERVICES.exe
240 LSASS.exe
396 svchost.exe
428 svchost.exe
480 spoolsv.exe
540 regsvc.exe
556 mstask.exe
584 WinMgmt.exe
628 inetinfo.exe
1092 explorer.exe
888 CTHELPER.exe
992 EM_EXEC.exe
1132 loadqm.exe
844 netsrv.exe
1212 Proxomitron.exe
308 IEXPLORE.exe
1084 msimn.exe
860 NC.exe
1076 NTVDM.exe
284 NTVDM.exe
1040 CMD.exe
1252 LIBM.exe
1208 DRWTSN32.exe
0 _Total.exe

(00400000 - 00404000)
(77F80000 - 77FFB000)
(77E80000 - 77F35000)

State Dump for Thread Id 0x364

eax=7ffdec08 ebx=00000045 ecx=7ffb0222 edx=00000014 esi=00000104 edi=00000004
eip=77f82eb0 esp=0012f8b4 ebp=0012f8c0 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297

function: RtlUnicodeToMultiByteN
77f82e8f 8a1c0b mov bl,[ebx+ecx] ds:7ffb0222=00
77f82e92 885ef9 mov [esi+0xf9],bl ds:00a7d6d6=??
77f82e95 0fb758f4 movzx ebx,word ptr [eax+0xf4] ds:80a5c1db=????
77f82e99 8a1c0b mov bl,[ebx+ecx] ds:7ffb0222=00
77f82e9c 885efa mov [esi+0xfa],bl ds:00a7d6d6=??
77f82e9f 0fb758f6 movzx ebx,word ptr [eax+0xf6] ds:80a5c1db=????
77f82ea3 8a1c0b mov bl,[ebx+ecx] ds:7ffb0222=00
77f82ea6 885efb mov [esi+0xfb],bl ds:00a7d6d6=??
77f82ea9 0fb758f8 movzx ebx,word ptr [eax+0xf8] ds:80a5c1db=????
77f82ead 8a1c0b mov bl,[ebx+ecx] ds:7ffb0222=00
FAULT ->77f82eb0 885efc mov [esi+0xfc],bl ds:00a7d6d6=??
77f82eb3 0fb758fa movzx ebx,word ptr [eax+0xfa] ds:80a5c1db=????
77f82eb7 8a1c0b mov bl,[ebx+ecx] ds:7ffb0222=00
77f82eba 885efd mov [esi+0xfd],bl ds:00a7d6d6=??
77f82ebd 0fb758fc movzx ebx,word ptr [eax+0xfc] ds:80a5c1db=????
77f82ec1 8a1c0b mov bl,[ebx+ecx] ds:7ffb0222=00
77f82ec4 885efe mov [esi+0xfe],bl ds:00a7d6d6=??
77f82ec7 0fb758fe movzx ebx,word ptr [eax+0xfe] ds:80a5c1db=????
77f82ecb 8a1c0b mov bl,[ebx+ecx] ds:7ffb0222=00
77f82ece 885eff mov [esi+0xff],bl ds:00a7d6d6=??
77f82ed1 6a10 push 0x10
77f82ed3 2bd7 sub edx,edi

*----> Stack Back Trace <----*

FramePtr ReturnAd Param#1 Param#2 Param#3 Param#4 Function Name
0012F8C0 77F8302A 00000100 00000014 0012F8F8 7FFDEC00 ntdll!RtlUnicodeToMultiByteN
0012F8EC 77E88B87 0012F908 00000014 00000000 00132FA7 ntdll!RtlUnicodeStringToAnsiString
0012F910 00401EB6 00000014 00000100 7FFDF000 00000000 kernel32!GetCurrentDirectoryA
0012FFBC 004018BE 77E8D326 00000000 00000000 7FFDF000 !<nosymbols>
0012FFF0 00000000 004018B9 00000000 000000C8 00000100 !<nosymbols>

*----> Raw Stack Dump <----*
0012f8b4 f8 eb fd 7f 08 f9 12 00 - 00 00 00 00 ec f8 12 00 ................
0012f8c4 2a 30 f8 77 00 01 00 00 - 14 00 00 00 f8 f8 12 00 *0.w............
0012f8d4 00 ec fd 7f 28 00 00 00 - fe ff 00 00 f8 eb fd 7f ....(...........
0012f8e4 bf fd 12 00 00 00 00 00 - 10 f9 12 00 87 8b e8 77 ...............w
0012f8f4 08 f9 12 00 14 00 00 00 - 00 00 00 00 a7 2f 13 00 ............./..
0012f904 bc fc 12 00 14 00 fe ff - 00 01 00 00 bc ff 12 00 ................
0012f914 b6 1e 40 00 14 00 00 00 - 00 01 00 00 00 f0 fd 7f ..@.............
0012f924 00 00 00 00 00 00 00 00 - 00 e0 20 20 00 00 80 d3 .......... ....
0012f934 a0 d3 8f b4 c2 01 80 d3 - a0 d3 8f b4 c2 01 80 d3 ................
0012f944 a0 d3 8f b4 c2 01 00 00 - 00 00 1f 01 00 00 00 00 ................
0012f954 00 00 00 00 00 00 54 77 - 6f 2e 61 73 6d 00 63 00 ......Two.asm.c.
0012f964 0c fd 12 00 0c fd 12 00 - 0c fd 12 00 19 99 fb 77 ...............w
0012f974 78 b2 f8 77 ff ff ff ff - 98 fc 12 00 c3 fe f8 77 x..w...........w
0012f984 30 fd 12 00 00 f0 fd 7f - 00 e0 fd 7f 00 00 00 00 0...............
0012f994 43 00 3a 00 5c 00 57 00 - 49 00 4e 00 4e 00 54 00 C.:.\.W.I.N.N.T.
0012f9a4 5c 00 53 00 79 00 73 00 - 74 00 65 00 6d 00 33 00 \.S.y.s.t.e.m.3.
0012f9b4 32 00 5c 00 6e 00 74 00 - 64 00 6c 00 6c 00 2e 00 2.\.n.t.d.l.l...
0012f9c4 64 00 6c 00 6c 00 00 00 - 72 00 6f 00 73 00 6f 00 d.l.l...r.o.s.o.
0012f9d4 66 00 74 00 5c 00 57 00 - 69 00 6e 00 64 00 6f 00 f.t.\.W.i.n.d.o.
0012f9e4 77 00 73 00 20 00 4e 00 - 54 00 5c 00 43 00 75 00 w.s. .N.T.\.C.u.

You don't have to confuse about the different things between 9X - NT (It's only based on my experienced :) ) as long as you play safety, e.g.: use a short filename (8.3 format) just leave it to the system.

Posted on 2003-01-05 06:55:21 by HermanT
NaN, indeed the TerminateProcess call should be superfluous; if you're going to use it for other purposes another day, know that it's not a 100% clean way to terminate other apps, as certain resources might not be cleaned out. DLL usage counts etc...

Also, as herman noticed, use INFINITE as you value for WaitForSingleObject. The neat thing about WaitForSingleObject is that your thread wont be scheduled until the object is triggered, which means you use a little less CPU time than looping WaitForSingleObject or GetExitCodeProcess. Sure, this is hardly noticeable in cases like this, but every little thing counts, right? ;)

herman, why is it you call or cmd.exe to spawn external processes? It ought to be superfluous (unless you're doing it to use shell commands like "copy", in which case you really ought to write your own routines :)).
Posted on 2003-01-05 08:41:19 by f0dder
Thanx to:

f0dder, HermanT, Hutch, Japheth, and Thomas

Everything should be corrected for now.

I also added in an optional directory output location, so you can specifiy an dir if you dont want to use the temp dir.

Use optional output dir:

LIBM .\include\MyAsm.asm .\NewOutDir
LIBM X:\Path\MyAsm.asm X:\Path\NewOutDir

Use Temp Dir:

LIBM .\include\MyAsm.asm
LIBM X:\Path\MyAsm.asm

You only need to spell out the ;#HEADER START/STOP section. Everything else is done automatically (assuming you use PROC and ENDP statements ;) ).

I didnt remake an assembler, so dont expect perfect handling for all cases. If your source compiles, it will run fine thru this tool. However, you have to be mindfull of any .data/.const./data? sections you use.

If you want it to repeate into every function, place it in the scope of the HEADER area. If its specific to a function, place it in the scope of the PROC/ENDP statements..

Thats it! Have fun, and please give feedback ;)
Posted on 2003-01-05 14:28:59 by NaN
Posted on 2003-01-05 15:05:08 by NaN
Your tool's becoming very dangerous, NaN :grin:

I have tested version 2.1 (you may soon reach version 10.0 if speed doesnt slow down)

A very simple test with:

.model flat,stdcall
option casemap:none



myfavorite proc private
mov eax, 1
mov ecx, 2
myfavorite endp

mypublic proc public
call myfavorite
mov edx, 1
mypublic endp



Now guess what has happened! :)

1. compile error since procs myfavorite and mypublic are in different source files (You obviously havent read my last post. Your tools ignores the #COFF statements now, which is BAD.

2. myfavorite as file name has been truncated to 8 bytes. Im relatively sure if I would have 2 procs myfavorite1 and myfavorite2, I will possibly have to search a while for .OBJ of myfavorite1. Havent tested that though


Posted on 2003-01-05 16:34:39 by japheth
japheth: although NaN has written a nice tool, why doesn't the method on the site bitRAKE showed work for you? The only downside is that you have to create a segment around each function and run a utility to set the comdat flags but isn't the result the same as with NaN's tool?

Posted on 2003-01-05 17:02:19 by Thomas
Im open for suggetions...

If you think you can spell out a methology to do things correctly, let me know...
Perhaps external definitions in the header? (i will play around a bit).

If you have a better idea for the file names, please speak up, cause it has to come from somewhere (and i limit it to 8.3 standard). How about FILE001.asm, FILE002.ASM .... would that be better? (perhaps another switch?)

Posted on 2003-01-05 17:12:19 by NaN

Its nice to see someone likes it...

As well, i tried the tool from the link, and while it claims to set up COFF flags in the .OBJ, it doesnt, and still links the entire .obj, not just the proc being called for..

This is why i decieded to make this tool.. (( I hade CFile.asm already which took out 40% of the problem to start with ~~ if you remember this class )).

Posted on 2003-01-05 17:15:27 by NaN
NaN: I tried it with two procs and only one actually used and the proc that wasn't referenced disappeared from the mapfile when I used the enablecomdat tool. Though I haven't checked the actual executable, I assume the map file is correct. I'll have another look tomorrow, I'm too tired right now.

Posted on 2003-01-05 17:18:44 by Thomas
Ya, i dissasembled the One / Two example with one nop, and two nop's in speparate proc's (with segments around it). When dissasembled with OlyDbg it has both of them in there even tho i only called One. :rolleyes:
Posted on 2003-01-05 17:28:47 by NaN
NaN, my suggestion ?s:

1. procs outside of #COFF blocks your tools may handle like it does currently. procs inside #COFF blocks your tool shouldnt touch.

2. your tool shouldnt care about the 8.3 naming scheme, at least not inside #COFF blocks.
Posted on 2003-01-05 18:02:30 by japheth

NaN, my suggestion ?s:

1. procs outside of #COFF blocks your tools may handle like it does currently. procs inside #COFF blocks your tool shouldnt touch.

2. your tool shouldnt care about the 8.3 naming scheme, at least not inside #COFF blocks.

This makes no sense to me. If you want to ignore stuff inside coff blocks, then simply dont add the 'stuff'...?

As well, there is no 8.3 scheme inside anything.. Its to generate file names from function lables.??
Posted on 2003-01-05 18:08:12 by NaN
Japheth, the tool as is will handle your concerns... i just had to play and discover how to handle them :grin:

Here is a sample test file:

.model flat,stdcall
option casemap:none

include D:\MASM32\INCLUDE\


One proc uses ecx edi edx esi String:DWORD
NEST_NEXT TEXTEQU <endp>[/b] ; This could be placed in the header as well
invoke Two,1

One [b]NEST_NEXT[/b]

Two proc [b]private[/b] String:DWORD
Two endp


If i comple a test program to use the generated

.model flat,stdcall
option casemap:none

include \masm32\include\
includelib \masm32\lib\kernel32.lib

includelib test.lib
invoke One, 4
invoke ExitProcess, 0
end start

The generated file is:
00401008 > $ 6A 04          PUSH 4

0040100A . E8 0D000000 CALL TEST2.0040101C
0040100F . 6A 00 PUSH 0 ; /ExitCode = 0
00401011 . E8 00000000 CALL <JMP.&KERNEL32.ExitProcess> ; \ExitProcess
00401016 $-FF25 00104000 JMP DWORD PTR DS:[<&KERNEL32.ExitProcess>
0040101C /$ 55 PUSH EBP
0040101D |. 8BEC MOV EBP,ESP
0040101F |. 51 PUSH ECX
00401020 |. 57 PUSH EDI
00401021 |. 52 PUSH EDX
00401022 |. 56 PUSH ESI
00401023 |. 90 NOP
00401024 |. 90 NOP
00401025 |. 6A 01 PUSH 1
00401027 |. E8 08000000 CALL TEST2.00401034
0040102C |. 5E POP ESI
0040102D |. 5A POP EDX
0040102E |. 5F POP EDI
0040102F |. 59 POP ECX
00401030 |. C9 LEAVE
00401031 \. C2 0400 RETN 4
00401034 /$ 55 PUSH EBP
00401035 |. 8BEC MOV EBP,ESP
00401037 |. 90 NOP
00401038 |. 90 NOP
00401039 |. 90 NOP
0040103A |. 90 NOP
0040103B |. C9 LEAVE
0040103C \. C2 0400 RETN 4

And the generated only has a proto for the function One! Its design is to find a 'PROC' then find the 'ENDP' and then make an asm file based on the data inbetween.. So you nest as many private procs all the way down with the macro, just place a final ENDP when the last private proc is included (to close the file's scope).

Hopefully this is what your hoping for? The only overhead is masking the ENDP until the desired scope is finished, and providing a PROTO within the calling PROC, which is a hell of alot easier than redesigning the tool's engine...

Try it out and let me know what you think..
Posted on 2003-01-05 18:33:18 by NaN

An obvious suggestion if you want synchronous operation with command line tools is to write a batch file that does the work you want. It does not work with GUI apps with a message loop but a linear console app works fine.

Posted on 2003-01-05 20:19:06 by hutch--