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.
Related
I saw a Delphi (Object Pascal) code that force unloads any module (dll) that is loaded inside my software. Then with base in this code, I'm wanting and needing of something similar now in C++ (Dev C++).
Someone can help me please?
Here is Delphi code that I saw:
procedure ForceRemove(const ModuleName: string);
var
hMod: HMODULE;
begin
hMod := GetModuleHandle(PChar(ModuleName));
if hMod=0 then
exit;
repeat
until not FreeLibrary(hMod);
end;
The functions
HMODULE GetModuleHandle(LPCTSTR modulename)
BOOL FreeLibrary(HMODULE)
are functions of the Windows API. It can be called from any language that supports programming against the Windows API, as C++
Only recommendation: Remove the loop (the repeat until not ...) in your sample above. It should be replaced by code that interprets the return value of the call to FreeLibrary, documentation here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms683152(v=vs.85).aspx
The DLL will be unloaded from memory (that is to say, the address space of the executing process), as soon as its per-process reference count goes zero; you cannot force unloading a DLL globally by repeatedly executing FreeLibrary() if another process still holds a reference.
EDIT: included a direct translation of OP's sample into a C++ snippet:
void ForceRemove(LPCTSTR ModuleName)
{
HMODULE hMod;
hMod = ::GetModuleHandle(ModuleName);
if(hMod==0)
return;
/* DISCLAIMER potentially infinite loop
* not advisable in production code,
* included by request of the OP to
* match his original */
while(::FreeLibrary(hMod));
}
I'm writing a unmanaged dll (with c++ and WinAPI) but I want to use some C# methods, so, I created a wrapper using C++/CLI.
But the problem is: The unmanaged dll will be 'injected' (LoadLibrary) and I'm stuck here with no clue of how I can call the Wrapper functions.
Unmanaged code:
#include <Windows.h>
//the function I want to call
__declspec(dllexport) void SimpleTest(int *p);
extern "C" __declspec(dllexport) void MyEntryPoint()
{
int* test;
SimpleTest(test);
}
BOOL WINAPI DllMain(/*DllMain parameters*/)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
MyEntryPoint();
break;
}
return TRUE;
}
The wrapper (C++/CLI):
__declspec(dllexport) void SimpleTest(int* p)
{
*p = 1;
}
I don't know what's happening here. The .NET environment wasn't loaded? The Wrapper DLL wasn't loaded? (I looped through the modules and the wrapper wasn't there).
Do I have to initialize the CLR manually?
I read about loader lock but I'm not sure if it is the problem here.
From the documentation to DllMain:
There are significant limits on what you can safely do in a DLL entry point. See General Best Practices for specific Windows APIs that are unsafe to call in DllMain. If you need anything but the simplest initialization then do that in an initialization function for the DLL. You can require applications to call the initialization function after DllMain has run and before they call any other functions in the DLL.
Calling into managed code is one of those things that you cannot do!
The standard way to deal with this is to create a thread in the DllMain, an action that is allowed, and call into the managed code from that thread.
Dlls and shared objects have a problematic life in the C/C++ standards.
The operating system loads a DLL, calls DllMain initialising global variables, then loads dependent DLLs.
This means that
a) during DllMain/global construction you have the loader lock
b) the depend DLLs might not be loaded.
This means that CLR may not be active (not initialized) and may hang if it requires loader lock.
Defering till later is the best solution.
class ImportantOnceWork{
ImportantOnceWork()
{
MyEntryPoint();
}
};
int DoOnce()
{
static ImportantOnceWork val;
}
Then call DoOnce(); at each hook point.
Can I use C++ SetDllDirectory and LoadLibrary commands inside a C++ DLL to load another DLL? I have tried using them like this:
Executable calls the 1st DLL,
then 1st DLL loads the 2nd DLL,
then 2nd DLL does the calculations...
but when I run the executable, I get this error message:
This application has requested the Runtime to terminate it in an unusual way. Please contact the applications support team for more information.
2nd DLL works fine when linked directly to Executable!
This is the code inside my executable:
#include <windows.h>
#include <iostream>
int main(){
HINSTANCE hDLL_Link=NULL;
SetDllDirectory((LPCWSTR)L"C:\\Users\\MC\\Documents\\2014_07_01a_FDD_VC2008\\test_call_DLL\\EXE_calls_Link_DLL\\Release");
hDLL_Link=LoadLibrary((LPCWSTR)L"Link_DLL.dll");
if(hDLL_Link==NULL) std::cout<<"did not load"<<'\n';
typedef void (*Ptr_OPS_Link)();
Ptr_OPS_Link Ptr_OPS_Link_0;
Ptr_OPS_Link_0=(Ptr_OPS_Link)GetProcAddress(hDLL_Link,"OPS_Link");
Ptr_OPS_Link_0();
FreeLibrary(hDLL_Link);
system("pause");
}
this is the code inside the 1st DLL:
#include "Link.h"
extern "C" __declspec(dllexport)
void OPS_Link(){
Link*Link_Ptr_Object=NULL;
if(Link_Ptr_Object==NULL){
Link_Ptr_Object=new Link();
}
if(Link_Ptr_Object==NULL){
//can not throw inside __declspec(dllexport) functions marked extern "C" that's why std::cout is implemented:
std::cout<<"Error: could not link to FDD DLL"<<'\n';
system("pause");
}
delete Link_Ptr_Object;
Link_Ptr_Object=NULL;
}
Link::Link()
:m_void_Ptr_ObjectByDLL(NULL){
HINSTANCE hDLL=NULL;//handle to DLL
SetDllDirectory((LPCWSTR)L"C:\\Software\\Octave-3.6.1\\bin\\");
hDLL=LoadLibrary((LPCWSTR)L"C:\\Users\\MC\\Documents\\2014_07_01a_FDD_VC2008\\Executable\\Release\\FDD_DLL.dll");
if(hDLL==NULL){
throw "DLL loading could not be done";
}else if(hDLL!=NULL){
typedef void (*Ptr_OPS_FDD)(std::string, int, int);
Ptr_OPS_FDD Ptr_OPS_FDD_0;//pointer to procedure inside DLL
Ptr_OPS_FDD_0=NULL;
Ptr_OPS_FDD_0=(Ptr_OPS_FDD)GetProcAddress(hDLL,"OPS_FDD");
if(Ptr_OPS_FDD_0==NULL){
FreeLibrary(hDLL);
throw "DLL exported function address could not be determined";
}else{
//run the procedure inside DLL:
Ptr_OPS_FDD_0("FDD_INPUT_Truss_Bridge_Data2_Ambient_Inch_11Channels_77824Samples_SamplingFreq_256Hz.txt",11,256);//LabScaleTruss
//Ptr_OPS_FDD_0("FDD_INPUT_Ambient_EW_15Channels_3000Samples_SamplingFreq_20Hz.txt",15,20);//AmbientEW
//Ptr_OPS_FDD_0("FDD_INPUT_Meriden_3Channels(3_5_8)_3686400Samples_SamplingFreq_2048Hz.txt",3,2048);//MeridenBridge
FreeLibrary(hDLL);
}
}
}
There are a few things in your code that can cause failure:
You do not exit if the DLL cannot be loaded:
You are passing objects that internally use dynamic allocation, and thus will use the heap manager.
For 1. above, your main() function only does a simple cout if the library cannot be found. However instead of exiting, the main function proceeds as if the library was found.
For 2. above, passing std::string as a parameter to a DLL function is error prone and not recommended unless you know exactly what you're doing. The reason it is error prone is
The DLL that contains the function call may have been built with a different set of options than the DLL that calls the function. These differing options could cause a difference in the way that std::string is implemeted, how it's layed out in memory, etc.
The DLL that contains the function call may have been built by a different version of the compiler than the DLL that calls the function. Again, same issue with differing implementations of std::string
The DLL's and modules using std::string may not have been built using the DLL version of the C runtime library. If the DLL's/modules are not built and linked using the DLL version of the runtime library, the DLL will be using a different heap than the module. Any operation on std::string will be invalid, due to differing memory heaps being used.
So in a nutshell, unless you can guarantee that
You are building the modules and DLL's with the exact same version of the compiler and compiler options.
You are linking all modules to the DLL version of the runtime library.
Then passing std::string as a parameter, and in general, passing any object that maintains dynamically allocated memory, may or will lead to runtime errors.
Besides the inadequate error handling and using the standard library across module boudaries ,there are two other things to consider.
Can I use SetDllDirectory in a dll to ... ?
Yes you can ,but you SHOULDN'T ! (BUGS waiting to happen).
Why ? because the only entity that is responsable for changing environment is the main-application.
Library code (static or dll) doesn't know in which application it's going to be used.
It might work correctly in some programs and it may fail in others.
Can I use C++ LoadLibrary/FreeLibrary in a dll to ... ?
Yes you can ,but don't use them in the dllmain function since it can deadlock your program.
I solved the problem, and showed how here:
I changed the code inside executable and 1st DLL like below, to consider error handling, and also I added the "return 0;" now the executable links to 1st DLL and it works perfect... Actually the problem was that main needed to return something...I raplaced all the "std::string" with "char*" at the DLL boundaries...By the way, the reason that I want to develop two DLLs and I'm using "SetDllDirectory" inside the 1st one is that I want to call a DLL with a C# GUI, and the problem is that there is no "SetDllDirectory" command available in C#, therefore, I came up with the idea of developing two DLLs, inside first DLL, I will use "SetDllDirectory" to take care of the required dependencies (DLL is dependent on Octave and Octave Bin directory) and then I developed a 2nd DLL which carries out the actual computations...I know that there are some methods like "[DllImport("Kernel32.dll")]" and from there we can use "SetDllDirectory" in C# but that method looks painful.
The corrected code inside executable:
#include <windows.h>
#include <iostream>
int main(){
try{
HINSTANCE hDLL_Link=NULL;
hDLL_Link=LoadLibrary((LPCWSTR)L"Link_DLL.dll");
if(hDLL_Link==NULL){
throw "Link DLL did not load";
}else{
typedef void (*Ptr_OPS_Link)();
Ptr_OPS_Link Ptr_OPS_Link_0=NULL;
Ptr_OPS_Link_0=(Ptr_OPS_Link)GetProcAddress(hDLL_Link,"OPS_Link");
if(Ptr_OPS_Link_0==NULL){
throw "Link DLL exported function not found";
FreeLibrary(hDLL_Link);
}else{
Ptr_OPS_Link_0();
FreeLibrary(hDLL_Link);
}
}
}
catch(char*char_Ptr_Exception){
std::cerr<<"Error: "<<char_Ptr_Exception<<'\n';
}
system("pause");
return 0;
}
The corrected code inside 1st DLL:
#include "Link.h"
extern "C" __declspec(dllexport)
void OPS_Link(){
Link*Link_Ptr_Object=NULL;
if(Link_Ptr_Object==NULL){
Link_Ptr_Object=new Link();
}
if(Link_Ptr_Object==NULL){
////can not throw inside __declspec(dllexport) functions marked extern "C" that's why std::cout is implemented:
//std::cout<<"Error: could not link to FDD DLL"<<'\n';
system("pause");
}
delete Link_Ptr_Object;
Link_Ptr_Object=NULL;
}
Link::Link()
:m_void_Ptr_ObjectByDLL(NULL){
HINSTANCE hDLL=NULL;//handle to DLL
SetDllDirectory((LPCWSTR)L"C:\\Software\\Octave-3.6.1\\bin\\");
//path relative to executable (C# executable or C++ executable)
hDLL=LoadLibrary((LPCWSTR)L"FDD_DLL.dll");
if(hDLL==NULL){
throw "FDD DLL did not load";
}else if(hDLL!=NULL){
typedef void (*Ptr_OPS_FDD)(char*, int, int);
Ptr_OPS_FDD Ptr_OPS_FDD_0;//pointer to procedure inside DLL
Ptr_OPS_FDD_0=NULL;
Ptr_OPS_FDD_0=(Ptr_OPS_FDD)GetProcAddress(hDLL,"OPS_FDD");
if(Ptr_OPS_FDD_0==NULL){
throw "FDD DLL exported function not found";
FreeLibrary(hDLL);
}else{
//run the procedure inside DLL:
Ptr_OPS_FDD_0("FDD_INPUT_Truss_Bridge_Data2_Ambient_Inch_11Channels_77824Samples_SamplingFreq_256Hz.txt",11,256);//LabScaleTruss
//Ptr_OPS_FDD_0("FDD_INPUT_Ambient_EW_15Channels_3000Samples_SamplingFreq_20Hz.txt",15,20);//AmbientEW
//Ptr_OPS_FDD_0("FDD_INPUT_Meriden_3Channels(3_5_8)_3686400Samples_SamplingFreq_2048Hz.txt",3,2048);//MeridenBridge
FreeLibrary(hDLL);
}
}
}
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().
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!