News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Tutorial #3 - Creating a window question

Started by Sieg, April 23, 2015, 12:47:09 PM

Previous topic - Next topic

Sieg

http://www.programminghorizon.com/win32assembly/tut3.html


    [...]
    mov   wc.lpfnWndProc, OFFSET WndProc
    mov   wc.cbClsExtra,NULL
    mov   wc.cbWndExtra,NULL
    push  hInstance
    pop   wc.hInstance
    mov   wc.hbrBackground,COLOR_WINDOW+1
    mov   wc.lpszMenuName,NULL
    [...]


Hi, I had to lookup the difference for why the author used push/pop instead of mov. Is this because hInstance is a memory address and mov will not work with a memory address? I think I understood that correctly. Any clarification? Thanks!

NoCforMe

Both MOV and PUSH/POP work with memory operands. The author simply chose to use the stack to move hInstance,  rather than MOVing it.

They could just have easily and correctly done this:

    MOV  EAX, hInstance
    MOV   wc.hInstance, EAX


assuming EAX isn't being used for something; I didn't look at the example, so maybe that register is being used to hold a value.

The end effect is the same. The PUSH/POP sequence is useful in certain situations, for example when all other scratch registers (EAX, ECX, EDX) are being used and you don't want to change their contents.
Assembly language programming should be fun. That's why I do it.

Mikl__

Hi, Sieg!
write instead    push  hInstance
    pop   wc.hInstance
instruction   mov wc.hInstance,400000h

dedndave

what hasn't been explained....

there are very few instructions that actually perform memory-to-memory moves
the MOVS string instructions and PUSH/POP are the only ones that come to mind

this is not legal addressing
    mov     wc.hInstance,hInstance

i work around this issue altogether by declaring a single WNDCLASSEX structure in the .DATA section
i use the same structure for all window classes
then, rather than storing hInstance in a dword of it's own, i store it directly in wc.hInstance
throught the rest of the program, i use wc.hInstance as the instance handle

it makes sense to do it because it takes fewer bytes in the .DATA section
than the number of code bytes it takes to initialize the structure - lol

jj2007

Quote from: Mikl__ on April 23, 2015, 01:52:44 PM
Hi, Sieg!
write instead    push  hInstance
    pop   wc.hInstance
instruction   mov wc.hInstance,400000h

Don't do that, it won't work on all Windows versions.

Mikl__

Hi, jj2007!
QuoteDon't do that, it won't work on all Windows versions
Why? If you not changed Linker Options \base then hIstance=400000h. It is example my Iczelion's tut#3 .586
.model tiny,stdcall
;for WinXP - 508 bytes
include windows.inc
exebase equ 400000h
.code
main:
include capito.asm
;---------------------------------------------------------
start: xor ebx,ebx
mov esi,exebase
mov edi,offset wTitle+exebase
;------------------------------
; registering the window class
;------------------------------
invoke RegisterClass,esp,ebx,offset window_procedure+exebase,\
        ebx,ebx,esi,ebx,10011h,COLOR_WINDOW+1,ebx,edi
;--------------------------+
; creating the main window |
;--------------------------+
push ebx
push esi
shl esi,9
invoke CreateWindowEx,ebx,edi,\
edi,WS_OVERLAPPEDWINDOW or WS_VISIBLE,esi,esi,esi,esi,ebx,ebx
mov ebp,esp
;---------------------------+
; entering the message loop |
;---------------------------+
message_loop: invoke GetMessage,ebp,ebx,ebx,ebx
invoke DispatchMessage,ebp
jmp message_loop
;----------------------+
; the window procedure |
;----------------------+
window_procedure: cmp dword ptr [esp+08],WM_DESTROY
je short wmDESTROY
jmp DefWindowProc+exebase
wmDESTROY: invoke ExitProcess,ebx
;---------------------------------------------                         
wTitle db 'Iczelion Tutorial #3:A Simple Window in MASM'
;-------------------------------------------------------
import:
dd 0,0,0,user32_dll,user32_table
dd 0,0,0,kernel32_dll,kernel32_table
dd 0
kernel32_dll db 'kernel32'
dd 0
kernel32_table:
ExitProcess             dd _ExitProcess,0
user32_table:
RegisterClass dd _RegisterClass
CreateWindowEx          dd _CreateWindowEx
GetMessage              dd _GetMessage
DispatchMessage         dd _DispatchMessage
DefWindowProc           dd _DefWindowProc
                        dw 0
_RegisterClass db 0,0,'RegisterClass'     
_CreateWindowEx db 'A',0,'CreateWindowEx'
_GetMessage db 'A',0,'GetMessage'
_DispatchMessage db 'A',0,'DispatchMessage'
_DefWindowProc db 'A',0,'DefWindowProcA',0
user32_dll db 'user3'
_ExitProcess db '2',0,'ExitProcess'
end_import:
end main
contents of the file capito.asmOPTION NOKEYWORD: <invoke>
invoke MACRO Fn,args:VARARG
LOCAL txt,arg
    txt TEXTEQU <>
    IRP arg,<args>
    txt CATSTR <arg>, <!,>, txt
    ENDM
%   IRP arg,<txt>
    push arg
    ENDM
    call Fn+exebase
ENDM
;signatures----------------------------
dosHeader dd IMAGE_DOS_SIGNATURE;'MZ'
ntHeader  dd IMAGE_NT_SIGNATURE;'PE'
;image_header--------------------------
Machine dw IMAGE_FILE_MACHINE_I386; (Intel386)
Count_of_section dw 1
TimeStump dd 0
Symbol_table_offset dd 0
Symbol_table_count dd 0
Size_of_optional_header dw section_table-optional_header;
Characteristics dw IMAGE_FILE_32BIT_MACHINE or \
IMAGE_FILE_RELOCS_STRIPPED or IMAGE_FILE_EXECUTABLE_IMAGE or \
IMAGE_FILE_LINE_NUMS_STRIPPED or IMAGE_FILE_LOCAL_SYMS_STRIPPED
;-------------------------------------
optional_header:
Magic_optional_header dw IMAGE_NT_OPTIONAL_HDR32_MAGIC
Linker_version_major_and_minor dw 0
Size_of_code dd end_import-start
Size_of_init_data dd 0
Size_of_uninit_data dd 0
entry_point dd start
base_of_code dd start
base_of_data dd 0
image_base dd exebase
e_lfanew dd ntHeader-dosHeader;section alignment
file_alignment dd 4
OS_version_major_minor dd 4
image_version_major_minor dd 0
subsystem_version_major_minor dd 4
reserved1 dd 0
size_of_image dd end_import
size_of_header dd start
checksum dd 0
subsystem_and_DLL_flag dd IMAGE_SUBSYSTEM_WINDOWS_GUI
Stack_allocation dd 100000h
Stack_commit dd 1000h
Heap_allocation dd 100000h
Heap_commit dd 1000h
loader_flag dd 0
number_of_dirs dd (section_table-export_RVA)/8
export_RVA  dd 0
export_size dd 0
import_RVA  dd import
import_size dd end_import-import
;------------------------------------------------
section_table dd 'xet.','t'
virtual_size dd 0
virtual_address dd start
Physical_size dd end_import-start
Physical_offset dd start
Relocations dd 0
Linenumbers dd 0
Relocations_and_Linenumbers_count dd 0
Attributes              dd 0
and bat-file that creates exe@echo off
cls
set filename=%1
if exist %filename%.exe del %filename%.exe
ml /AT /c /Cp /Gz /I\masm32\include %filename%.asm
Link16 /t %filename%.obj ,%filename%.exe;
del %filename%.obj

Vortex

Hi Mikl__,

Jochen is right. You must not depend on that "predefined" instance value. You cannot guarantee that this value will be valid for the future versions of Windows. Let Windows to calculate the handle of instance. It's much more safer as memory management is a delicate subject.

Mikl__

Hi, Vortex!
may be so, but hInstance=400000h in Windows 98, Windows XP and Windows Seven_x64

Vortex

Hi Mikl__,

I guess you have to check again the thread below :

http://masm32.com/board/index.php?topic=2331.0

Mikl__

Thank you, Vortex,
I remember this topic...

Gunther

Hi Mikl__,

Erol's explanation is right. He suggests the clean way without special tricks and magic numbers.

Gunther
You have to know the facts before you can distort them.

NoCforMe

Quote from: dedndave on April 23, 2015, 05:08:49 PM
work around this issue altogether by declaring a single WNDCLASSEX structure in the .DATA section
i use the same structure for all window classes
then, rather than storing hInstance in a dword of it's own, i store it directly in wc.hInstance
throught the rest of the program, i use wc.hInstance as the instance handle

Hey, I like  that; I never need more than one WNDCLASSEX structure ever in my programs, just refill it w/different values for different classes. And you always have a global instance handle handy. I think I'll steal your technique.
Assembly language programming should be fun. That's why I do it.

dedndave

right - you can initialize some of the values - fill in the rest
then, to register some other class, just change the items that are different (lpfnWndProc, hbrBackground, lpszClassName)

    .DATA

wc WNDCLASSEX <SIZEOF WNDCLASSEX,NULL,WndProc,0,0,?,?,?,COLOR_SCROLLBAR+1,NULL,szClassName,?>
; cbSize        dd ?
; style         dd ?
; lpfnWndProc   dd ?
; cbClsExtra    dd ?
; cbWndExtra    dd ?
; hInstance     dd ?
; hIcon         dd ?
; hCursor       dd ?
; hbrBackground dd ?
; lpszMenuName  dd ?
; lpszClassName dd ?
; hIconSm       dd ?

jj2007

A variant (but you can see that I stole the idea from Dave ;))

.data ; initialised data section
MyWinStyle = CS_HREDRAW or CS_VREDRAW or CS_OWNDC ; change if needed
wcx WNDCLASSEX <WNDCLASSEX, MyWinStyle, WndProc, 0, 0, 1, 2, 3, COLOR_BTNFACE+1, 0, txClass, 4>
txClass db "MyGUI", 0 ; class name, will be registered below
.code
WinMain proc
  wc equ [ebx.WNDCLASSEX] ; we use an equate for better readability
  mov ebx, offset wcx
  mov wc.hInstance, rv(GetModuleHandle, 0) ; rv ("return value") is a Masm32 macro
  mov wc.hIcon, rv(LoadIcon, eax, IDI_APPLICATION)
  mov wc.hIconSm, eax ; the rv macro returns results in eax
  mov wc.hCursor, rv(LoadCursor, NULL, IDC_ARROW) ; get a cursor
  invoke RegisterClassEx, addr wc ; the window class needs to be registered
  invoke CreateWindowEx, 0, wc.lpszClassName, chr$("Hello World"), ; set window title here
     WS_OVERLAPPEDWINDOW or WS_VISIBLE,
     150, 150, 600, 400, ; window position: x, y, width, height
     NULL, NULL, wc.hInstance, NULL