 |
|
|
 |
|
 |
Hello, i'm really like this article. I'm the webmaster/coder of Forum Poster, nice to meet you
modified 29 Mar '12.
|
|
|
|
 |
|
|
 |
|
 |
just one question .. i am curious generally when we have to get an executables path i use
GetModuleFileName(NULL
what happens when i call it from a DLL, that doesn't get the path?
|
|
|
|
 |
|
 |
Nope, In a DLL you need to provide an hInstance to GetModuleFileName.. When you're not coding a MFC DLL, or if the DLL you're modifying doesn't have a DllMain function, you can't get the hInstance unless you use the method described in this article.
Granted, a lot of people probably won't need to get the full path to the DLL (especially if they use the registry for DLL-specific settings), but I did, and I'm sure the other half-dozen or so remaining C++ developers on CodeProject could use the info at some point.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
John Simmons / outlaw programmer wrote: Nope, In a DLL you need to provide an hInstance to GetModuleFileName.. When you're not coding a MFC DLL, or if the DLL you're modifying doesn't have a DllMain function, you can't get the hInstance unless you use the method described in this article.
Cool i didn't know that.
|
|
|
|
 |
|
 |
If you call GetModuleFileName(NULL, ...) from within a DLL, you will get the path and filename of the top-level EXE file that ultimately loaded your DLL, *not* the filename of the DLL itself.
One way to do get the module handle is to either (1) add your own DllMain() implementation or (2) modify the existing one to save the module handle as a global variable:
// Global var to contain DLL module handle
HANDLE g_hDLL = NULL;
// DllMain() function - Win32 entry point for a DLL
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
// Save the module handle when DLL is loaded
if (dwReason == DLL_PROCESS_ATTACH)
g_hDLL = hModule;
return TRUE;
}
You can then use the module handle as needed downstream in your code:
GetModuleFileName(g_hDLL, ...);
|
|
|
|
 |
|
 |
Well of course, googles, you're right, but notice that in this article, I'm not passing NULL to the function.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
I may be wrong about your pseudo-variable but I believe that it refers to the preferred DLL base address. If the address space that the DLL prefers is already used by another DLL, your DLL will be rebased and the your variable will not be the correct HINSTANCE for your DLL.
You have another option to get this value that is also very few lines of code and supported
// Near the top of your CPP file:
static HINSTANCE hDynImageBase = NULL;
// Perhaps in DllMain or whenever you need to use it...
if (NULL == hDynImageBase)
{
GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCWSTR)&hDynImageBase, &hDynImageBase);
}
// Now, whenever you need the path
WCHAR* pszDllPath = new WCHAR[_MAX_PATH];
GetModuleFileName(hDynImageBase, pszDllPath, _MAX_PATH);
Making hDynImageBase static places the variable location in the image's .data section. The address of this variable will be within the module's mapped address space (as opposed to the stack or the heap). You can use this address to get the handle to the module.
Thanks,
Doug Coburn
|
|
|
|
 |
|
 |
Prove your theory, and I'll accept that my approach might be wrong.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
I stand corrected. It appears that both my way and your way works for both the first DLL and the rebased DLL. What's more, it even works on Vista when compiled with the Vista SDK compiler/linker.
It appears that I went wrong in assuming that your code was pulling the preferred base address from the IMAGE_DOS_HEADER structure. IMAGE_DOS_HEADER __ImageBase is actually the first value of the binary image, the address of which is the base address of the DLL and therefore the module's HINSTANCE. Like you said, you need a linker that will resolve the pseudo-variable for you to make your solution work.
Thanks,
Doug Coburn
|
|
|
|
 |
|
 |
Yep - this only works on VS2002 and later.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
Doug:
Your method will only work on Win XP, Win Server 2003, or Vista, since GetModuleHandleEx() does not exist in earlier OS's. Here is an alternative method, using VirtualQuery(), that will work on any version of Windows:
MEMORY_BASIC_INFORMATION mbiInfo = { 0 };
TCHAR szPath [MAX_PATH + 1] = "";
HMODULE hmodModule = NULL;
DWORD dwRet = 0;
dwRet = VirtualQuery (((LPCVOID) (&mbiInfo)), &mbiInfo,
((DWORD) (sizeof (MEMORY_BASIC_INFORMATION))));
if (dwRet)
{
hmodModule = ((HMODULE) (_mbiInfo.AllocationBase));
GetModuleFileName (hmodModule, szPath, MAX_PATH);
}
Moshe
|
|
|
|
 |
|
 |
This method will also work in legacy compilers prior to VS2002 (which was noted in my article). I see no reason why it shouldn't work in newer compilers as well, but the method in this article only requires two lines of code.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
John Simmons / outlaw programmer wrote: but the method in this article only requires two lines of code.
MEMORY_BASIC_INFORMATION mbiInfo = { 0 };
TCHAR szPath [MAX_PATH + 1] = "";
if (VirtualQuery(((LPCVOID)(&mbiInfo)),&mbiInfo,((DWORD)(sizeof(MEMORY_BASIC_INFORMATION)))))
GetModuleFileName ((HMODULE)(_mbiInfo.AllocationBase), szPath, MAX_PATH);
That's putting it in four lines of code.
But even it would take 50 lines - portability is what counts
U should avoid "tricky optimizations" like this wherever possible. See also this article.
|
|
|
|
 |
|
 |
People using VS2005 will probably not be revisiting VC6 anytime soon. Portability is not within the scope of the article.
"Why don't you tie a kerosene-soaked rag around your ankles so the ants won't climb up and eat your candy ass..." - Dale Earnhardt, 1997 ----- "...the staggering layers of obscenity in your statement make it a work of art on so many levels." - Jason Jystad, 10/26/2001
|
|
|
|
 |
|
 |
Many of large projects use plugin scheme with VC++ 6.0 DLLs.
So portability is a big issue.
|
|
|
|
 |
|
 |
It seem to be a bug:
GetModuleFileName ((HMODULE)(_mbiInfo.AllocationBase), szPath, MAX_PATH);
shouldn't it be
"mbiInfo.AllocationBase"
nn.
Codemania
|
|
|
|
 |
|
 |
You're right. I've copied & pasted & compacted code from a previous posting from mmatitya, that was mistyped. I would'nt call this a bug.
To be honest I didn't compile & test it. My point was just to state, that
"this solution takes only two lines"
is _no_ argument at all.
|
|
|
|
 |
|
 |
Changed below line to static variable, then now work!
>> static MEMORY_BASIC_INFORMATION mbiInfo = { 0 };
but I cannot explain the reason exactly,
( progam address vs. data address vs. heap address )
and my guess is this solution is not perfect.
|
|
|
|
 |
|
 |
I had to make it "static" to work under XP also. Probably something to do with alignment?
|
|
|
|
 |