OK, that won't work for us.
When you created the dll, did you have to create an Entry point function called dllMain?
If so, the first parameter is our hInstance. It must be stored in a module-level variable from dllMain and can than be accessed from our set hook procedure.
I don't think the dllMain function is even being called.
I went ahead and compiled the demo program and all the functions worked, but then I tried to access values in the dllMain routine and always got 0.
A couple errors I notice in the coding were.
dllmain function should return integer since that is bool in C
parameters should be passed in byval
VB Code:
Public Function DllMain(ByVal hInst As Long, ByVal fdwReason As Long, _
ByVal lpvReserved As Long) As Integer
hInstance = hInst
Select Case fdwReason
Case DLL_PROCESS_DETACH
' No per-process cleanup needed
Case DLL_PROCESS_ATTACH
DllMain = 1
Case DLL_THREAD_ATTACH
' No per-thread initialization needed
Case DLL_THREAD_DETACH
' No per-thread cleanup needed
End Select
End Function
regardless, it still doesn't work
Another thing is that you don't have to export hte Maindll function
Idea:
Try passing App.hInstance to the dll as a parameter to the set hook function.
After reading a bit more, I've decided that the DllMain function is probably not going to work in VB.
When you create a dll from C, the default entrypoint is the _DllMainCRTStartup function. This function initializes the C runtime libraries and then calls the DllMain function.
I noticed that the linker parameters for the VB dll included an entry /ENTRY:__vbaS, which means that this is the VB version of _DllMainCRTStartup. Unfortunately, it probably does not make a call to any DllMain that may exist. Even if it did, our DllMain function was probably renamed when it was compiled in the object code.
I think that the best bet is to pass the instance handle to the sethook function and see if that works.
When you create a dll from C, the default entrypoint is the _DllMainCRTStartup function. This function initializes the C runtime libraries and then calls the DllMain function.
I noticed that the linker parameters for the VB dll included an entry /ENTRY:__vbaS, which means that this is the VB version of _DllMainCRTStartup. Unfortunately, it probably does not make a call to any DllMain that may exist. Even if it did, our DllMain function was probably renamed when it was compiled in the object code.
You were almost there moeur, but u gave up at the last moment.. Whereas i continued researching and found the click!
Did u ever doubted to change this param just to see what could happen?
Code:
/ENTRY:__vbaS
Code:
/ENTRY:DLLMain
One More Trick is to create your project as Standard EXE and File -> Make 'MyDLL.dll'
I am not joking lol... but its not over yet.. You need your customized DLL to check all these... The LINKER will prompt you if u have a .DEF under your project dir, whether it shd modify the parameters and send or just compile it as usual (giving you both the options).
If you choose Yes (the first option) 3 modifications occur in the parameters:
· Number 1: Change the /ENTRY switch to point to DLLMain
I am so excited that it works now.. And finally a breakthrough in the world of programming that we Visual Basic programmers can too create a pure Win32DLL
Yes there were a fair few mistakes and things left out of the article. Seeing as the linker is the same as the VC++ one that is why you can change the entry point, export definitions etc. manually. (Also the ByVals etc. were wrong in the article. however moeur you should be clear that a bool is a VB Long, whereas a BOOL on the other hand is an Integer.)
Originally Posted by CodeBlock
One More Trick is to create your project as Standard EXE and File -> Make 'MyDLL.dll'
Now that is a good idea! I never thought of that. That way you eliminate the need for a redundant class module.
However I have to ask what exactly is the point of changing the base address?
I'm not sure this is going to get us anywhere.
If you change the entry point, then how are the runtime libraies going to be initialized? You may get into the DllMain, but not be able to call any VB functions.
I'm not sure this is going to get us anywhere.
If you change the entry point, then how are the runtime libraies going to be initialized? You may get into the DllMain, but not be able to call any VB functions.
No runtime libraries are initialised anyway. Remember thats why we couldnt use MsgBox or App.hInstance in the first place
However I have to ask what exactly is the point of changing the base address?
EXEs have a Base Address specification of 0x400000
and DLLs 0x10000000
I learned this by comparing two linking: one an EXE and the other a DLL ... so it is basically implementing the 3rd point.. without point 2, nothing works.
Originally Posted by moeur
If you change the entry point, then how are the runtime libraies going to be initialized? You may get into the DllMain, but not be able to call any VB functions.
VB Code:
Public Function DLLMain(ByVal hInst As Long, ByVal fdwReason As Long, lpvReserved As Long) As Long
Select Case fdwReason
Case DLL_PROCESS_DETACH
' No per-process cleanup needed
Case DLL_PROCESS_ATTACH
' No per-process initialization needed
' MessageBox 0, "Hi from DLL " & hInst, "Hi", 1
MsgBox "This is a VB Function", vbCritical
Case DLL_THREAD_ATTACH
' No per-thread initialization needed
Case DLL_THREAD_DETACH
' No per-thread cleanup needed
End Select
hInstance = hInst ' This is the App.Instance Parameter
DLLMain = 1 ' Return 1 on success
End Function
The above code works fine when run under a VB App... thats all we wanted.
See, You are not simply changing the entry point DLLMain, we dont have a direct access anywhere and to anything!!! We are asking only the linker to do them all. It might be the linking process which does all these hiding things from us.
I'm going to be n00bish and ask how the runtime library is referenced. Seeing as it is part of the linking process. I looked at the command line arguments for the linker log and there was no mention of msvbm60.dll, So presumably it is part of the .obj file. I am going to try and copy the .obj file out at compile time and disassemble it to have a look. maybe if it is in there then we can add it in so it gets linked with the DLL.
The original entry point is defined as /ENTRY:__vbaS where __vbaS is a routine that initializes the runtime libraries
The above point is right... But what if a predefined routine Microsoft made in the name of "DLLMain" hehe.. sounds kinky
I will be back with more TESTs. Also i am not sure how i could call in VC++ I have almost no knowledge of VC++ ... so i just need a code from moeur or someone to call an API... that might give solution to our questions
It's probably better to no run too much code out of the dllMain since all the initialization could not be complete.
I'll try to implement the changes you've made to the linker, test it on a simple program, then see if it works when I try to hook Notepad or something.
If MsgBox("Click Yes to apply DLL options over a Standard EXE Project," & vbCrLf & "otherwise click No to let VB handle the usual way.", vbYesNo) = vbNo Then
LINK ' Default
End If
End If
End Sub
Sub LINK(Optional CmdLine As String = vbNullString)
If CmdLine = vbNullString Then CmdLine = Command$
If CmdLine = vbNullString Then
Unload Me
Exit Sub
End If
Open App.Path & "\LinkerLog.txt" For Append As #1
Print #1, Now & ": ============================================"
Print #1, Now & ": Command Line Options: "; Command$
If CmdLine <> Command$ Then
Print #1, Now & ": Command Line Options: "; CmdLine ' Write only if there was a change
End If
Print #1, Now & ": ============================================"
Print #1, vbCrLf & vbCrLf
Close #1
ShellWait App.Path & "\" & LINK_FILE & " " & CmdLine ' i.e.: My SuperShell function
Unload Me
End Sub
Post on any doubts or errors, i am available! :)
HTH
Neo
EDIT: Had to edit coz 'BookMark #1' line shows a emoticon where it has the line of code /ENTRY:DLLMain
Last edited by CodeBlock; Jul 20th, 2005 at 07:03 AM.
I have told you that the base address specification of an EXE is 0x400000 which means Windows will first try to start mapping that EXE at the address 0x400000. If there is any other program already mapped at that address Windows will then allocate the next free address say: 0x470216 taking in count all the running programs at various memory addresses.
But DLLs have a different Base Address. Commonly, it is 0x10000000
To test this open a new ActiveX DLL project in VB. Goto Project->Properties and see Compile where the DLL Base Address will be showing: &H11000000
Hmm, now somebody might ask why is it 0x10000000 and not 0x11000000
If you want to know what is the difference between a regultar DLL and an ActiveX DLL, u must have known COM. But, i cannot explain COM, because it is a BIG BIG issue by itself and to learn about.
Question #1: Now, what is the real reason, that we should create our Win32DLL via a Standard EXE.. What is the need and y not through an ActiveX DLL project?
Answer:
Create an empty ActiveX DLL -> just open a new ActiveX DLL Project and Make 'Project1.DLL' Start->Programs->Microsoft Visual Studio 6.0->Microsoft Visual Studio 6.0 Tools->Depends
Open the File you just created and you will see that even if u haven't written any code or function there are two functions already in the DLL namely DLLRegisterServer and DLLUnRegisterServer
How did these two appear? Because you have created an ActiveX DLL Visual Basic automatically includes & compiles these two functions.. This is part of the compilation process. VB compiler adds this information in the .OBJ file which corresponds to the StartUp Object specified by the Project. This is the reason why we are creating the DLL via the Standard EXE and changing the switch while LINKing.
_________
We will move on to the first point now and see how that works (If you have not seen all my 3 points go here):
Code:
/ENTRY:__vbaS
I have now come to the point that the above parameter does not tell the VB Linker to include VB Runtimes.. Then Whats the use of the above one?
To know the answer, read on.
This is an extract from the MSDN documentation for VC++ Linker options.. I know that we are not dealing with VC++ LINKer options but this is just basically how every linker works.
Code:
mainCRTStartup (or wmainCRTStartup) An application using /SUBSYSTEM:CONSOLE; calls main (or wmain)
WinMainCRTStartup (or wWinMainCRTStartup) An application using /SUBSYSTEM:WINDOWS; calls WinMain (or wWinMain), which must be defined with __stdcall
_DllMainCRTStartup A DLL; calls DllMain, which must be defined with __stdcall, if it exists
If the /DLL or /SUBSYSTEM option is not specified, the linker selects a
subsystem and entry point depending on whether main or WinMain is defined.
The functions main, WinMain, and DllMain are the three forms of the
user-defined entry point.
When creating a managed image, the function specified with /ENTRY must
have a signature of (LPVOID var1, DWORD var2, LPVOID var3).
A linker's main aim is to collect all the .OBJ files and the specified .LIB files along with the language's RUNTIME files. No matter what VB LINK.exe does exactly the same and it includes the MSVBVM60.DLL Runtime DLL (You will see the proof soon)
The /ENTRY option has __vbaS value. This is nothing but a function inside one of those OBJ files.. The VB Compiler decides which OBJ by looking at the Startup Object specified in your project. If it is a form: frmMain then the __vbaS function has code to create the form object and show it eventually! (Have you ever wondered how your default form was loaded and shown?)
Originally Posted by penagate
I'm going to be n00bish and ask how the runtime library is referenced. Seeing as it is part of the linking process. I looked at the command line arguments for the linker log and there was no mention of msvbm60.dll, So presumably it is part of the .obj file. I am going to try and copy the .obj file out at compile time and disassemble it to have a look. maybe if it is in there then we can add it in so it gets linked with the DLL.
It is not the part of the .OBJ file but it is the linking process like i said there.. You may also doubt on the highlighted part below.. This LIB file is LINKED with any VB project. No Matter it is a Standard EXE or an ActiveX DLL
To know the proof: I have attached my WIN32DLL done in VB 'test2.dll'. Open that DLL file through Depends, you will see MSVBVM60.DLL in the dependency tree.
Atlast, this means that we can include our Win32DLL in any project, such as VC++ or our own VB Project (obvious)
Questions are welcome!
Last edited by CodeBlock; Jul 20th, 2005 at 09:24 AM.
It is not the part of the .OBJ file but it is the linking process like i said there.. You may also doubt on the highlighted part below.. This LIB file is LINKED with any VB project. No Matter it is a Standard EXE or an ActiveX DLL
Yep I found that also. I'm also interested in the fact that it becomes possible to create VB apps without using the runtime (To a degree). Would be good for things such as installers. Also if we wrote a lot of our own replacements for the functions in the runtime lib.
I'm getting the code ready for upload now. Just removing any evidence of potentially embarassing past stupid mistakes
I use my own linker whick I modified. While playing around I noticed that if the linker runs into an error it exits with no messages. To see the linker messages, you can use the one that comes with C++. Here are the linker options I get.
Code:
Command line arguments after modification:
"C:\Documents and Settings\All Users\Documents\VBwin32DLL\dllHook\dllHook.OBJ" "C:\Documents and Settings\All Users\Documents\VBwin32DLL\dllHook\dllHook1.OBJ" "C:\Program Files\Microsoft Visual Studio\VB98\VBAEXE6.LIB" /ENTRY:DllMain /OUT:"C:\Documents and Settings\All Users\Documents\VBwin32DLL\dllHook\dllHook.dll" /BASE:0x10000000 /SUBSYSTEM:WINDOWS,4.0 /VERSION:1.0 /DEF:"C:\Documents and Settings\All Users\Documents\VBwin32DLL\dllHook\dllHook.def" /DLL /INCREMENTAL:NO /OPT:REF /MERGE:.rdata=.text /IGNORE:4078
The DllMain syntax is
VB Code:
Public Function DllMain(ByVal hInst As Long, ByVal fdwReason As Long, _
ByVal lpvReserved As Long) As Long
All should be passed by value not by reference I do know this much since I write C dlls. The return type BOOL penagate points out should be Long, but I couldn't confirm that so I tried both.
OK, here it is:
The linker, dll and VB test program
The test program call a routine in the dll to try to hook notepad.
It subclasses itself to receive return messages from Notepad.
Right now I've commented out the code inside the Notepad's hook routine to see if I could stop Notepad from crashing
I'll explain my last couple of posts a bit more, since we all seem to be doing it
BOOL along with a plethora of others is defined in windef.h.
Code:
typedef int BOOL;
an int in C++ is a 32-bit word which is a VB Long.
However a bool is 1 byte long (as of VC++ 5 anyway) so in VB it would be a Byte.
Since lpvReserved is typed LPVOID this indicates it is a long pointer to an untyped memory space. VB doesn't have these (although I recently discovered you can do some obscene memory tricks using strings as pointers) so we need to pull the pointer value from the stack directly by using ByVal. Of course this is immaterial since being reserved it is never touched anyway.
Ahh, Man! Everyone knows that if u need to extract the pointer u need to change your declaration as .. , ByVal lpvReserved As Long)
But the point is we just need the hInstDLL which can be declared directly as (hInst As Long,...
Why, declare as ByVal hInst and CopyMemory.. lol.. i just pointed out that to see whether moeur's had done it by mistake...
So, it appears that there is something wrong with the dll since I have no problem if the dll is written in C
Why do u always blame the DLL, lol
When i Ran your EXE code i had a break point right at the cmdHook_Click and stepped over.. I found out that VB was trying to show some error message highlight as below: