it occures 'unresolved external variable' error at DLL make(link) step.
How do I resolves this problem ?
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.
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
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 ?
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
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
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
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
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.
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?