Hey guys! First of all, I'd like to introduce myself. I'm Yart, and I've been coding in C++, HTML, BASIC, and some other languages for years now, with my primary interest in game development. I decided to take a stab at ASM because the efficiency and speed is extremely appealing to me and I love doing things with the least amount of resources/smallest footprint possible. I love the challenge.
I learned some basic DOS ASM in college, but I decided to take what I learned and go into Windows 32 development, hoping to make my games in ASM in the future. Crazy? Probably, but it's something I really want to try!
So anyways, it's been years since the last time I did ASM, but looking through Iczelion's MASM32 tutorials ( http://win32assembly.online.fr/tutorials.html ) it was starting to come back to me. I got to Tutorial 3 ( http://win32assembly.online.fr/tut3.html ) and after a lot of typing and hours trying to find a misplaced comma, I finally had it up and working and was proud of my first Window I ever drew in ASM.
I wanted to take the WinMain procedure and put it in a separate .inc file for easy management and to keep things neat and clean (and for re-using later in other programs). However, I ran into a few problems transferring it over to a new file.
I added two more arguments to the prototype, one for ClassName and one for AppName, so it knows what they are in the .inc file (I got an error that it had no clue what it was but this fixed that). Now my problem is this when I try to compile:
Quotewinmain.inc(30) : error A2098: invalid operand for OFFSET
Line 30 is this:
mov wc.lpszClassName,OFFSET ClassName
Now, I don't know what I'm doing wrong here. That line works when I have all the code inside the main tut3.asm file.
I'll put my code (both files) as an attachment. What am I missing? I can't figure it out, and I've tried Googling the error but I get different results.
And yea it's pretty much an exact copy (with my own notes for my own reference) of the tutorial, only with my attempt to organize it nicer.
Thanks in advance! :)
You can't use OFFSET on function arguments. It expects an absolute location (e.g. global variables), whereas arguments and local variables are on the stack, so they're relative to esp (and ebp with a stack frame.)
Pass in ClassName and AppName as pointers to strings, rename them to pClassName and pAppName, and then use them directly as values (no OFFSET or ADDR)
Also, you don't need to strip off the variable names for PROTO, this is fine:
WinMain proto hInst:HINSTANCE,pCmdLine:LPSTR,CmdShow:DWORD,pClassName:DWORD,pAppName:DWORD
(CmdLine is also a pointer to a string, and hPrevInst isn't used since Windows 3.1)
Alright thanks for the quick reply!
Now I'm pretty fuzzy on pointers for ASM (I don't even recall learning them), but from what I understand I put the address in EAX with mov right?
So like,
mov eax, ClassName
?
And do I do that from outside the procedure? Or am I missing something obvious here?
Yea I'm completely lost when it comes to pointers. Even in C++ I had trouble with 'em. I guess the topic should be "How do I make a pointer?" eh? :P
EDIT: I'm going to relearn basic command line ASM (32-bit) before tackling a Win32 application. If I can't even do pointers yet, I think jumping right to graphical programs will be a little much and silly of me. Haha!
QuoteAlso, you don't need to strip off the variable names for PROTO
i find it makes it easier to read if you do, though :P
what makes sense is to ensure that the types match
i modified the lines like this...
WinMain PROTO :HINSTANCE,:HINSTANCE,:LPSTR,:UINT,:LPSTR,:LPSTR
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:UINT,lpszClassName:LPSTR,lpszAppName:LPSTR
invoke WinMain,hInstance,NULL,CommandLine,SW_SHOWDEFAULT,offset szClassName,offset szAppName
if you don't already know...
"lp" is Hungarian notation for long pointer
"p" is for pointer (same as long pointer in win32)
"sz" refers to a zero-terminated string
"lpsz" refers to a long pointer to a zero-terminated string
see attached...
Don't worry about pointers, they're actually very simple - too simple - especially when compared to C++ pointers. That's why you don't remember learning them - there isn't anything to learn!
A value is a value.
A pointer is the address/offset of a value, e.g. a variable or a function.
Class dismissed. :bg
The call to WinMain would look like this:
invoke WinMain, hInstance,CommandLine,SW_SHOWDEFAULT,ADDR ClassName,ADDR AppName
;in other words: WinMain(<value>,<pointer_value>,<value>,<pointer>,<pointer>)
And then in WinMain itself:
WinMain proc hInst:HINSTANCE,pCmdLine:LPSTR,CmdShow:DWORD,pClassName:DWORD,pAppName:DWORD
LOCAL wc:WNDCLASSEX ;create local variables on stack
LOCAL msg:MSG
LOCAL hwnd:HWND
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style,CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
;using the passed in value
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_WINDOW+1
mov wc.lpszMenuName,NULL
;pClassName is a pointer to the classname string
;can't do a direct memory-to-memory move
mov eax,pClassName
mov wc.lpszClassName,eax
invoke LoadIcon,NULL,IDI_APPLICATION
mov wc.hIcon,eax
mov wc.hIconSm,eax
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
invoke RegisterClassEx, addr wc ;Register the window class.
INVOKE CreateWindowEx, NULL,\
;values can be pushed directly though (pointers are values that point to other values)
pClassName,\
;him too
pAppName,\
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,\
NULL,\
NULL,\
hInst,NULL
mov hwnd,eax
invoke ShowWindow, hwnd,CmdShow ;Show the window on desktop.
invoke UpdateWindow, hwnd ;Refresh client area
;...etc...
ret
WinMain endp
Quote from: dedndave on September 28, 2011, 02:07:04 PM
QuoteAlso, you don't need to strip off the variable names for PROTO
i find it makes it easier to read if you do, though :P
I wonder what this function does..
Mystery PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORDYes, very easy to read, but meaningless.
Quote from: Tedd on September 28, 2011, 03:10:36 PM
I wonder what this function does.. Mystery PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
Yes, very easy to read, but meaningless.
:bg
i understand where you're coming from, Ted
but is the list of prototypes where we would look for that info ? - probably not
more likely we would look at the proc for the parameter names and what they are
prototypes describe the proc to the
assembler so that it knows what to expect in an INVOKE
have a look at kernel32.inc :P
I personally see the main advantage of PROTO is the data size and count checking rather than its documentation. The matching PROC must have the arguments by name which you can use as the documentation and I tend to put notes in procedures so you can read what they do later but there is nothing wrongs with using named variables in a prototype, just a bit more typing. System function are normally independently documented such as Windows API calls so there is little need to go poking around an include file for the arguments.
well - i do see one drawback in placing parameter names in the prototypes
if you change them, now you have to change them in 2 places
there is a good chance that the PROTOtype may wind up not matching the PROC
If you change the parameters, you need to make sure the prototype matches in any case.
The difference is, with parameter names, it's a simple copy-and-paste of one line (and change proc to proto) - quick, easy, no mistakes.