How declaries Public variables between main module and DLL ?

Started by vega, September 23, 2008, 07:21:24 AM

Previous topic - Next topic

vega

it occures 'unresolved external variable' error at DLL make(link) step.
How do I resolves this problem ?

hutch--

Basically you need to change the architecture as a DLL is built separately from the EXE that calls it so it has no way of knowing what the globals are in the caling EXE. You can pass direct arguments or if there are a lot of variables, pass an address where the DLL can get the addresses of the variables it requires. This is passing the adress of either an array of other addresses or a structure address that holds addresses as its members.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

PBrennick

To get that address, use GetProcAddress. In my Code Librarian, I have lots of data blocks. I wantwed to easily access any block at any given time so instead of declaring globals in the DLL, I made each data block its own procedure.

For example, in the DLL I have:
szWindow    PROC
;---------------------------------------
    db  20h, 20h, 20h, 20h, 2Eh, 33h, 38h, 36h, 0Dh, 0Ah, 20h, 20h, 20h, 20h
    db  2Eh, 6Dh, 6Fh, 64h, 65h, 6Ch, 20h, 20h, 66h, 6Ch, 61h, 74h, 2Ch, 20h
    db  73h, 74h, 64h, 63h, 61h, 6Ch, 6Ch, 0Dh, 0Ah, 20h, 20h, 20h, 20h, 6Fh

; Lots of data deleted to save room ...

    db  6Ch, 61h, 63h, 65h, 20h, 6Dh, 6Fh, 72h, 65h, 20h, 70h, 72h, 6Fh, 63h
    db  65h, 64h, 75h, 72h, 65h, 73h, 2Ch, 20h, 68h, 65h, 72h, 65h, 0Dh, 0Ah
    db  0Dh, 0Ah, 20h, 20h, 20h, 20h, 45h, 4Eh, 44h, 20h, 73h, 74h, 61h, 72h
    db  74h, 0Dh, 0Ah
    db  0                               ; This NULL byte terminates the data block
;---------------------------------------
szWindow    ENDP


I access it this way:


.data
;----------
pWindow         db  'szWindow', 0

.code
;--------
        invoke  GetProcAddress, [hLib], ADDR pWindow    ; Get the address of the "procedure"
        mov     pText, eax                              ; containing the text
        invoke  SendMessage,hEditorBox, WM_SETTEXT, 0, pText      ; Display the text


Where hEditorBox is the richEdit control. If you where to add text to existing text, at an insertion point, for example, you would use EM_REPLACESEL instead of WM_SETTEXT and change the zero to TRUE if you wish to be able to undo the action.

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

jj2007

Quote from: PBrennick on September 24, 2008, 02:18:38 PM
To get that address, use GetProcAddress. In my Code Librarian, I have lots of data blocks. I wantwed to easily access any block at any given time so instead of declaring globals in the DLL, I made each data block its own procedure.

For example, in the DLL I have:
szWindow    PROC
;---------------------------------------
    db  20h, 20h, 20h, 20h, 2Eh, 33h, 38h, 36h, 0Dh, 0Ah, 20h, 20h, 20h, 20h


Interesting technique. Since it's in the code section, do you
have to use the linker option /SECTION:.text,ERW ?

PBrennick

JJ,
Nope, it is pretty standard actually, the batch file is below:

\GeneSys\bin\ml /c /coff SourceCode.asm
\GeneSys\bin\polink /SUBSYSTEM:WINDOWS /DEF:SourceCode.def SourceCode.obj

PAUSE


The DEF file lists all the procedures and the data is pulled from them as I showed above. I can make you a working example if you like. As far as the technique goes, Vortex was the brain behind it. I wanted a method that did not require EXTERNs and all that crap. Just load the DLL and use GetProcAddress. What could be simpler? Pretty cool, actually, The CodeLibrarian is what it was designed for. Just goes to show what you can do with a DLL.
-- Paul
The GeneSys Project is available from:
The Repository or My crappy website

jj2007

Yes it works fine, but it is read-only, and the linker option ERW does not work:

DLL:
.code

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

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

MyData proc EXPORT
MyCounter dd 12345678
MyString db "A string", 0
MyData endp


Caller:
MyString = 4 ; the offset inside MyData

invoke GetProcAddress, hDll, chr$("MyData") ; test - this is actually MsgBox
mov ecx, [eax] ; reading MyCounter works fine
; mov [eax], ecx ; chokes with exception 5, access denied

add eax, MyString ; offset 4
invoke MessageBox, 0, eax, chr$("Title"), MB_OK

jj2007

A slightly more elegant version of what I posted above.

DLL:

include \masm32\include\masm32rt.inc

.data
S0 db "Wow, a string:", 0
S1 db "This is the first string in the data section of the DLL", 0
S2 db "This is the second string in the data section of the DLL", 0

.code

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

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

MyData proc EXPORT
  MyTitle dd S0
  My1st dd S1
  My2nd dd S2
MyData endp

end LibMain


Call:
include \masm32\include\masm32rt.inc

.data
hDll dd 0
ExpNF db "DLL entry not found:",0
dll_notf db "DLL not found:", 13,10 ; no zero byte here...
dll_name db "DataDllMake.dll", 0

.code
start:
  invoke LoadLibrary, addr dll_name
  .if eax==0 ; dll not found
invoke MessageBox, 0, addr dll_notf, chr$("DEMO"), MB_OK
ret
  .endif

  mov hDll, eax

  TitleString = 0 ; the offset inside MyData
  FirstText = 1
  Second = 2

  invoke GetProcAddress, hDll, chr$("MyData")

mov edx, [eax+4*TitleString]
mov ecx, [eax+4*FirstText]
invoke MessageBox, 0, ecx, edx, MB_OK

  invoke FreeLibrary, hDll
  exit

end start

PBrennick

Duh, of course it is read only.

and what was the point of this?
and the linker option ERW does not work:


I made no mention of that option?!?

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

jj2007

Quote from: PBrennick on September 25, 2008, 11:08:55 PM
I made no mention of that option?!?
I mentioned it myself. I just wondered why one cannot write to the DLL's code section. ERW works fine with the code section of executables.

vega

Thank you, Hutch, paul & jj2007
I solved my problem by your good advices.
I'm a trainning assembly student.
so, i tried to solve my problem with basically ways.
but your high advices given many ideas to me.
thank you.

This is my resolved Code.




;; MycroDll.asm  (DLL Source)


INCLUDE  \MASM32\MAL\Mycro\Windows.inc
INCLUDE  \MASM32\MAL\Mycro\Mycro.mac


Begin_of_program

   Begin_of_data
      Set_null_byte     CallCount
      Set_null_dword   hRichEdit
      Set_string          myName, "I am CPU, Sir."
      ;

   Begin_of_code
   
      DllEntry  PROC  hInstDLL:HINSTANCE, reason:DWORD, reserved1:DWORD
         Set  EAX, TRUE
         Return
      DllEntry  ENDP
     
     
      Who_are_you?  PROC
         PUSH  EBP
         Set  EBP, ESP
         
         .IF CallCount is 0
              Sync  hRichEdit, [EBP+8]
         .ELSEIF CallCount is 1
              Sync  hRichEdit, [EBP+12]
         .ENDIF   
         
         Invoke  SendMessage, hRichEdit, EM_REPLACESEL, TRUE, SADD(CRLF)
         Invoke  SendMessage, hRichEdit, EM_REPLACESEL, TRUE, ADDR myName
         
         POP  EBP
         RET
      Who_are_you?  ENDP
     
         ;
         Who?  PROC
            Set  CallCount, 1
            Call  Who_are_you?
            Set  CallCount, 0
            Return
         Who?  ENDP
         ;
         What_is_your_name?  PROC
            Set  CallCount, 1
            Call  Who_are_you?
            Set  CallCount, 0
            Return
         What_is_your_name?  ENDP
         ;
         Whats_your_name?  PROC
            Set  CallCount, 1
            Call  Who_are_you?
            Set  CallCount, 0
            Return
         Whats_your_name?  ENDP
         ;
           
     
      END  DllEntry
   
   
   End_of_code


End_of_program






;; Calling program

                  @@:
                  rems; Call LoadLibrary with the name of the desired DLL.
                      If the call is successful, it will return the handle to the library (DLL).
                      If not, it will return NULL.
                      You can pass the library handle to GetProcAddress or any function that requires
                      a library handle as a parameter.
                      ;
         
                  Load library  literal("\MASM32\Mal\MycroDll.dll")
                   
                  .IF ReturnValue is NULL
                     Display message box with  MessageBoxTitle, DllNotFoundMessage, MB_OK
                     
                  .ELSE
                     rems; When you get the library handle, you pass it to GetProcAddress with the address
                         of the name of the function in that DLL you want to call.
                         It returns the address of the function if successful.
                         Otherwise, it returns NULL.
                         Addresses of functions don't change unless you unload and reload the library.
                         So you can put them in global variables for future use.
                         ;
         
                     Set  hLib, ReturnValue
                     
                     Get proc address of   FindTextBuffer
                     .IF GetValue is NULL
                        Display message box with  MessageBoxTitle, ProcNotFoundMessage, MB_OK

                     .ELSE
                     
                         rems; Next, you can call the function with a simple call with the variable containing
                         the address of the function as the operand.
                         ;
                           
                         Set   ProcAddress, GetValue
                         ;
                         Push data to stack  hRichEdit
                         ;
                         Call  [ProcAddress]
                     
                     .ENDIF

                     ;;When you don't need the library anymore, unload it with FreeLibrary.
                     Unload library  hLib
                         
                  .ENDIF








;; MycroDll.def

LIBRARY   MycroDll
EXPORTS   Who_are_you?
EXPORTS   Who?
EXPORTS   What_is_your_name?
EXPORTS   Whats_your_name?