Here is a new technique enabling function calls without using the invoke statement.
An example:


.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include stcall.inc
include kernel32.inc
include user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data
MsgCaption db "Iczelion's tutorial no.2",0
MsgBoxText db "Win32 Assembly is Great!",0

.code
start:
MessageBox, NULL,addr MsgBoxText, addr MsgCaption, MB_OK
ExitProcess,NULL
end start


The technique is based on using equates having the same name of API functions. All these equates are associated with a macro simulating the invoke statement. For this purpose, I coded a tool named convinc creating new ones from Hutch's include files:


EXTERNDEF MessageBoxA@16:proc
MessageBox equ <stcall MessageBoxA@16,4>

EXTERNDEF MessageBoxExA@20:proc
MessageBoxEx equ <stcall MessageBoxExA@20,5>

EXTERNDEF MessageBoxIndirectA@4:proc
MessageBoxIndirect equ <stcall MessageBoxIndirectA@4,1>


The stcall macro works like invoke,it creates the necessary push,lea and call instructions for every API function call in the source file. Plus, stcall checks the number of passed parameters and the user is notified if the number of parameters is incorrect.

The macro for stdcall functions:


stcall MACRO funcname:REQ,param,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19,p20
local pos,counter
counter=0

FOR arg,<p20,p19,p18,p17,p16,p15,p14,p13,p12,p11,p10,p9,p8,p7,p6,p5,p4,p3,p2,p1>

IFNB <arg>
counter=counter+1
pos=@InStr(1,arg,<ADDR>) OR @InStr(1,arg,<addr>) OR @InStr(1,arg,<Addr>)

IF pos

IF (OPATTR(@SubStr(arg,%pos+5))) EQ 98
lea eax,@SubStr(<arg>,%pos+5)
push eax
ELSE
push OFFSET @SubStr(<arg>,%pos+5)
ENDIF

ELSE
push arg
ENDIF
ENDIF
ENDM
IF counter NE param
tmp TEXTEQU %(@Line)
% echo Line : tmp , funcname , invalid number of parameters
.ERR
ENDIF

call funcname
ENDM


I coded also two other macros ccall and ccallva ( variable arguments) for C functions. You can create your own functions to be called directly without using the invoke macro. Instead of declaring WinMain with proto, you can type the following:


WinMain equ <stcall _WinMain,4>


Here, the underscore or any valid symbol is required to avoid conflicts / symbol redefinitions:


_WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
.
.
_WinMain ENDP


Even, it's possible to use Hutch's func macro to nest function calls in other direct function calls:


FUNC MACRO parameters:VARARG
parameters
EXITM <eax>
ENDM
.
.
mov hInstance,FUNC(GetModuleHandle,NULL)
mov CommandLine,FUNC(GetCommandLine)


The usage of convinc:


convinc incfile.inc [-w] ( Optional -w for UNICODE )


An example:


convinc \masm32\include\kernel32.inc

The new kernel32.inc will be created at the current directory.
Statements like *.inc are O.K.

You can check the attachment for various examples of direct function calls.
Posted on 2004-03-14 09:23:54 by Vortex
You just gave me a crazy idea.....

What I was using:


externdef _imp__ExitProcess@4:NEAR

LCALL@4 TYPEDEF proto :dword
FCALL@4 TYPEDEF PTR LCALL@4

ExitProcess TEXTEQU <FCALL@4 PTR _imp__ExitProcess@4>

.code
invoke ExitProcess,0

My crazy idea:


externdef _imp__ExitProcess@4:NEAR

LCALL@4 TYPEDEF proto :dword
FCALL@4 TYPEDEF PTR LCALL@4

ExitProcess TEXTEQU <invoke FCALL@4 PTR _imp__ExitProcess@4>

.code
ExitProcess,0

:grin:
Posted on 2004-03-14 19:55:25 by ThoughtCriminal
; Create protos PR0..PR24, ; ptr's PPR0..PPR024
; and equates PROTO0 equ proto:dword...

makeproto MACRO _COUNT
$ARGS equ <>
repeat _COUNT
$ARGS catstr $ARGS, <, :DWORD>
endm
CNT@ textequ %_COUNT
% PR&CNT@ typedef PROTO $ARGS ; PR0, PR1...
% PPR&CNT@ typedef ptr PR&CNT@ ; PPR0, PPR1...
% PROTO&CNT@ equ PROTO&$ARGS ; PROTO0, PROTO1...
ENDM

I@ = 0
while I@ lt 25
makeproto I@
I@ = I@ + 1
endm

Now we have general typedefs PPR0..PPR24

Tip: Put the ',' in the equate

externdef _imp__ExitProcess@4:NEAR
ExitProcess TEXTEQU <invoke PPR1 _imp__ExitProcess@4,>

externdef _imp__MessageBox@16:NEAR
ExitProcess TEXTEQU <invoke PPR4 _imp__MessageBox@16,>

.code
MessageBox hwnd, text, cap, style
ExitProcess 0
Posted on 2004-03-15 05:07:56 by gfalen
:grin:

To make it C like,

Why not

externdef _imp__ExitProcess@4:NEAR
ExitProcess( TEXTEQU <invoke PPR1 _imp__ExitProcess@4,>

externdef _imp__MessageBox@16:NEAR
ExitProcess( TEXTEQU <invoke PPR4 _imp__MessageBox@16,>

) TEXTEQU " "

.code
MessageBox( hwnd, text, cap, style)
ExitProcess(0)

Or something like that.. Have not tested it though..
Posted on 2004-03-15 05:43:50 by roticv
externdef _imp__MessageBox@16:NEAR
ExitProcess MACRO params:VARARG
EXITM @CatStr(<invoke PPR4 _imp__MessageBox@16,>,params)
ENDM

ExitProcess(0)

...should work, or something similar. One problem is that I like to put my INVOKE's split between lines, and this cannot be done with macros using EXITM.

MessageBox( hwnd, \

text, \
cap, \
style)
...does not work.
Posted on 2004-03-15 07:51:37 by bitRAKE
Wow,

That's great, maybe with just a little more work we can make assembler look like this :

static LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)

{
switch (msg)
{
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
}
break;

case WM_SETCURSOR:
SetCursor(NULL);
return 1;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
break;
}

return 0;
}


Sorry, just couldn't resist. I tried but I couldn't stop myself :)
Posted on 2004-03-15 11:50:15 by donkey
ThoughtCriminal,

Many thanks for your method using the TYPEDEF and PTR operators. From your previous posts, I saw that you worked a lot on the PROTO problem. Nice works.
This one also works:


.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

externdef _imp__ExitProcess@4:NEAR
externdef _imp__MessageBoxA@16:NEAR

LCALL@16 TYPEDEF PROTO :DWORD,:DWORD,:DWORD,:DWORD
FCALL@16 TYPEDEF PTR LCALL@16
LCALL@4 TYPEDEF PROTO :DWORD
FCALL@4 TYPEDEF PTR LCALL@4

ExitProcess TEXTEQU <invoke FCALL@4 PTR _imp__ExitProcess@4>
MessageBox TEXTEQU <invoke FCALL@16 PTR _imp__MessageBoxA@16>

.data
MsgCaption db "Iczelion's tutorial no.2",0
MsgBoxText db "Win32 Assembly is Great!",0

.code
start:
MessageBox,NULL,addr MsgBoxText, addr MsgCaption, MB_OK
ExitProcess,NULL
end start
Posted on 2004-03-15 14:18:52 by Vortex

Wow,

That's great, maybe with just a little more work we can make assembler look like this :



<<C code snipped>>


Sorry, just couldn't resist. I tried but I couldn't stop myself :)


Sorry, it takes a little be more effort that "just a little more work" :-).
Been there, done that. The HLA source code is well in excess of 100,000 lines of code.

BTW, don't think I didn't try the MASM macro route a long time ago. Alas, there are a couple of problems if you keep pushing the envelope:

1. The macros aren't very robust. Everything looks cool until you make a mistake in the source code. Then the bizarre set of error messages you get back make you truly realize that macros in MASM aren't, perhaps, the best way to do this.

2. You discover real quick that MASM's macro facilities are just strong enough to let you do some really cool things, but they tend to be insufficient to carry the whole thing to it's logical conclusion (i.e., "with just a little more effort").

Of course, the last point I'd make is that although these macros are kind of cute, you can do all this stuff today with HLA, e.g.,


procedure WindowProc( hwnd:HWND_t; msg:uns32; wParam:WPARAM_t; lParam:LPARAM_t)
begin WindowProc;

mov( msg, eax );
switch (eax)

case( WM_KEYDOWN )
if( wParam, = VK_ESCAPE then

PostQuitMessage(0);

endif;

case( WM_SETCURSOR )
SetCursor(NULL);
mov( 1, eax );
exit WindowProc;

case( WM_DESTROY )
PostQuitMessage(0);


default
DefWindowProc(hwnd, msg, wParam, lParam);
exit WindowProc;

endswitch;
mov( 0, eax );

end WindowProc;


Note, btw, that "switch/case/default/endswitch" is a macro in HLA. HLA does have slightly more powerful macro facilities than MASM :-).
Cheers,
Randy Hyde
Posted on 2004-03-15 14:47:51 by rhyde
Hi Randy,

I was being facetious, I personally like assembler and see no need to make it more like C, if you want C there are many free compilers for it. If you want assembler then there are many free versions of that as well. I purposefully picked a peice of code with Switch/Case in it, you know exactly why ;)
Posted on 2004-03-15 15:12:23 by donkey
Thanks Vortex,

The TYPEDEF idea came from bitRAKE. Way back when I was trying to figure out how to do an indirect call.

The idea behind the TEXTEQUs it to pack as much needed information as possible into one statement.

Rather than doing it this way: (from kernel32.inc)

CopyFileExW PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
CopyFileW PROTO :DWORD,:DWORD,:DWORD

I can just use:

CopyFileExW TEXTEQU <invoke FCALL@4 PTR _imp__CopyFileExW@24>
CopyFileW TEXTEQU <invoke FCALL@4 PTR _imp__ExitProcess@12>

Now you got the invoke, type checking ect. all in one line.

I dont use the MASM32 includes much anymore. This way is easier for me.

gfalen:

Regarding the ',', I guess it is a matter of personal preferance. Adding the comma myself just seems right to me. With your style only the first would not need a comma, but any additional would need a comma. The first parameter is now a special case. Also there are some APIs or even using this style for your own procedures, you might not have any paramaters. That is another special case where you do not include the ',' in the definition.
Posted on 2004-03-15 20:22:13 by ThoughtCriminal
The limitation in MASM macros inside brackets that can only occur on a single line is a pain but you can do most of the HLL stuff with a few others that don't enclose whole function calls in brackets.


Switch uMsg
Case WM_INITDIALOG
fn SendMessage,hWin,WM_SETICON,1, \
FUNC(LoadIcon,NULL,IDI_ASTERISK)
m2m hWnd, hWin
return 1
Case WM_COMMAND
Switch wParam
Case IDOK
fn MessageBox,hWnd,"You clicked OK", \
"Hi",MB_OK or MB_ICONINFORMATION
Case IDCANCEL
jmp quit_dialog
EndSw
Case WM_CLOSE
quit_dialog:
invoke EndDialog,hWin,0
EndSw

return 0

I actually don't care if assembler does not look like C, I prefer it to look like assembler and that can be done with MASM.
Posted on 2004-03-15 21:20:32 by hutch--
i agree with donkey though -

There is no reason to make ASM look like C, or any other HLL for that matter. People use ASM for size, and speed, and when you add an excessive amount of overhead macros, it creates alot of code bloat. Besides, why use ASM at all if you're only trying to mimic HLL syntax? Why not just use C? You can create smaller binaries and more optimized code using HLL C, than you can using HLL ASM.


-drocon
Posted on 2004-03-15 22:00:14 by Drocon
Hi ThoughtCriminal,

The method based on using TYPEDEF syntax to embed the "invoke" doesn't work with static libraries. It looks like that it's not possibe to declare functions from static libs with the TYPEDEF PROTO syntax. Is there any solution for this problem?
Posted on 2004-03-19 13:05:55 by Vortex
What's the problem with TYPEDEF PROTO for static libs? Perhaps a name decoration thing? STDCALL vs. C, probably... try doing "TYPEDEF PROTO C ..." instead of "TYPEDEF PROTO ..."?
Posted on 2004-03-19 13:21:40 by f0dder
Hi f0dder,

First problem, the masm32 lib function declaration below doesn't work (linking error):


EXTERNDEF _imp__StdOut@4:PROC
StdOut equ <invoke ppr1 PTR _imp__StdOut@4>


So, changing it to:


EXTERNDEF szCatStr@8:PROC
szCatStr equ <invoke ppr2 PTR szCatStr@8>

EXTERNDEF StdOut@4:PROC
StdOut equ <invoke ppr1 PTR StdOut@4>
.
.
.data
msg1 db 'Hello ',0
db 11 dup(0)
msg2 db 'my friend!',0

.code
start:
szCatStr,ADDR msg1,ADDR msg2
StdOut,ADDR msg1
ExitProcess,0
END start


This example seems to be builded O.K, but it crashes on running:


00401000 >/$ 68 12304000 PUSH Console.00403012 ; ASCII "my friend!"
00401005 |. 68 00304000 PUSH Console.00403000 ; ASCII "Hello "
0040100A |. FF15 30104000 CALL DWORD PTR DS:[401030] <---- Here
00401010 |. 68 00304000 PUSH Console.00403000 ; ASCII "Hello "
00401015 |. FF15 60104000 CALL DWORD PTR DS:[401060] <---- Here
0040101B |. 6A 00 PUSH 0 ; /ExitCode = 0
0040101D \. FF15 08204000 CALL DWORD PTR DS:[<&kernel32.ExitProces>; \ExitProcess
00401023 CC INT3
00401024 CC INT3
00401025 CC INT3
00401026 CC INT3
00401027 CC INT3
00401028 CC INT3
00401029 CC INT3
0040102A CC INT3
0040102B CC INT3
0040102C CC INT3
0040102D CC INT3
0040102E CC INT3
0040102F CC INT3
00401030 . 558BEC57 DD 57EC8B55 <--- Here is the problem


You can check the console example in the attachment ( included a new version of convinc )
Posted on 2004-03-19 14:54:36 by Vortex
I have never used masm32.lib, but I dumped the public symbols:

If your are going to use this style, it helps to dump the symbols to make sure you are using the same syntactic form:

1CAA _szLeft@12
1E0E _szCatStr@8
1F96 _StrLen@4
210C _StripRangeX@16
2288 _StripLF@4
23DE _StripRangeI@16
2558 _StdOut@4
2748 _StdIn@8

All the Microsoft libs use the _imp__ form.
That explains your linker error.

I'll take a closer look later, but right now try this:


Change this:

EXTERNDEF szCatStr@8:PROC
szCatStr equ <invoke ppr2 PTR szCatStr@8>

EXTERNDEF StdOut@4:PROC
StdOut equ <invoke ppr1 PTR StdOut@4>

to this...

EXTERNDEF [COLOR=red]_[/COLOR]szCatStr@8:PROC
szCatStr equ <invoke ppr2 PTR szCatStr@8>

EXTERNDEF [COLOR=red]_[/COLOR]StdOut@4:PROC
StdOut equ <invoke ppr1 PTR StdOut@4>
Posted on 2004-03-19 22:25:35 by ThoughtCriminal
Also change this:


EXTERNDEF szCatStr@8:PROC
szCatStr equ <invoke [COLOR=red]pr2[/COLOR] PTR szCatStr@8>

EXTERNDEF StdOut@4:PROC
StdOut equ <invoke [COLOR=red]pr1[/COLOR] PTR StdOut@4>

I cannot get your example to assemble. I'm using MASM32 v7.

The problem is it is treating the proc entry point as a pointer:


0040100A |. FF15 30104000 CALL DWORD PTR DS:[401030] --->DD 57EC8B55
00401010 |. 68 00304000 PUSH Console.00403000 ; ASCII "Hello "
00401015 |. FF15 60104000 CALL DWORD PTR DS:[401060] <---- Here

The original reason for the TYPEDEFs was to get pointer style calls that start with FF.
Using just pr2 should get you the proper E8 or relative call for static code.
Posted on 2004-03-20 00:21:30 by ThoughtCriminal
ThoughtCriminal,

This syntax works fine:


EXTERNDEF szCatStr@8:PROC
szCatStr equ <invoke pr2 PTR szCatStr@8>

EXTERNDEF StdOut@4:PROC
StdOut equ <invoke pr1 PTR StdOut@4>


Many thanks.

Another thing, when I use your invoke & TYPEDEF PROTO method,I get direct function calls instead of the lookup table.
Posted on 2004-03-20 02:39:54 by Vortex
Seems like a few people are still confused about static libraries and import libraries :). The __imp__* convention is "usually a pretty good indication" :) that you're dealing with an import, at least in microsoft generated libraries. In theory you could call them anything you want, I dunno if there's tools that generate non-standard names (Borland? GNU?).

Static libraries obviously don't generate IAT slots and jmp-indirect thunks, so you just call those funcs directly, as they get embedded in your executable (masm32.lib, d3d8x.lib, htmlhelp.lib ...).

When generating includes from libraries, things can become tricky - a few libraries include both static code and imports, if nothing else a bunch of COM stuff has static IID's and imports from DLLs.

My protoize tool currently chokes on libs like this, I really need to fix up the library parsing + symtab code a bit :)
Posted on 2004-03-20 04:36:47 by f0dder
f0dder,

Did you release your protoize tool? I guess you are working since longtime on this tool.

My original stcall macro handles successfully API functions and static lib functions.
Posted on 2004-03-20 05:10:25 by Vortex