When defining parameters with the PROC directive, when exactly should they be marked as pointers?
For example, is it correct to define the prototype of the callback function used by SetWindowSubclass (https://learn.microsoft.com/en-us/windows/desktop/api/commctrl/nf-commctrl-setwindowsubclass) so that the type of the uIdSubclass and dwRefData parameters is just DWORD (as in the following code), or should it be PTR DWORD?
mysubclassproc PROC hWnd:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD, uIdSubclass:DWORD, dwRefData:DWORD
According to Windows Data Types (https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types), the type DWORD_PTR (the Windows type of dwRefData) is an unsigned long type for pointer precision to be used when casting a pointer to a long type to perform pointer arithmetic, and the same basically goes for UINT_PTR (the Windows type of uIdSubclass), so I think it should be DWORD but – as I understand it – the dwRefData value is supposed to act as a pointer to void, and I'd like it to be an offset. Do I understand that using PTR is just a matter of preference here, just like it's a matter of preference to enclose direct memory operands in brackets?
In assembly language, there's no need to get that fancy with argument types.
A DWORD is a DWORD is a DWORD.
How it's used is entirely up to the code in the function. If it's a pointer, it gets dereferenced at some point. So far as the procedure definition (PROC) is concerned, it's just another 32-bit value that gets passed in on the stack. No need to communicate anything about its type or usage in the PROC parameters.
The important thing is the argument's size (and in 32-bit code that's always going to be a DWORD; no WORDs or BYTES allowed here).
This ain't C.
So it is just a matter of personal preference, isn't it?
I'd just like to know if it's worth the effort to use PTR knowing that the actual parameter is going to be an address and that it will be necessary for the function to dereference it.
I would say no.
My preference is in how you name the parameter, which unfortunately a lot of programmers are pretty careless about, using cryptic, meaningless names.
You don't have to have a rigid system like Micro$oft uses ("Hungarian" notation). If it's a pointer, I would indicate that in the name:
DataObjectPtr
That way you, the programmer, know what it is every time you see that variable. And you'll know that you need to dereference it.
The function doesn't need to know anything.
Even in 64-bit code, it's irrelevant: you receive a QWORD even if you pass a DWORD because
mov ecx, somevalue and
mov rcx, somevalue
achieve the same result: rcx is set. What counts is what you pass, not how you name what you get.
Quote from: jj2007 on July 06, 2024, 05:17:03 AMWhat counts is what you pass, not how you name what you get.
Au contraire, unless I misunderstand you.
The importance of naming is so that
you, the programmer, know what the thing you're looking at is. No relevance to the assembler, yes.
Take SendMessage. Does anybody care for wParam or lParam? It's either DWORDs or pointers. Which are the same in 32-bit land.
I'm not sure what your point is here, JJ. We were discussing how to identify whether a parameter is a pointer or not. True, the wParam and lParam parameters tell us nothing by their names; they can be used for all sorts of things, pointers, simple scalar values, indices, etc.
I'm talking about naming parameters to one of your functions to make it clear whether they're a pointer or not. Which is what the OP was asking about.
Quote from: NoCforMe on July 06, 2024, 05:55:16 AMI'm talking about naming parameters to one of your functions
Here is "my" function. Use comments.
SendMessage proc uses esi edi ebx uMsg:DWORD, arg1:WPARAM, arg2:LPARAM
Switch uMsg
Case WM_SETFONT
m2m CurrentFont, arg1 ; wParam is the handle
.if arg2
call RedrawThisControl ; lParam is the redraw flag
.endif
...
Quote from: NoCforMe on July 06, 2024, 05:55:16 AMI'm not sure what your point is here, JJ. We were discussing how to identify whether a parameter is a pointer or not. True, the wParam and lParam parameters tell us nothing by their names; they can be used for all sorts of things, pointers, simple scalar values, indices, etc.
I'm talking about naming parameters to one of your functions to make it clear whether they're a pointer or not. Which is what the OP was asking about.
Actually, I'm more interested in types rather than names. I know that ASM can be seen as (or is) typeless; I'd just like to know if there's any scenario where it's actually better to declare a parameter as a pointer.
Quote from: kkurkiewicz on July 06, 2024, 08:27:53 AMI'd just like to know if there's any scenario where it's actually better to declare a parameter as a pointer.
No, because unlike C (and other languages), the assembler does nothing whatsoever to treat any variable automagically as a pointer: that's completely on you, the programmer.
Except for sizes (byte, word, dword, qword), assembly
is completely typeless. There's not even any distinction, type-wise, between signed and unsigned variables; that's also up to you as the programmer to distinguish the two.
Really the only time you need to use
PTR is when you're dealing with a memory reference that's otherwise invalid or ambiguous, similar to a C type cast:
MOV AX, WORD PTR wParam ;Because wParam is a defined as a DWORD
Quote from: NoCforMe on July 06, 2024, 08:35:53 AMThere's not even any distinction, type-wise, between signed and unsigned variables
There is:
include \masm32\include\masm32rt.inc
.data
signedvar SDWORD -123
unsigned DWORD -123
.code
start:
.if signedvar<0
print "sv is negative", 13, 10
.else
print "sv is positive", 13, 10
.endif
.if unsigned<0
print "usv is negative", 13, 10
.else
print "usv is positive", 13, 10
.endif
inkey "q.e.d."
exit
end start
Well, there is if you use macros, as you do. If you don't use them, no difference.
I manage to muddle along fine with JL/JG or JB/JA. You know, assembly language instructions.
I hope I'm not belaboring my point too much, but I think it's worth defending as a Good Thing for programming, and as a (possible) answer to the OP's questions.
This simple matter of using a descriptive name to, essentially, give a variable a type is more profound than meets the eye. To answer JJ's suggestion of using comments to give a variable's type, I think my way is better, because it doesn't depend on a comment at the top of a function which may be half a mile away from where the variable gets used. (Well, sure, another comment there would do the trick, but what if you fail to put it in?)
Consider a function:
SomeFunction PROC numberPtr:DWORD
; numberPtr points to a # (DWORD)
When it comes time to use this parameter, because it's named as a pointer, we know we have to do this to get at the number it points to:
MOV EDX, numberPtr
MOV EAX, [EDX]
and not this:
MOV EAX, numberPtr
Assembler is (mostly) typeless; C is not. And while the C programmer might get annoyed at having to assign all those types to their variables, they benefit from it: the compiler helps to protect us from at least some dangers by warning us if, say, we try to use a pointer as a scalar or vice versa. (The C programmer can still get in plenty of trouble, as C is still a fairly low-level language.)
Assembly language gives us a lot more freedom, which of course means freedom to succeed or freedom to fail spectacularly. But as they say, with great freedom comes great responsibility, and it's the assembly-language programmer's responsibility to use their variables properly. Assembler lacks those guard rails that C provides. Which is why it's useful to come up with schemes to help avoid failures due to incorrect "types". Which is why I name my pointers "xxxxPtr".
Names matter. Pay attention to the names you assign your things, variables, functions, structures, structure elements. Come up with a scheme that works for you and stick to it.
Quote from: NoCforMe on July 06, 2024, 08:35:53 AMThere's not even any distinction, type-wise, between signed and unsigned variables
Quote from: NoCforMe on July 06, 2024, 09:47:26 AMWell, there is if you use macros
You know why there is the "M" in "MASM", right? Calling
.if a "macro" is absurd. Be careful what you write in The Campus.
Quote from: kkurkiewicz on July 06, 2024, 04:09:12 AMSo it is just a matter of personal preference, isn't it?
It depends. If you need to debug your code, using PTR combined with a type can greatly simplify the task. That's because the codeview debug information has all type information included that the debugger needs to know. So, for example, if the argument is a pointer to a STRUCT, the debugger will allow you'll to watch all members of that struct inside the PROC.
Which debugger? Certainly not Olly, I don't think.
Quote from: NoCforMe on July 06, 2024, 06:22:01 PMWhich debugger? Certainly not Olly, I don't think.
never used OllyDbg. But all debuggers based on the MS Debug Engine (https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-engine-overview) should be ok. Even the ancient CodeView for DOS had that capability.
Quote from: _japheth on July 06, 2024, 06:18:55 PMQuote from: kkurkiewicz on July 06, 2024, 04:09:12 AMSo it is just a matter of personal preference, isn't it?
It depends. If you need to debug your code, using PTR combined with a type can greatly simplify the task. That's because the codeview debug information has all type information included that the debugger needs to know. So, for example, if the argument is a pointer to a STRUCT, the debugger will allow you'll to watch all members of that struct inside the PROC.
Quote from: _japheth on July 06, 2024, 06:51:10 PMQuote from: NoCforMe on July 06, 2024, 06:22:01 PMWhich debugger? Certainly not Olly, I don't think.
never used OllyDbg. But all debuggers based on the MS Debug Engine (https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/debugger-engine-overview) should be ok. Even the ancient CodeView for DOS had that capability.
I can confirm this.
With VS Debugger it's very useful and comfortable. WinDbg should also do the job.
I gonna try to random shuffle dword pointers for my card game than complete data for each card must become faster
include \masm32\include\masm32rt.inc
.code
MyTest proc arg1
Local rc:RECT
Local prc:PTR RECT
mov rc.left, 111h
mov rc.top, 222h
mov rc.right, 333h
mov rc.bottom, 444h
lea eax, rc
int 3
mov prc, eax
ret
MyTest endp
start:
invoke MyTest, 55555555h
exit
end start
(https://i.postimg.cc/JsHpPDTK/Decode-As-Structure.png) (https://postimg.cc/JsHpPDTK)
Quote from: jj2007 on July 07, 2024, 10:21:52 AM(https://i.postimg.cc/JsHpPDTK/Decode-As-Structure.png) (https://postimg.cc/JsHpPDTK)
1. What debugger is that? Olly?
2. You don't need to declare anything as a PTR to do that, correct? Just right-click and choose "Decode as structure" anywhere in the data, yes?
Nice to be able to do that, for sure.
Quote from: NoCforMe on July 07, 2024, 10:26:16 AMQuote from: jj2007 on July 07, 2024, 10:21:52 AM(https://i.postimg.cc/JsHpPDTK/Decode-As-Structure.png) (https://postimg.cc/JsHpPDTK)
1. What debugger is that? Olly?
Looks like olly... :biggrin: but I have never seen that option. Using a plugin??
Quote from: zedd151 on July 07, 2024, 10:30:20 AMLooks like olly... :biggrin: but I have never seen that option. Using a plugin??
That's Olly indeed, and not a plugin.
The choice of structures is limited. I've checked the whole Olly folder to find out where this info is stored, no luck.
poide + poasm
;include \masm32\include\masm32rt.inc
RECT STRUC
left sdword ?
top sdword ?
right sdword ?
bottom sdword ?
RECT ENDS
.code
MyTest proc arg1
Local rc:RECT
Local prc:PTR RECT
mov rc.left, 111h
mov rc.top, 222h
mov rc.right, 333h
mov rc.bottom, 444h
lea eax, rc
int 3
mov prc, eax
ret
MyTest endp
start:
invoke MyTest, 55555555h
ret
end start
(https://i.postimg.cc/4KBb17G6/poide-dbg.png) (https://postimg.cc/4KBb17G6)
I think I understand, thank you.
I just got confused by the _PTR suffix in the name of the type.
Quote from: kkurkiewicz on July 09, 2024, 02:17:08 AMI think I understand, thank you.
I just got confused by the _PTR suffix in the name of the type.
The _PTR sais that this type always has the size of a Pointer, which means in x86 4 bytes and in x64 8 bytes.
ifdef _WIN64
INT_PTR typedef sqword
UINT_PTR typedef qword
LONG_PTR typedef sqword
ULONG_PTR typedef qword
else
INT_PTR typedef sdword
UINT_PTR typedef dword
LONG_PTR typedef sdword
ULONG_PTR typedef dword
endif