Help with MASM32 DLL creation/use (new to MASM32 and ASM in general)

Started by Chesso, May 25, 2007, 07:02:17 AM

Previous topic - Next topic

Chesso

Hi all, I am new here and as stated for the Subject line, new to MASM32 and ASM in general (I hope that this is the appropriate place to post this question).

I am interested in learning ASM in general, but didn't know where to really start at all, so searching led me to MASM32 and the first thing I thought I would try, is porting a pretty basic DLL I made in C++ (using Dev-CPP) for one of my Delphi programs.

For the most part, I have managed to pull it off purely slightly adjusting my Delphi code for using LoadLibrary and the function, google searching and looking in the windows.inc file to see how on earth I can use the equivalent of a byte type (or more importantly a plain integer) in delphi (DWORD seems to work fine in MASM32 for this purpose, seen as there is no INT).

The only problem I am stuck on now for this particular project (a C++ to MASM32 conversion I suppose) is getting the contents (or I assume literally a pointer to) from a global variable (in .data using varname db "string", NULL) into a local variable (DWORD, no idea what else to use?).

I tryed to (e.g.):

mov localvar, offset globalvar

But that appears to not work really at all, again I am fresh to ASM in general, so I am not familiar with basic things like mov, push, pop etc, let alone how I can use them in MASM32 and for this *particular* purpose.

Of course in C++ and Delphi, I am used to a general myvar = someothervar assignment, or similar syntax, what could I do to obtain the a similar result in MASM32?

Here is a small pseudo of what I am trying to do:


.data
  msg_0 db "ERROR: Some Error.", NULL

  msg_type db "ERROR", NULL

.code
; proc and what not all done and working.

LOCAL: msg:DWORD

; put msg_0 or pointer to etc into local msg.
mov msg, offset msg_0

invoke MessageBox, NULL, addr msg, offset msg_type, MB_ICONSTOP

; ending and things here.


I hope this all makes sense to someone lol, I would post the entire DLL source somewhere, but my speed is capped (supposedly at 56kbps (dial-up), but really from tests it seems more 28.8kbps).

I can later if necessary though.

Any help is greatly appreciated (and yes I am of course interested in learning exactly what all the mov, pop, push etc is all about :P).

fearless

Quote from: Chesso on May 25, 2007, 07:02:17 AM


; put msg_0 or pointer to etc into local msg.
mov msg, offset msg_0

invoke MessageBox, NULL, addr msg, offset msg_type, MB_ICONSTOP


no need for addr msg in the last line as it (msg) already contains the pointer to msg_0

so have you tried:

invoke MessageBox, NULL, msg, offset msg_type, MB_ICONSTOP


I sometimes do what your doing this way:


.data
ErrMsgTitle  db "Something went wrong",0
ErrMsg0      db "Error: Program Died...",0
ErrMsg1      db "Error: Programmer Died",0

.code
.
.
PROC MyLittleProc ; our little procedure, pehaps with parameters? or not.
LOCAL ErrMsg:DWORD ; define local var errmsg

lea ebx, offset ErrMsg0 ; Load effective address of errmsg0 string into ebx register
mov ErrMsg, ebx ; save the contents of ebx to our local var errmsg
invoke MessageBox, NULL, ErrMsg, Addr ErrMsgTitle, MB_ICONSTOP ; call msgbox with errmsg value (pointer to errmsg0) as a param
.
.


hope this helps

KSR
ƒearless

Chesso

Ahhh your the best, that's perfect :P.

I figured I could do a straight mov, as I don't really know much about the other *commands* as one might say.

But now I understand what's going on, I need to move it the register thing first and retrieve it from it, not do a direct copy so to speak?

And removing the addr, helps fix malformed text, is this because it already has a pointer to this string text I want to show? (so if I set it up normally, would I need addr, or not really for local variables?).

Oh and it's works great now btw (going to fix the whole thing up and see).


EDIT: I got one little problem, I can't seem to properly use lea and move with ebx register using MB_ contsants (like MB_ICONSTOP, MB_OK etc).

I set like, msg_icon_error db MB_ICONSTOP, NULL

And local like, LOCAL msg_icon:DWORD

And use the same method as you showed me.

I'm not sure why this fails, Is there another keyword I should use in the .data section?, I am unfamiliar with what db means and if there as alternatives for other situations, as I have just used it to store strings thus far.

Chesso

It seems I can just use mov msg_icon, MB_ICONSTOP manually hmm.

There must be a problem with what/how I am storing the MB_ constants in .data section.

I would actually prefer to store these strings and MB_ constants locally (it seems wastefull to have them already initialized when the dll loaded, when I only use them in one function).

How would I set them up locally?

Like LOCAL myvar:sometype, (what type for a string?) and how to set the string lol.

So many questions haha :P.

petezl

Hi guys,

Been away from here for a long time so need to get back in with the swing of things!

@Chesso

Can I very politely remind you of the Iczillon tutorials that are probably on this site somewhere they are indespensible for learning win32 asm from the ground up!

Anyway, you mention masm32 dll's then refferring to windows dll's

Here is a simple MesageBox example.
Note that the pointers are pushed before calling a windows dll


.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.data  ;<<< initiallised data goes under here (these are Global)

szDlgTitle db "Minimum MASM",0
szMsg db " --- Assembler Pure and Simple --- ",0

.data?  ;<<< un-initiallised data goes under here (these are global)
hInstance HINSTANCE ? ; Could have been hInstance dd ?


.code
start:
call GetModuleHandleA 
mov hInstance, eax   ; means move val eax to hInstance var

push 0h                  ; push immediate number to the stack
push OFFSET szDlgTitle   ; push pointer to the stack etc etc
push OFFSET szMsg        ; in reverse order
push hInstance
call MessageBoxA

;Alternative method <<< Masm32 way ? (second call to MessageBox)

invoke MessageBox,hInstance,ADDR szMsg,ADDR szDlgTitle,MB_OK ; or in correct order
invoke ExitProcess,hInstance

end start


And here is a simple procedure. Note that the pointers were already pushed before the procedure so inside the proc you only need to push the values


;### Using a procedure ###
.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

MyBox PROTO :DWORD,:DWORD  ;<<< My Message Box procedure ProtoType

.data  ;<<< initiallised data goes under here (these are Global)

szDlgTitle db "Minimum MASM",0
szMsg db " --- Assembler Pure and Simple --- ",0


.data?  ;<<< un-initiallised data goes under here (these are global)

hInstance HINSTANCE ? ; Could have been hInstance dd

.code
start:
call GetModuleHandleA 
mov hInstance, eax

push OFFSET szDlgTitle
push OFFSET szMsg
call MyBox
push eax
call ExitProcess

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
MyBox proc firstMessage:DWORD,secondMessage:DWORD

push 0h
push secondMessage
push firstMessage
push hInstance
call MessageBoxA

ret

MyBox endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

end start



Petezl
Cats and women do as they please
Dogs and men should realise it.

Chesso

I meant that I myself am making a DLL in MASM32, anything refering to windows would be the fact that's a windows dll (or refering to useing a function from one, an API).

Here's a code paste of the full source, as it stands:

http://rafb.net/p/dUAccL71.html (plain text, they don't have assembly, or MASM32 highlighting).

The only problem I have now interfacing my DLL with my Delphi Application, is that I cannot these values:

  msg_icon_warn dd MB_ICONWARNING, NULL
  msg_icon_error dd MB_ICONSTOP, NULL
  msg_icon_info dd MB_ICONINFORMATION, NULL

In the .data section.

I want to put one of these in msg_icon (local to Msg proc) depending on what message the user wants to display (the one and only parameter of the Msg proc).

If you look at the lea, mov methods used for the core msg and title of the messagebox, I tried the exact same approach for the MB_ constants listed above, but it fails (nothing happens when loading th e library and calling the function).

I have looked at the tutorials you mentioned, they are useful (for me at least) mostly for the specific syntax of setting up actual windows (display windows, not child control windows yet lol).

I use MessageBox a lot in both Delphi and C++, so I am quite familiar with it, and a similar approach is how I do this DLL in C++ (using Dev-CPP, built in delphi :P).

I figured it might be fun to port it, as my first attempt at win32 assembly, and so far it's been fun to play with lol.

petezl

Sorry, useless at C++ and worse at Delphi.

If you are calling a windows dll from your dll then no need to bother with adding these things to your dll.asm data section. Have a look in \masm32\include\windows.inc file and search for these strings and you will find the values:


MB_OK                                equ 0h
MB_OKCANCEL                          equ 1h
MB_ABORTRETRYIGNORE                  equ 2h
MB_YESNOCANCEL                       equ 3h
MB_YESNO                             equ 4h
MB_RETRYCANCEL                       equ 5h
MB_ICONHAND                          equ 10h
MB_ICONQUESTION                      equ 20h
MB_ICONEXCLAMATION                   equ 30h
MB_ICONASTERISK                      equ 40h
MB_USERICON   equ 80h
MB_ICONERROR                         equ MB_ICONHAND
MB_ICONINFORMATION                   equ MB_ICONASTERISK
MB_ICONSTOP                          equ MB_ICONHAND
MB_ICONWARNING                       equ MB_ICONEXCLAMATION


I usually load my dll's as and when neccesary.


test.dll
snip<

DllEntry Endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; MyBox proc exported in def file
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
MyBox proc

>snip


dll name and exports example (in dll def file):

LIBRARY test ; dll name without ext
EXPORTS MyBox ; Exported processes



Calling from main program:

snip<
invoke LoadLibrary,addr LibName
.if eax==NULL
invoke MessageBox,NULL,addr DllNotFound,addr AppName,MB_OK
.else
mov hLib,eax
invoke GetProcAddress,hLib,addr FunctionName
.if eax==NULL
invoke MessageBox,NULL,addr FunctionNotFound,addr AppName,MB_OK
.else
mov MyBoxAddr,eax
call [MyBoxAddr]

.endif
invoke FreeLibrary,hLib
.endif

>snip


I realise just how rusty I am now! :red


Petezl
Cats and women do as they please
Dogs and men should realise it.

Chesso

lol.

No I am loading this assembly made dll from my delphi program (which does work, the problem resides in the MASM32 dll I making, a dll in MASM32 that is).

function and exporting works perfectly how I have done it so far.

It's the MB_ constants I am having trouble with.

I cannot use the normal lea, move commands (well I call them commands), I figure it's because it doesn't like me having these MB_ constants, they don't like me using the DD or DB (like when setting up the strings).

If I directly set the local DWORD (msg_icon) to say MB_ICONSTOP (using mov msg_icon, MB_ICONSTOP), it works, but I want a variable from the .data section that represents an MB_ constant I want to use (not input it directly with the mov command).

If I setup the MB_'s like so:

msg_icon_error db MB_ICONSTOP, NULL
or
msg_icon_error dd MB_ICONSTOP, NULL

using:

mov msg_icon, msg_icon_error

Doesn't seem to do me any good (it compiles, but fails to function entirely).

But just using mov, msg_icon, MB_ICONSTOP, does work.

Tedd

A few comments/pointers in the right direction (I hope :wink)...

If you use the registers 'ebx', 'esi', or 'edi' it's generally assumed (by other functions/procedures; and also you can assume the same about others) that you will preserve their values -- push/pop

"offset ..." gives a constant value (at 'compile' time) so it's just a number, there's no need to put it in a register and then to memory although, using 'offset' with 'lea' is redundant

The 'MB_ICON...' values are also just constants, so there's rarely use in storing them separately (referring to your "msg_icon_..." variables)

Anyway, for something like this, you have enough registers to play with: eax,ecx,edx


Msg proc n:DWORD
.if n==0
   lea eax, [msg_0]         ;msg
   lea ecx, [msg_warn]      ;msg_title
   mov edx, MB_ICONWARNING  ;msg_icon
.elseif n==1
   lea eax, [msg_1]
   lea ecx, [msg_warn]
   mov edx, MB_ICONWARNING
.elseif n==2
   lea eax, [msg_2]
   lea ecx, [msg_error]
   mov edx, MB_ICONSTOP
.elseif n==3
   lea eax, [msg_3]
   lea ecx, [msg_error]
   mov edx, MB_ICONSTOP
.elseif n==4
   lea eax, [msg_4]
   lea ecx, [msg_error]
   mov edx, MB_ICONSTOP
.elseif n==5
   lea eax, [msg_5]
   lea ecx, [msg_info]
   mov edx, MB_ICONINFORMATION
.elseif n==6
   lea eax, [msg_6]
   lea ecx, [msg_info]
   mov edx, MB_ICONINFORMATION
.elseif n==7
   lea eax, [msg_7]
   lea ecx, [msg_info]
   mov edx, MB_ICONINFORMATION
.elseif n==8
   lea eax, [msg_8]
   lea ecx, [msg_error]
   mov edx, MB_ICONSTOP
.elseif n==9
   lea eax, [msg_9]
   lea ecx, [msg_error]
   mov edx, MB_ICONSTOP
.elseif n==10
   lea eax, [msg_10]
   lea ecx, [msg_error]
   mov edx, MB_ICONSTOP
.elseif n==11
   lea eax, [msg_11]
   lea ecx, [msg_error]
   mov edx, MB_ICONSTOP
.endif
   invoke MessageBox, NULL,eax,ecx,edx
   ret
Msg endp


And once you think you've got that, your next mission is to use an array to shorten the Msg procedure to under ten lines! :bdg
No snowflake in an avalanche feels responsible.

Chesso

Not quite sure what you mean by the array (for loop?).

So using the [] in that case, that's for readability only (I like it, makes it clearer, especially with no syntax highlighting to my disposal lol, always like keywords being bold especially for some reason).

So this would remove the need for the local variables (as I have 3 registers to my disposal which is all I need at this point).

I thought the returning value (fixing up) was the other 3 registers lol (got mixed up :P).

I noticed in the high level list of MASM32, there's no case or switch statement? (it's a handy little thing I like to use for readability, well in delphi anyway), like:

Case myvar Of
1: ;
2: ;
3: ;
End;

But yeah, being able to use if/else/else if more than enough of a god send :P.

And using offset is unnecssary (when using lea?), but if I were to do them directly when invoking an API, using offset would be a good idea?

Just got to get used to what to use and when lol.

I'll give your example a try :).

hutch--

Chesso,

Look in the High Level Help helpfile for "switch" "case support. You must include the masm32 macros which will give you a fast and flexible form of a C switch/case block.

The original author was Greg Falen for the switch block and a modified version switch$ / case$ was written by Michael Webster, one of our moderators. You certainly could use the .if .elseif .else . endif notation to do it as well.

You will find MASM is very good here, much of the convenience of high level for normal code yet it can tune in to performance oriented areas with no problems.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Chesso

I know and the fact that I'm only realistically interested in Win32 development, it suites me well (though I still like syntax highlighting with bold on keywords :P). I find that I can still reasonably structure my code in MASM32 (using some of the higher level syntax) and have it highly readible even without the highlighting :D.

God damn paying with this is so much fun :P.

I only do all of this as a hobby lol, but it's heaps fun, if only I had the money, I would be right into the hardware as well.

I'll take a look at that, although the if/elseif/else is quite good, seen as there is no syntax highlighting having the . in-front of if/elseif/else is extremely handy for readability :).