News:

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

Main Menu

Extern vs Proto

Started by buckhamman, December 09, 2023, 06:16:48 PM

Previous topic - Next topic

buckhamman

After stumbling through random online resources, I'm still a bit confused about the differences between extern, externdef, and proto. From what I've gathered so far, my mental model is this:

'public name' is declared in the implementation file to allow name external use, and is default for procedures.

'extern name:type' is declared in files using the label name if it is public. The type for procedures is proc.

'externdef name:type' is the same as 'public name' in implementation files or 'extern name:type'  in files using the label name.

'proto name' is either the same as 'extern name:proc' or 'externdef name:proc'.

However, I'm not sure about the last point. Is proto the same as extern or externdef, or is there some difference I don't know about?

NoCforMe

#1
One suggestion to make things easier since you're just starting in assembly language:
Keep your project to one file at first, so there's no need for public or extern definitions. They're a bit confusing, as you've discovered.

One thing: even in a single file, you need to use prototypes for any functions that take arguments, so the assembler can resolve references to them:
; At top of file:
TheDialogProc        PROTO hWin:HWND, uMsg:DWORD, wParam:DWORD, lParam:DWORD

; The actual function definition:
TheDialogProc    PROC hWin:HWND, uMsg:DWORD, wParam:DWORD, lParam:DWORD

The nice thing is you can just copy and paste the definition and change "PROC" to "PROTO". You don't have to have the names (hWin, uMsg, etc.) in the PROTO, but I like to keep them so I know what the parameters actually mean. All PROTO cares about are the types of the parameters (which in the case of Win32 are invariably DWORDs).

If you have a function that doesn't take any arguments, you don't need a PROTO for it.

Another thing: hopefully you'll learn to use the INVOKE macro when calling a function:

; Function definition:
SomeFunction    PROC parm1:DWORD, parm2:DWORD

; Calling that function:
    INVOKE SomeFunction, EAX, someVariable

; instead of the "old school" way (ugh!):
    PUSH someVariable
    PUSH EAX
    CALL SomeFunction
This of course is a stylistic preference; either method will work. If you use "push-push-call", the assembler won't warn you if you have the wrong number of parameters, whereas it will with INVOKE.
Assembly language programming should be fun. That's why I do it.

buckhamman

Hello NoCForMe,
Firstly, I think I accidently posted in the wrong board, sorry about that.  :tongue:

Invoke is an interesting way of calling functions I haven't been using, and it explains alot. So basically, for invoke to work you need a function definition and declaration with specific parameter types, and proto is the declaration. Of course, extern:proc probably can't define paramater types, which didn't matter when I used the x64 calling convention but it doesn't work for invoke. Unfortunately, based on what msdn is saying, invoke only works for 32-bit and I've been doing stuff in 64-bit assembly. But when I try out some 32-bit assembly, I'll check it out. This definitely answers my question for why proto exists when extern is already there, so thank you for that.

Thank you a lot,  :thumbsup:   

jj2007

Hi buckhamman,

First things first: welcome to the Forum :thup:

NoCforMe has got it all right, but maybe it's good to see it from a different angle:

The assembler doesn't know how many args a proc needs, so we must tell it. Check \Masm32\include\, there are over 300 files defining PROTOs. Example from Gdi32.inc:
CreateFontA PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
You can use push-push-call with 14 arguments, if you want to feel like a real poke-your-stuff-close-to-the-metal coder, but honestly, most of the members here are over 60, counting to 14 is tough, and we leave the long bug chasing nights to young people in CS courses. We oldies want to see a rude error message if we gave 15 arguments to CreateFont, or 10 to CreateWindowEx - so we use the invoke macro :cool:
invoke CreateFont, 80,0,600,0,400,0,0,0,DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH or FF_SCRIPT, chr$("Tahoma")
That was about calling the Windows API. It is slightly different with your own functions:
include \masm32\include\masm32rt.inc    ; frequently used includes and macros
.code
SayHello proc arg
  invoke MessageBox, 0, arg, chr$("Title"), MB_OK
  ret
SayHello endp
start:
  invoke SayHello, chr$("Hi buckhamman")
  invoke ExitProcess, 123
end start

If you try invoke SayHello, chr$("Hi buckhamman"), 123, you will see error A2136: too many arguments to INVOKE - cute, isn't it?

You may have noted that there is no PROTO here. Why? The Assembler sees first the proc, then the invoke, so it knows already what SayHello needs. However, if you move SayHello proc... below the invoke SayHello..., the Assembler does not know how many args it needs. That's when we need PROTO:
include \masm32\include\masm32rt.inc    ; frequently used includes and macros
.code
start:
  SayHello PROTO :DWORD
  invoke SayHello, chr$("Hi buckhamman")
  invoke ExitProcess, 123
SayHello proc arg
  invoke MessageBox, 0, arg, chr$("Title"), MB_OK
  ret
SayHello endp
end start

Now to externdef (you will never need extern, it's kind of obsolete):
I agree with NoCforMe that splitting your code into a dozen little files is a silly idea, but if you feel like doing that, and you have a main.asm and an aux.asm whose *.obj or *.lib files get linked together, then main.asm must be told that myauxvar is defined elsewhere. This will not work:
aux.asm: 
myauxvar db "I am outside", 0main.asm:
mov esi, offset myauxvar
Instead, we need this:
aux.asm: 
EXTERNDEF myauxvar:BYTE  ; tell main what kind of stuff this is
myauxvar db "I am outside", 0
main.asm:
ExternDef myauxvar:BYTE  ; it's defined elsewhere, and it's a BYTE size variable
mov esi, offset myauxvar

Simple, right? I have the habit to use uppercase EXTERNDEF where the variable is being defined, and camelcase ExternDef where it's being used, but that's just a matter of taste.

Vortex

#4
Hi buckhamman,

Welcome to the forum. About the differences between EXTERNDEF and EXTERN :

If name is defined in the module, it is treated as PUBLIC.
If name is referenced in the module, it is treated as EXTERN.
If name is not referenced, it is ignored. The type can be ABS, which imports name as a constant.
Normally used in include files.

https://learn.microsoft.com/en-us/cpp/assembler/masm/externdef?view=msvc-170

stonelightning

Quote from: buckhamman on December 09, 2023, 06:16:48 PMAfter stumbling through random online resources, I'm still a bit confused about the differences between extern, externdef, and proto. From what I've gathered so far, my mental model is this:

'public name' is declared in the implementation file to allow name external use, and is default for procedures.

'extern name:type' is declared in files using the label name if it is public. The type for procedures is proc.

'externdef name:type' is the same as 'public name' in implementation files or 'extern name:type'  in files using the label name.

'proto name' is either the same as 'extern name:proc' or 'externdef name:proc'.

However, I'm not sure about the last point. Is proto the same as extern or externdef, or is there some difference I don't know about?
since im new to assembler aswell i can really recommend you the tutorial from Iczelion!
https://web.archive.org/web/20171110201344/http://win32assembly.programminghorizon.com/tutorials.html