First off, do I need an actual window to accept keyboard input? I've looked at Iczelion's tutorials and he did but a window shouldn't be necessary right? How do I accept keyboard input then? Right now I have
include \masm32\include\masm32rt.inc
.data
PromptOne db "Enter a 0 or 1",0
PromptTwo db "Enter another 0 or 1",0
Yes db "Yes",0
No db "No",0
.data?
InputOne db ?
InputTwo db ?
.code
start: push 0
push offset PromptOne
push offset PromptOne
push 0
call MessageBoxA
;get input here
push 0
push offset PromptTwo
push offset PromptTwo
push 0
call MessageBoxA
;get second input
OR InputOne,InputTwo
mov eax,offset Yes
jnz done
mov eax,offset No
done: push 0
push eax
push eax
push 0
call MessageBoxA
push 0
call ExitProcess
end start
I don't really care about limiting my input to just 0 or 1 right now, just getting the input.
Generally speaking, you must have a window to get any input. This window, however, can be a pre-built dialog box, a console window, a task dialog, a "hidden" window that only responds to key events, etc. Even a message box is still a "window", in the technical sense
Well I was meaning a window in the sense of a sizable, minimize/maximize/exit window. So I can get keyboard input with just a messagebox?
3 choices, a console app that handles input and output, a dialog application that has a text box to input data or a normal Window that has a text box to enter text.
What about Iczelion's tutorial 6? (http://win32assembly.online.fr/tut6.html) It has no textbox.
How would I get started in making windows without all the macros? I don't really like using them.
> I don't really like using them.
Thats simple, write the code yourself. There are plenty of examples around. In the masm32 example code, have a look at the example masm1k. If you want a window written in lower level again, look at the axample POASM1k.
if you find a macro you want to replace - the code is in the macros folder
figuring out how the macro works will also help you understand how to write them :U
Thanks hutch. I'm thinking I'll use the POASM1k one and see if I can make any sense of it.
From there, what do I do to get the input? I know I can add a textbox but what other options are there?
And thanks for the tip dave.
there is a nice example...
C:\masm32\examples\exampl02\qikpad
is that what you are looking for ?
I was really just trying to have the user enter 2 single bit numbers and make a half adder or full adder just for fun.
so - you want an edit control...
C:\masm32\examples\bcraven\popinfo
you can combine it with the one in the middle of this demo if you want all the bells and whistles :P
C:\masm32\examples\bcraven\controls
Thanks dave, that should be helpful once I understand how to make a window. Sometimes I just feel :dazzled:
one step at a time
start:
push ebp ; set up a stack frame
mov ebp, esp
sub esp, 96 ; create stack space for locals
xor edi, edi
At the xor edi,edi part there isn't anything in edi yet. Does that just set it to 0 to get it ready?
well - yes
the question is - get it ready for what
you'll have to see how EDI is used in the rest of the code
it probably ought to be pushed, too :P
but...
QuoteAt the xor edi,edi part there isn't anything in edi yet.
there is always something in EDI
Yeah, a couple instructions after that EDI gets pushed and LoadCursor is called.
What does EDI have in it though? How does it have a value when the program starts?
EDIT:
xor edi, edi
mov esi, 400000h ; use constant for the hInstance
mov ebx, OFFSET szClassName
push IDC_ARROW
push edi
call LoadCursor
So EDI gets set to 0 by using exclusive-OR; then I don't know what happens with ESI because I haven't look farther; then EBX gets the window title; and finally we push the arrow we want (our default arrow), and EDI (which equals 0) because the LoadCursor command is LoadCursor(NULL[or 0], Cursor). Instead of IDC_ARROW I could use WAIT or any of the other cursors available.
who knows what it is - lol
all registers always contain some value
well - FPU registers may be an exception to that (pun)
actually, even the FPU registers that are marked as empty have a value of some sort
EDI is one of the registers that should be preserved across function calls
i don't know where the code is coming from, so i don't know the context
The reason why you generally use the "macros" when you write high level code is that it makes your code far more readable than guessing your way through bare mnemonic code. The API calls are all documented in Microsoft reference material and the very simple Intel mnemonics are all fully documented in the Intel manuals.
Bare mnemonic code is not where you start with this stuff, you learn how to write mnemonic code then you learn how to call Windows API functions and this is just to get a bare window up and running.
Dave,
The procedure for the entry point is also the procedure for the exit with RETN so the use of EBX ESI and EDI don't cause any problems in an example as simple as this one. You will note though that ESP and EBP are saved and restored.
thanks Hutch
i wasn't sure if it was a function or the main :P
Here's an example of what you might be looking for as I prefer to hard code too, meaning I don't even use invoke. Not that using INVOKE is wrong, it's just not my preference as I like doing other calculations as the stack is being populated
Begin proc uses edi
LOCAL Wc:WNDCLASSEX, Msg:MSG
lea edi, Wc
push edi
xor eax, eax
push eax
mov al, sizeof WNDCLASSEX
stosd ; cbSize
mov al, CS_HREDRAW or CS_VREDRAW or CS_OWNDC
stosd ; style
mov eax, MainWndProc
stosd ; lpfnWndProc
pop eax
stosd ; cbClsExtra
stosd ; cbWndExtra
push eax
push eax ; lpModuleName
call GetModuleHandle
stosd ; hInstance
mov hInst, eax ; Save global copy of instance handle
pop eax
stosd ; hIcon
push eax ; preserve null
push IDC_ARROW ; lpCursorName
push eax ; hInstance
call LoadCursor
stosd ; hCursor
pop eax ; restore null
mov al, COLOR_APPWORKSPACE + 1
stosd ; hbrBackground
mov al, 0
push eax
stosd ; lpszMenuName
mov eax, offset AppName
stosd ; lpszClassName
pop eax
stosd ; hIconSm
call RegisterClassEx
test ax, ax
.if Zero?
push MB_ICONSTOP or MB_OK
push offset ErrCaption
push offset ErrDefText
push 0 ; Desktop as there isn't a app window yet
call Ext_Error
.else
STYLE equ WS_VISIBLE or WS_MINIMIZEBOX or WS_SYSMENU
push 0 ; lpParam
mov eax, hInst
push eax ; hInstance
xor eax, eax
push eax ; hMenu
push eax ; hWndParent
bts eax, 31
push 320 ; nHeight
push 400 ; nWidth
push 120 ; y
push 128 ; x
push STYLE ; dwStyle
push offset AppTitle ; lpWindowName
push offset AppName ; lpClassName
push WS_EX_STATICEDGE or \
WS_EX_WINDOWEDGE ; dwExStyle
call CreateWindowEx
or eax, eax
.if !Zero?
mov MainWnd, eax ; Save handle to window
lea edi, Msg
.while 1
xor eax, eax ; Nullify register
push eax ; wMsgFilterMax
push eax ; wMsgFilterMin
push eax ; hWnd, pointer to desktop
push edi ; lpMsg
call GetMessage
.break .if (!eax)
push edi ; lpMsg
call TranslateMessage
push edi ; lpMsg
call DispatchMessage
.endw
.endif ; Creating main window failed
.endif ; Registering class failed
mov eax, Msg.lParam ; Set return value in eax
ret
Begin endp
It may be of a benefit too, that although everything created using CreateWindowEx is a window, there is a subtle distinction between a window and controls. As an example, a text box is usually referred to as a control, although technically it is a window. The difference is one of MS dll's will have additional code that gives the control is characteristic behavior, such as horizontal and vertical scrolling.
Thanks Tight_Coder but I think I'll keep with POASM1k for right now.
in this:
; -----------------------------------
; manually coded WNDCLASSEX structure
; -----------------------------------
mov DWORD PTR [ebp-96], 48
mov DWORD PTR [ebp-92], CS_VREDRAW or CS_HREDRAW
mov DWORD PTR [ebp-88], OFFSET MyWndProc
mov DWORD PTR [ebp-84], edi
mov DWORD PTR [ebp-80], edi
mov DWORD PTR [ebp-76], esi
mov DWORD PTR [ebp-72], edi
mov DWORD PTR [ebp-68], eax
mov DWORD PTR [ebp-64], COLOR_BTNFACE+1
mov DWORD PTR [ebp-60], edi
mov DWORD PTR [ebp-56], ebx
mov DWORD PTR [ebp-52], edi
The mov DWORD PTR [ebp-96], 48 is telling the program to move a dword containing 48 to the memory location EBP-96 (Value at EBP minus 96) correct? And the rest of them follow the same calling so that mov DWORD PTR [ebp-80],edi will move a dword containing the value of EDI to the memory location EBP-80 (value at EBP minus 80)?
Generally you have the right idea. To know if there would be any problem one would need to see how WNDCLASSEX was declared in the procedure,
96 = 60H and the structure is 48 = 30H.
This means there are 48 bytes declared for something else beforehand. If these were to be removed, then the values 96-52 would be offset +- those declartions. I'm not familiar with POASAM1k, so do you not have local declarations like ML, ie LOCAL Wc:WNDCLASSEX?
\masm32\examples\poasm\poasm1k
It uses just mnemonics to make a window.
Good example and without modification you can see this is the resultant code
LOCAL msg :MSG
LOCAL wc :WNDCLASSEX
LOCAL Wwd :DWORD
LOCAL Wht :DWORD
LOCAL Wtx :DWORD
LOCAL Wty :DWORD
88 00401037 c745d030000000 mov dword ptr [ebp-30h],30h
89 0040103e c745d400200000 mov dword ptr [ebp-2Ch],2000h
90 00401045 c745d88a114000 mov dword ptr [ebp-28h],offset Winenum!WndProc (0040118a)
91 0040104c c745dc00000000 mov dword ptr [ebp-24h],0
92 00401053 c745e000000000 mov dword ptr [ebp-20h],0
93 0040105a ff7508 push dword ptr [ebp+8]
93 0040105d 8f45e4 pop dword ptr [ebp-1Ch]
94 00401060 c745f010000000 mov dword ptr [ebp-10h],10h
95 00401067 c745f400000000 mov dword ptr [ebp-0Ch],0
96 0040106e c745f8da104000 mov dword ptr [ebp-8],offset Winenum!szClassName (004010da)
Now move the declaration to the bottom of the list then
LOCAL msg :MSG
LOCAL Wwd :DWORD
LOCAL Wht :DWORD
LOCAL Wtx :DWORD
LOCAL Wty :DWORD
LOCAL wc :WNDCLASSEX
88 00401037 c745a430000000 mov dword ptr [ebp-5Ch],30h
89 0040103e c745a800200000 mov dword ptr [ebp-58h],2000h
90 00401045 c745ac8a114000 mov dword ptr [ebp-54h],offset Winenum!WndProc (0040118a)
91 0040104c c745b000000000 mov dword ptr [ebp-50h],0
92 00401053 c745b400000000 mov dword ptr [ebp-4Ch],0
93 0040105a ff7508 push dword ptr [ebp+8]
93 0040105d 8f45b8 pop dword ptr [ebp-48h]
94 00401060 c745c410000000 mov dword ptr [ebp-3Ch],10h
95 00401067 c745c800000000 mov dword ptr [ebp-38h],0
96 0040106e c745ccda104000 mov dword ptr [ebp-34h],offset Winenum!szClassName (004010da)
So you can see, using declarations is better than hard coding, but not to say hard coding wouldn't work. You just have to pay closer attention
Well I hate to bring this thread up again but I have a couple more questions.
include \masm32\include\masm32rt.inc ;standard include
.code
szClassName db "My First Window", 0 ;window title and class name
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
push ebp ; set up a stack frame
mov ebp, esp
sub esp, 96 ; create stack space for locals
xor edi, edi
mov esi, 400000h ; use constant for the hInstance
mov ebx, OFFSET szClassName
push IDC_ARROW ;sets the cursor to the default arrow
;can also use IDC_LOAD, etc
push edi ;EDI now = 0
call LoadCursor ;LoadCursor(NULL[0],LoadCursor[arrow,wait,etc])
; -----------------------------------
; manually coded WNDCLASSEX structure
; -----------------------------------
mov DWORD PTR [ebp-96], 48 ;size in bytes of the structure (48 bytes)
mov DWORD PTR [ebp-92], CS_VREDRAW or CS_HREDRAW ;window class style
;vredraw redraws whole window if a movement or size adjustment
;changes height of area
;hredraw redraws if movement or size adjustment changes width
mov DWORD PTR [ebp-88], OFFSET MyWndProc ;pointer to window procedure
mov DWORD PTR [ebp-84], edi ;number of bytes to allocate following window class structure
;edi = 0 here
mov DWORD PTR [ebp-80], edi ;number of bytes to allocate following window instance
;edi = 0 here still
mov DWORD PTR [ebp-76], esi ;400000h - handle instance that contains window procedure
mov DWORD PTR [ebp-72], edi ;handle to class icon-must be a resource. 0 gives default icon
;edi still = 0
mov DWORD PTR [ebp-68], eax ;handle to class cursor-must be a resource. 0 means application
;must explicitly set cursor shape whenever mouse moves into window
mov DWORD PTR [ebp-64], COLOR_BTNFACE+1 ;handle to background brush-can be physical brush for painting or
;can be color value
mov DWORD PTR [ebp-60], edi ;specifies resource name of class menu. 0 means no default menu
mov DWORD PTR [ebp-56], ebx ;specifies window class name
;ebx contains the string in .data section
mov DWORD PTR [ebp-52], edi ;handle to small icon associated with window class
That's as far as I've gotten in my understanding though I haven't had a whole lot of time to look at it lately. Anyway, at this line:
mov DWORD PTR [ebp-68], eax
EAX has not had anything in it. Dave said that EDI always has something in it. Is this true of EAX as well? I'm assuming that EAX=0 right here just because of the syntax for the RegisterClassEx though. If you want the rest of the code I'll post it.
Look at the last API called, LoadCursor(). Its return value is in EAX and that is being written to the WNDCLASSEX structure.
Oh! I thought that method calls stored the return value in the first listed register though.
So right there EAX contains the IDC_ARROW value. Thanks hutch!
Alright, another clarification.
HWND WINAPI CreateWindowEx(
__in DWORD dwExStyle,
__in_opt LPCTSTR lpClassName,
__in_opt LPCTSTR lpWindowName,
__in DWORD dwStyle,
__in int x,
__in int y,
__in int nWidth,
__in int nHeight,
__in_opt HWND hWndParent,
__in_opt HMENU hMenu,
__in_opt HINSTANCE hInstance,
__in_opt LPVOID lpParam
);
SO these would get pushed in reverse order starting with lpParam right? It should be because it doesn't make sense the other way with the descriptions. However,
typedef struct tagWNDCLASSEX {
UINT cbSize;
UINT style;
WNDPROC lpfnWndProc;
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance;
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCTSTR lpszMenuName;
LPCTSTR lpszClassName;
HICON hIconSm;
} WNDCLASSEX, *PWNDCLASSEX;
in my window is pushed in the listed order it looks like. What is up with that?
Quote from: HiddenDragon on December 17, 2010, 04:02:35 AM
SO these would get pushed in reverse order starting with lpParam right?
What do you mean by "pushed"?
first, the structure is defined for you in windows.inc...
WNDCLASSEX STRUCT
cbSize DWORD ?
style DWORD ?
lpfnWndProc DWORD ?
cbClsExtra DWORD ?
cbWndExtra DWORD ?
hInstance DWORD ?
hIcon DWORD ?
hCursor DWORD ?
hbrBackground DWORD ?
lpszMenuName DWORD ?
lpszClassName DWORD ?
hIconSm DWORD ?
WNDCLASSEX ENDS
structure definitions define a data type - similar to BYTE, WORD, or DWORD
now, to define data using that structure, in the data segment...
.DATA?
WndClss WNDCLASSEX <>
that creates a structure, named WndClss, of type WNDCLASSEX
you can access the elements in the structure, as shown
then, to use the structure, you pass it's address to the function
(after setting the values in the structure, in this case) for example....
mov WndClss.cbSize,sizeof WndClss
INVOKE RegisterClassEx,offset WndClss
only the address of the structure is passed (pushed) - not all the individual elements
sizeof WNDCLASSEX would also work to describe the number of bytes in the structure
however, offset WNDCLASSEX will not work, as the type definition itself has no address
you can use offset in this case because the data is globally defined in the uninitialized data segment
most importantly - if the structure is defined locally inside a proc, you must use addr rather than offset
SomeProc PROC
LOCAL WndClss:WNDCLASSEX
mov WndClss.cbSize,sizeof WndClss
INVOKE RegisterClassEx,addr WndClss
this is because the address of WndClss is relative to the stack pointer and referenced by EBP, the stack frame base pointer
the actual code generated in this case is something like this
push ebp
mov ebp,esp
sub esp,48 ;actual size of structure in bytes
mov WndClss.cbSize,48
lea eax,[ebp-48] ;address of WndClss
push eax
call RegisterClassEx
And here is a quick demonstration of why structures are generally passed by reference, instead of the individual elements being passed by value.
;==============================================================================
include \masm32\include\masm32rt.inc
.686
include \masm32\macros\timers.asm
;==============================================================================
.data
wcx WNDCLASSEX <>
.code
;==============================================================================
test0 proc pwcx:DWORD
mov edx, pwcx
mov eax, [edx].WNDCLASSEX.cbSize
mov eax, [edx].WNDCLASSEX.style
mov eax, [edx].WNDCLASSEX.lpfnWndProc
mov eax, [edx].WNDCLASSEX.cbClsExtra
mov eax, [edx].WNDCLASSEX.cbWndExtra
mov eax, [edx].WNDCLASSEX.hInstance
mov eax, [edx].WNDCLASSEX.hIcon
mov eax, [edx].WNDCLASSEX.hCursor
mov eax, [edx].WNDCLASSEX.hbrBackground
mov eax, [edx].WNDCLASSEX.lpszMenuName
mov eax, [edx].WNDCLASSEX.lpszClassName
mov eax, [edx].WNDCLASSEX.hIconSm
ret
test0 endp
;==============================================================================
test1 proc cbSize:DWORD,style:DWORD,lpfnWndProc:DWORD,cbClsExtra:DWORD,
cbWndExtra:DWORD,hInstance:DWORD,hIcon:DWORD,hCursor:DWORD,
hbrBackground:DWORD,lpszMenuName:DWORD,lpszClassName:DWORD,
hIconSm:DWORD
mov eax, cbSize
mov eax, style
mov eax, lpfnWndProc
mov eax, cbClsExtra
mov eax, cbWndExtra
mov eax, hInstance
mov eax, hIcon
mov eax, hCursor
mov eax, hbrBackground
mov eax, lpszMenuName
mov eax, lpszClassName
mov eax, hIconSm
ret
test1 endp
;==============================================================================
start:
;==============================================================================
invoke Sleep, 3000
counter_begin 10000, HIGH_PRIORITY_CLASS
invoke test0, ADDR wcx
counter_end
print str$(eax)," cycles, by reference",13,10
counter_begin 10000, HIGH_PRIORITY_CLASS
invoke test1, wcx.cbSize,wcx.style,wcx.lpfnWndProc,wcx.cbClsExtra,
wcx.cbWndExtra,wcx.hInstance,wcx.hIcon,wcx.hCursor,
wcx.hbrBackground,wcx.lpszMenuName,wcx.lpszClassName,
wcx.hIconSm
counter_end
print str$(eax)," cycles, by value",13,10,13,10
inkey "Press any key to exit..."
exit
;==============================================================================
end start
Running on a P3:
13 cycles, by reference
31 cycles, by value
And note that the difference would be larger if the called procedures did not access all of the elements.
Well in this:
push edi ;pointer to value through CreateStruct
push esi ;handle instance
push edi ;handle to menu or child
push edi ;handle window parent
push edi ;height in device units
push ecx ;width in device units
push edi ;initial vertical position
push ecx ;initial horizontal position
push WS_OVERLAPPEDWINDOW ;style of window being created
push ebx ;window name
push ebx ;class name, must be created by RegisterClass function
push edi ;extended style of the window
call CreateWindowEx ; create the main window
The parts are pushed in reverse order, as they should be. However, in
; -----------------------------------
; manually coded WNDCLASSEX structure
; -----------------------------------
mov DWORD PTR [ebp-96], 48 ;size in bytes of the structure (48 bytes)
mov DWORD PTR [ebp-92], CS_VREDRAW or CS_HREDRAW ;window class style
;vredraw redraws whole window if a movement or size adjustment
;changes height of area
;hredraw redraws if movement or size adjustment changes width
mov DWORD PTR [ebp-88], OFFSET MyWndProc ;pointer to window procedure
mov DWORD PTR [ebp-84], edi ;number of bytes to allocate following window class structure
;edi = 0 here
mov DWORD PTR [ebp-80], edi ;number of bytes to allocate following window instance
;edi = 0 here still
mov DWORD PTR [ebp-76], esi ;400000h - handle instance that contains window procedure
mov DWORD PTR [ebp-72], edi ;handle to class icon-must be a resource. 0 gives default icon
;edi still = 0
mov DWORD PTR [ebp-68], eax ;handle to class cursor-must be a resource. 0 means application
;must explicitly set cursor shape whenever mouse moves into window
mov DWORD PTR [ebp-64], COLOR_BTNFACE+1 ;handle to background brush-can be physical brush for painting or
;can be color value
mov DWORD PTR [ebp-60], edi ;specifies resource name of class menu. 0 means no default menu
mov DWORD PTR [ebp-56], ebx ;specifies window class name
;ebx contains the string in .data section
mov DWORD PTR [ebp-52], edi ;handle to small icon associated with window class
The elements are moved in normal order which is backwards what it should be.
EDIT: Is this because the DWORDS for the WndClassEx structure are being moved, not pushed?
No, its because you fail to understand the difference between a function and a structure. Windows function regularly use the VAX format of filling a structure then passing its ADDRESS to the function code.
Functions under STDCALL are pushed right to left where a structure is something like an array.
When you indicated that you did not want to use any high level constructs in MASM you were offered bare mnemonic code which is possible under MASM but the problem is you don't yet know enough to write that type of code.
Deriving theories about how this style of code works is a waste of members time.
What you need to do is learn some very basic things about x86 assembler, data SIZE to register SIZE, how function calls work under different calling conventions, differences between the address of a variable and its content, how and why data structures are written and some grasp of how the Windows API functions work.
If you can get this together it will make your questions a lot easier to answer and you will get off the ground much faster. When you know enough you will easily be able to write the lowest level of mnemonic code if you have some reason to do so.
Alright, I'll start working with Iczelion's tutorials then hutch.