News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Simple Example To Call a DLL

Started by nathanpc, October 02, 2009, 05:44:25 PM

Previous topic - Next topic

nathanpc

Hello,
I'm now seeing somethings od DLLs, but i was seeing the Call DLL example and it's a graphical app, then it's so much confusing for me and the DLL that i'm testing is for a console app, but now i want to know how i can do a program that call my DLL, see my code of the test DLL:
.386
option casemap :none   ; case sensitive
include \masm32\include\masm32rt.inc

.code
LibMain proc
    jmp @F
      MbMess db "Test function",0
    @@:
    ret
print MbMess, 13, 10

LibMain endp
End LibMain


Regards,
Nathan Paulino Campos

Astro

Hi,

.386
.model stdcall,flat

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

.data?
hLib dword ?
hProc dword ?

.data
lib byte "DLL.dll",0
function byte "FunctionNameHere",0

.code

start:

push offset lib
call LoadLibrary
mov hLib,eax

push offset function
push hLib
call GetProcAddress
mov hProc,eax

call hProc ; will call your function in your DLL

push hLib
call FreeLibrary ; free the resource

ret ; end

end start


Best regards,
Robin.

nathanpc

The function name in this case will be LibMain?

Thanks.

Vortex

Hi nathanpc,

Here is an example for you :


.386
.model flat, stdcall
option casemap :none

include     \masm32\include\windows.inc
include     \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib
includelib ConsFuncs.lib

ClearScreen PROTO
StdOut      PROTO :DWORD
StrLen      PROTO :DWORD
locate      PROTO :DWORD,:DWORD

.data

message     db 'Hello world!',13,10
            db 'This is a console application.',13,10,0

.code

start:

   invoke  ClearScreen
   invoke  StdOut,ADDR message                                              
   invoke  ExitProcess,0

END start


The ClearScreen and StdOut functions are exported by a DLL named ConsFuncs.dll

The application prints a welcome message in the console window.

BogdanOntanu

You do not "call" a DLL.

Instead you or the OS loader "Load a DLL" ...  and later on in your program you can use the functions that are available and "exported" by the DLL.

The LibMain function (entry point of DLL) is normally used for initialization needs of the DLL itself. Normally application programs DO NOT use LibMain. Instead they use the functions available inside the DLL.

1) You can manually load a DLL by using the LoadLIbrary API. 2)Then you can use The GetProcAddress API in order to find the address of an function available in the DLL (if you know the function name or the ordinal ID). 3) Once you have the address of the desired function you can finnaly "CALL it" or even better "INVOKE it" .

Alternatively there is an easy way: you can "tell" the assembler / linker duo that you intend to use functions from a certain set of DLL's and the assembler/linker will solve all the problems for you.

In this case you can simply CALL or INVOLE the functions from the DLL in your source code "as if" they are already a part of your program. The OS loader will load the DLL for you and will patch the "call" to point to the right location at runtime transparently. The PE executable format used by Windows is designed for this.

For this you have to write some statements in your program: INCLUDELIBs, INCLUDES with STRUCtures, EQUates MACROS and function PROTOTYPES.

Usually you do need this kind of statements in your program anyway and MASM32 has a set of includes and prototypes/macros/functions already defined for helping you to start-up faster.

If you check the MASM32 examples you will notice those statements and includes at start of source code and if you explore them in more details then you will find the prototypes and constants / structures / macros definitions inside.


Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

nathanpc

Hello For All,
I follow the tips and the sample that Vortex posted, my code of the DLL is like this:
.386
option casemap :none   ; case sensitive
include \masm32\include\masm32rt.inc

.code

LibMain proc instance:dword,reason:dword,unused:dword
    mov     eax,1
    ret
LibMain     endp

PrintMess proc
    MbMess db "Test function",0
    ret
print MbMess, 13, 10
PrintMess endp

End LibMain

My program code is like this:
.386
.model stdcall,flat
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
includelib testdll.lib
PrintMess proto

.data?
hLib dword ?
hProc dword ?

.data
lib byte "testdll.dll", 0
function byte "PrintMess", 0

.code

start:
invoke PrintMess
end start

And when i try to execute the program i'm getting a error, see:


Best Regards,
Nathan Paulino Campos

PBrennick

Did you create a DEF file with the name of the procedures to export (probably did).  Here is where yiour problem probably is, the lib file that is the intermediate step of creating your DLL is not a standard lib file loaded by includelib. Decide which you want. If you want a static library of functions or a dynamic linled library. If your choice is the DLL way. You need to load the library (DLL) before you try to access the code in it. That code is missing. Also remove the includelib you added. Reread Bogdan's post. If you still need help we will give it.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

BlackVortex

Yep, it's best to remove that includelib and all other references. Then jsut use LoadLibrary and GetProcAddress.

BogdanOntanu

Quote from: BlackVortex on October 02, 2009, 11:16:49 PM
Yep, it's best to remove that includelib and all other references. Then jsut use LoadLibrary and GetProcAddress.

NO.  The use of LoadLibrary and GetProcAddress is rarely needed in a normal application. It is good to know that it is there but it is NOT recommended to be used  as a newbie's start method.

I created huge applications in ASM (SOL_ASM, HE_RTS) and never needed to use LoadLibrary/GetProcAddress... or used it only on very special occasions.

Instead I do strongly suggest that you follow Vortex's example. It contains ALL you need to do in order to create a DLL and then in order to USE that DLL in your own program.

The INCLUDDELIB statement is needed in this case; it will tip the linker to use the .LIB for your DLL when linking.

In your example you define a data string inside the PROCEDURE named PrintMess... Why do you do this? The procedure should contain the code to print that message but not the data of the message itself.

Usually such data definition statements are placed in the .DATA section of an program.

Try to logically understand each step you make. Why do you mix data an code? And how do you expect that the CPU will "execute" a data string defined with DB inside your PrintMess PROCEDURE?

I recomend that first you try and see if you can build and run Vortex's sample "as it is".

Then make very small incremental changes of his code and check that the result is functional after each small step. Continue to do this until you understand each step and you reach your target.

The error message suggest that there is some kind of a problem with the DLL itself.

Oh, an take care how you name things. "CALL" is a reserved keyword in MASM. It is not a problem to name an executable "call.exe" but it shows that you do not care...  and sooner or later you will make a mistake if you are not aware. Remember that you are  not "calling"... you are loading/using.

For example My_Load_DLL.exe and My_DLL.dll are more "normal" program names for a test.
Ambition is a lame excuse for the ones not brave enough to be lazy.
http://www.oby.ro

hutch--

 :bg

Yes I agree with the "Gospel According To Bogdan". Shortcuts lead to bad habits where the real action is to properly understand what you are doing and be thorough in how you execute it.

An EXEcutable file is one that the operating system will load and run by itself, it has the necessary startup code to do this where a DLL is a different animal, it cannot be directly run by itself and was designed in the first place to be able to be loaded, used then discarded when no longer required. This is literally what the term "Dynamic Link Library" means. To use this original technique you use the two API functions, LoadLibrary() to LOAD the DLL into memory then GetProcAddress() to get the starting address of the procedure in that library. When you have finished with the DLL you unload it using FreeLibrary().

The operating system is designed around DLLs and the vast majority of Windows API calls are calls to system DLLs that are always loaded into memory. This is why there is another technique that uses libraries and include files as it saves the extra overhead of making all of the Library and Procedure address calls and in most cases this is the most efficient way to do it. The distinction you need to draw is how your application needs to use a DLL, if it has to do a vast number of things in a large count of DLLs then the manual load technique has many advantages where if a DLL you write will be used by the application on a repeat basis, loading the DLL statically at startup is the most efficient way to do it.

There is a tutorial in the MASM32 sdk on how a DLL works so I don't need to repeat it here but the basics are a DLL must have a procedure that the operating system calls BEFORE the DLL is loaded into memory ready to use. Its name does not matter but its form does, the operating system expects a "LIBmain" or whatever you call it to have 3 x DWORD arguments and for the DLL to successfully start, the procedure must return NON-ZERO.

At the minimum you can write code like this,


mov eax, 1
ret


This will ensure the DLL will start but there are many more options available with a DLL. You can write a complete application in a DLL with complex initialisation and different code depending on whether it is called by a thread from the calling application.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Vortex

Hi nathanpc,

Here is how it works.

Test.asm, the source code of Test.dll :


.386
.model flat, stdcall
option casemap :none

include     \masm32\include\windows.inc
include     \masm32\include\msvcrt.inc


includelib  \masm32\lib\msvcrt.lib  ; The import library required to resolve
                                    ; the function printf

.data

MbMess      db 'Test function',0    ; Here goes the NULL terminated string

.code

LibMain     PROC instance:DWORD,reason:DWORD,unused:DWORD

    mov     eax,1
    ret

LibMain     ENDP

PrintMess   PROC

    invoke  crt_printf,ADDR MbMess  ; print a string in the data section
    ret                             ; Return the control to the main application

PrintMess   ENDP

END         LibMain


Demo.asm :


.386
.model flat, stdcall
option casemap :none

include     \masm32\include\windows.inc
include     \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

                     ;    kernel32.lib to resolve the function ExitProcess

includelib Test.lib ;   The import library required to resolve the function PrintMess

PrintMess   PROTO    ;   The function does not take any parameters so
                     ;   no need to specify here the expression :DWORD
.code

start:

    invoke  PrintMess
    invoke  ExitProcess,0

END start

nathanpc

Thanks very much for all!
Now all is working very nice.

Best Regards,
Nathan Paulino Campos