Code executed by shared library when loaded/unloaded - c++

Is it possible to create a function in a shared library (.dll on Windows, and .so on linux) that is executed right when the library is loaded (or unloaded)?
Just like the main() function is the entry point for an executable, can I define a function to execute when the DLL is loaded, or unloaded?
E.g.:
void _atstart()
{
// Initialize some stuff needed by the library
}
void _atexit()
{
// Release some allocated resources
}
I think I've seen such an example somewhere, but I couldn't find it any more, and couldn't find anything on the internet about this.
If it is of any use, I'm compiling the code with MinGW.

In C++ you can at least create a global instance of some class
class ResourceHolder {
public:
ResourceHolder() {
// at start
}
~ResourceHolder() {
// at exit
}
};
ResourceHolder theHolder;
Some awareness is required though if you use another global variables in your library.

For windows you can use DllMain:
BOOL WINAPI DllMain(
__in HINSTANCE hinstDLL,
__in DWORD fdwReason,
__in LPVOID lpvReserved
);
The second parameter fdwReason specifies if the library is loaded or unloaded. Full reference: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// code for library load
break;
case DLL_PROCESS_DETACH:
// code for library unload
break;
}
return (TRUE);
}
For Linux you might be able to use:
__attribute__ ((constructor))
__attribute__ ((destructor))
but this only came up after a google search, so you have to investigate by yourself - http://tdistler.com/2007/10/05/implementing-dllmain-in-a-linux-shared-library

As it has been said, under Window you can work from DllMain. But be careful what you will do since there is a lot of restrictions (use of COM CoInitialize function is forbidden, for instance). One thing you can't rely on is that there is no guaranty in what order dll will be loaded/unloaded, so you must not call functions from your DllMain that resides in an other of your dll : it can works today, but not tomorrow :)
More online on the MSDN :
[http://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx]
Patrice

Under Windows you can write your own version of DllMain().

Related

Download file from inside a DLL in C++

I 've been trying to download a file by using code inside a dll built in C++ on Windows. The DLL will be loaded with LoadLibraryA function, and I am trying to download the file the moment it is first loaded.
Searching through the internet I managed to implement the URLDownloadToFile() that works normally for a console app but not on a Dynamic-Link Library built in Visual Studio. In the project properties->Linker->Input I have added the additional dependencies Urlmon.lib;Wininet.lib;.
The code builds successfully (Release, x64), when I test it with rundll32.exe C:\Users\John\Desktop\Dll1\x64\Release\Dll1.dll, main I get the message "Starting" and then it freezes. No network activity shown (checked with eset and process monitor), no writing to file kkk.bin.
The code I am using so far looks like this:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <iostream>
#include <Urlmon.h>
#include <iomanip>
#include <thread>
#include <Wininet.h>
#pragma comment(lib,"WinInet.Lib" )
#pragma comment(lib,"Urlmon.Lib" )
__declspec(dllexport) void sample() {
MessageBox(NULL, (LPCWSTR)L"Starting", (LPCWSTR)L"title", MB_ICONWARNING);
const TCHAR url[] = _T("http://techslides.com/demos/samples/sample.txt");
const TCHAR filePath[] = _T("C:\\Users\\John\\Desktop\\kkk.bin");
DeleteUrlCacheEntry(url);
HRESULT hr = URLDownloadToFile(
NULL,
url,
filePath,
0,
NULL);
if (SUCCEEDED(hr))
{
MessageBox(NULL, (LPCWSTR)L"success", (LPCWSTR)L"title", MB_ICONWARNING);
}
else
{
MessageBox(NULL, (LPCWSTR)L"failed", (LPCWSTR)L"title", MB_ICONWARNING);
}
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
sample();
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
The DllMain entry point is intended to do only simple initialization or termination tasks. For technical reasons, the things that you are allowed to do in DllMain is very restricted. See this link for further information.
By initiating the download of a file, you are likely causing new DLLs to get loaded, which should not be done in DllMain, as it could cause a deadlock.
If you want your DLL to have an initialization function without these restrictions, then you should not use the DllMain entry point function (except for simple initialization). Rather, you should wait until the operating system has fully finished with loading your DLL.
For example, you could require that all programs that use your DLL to call a special initialization function inside your DLL before calling any other functions inside your DLL. It is quite common for DLLs to require this. For example, the DLL of the Microsoft Windows Sockets 2 Library (Ws2_32.dll) requires that you call WSAStartup before calling any other of its other functions. This special initialization function does not have the same restrictions as DllMain, because it is technically a normal function call. Therefore, it allows you to do things such as downloading files.
Alternatively, your DLL could keep track of whether it has initialized itself in a global variable. In DllMain, you do nothing else than set this global variable to 0 (false). Whenever an exported function in your DLL is called, your DLL function could check this global variable, and if your DLL has not yet initialized itself, it will call your DLL's special initialization function, which performs the download and sets the global variable to 1 (true).
However, checking a global variable on every function call could be expensive in terms of performance. Therefore, the first method may be preferable to the second method.

How to manually initialise the c runtime in an exe?

I'm trying to provide my own entry point to a visual c++ executable using the linker switch "/entry". The associated Microsoft documentation goes into some detail about how to manually initialise the c runtime in a dll, but I can't grasp how to initialise it in my exe. The best I've got is that I need to call _CRT_init, but I don't know if that's a function or a macro, or where it might be defined, and Visual Studio (and msbuild) don't recognise the identifier, so no hints there.
The rationale here is that I'm attempting to unit test, with Google Test, an executable, and it won't link because main() clashes. The two approaches mentioned in the GTest FAQ aren't really generalisable and would require considerable rework to about 30 legacy executables to implement. Renaming each unit test application's main() seems like a super-easy approach, if only I could initialise the c runtime.
So I searched through the C Runtime source this afternoon, and the answer is "no". You cannot provide your own executable entry point not named main if you want to use the C Runtime. Quite aside from _CRT_INIT() being the an initialisation function for a dll (mainCRTStartup() is one of the initialisation functions for executables), mainCRTStartup() and its like call main() by name.
You could initialize the CRT in a standalone wrapper module which has
main; obj1
Rename the main of all existing exe modules to xx_main; obj2, obj3.... objn
Link obj1 (main) with (obj2, obj3.... objn).
To test a foo.cc file, you need to compile and link it into your unit
test program
What's the issue with this scheme ?
Though not specifically documented, it is possible to initialize the CRT in an executable in the same way it can be done in a DLL.
EXTERN_C BOOL WINAPI _CRT_INIT( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved );
// ThreadProc for CreateRemoteThread, a possible use case.
DWORD WINAPI ArbitraryAlternateEntryPoint( LPVOID )
{
_CRT_INIT( GetModuleHandle( NULL ), DLL_PROCESS_ATTACH, NULL );
// CRT functions can be used here.
_CRT_INIT( GetModuleHandle( NULL ), DLL_PROCESS_DETACH, NULL );
return 0;
}
The documentation suggests that _CRT_INIT should be called for each new thread in addition to the initial call, but in practice this is not necessary.

winapi entry point without WinMain

So going through a source of a winapi program I found online I noticed there was no WinMain anywhere, I wonder if is possible in anyway to ever make a winapi program work like this, why do you think the original programmer did anything like this, we have the dialog procedure like this :
static INT_PTR CALLBACK mainDialogProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG:
{
//..............
}
}
}
and the main entrypoint was like this instead of void WINAPI WinMain (void);
void WINAPI entryPoint(void)
{
//........
}
Is this really possible? it goes against all that I have studied so far...
I'm really sure I'm missing something...
The entry point for an executable image is specified through the linker setting /ENTRY. If not explicitly set, it defaults to mainCRTStartup (or wmainCRTStartup) for a console application, WinMainCRTStartup (or wWinMainCRTStartup) for a GUI application, and _DllMainCRTStartup for a DLL. When using the CRT that ships as part of Visual Studio, all of the aforementioned raw entry points call into the user-provided entry points main (or wmain), WinMain (or wWinMain), and DllMain, respectively. This is implemented in crt0.c that ships as part of Visual Studio's CRT source code.
If you provide your own entry point (by specifying the /ENTRY linker option), it must have the following signature (for a 32-bit application):
DWORD CALLBACK RawEntryPoint(void);
Bonus reading: WinMain is just the conventional name for the Win32 process entry point
It is mostly compiler dependent, but the "entry point" of an executable is a function declared in the linker by means of appropriate options. WinMainCRTStarup is just the default, that -after an initialization of the default library- calls WinMain.
Anyway, code written like this, is probably defined to be self-contained, with the idea that you will call enrypoint from you own WinMain, where you can also do anything else.
You can use the /ENTRY:"entryPoint" linker option to set an arbitrary entry point.

CRT Initialization: runtime error - CRT not initialized

today I have been trying to get standard functions to work in my application such as sprintf, ect.
When ever using functions like sprintf, fgets or anything else standard my application crashes with runtime error - CRT not initialized
Current linker options:
/ignore:4098 /dll /entry:"_DllMainCRTStartup" /include:"XboxKrnlBuildNumber" /ALIGN:128,4096
Entry point:
BOOL APIENTRY DllMain(HANDLE hInstDLL, DWORD reason, LPVOID lpReserved)
Tried to init it my self by defining _CRT_INIT is an extern that takes no arguments and calling it in my DLL_PROCESS_ATTACH with no luck.
I've been stuck on this issue for months but just thought i'd come back to it and look a little deeper.
If you are doing something non-trivial in your DllMain function, this may be of help:
http://blog.barthe.ph/2009/07/30/no-stdlib-in-dllmai/
http://blogs.msdn.com/b/larryosterman/archive/2006/06/15/632502.aspx

C++ LoadLibrary ERROR_NOACCESS "Invalid access to memory location."

OK, so I have a situation in which I call LoadLibrary on a DLL that I wrote. This call to LoadLibrary returns error #998, or ERROR_NOACCESS "Invalid access to memory location."
The DLL in question uses MFC in one configuration, and not in another; only the MFC configuration has this problem. It used to work, but I have no idea what I changed: I'd actually moved on to the non-MFC version and been tinkering quite a lot with that and I have no idea what I could have done that affected the MFC version.
I don't know a lot about DLLs. The original loading code was actually given to me, and I haven't changed it. Below is that code:
// submodule loading
#ifndef MFC
// Project uses standard windows libraries, define an entry point for the DLL to handle loading/unloading
BOOL WINAPI DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved)
{
_MESSAGE("DllMain called.");
switch(dwReason)
{
case DLL_PROCESS_ATTACH: // dll loaded
hModule = (HMODULE)hDllHandle; // store module handle
_MESSAGE("Attaching Submodule ...");
break;
case DLL_PROCESS_DETACH: // dll unloaded
_MESSAGE("Detaching Submodule ...");
break;
}
return true;
}
#else
// Project uses MFC, we define here an instance of CWinApp to make this a 'well-formed' DLL
class CSubmoduleApp : public CWinApp
{
public:
virtual BOOL InitInstance()
{// dll loaded
hModule = m_hInstance; // store module handle
_MESSAGE("Attaching Submodule ...");
return true;
}
virtual int ExitInstance()
{// dll unloaded
_MESSAGE("Detaching Submodule ...");
return CWinApp::ExitInstance();
}
} gApp;
#endif
Obviously, MFC is defined in the MFC configuration, and not otherwise.
I doubt this is enough information to solve this problem; I realize that. What I'm actually hoping to learn is where to look for problems that might cause this error. I'll be happy to supply any information you need — once I know it's needed.
Thanks for any tips.
OK, this question was answered by a friend of mine (no idea if he has a StackOverflow account; not going to pester him with answering it twice).
The deal is that I had a global object, the class of which had a constructor that called a function that depended upon another global object (ironically enough, the function in question was _MESSAGE, but by the time DllMain or InitInstance gets called, that function works fine). C++ doesn't allow you to specify the order in which globals get initialized, so when this global's constructor got run (when the computer attempted to load the DLL), it caused a memory error by attempting to use another global that hadn't been created yet.
So... that's the answer. A really specific case, but I guess if anyone else finds they're getting 998 errors and need to know what sorts of problems to check, this is something to look for: make sure all your globals are independent!