A structure is made up of members that have a specified data
size. In a commonly used structure RECT,
you have four DWORD
left DWORD ?
top DWORD ?
right DWORD ?
bottom DWORD ?
In most instances, the
specifier is ? which means the member is
not initialised to a value.
1: If it is allocated in the .DATA
section, it can be initialised to preset values but if it is allocated on the
stack as a LOCAL
value within a procedure, the values have to be coded into the structure.
LOCAL Rct :RECT
mov Rct.left, 1
mov Rct.top, 2
mov Rct.right, 3
mov Rct.bottom, 4
It should be noted that a
structure member is a memory operand so you cannot directly move another memory
operand into it, you must either use a register to copy it or the stack
mnemonics, push / pop.
Then, you can refer to the
filled structure as a single unit with ADDR
Rct in an invoke directive procedure
call. If an API call requires the address of a structure, you fill the structure
with the values you require and call the API,
invoke APIcall,parameter1,parameter2,ADDR Rct
If you write a procedure where you want
the values in a structure passed to it, you can pass the structure by using the
structures data type in the procedure.
MyProc proc par1:DWORD,par2:DWORD,MyRect:RECT
Then: mov eax, MyRect.left ; copy first member into EAX
The individual members can then
be accessed by their names in the procedure that receives the RECT as a
parameter. You call the proc as follows,
invoke MyProc,par1, par2, Rct
A method common to 32 bit Windows is the use of nested structures. MASM has a notation for dealing with this type of construction. If you needed a structure that had multiple structures in it,
item1 RECT <>
item2 POINT <>
This structure uses the above RECT struct and the following POINT struct.
x DWORD ?
y DWORD ?
There are six members in this structure, four from the RECT structure and two from the POINT structure. Allocated on the stack it looks like this,
The six members are,
Advanced Structure Handling
Increasingly, there is a need to be able to handle structures that are passed as an address and this type of code is becoming more common in Windows code design. MASM has a specialised notation to make this process a lot easier to handle.
If for example you needed to pass the adress of a RECT structure to a procedure, you would normally make the call in the following manner,
invoke MyFunction,ADDR Rct
At the procedure end of this function call you would normally have something like the following,
MyFunction proc lpRect:DWORD
With a simple structure like RECT you can address the seperate parameters manually by putting the address into a register and write to the location of each member,
mov eax, lpRct
mov [eax], DWORD PTR 10
mov [eax+4], DWORD PTR 12
mov [eax+8], DWORD PTR 14
mov [eax+12], DWORD PTR 16
This works fine but with more complex structures this becomes much harder to work with and far more error prone.
The alternative is to use a method that MASM has to address the individual members by using the ASSUME directive.
ASSUME eax:PTR RECT
mov eax, lpRct
mov [eax].left, 10
mov [eax].top, 12
mov [eax].right, 14
mov [eax].bottom, 16
This works by telling the
assembler that the EAX register is to be treated
like a RECT
structure. The ASSUME eax:nothing
tells the assembler that the register is no longer being used in this manner.
There is an alternative notation where you can individually "type cast" each member.
mov eax, lpRct
mov (RECT PTR [eax]).left, 10
mov (RECT PTR [eax]).top, 12
mov (RECT PTR [eax]).right, 14
mov (RECT PTR [eax]).bottom, 16
The advantages of these techniques is that they use the convenience and reliability of a structure so that you do not have to calculate the offsets of each member and it also uses the normal member names. The downside of these techniques is that they use a register which is not always a desirable consequence. If the register usage is a problem, you will need to allocate LOCAL variables and copy the data from each required member to the variables.