SetDllDirectory LoadLibrary inside a DLL - c++

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);
}
}
}

Related

Calling managed code from DLLMain

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.

Creating and using a DLL in CPP

I am working on a project where I am creating a small DLL and then also creating a windows application to use it.
I can not figure out what is going on.
I have a function in the DLL called "startPicadorVisual" which takes one parameter which is a std::string.
In the application which is dependent on the DLL I have the following code in a mostly auto-generated .h file:
typedef void (__stdcall *f_startPicadorVisual)(string s);
namespace PicadorPrototype {
f_startPicadorVisual startPicadorVisual;
Form1(void) {
//Load DLL Funcs
HINSTANCE hGetProcIDDLL = LoadLibrary(L"..\\Debug\\Picador.dll");
if (!hGetProcIDDLL) {
std::cout << "could not load the dynamic library" << std::endl;
throw "Bad Stuff";
}
startPicadorVisual = (f_startPicadorVisual)GetProcAddress(hGetProcIDDLL, "startPicadorVisual");
if (!startPicadorVisual) {
std::cout << "could not locate the function" << std::endl;
throw "More Bad Stuff";
}
When which fails on the second step when I call GetProcAddress.
The functions are defined as follows in my DLL:
void __declspec(dllexport) startPicadorVisual(string fixtureNamet);
PicadorResults __declspec(dllexport) getPicadorReading(string fixtureName);
Can anyone tell me why this isn't working?
GetProcAddress fails if the name you give GetProcAddress doesn't match exactly the name of the function you're calling. By exact I mean everything -- characters that make up the function name, the function name must match casing, etc.
So either your DLL exported a different name and you didn't realize it, or you're not exporting the name at all.
The way you can find out the names of the exported DLL functions easily, you can use the Dependency Walker program found here: http://www.dependencywalker.com/
Also, it isn't a good idea to use C++ objects that allocate dynamic memory such as std::string as parameters. If you do that, your DLL will only work for applications that
Are compiled with the same version of Visual C++ as the DLL
Use the same compiler options when building the application and DLL
All components (DLL and app) must use the DLL version of the C runtime library.
Otherwise, your code would have undefined behavior, more than likely crash, even if you got as far as retrieving the function pointer correctly.

Returning non-primitive C++ type from a DLL function linked with a static runtime (/MT or /MTd)

Consider we have a dynamic library ("HelloWorld.dll") which is compiled with Microsoft Visual Studio 2010 from the following source code:
#include <string>
extern "C" __declspec(dllexport) std::string hello_world()
{
return std::string("Hello, World!"); // or just: return "Hello, World!";
}
And we also have an executable ("LoadLibraryExample.exe") which dynamically loads this DLL using LoadLibrary WINAPI function:
#include <iostream>
#include <string>
#include <Windows.h>
typedef std::string (*HelloWorldFunc)();
int main(int argc, char* argv[])
{
if (HMODULE library = LoadLibrary("HelloWorld.dll"))
{
if (HelloWorldFunc hello_world = (HelloWorldFunc)GetProcAddress(library, "hello_world"))
std::cout << hello_world() << std::endl;
else
std::cout << "GetProcAddress failed!" << std::endl;
FreeLibrary(library);
}
else
std::cout << "LoadLibrary failed!" << std::endl;
std::cin.get();
}
This works fine when being linked with a dynamic runtime library (/MD or /MDd switches).
The problem appears when I link them (the library and the executable) with a debug version of static runtime library (/MTd switch). The program seems to work ("Hello, World!" is displayed in the console window), but then crashes with the following output:
HEAP[LoadLibraryExample.exe]: Invalid address specified to RtlValidateHeap( 00680000, 00413F60 )
Windows has triggered a breakpoint in LoadLibraryExample.exe.
This may be due to a corruption of the heap, which indicates a bug in LoadLibraryExample.exe or any of the DLLs it has loaded.
This may also be due to the user pressing F12 while LoadLibraryExample.exe has focus.
The output window may have more diagnostic information.
The problem magically does not appear with a release version of static runtime library (/MT switch). My assumption is that the release version just doesn't see the error, but it is still there.
After a small research I found this page on MSDN which states the following:
Using the statically linked CRT implies that any state information saved by the C runtime library will be local to that instance of the CRT.
Because a DLL built by linking to a static CRT will have its own CRT state, it is not recommended to link statically to the CRT in a DLL unless the consequences of this are specifically desired and understood.
So the library and the executable have their own copies of CRT which have their own states. An instance of std::string is constructed in the library (with some internal memory allocations being made by the library's CRT) and then returned to the executable. The executable displays it and then calls its destructor (leading to deallocation of the internal memory by the executable's CRT). As I understand, this is where an error occurs: the underlying memory of std::string is allocated with one CRT and tried to be deallocated with another one.
The problem does not appear if we return a primitive type (int, char, float, etc) or a pointer from the DLL, because there are no memory allocations or deallocations in these cases. However, an attempt to delete the returned pointer in the executable results in the same error (and not deleting the pointer obviously results in a memory leak).
So the question is: Is it possible to work around this issue?
P.S.: I really don't want to have a dependency on MSVCR100.dll and make the users of my application to install any redistributable packages.
P.P.S: The code above produces the following warning:
warning C4190: 'hello_world' has C-linkage specified, but returns UDT 'std::basic_string<_Elem,_Traits,_Ax>' which is incompatible with C
which can be resolved by removing extern "C" from the library function declaration:
__declspec(dllexport) std::string hello_world()
and changing the GetProcAddress call as following:
GetProcAddress(library, "?hello_world##YA?AV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##XZ")
(function name gets decorated by the C++ compiler, the actual name can be retrieved with dumpbin.exe utility). The warning is then gone but the problem remains.
P.P.P.S: I see a possible solution in providing a pair of functions in the library for every such situation: one that returns a pointer to some data and the other that deletes a pointer to these data. In this case, the memory is allocated and deallocated with the same CRT. But this solution seems very ugly and non-friendly, as we must always operate with pointers and moreover a programmer must always remember to call a special library function to delete a pointer instead of simply using a delete keyword.
Yes, this is the primary reason that /MD exists in the first place. When you build the DLL with /MT, it will get its own copy of the CRT embedded. Which creates its own heap to allocate from. The std::string object you return will be allocated on that heap.
Things go wrong when the client code tries to release that object. It calls the delete operator and that tries to release the memory on its own heap. On Vista and Win7, the Windows memory manager notices that it is asked to release a heap block that's not part of the heap and that a debugger is attached. It generates an automatic debugger break and a diagnostic message to tell you about the problem. Very nice btw.
Clearly /MD solves the problem, both your DLL and the client code will use the same copy of the CRT and thus the same heap. It is not a sure-fire solution, you will still run into trouble with the DLL is built against a different version of the CRT. Like msvcr90.dll instead of msvcr100.dll.
The only complete error free solution is to restrict the API you expose from the DLL. Don't return any pointers to any objects that need to be released by the client code. Assign ownership of objects to the module that created it. Reference counting is a common solution. And if you have to then use a heap that's shared by all code in a process, either the default process heap (GlobalAlloc) or the COM heap (CoTaskMemAlloc) qualify. Also don't allow exceptions to cross the barrier, same problem. The COM Automation abi is a good example.

Calling c++ runtime from DllMain or global initializers

The question is inspired by this discussion.
It seems that fears concerning C++ runtime invokations from DllMain (or from global variables ctor's) are sligtly outdated. I'm using global initializers in dlls frequently without any faults, and now I've run a special test program (compiled with VC2010 Express w/o SP) containing exe module with static runtime linkage and dll with dynamic one. Dll is manualy loaded from exe by LoadLibrary().
Dll creates and fills a map object during global initialization (and therefore uses runtime library, at least memory allocatiion functions).
Dll code:
#include <map>
using namespace std;
struct A{
char* p;
static const int num=1000;
map<int,string> m;
A(){
for(int i=0; i<num; ++i){m[i]= *new string("some text");}
}
};
A a;
extern "C"{
_declspec(dllexport) const char* getText(int i){ return a.m[i].data(); }
}
Exe code (for Release configuration; change runtime library name to MSVCR100D.DLL for Debug):
#include <windows.h>
typedef const char* (*pfunc_t)(int idx);
int main(int argc, TCHAR* argv[])
{
HMODULE h_crt= GetModuleHandle("MSVCR100.DLL");
// ensure that runtime library is NOT loaded yet:
MessageBox(NULL,(NULL==h_crt)? "CRT NOT loaded by .exe module": "CRT Loaded by .exe module" ,"before LoadLibrary",MB_OK);
HMODULE hlib=LoadLibrary("dll_example.dll");
h_crt= GetModuleHandle("MSVCR100.DLL");
MessageBox(NULL,(NULL==h_crt)? "CRT NOT loaded": "CRT Loaded" ,"after LoadLibrary",MB_OK);
pfunc_t pfunc= (pfunc_t)(void*)GetProcAddress(hlib,"getText");
MessageBox(NULL,pfunc(99),"map[99]",MB_OK);
return 0;
}
The output is as expected:
before LoadLibrary: CRT NOT loaded by .exe module
after LoadLibrary: CRT Loaded
map[99]: some text
No failures, nullpointers, pagefaults, etc.
Profiling with DependencyWalker also confirms that runtime lib(MSVCR100.DLL) is loaded only after LoadLibrary call (and is not preloaded and initialized by exe).
It seems that dynamic runtime library is loaded and initialized correctly during the dll_example.dll loading process before global initialization phase.
any thoughts?
PS. I don't encourage to move any heavyweight initialization code to global init phase; but I suppose that simple memory allocation code is safe enough (?).
It all depends what you do inside DLLMain. Since the documentation refuses to state what can and cannot be done, and since the CRT doesn't make any promises, this always feels like a risky area.
Personally I would move all my global initialization into a single routine which is exported from the DLL and insist that all clients call this before calling any other function.
Getting the CRT initialized while loading a DLL is a very common scenario, it happens for any COM server for example. As such you can rely on the CRT explicitly supporting the scenario, as long as you don't require it to initialize your variables with code that in turn depend on the dangerous api calls. Getting managed objects initialized is a famous failure mode, the CLR cannot be initialized while the loader lock is held. The deadlock is very ugly to diagnose but very easy to detect. Which is in general true, you have no trouble finding out that you got a problem. Only finding a workaround for it.
There is however plenty of hell to pay by having the main program and one of more DLLs using different instances of the CRT. Which is what is happening in your test program. You have to very carefully craft the exported functions of the DLL to not return any pointers or C++ objects. You'll get away with returning const char*, the caller isn't supposed to take ownership of that pointer. Presumably.

Explicit Loading of DLL

I'm trying to explicitly link with a DLL. No other resources is available except the DLL file itself and some documentation about the classes and its member functions.
From the documentation, each class comes with its own
member typedef
example: typedef std::map<std::string,std::string> Server::KeyValueMap, typedef std::vector<std::string> Server::String Array
member enumeration
example: enum Server::Role {NONE,HIGH,LOW}
member function
example: void Server::connect(const StringArray,const KeyValueMap), void Server::disconnect()
Implementing the codes from google search, i manage to load the dll can call the disconnect function..
dir.h
LPCSTR disconnect = "_Java_mas_com_oa_rollings_as_apiJNI_Server_1disconnect#20";
LPCSTR connect =
"_Java_mas_com_oa_rollings_as_apiJNI_Server_1connect#20";
I got the function name above from depends.exe. Is this what is called decorated/mangled function names in C++?
main.cpp
#include <iostream>
#include <windows.h>
#include <tchar.h>
#include "dir.h"
typedef void (*pdisconnect)();
int main()
{
HMODULE DLL = LoadLibrary(_T("server.dll"));
pdisconnect _pdisconnect;`
if(DLL)
{
std::cout<< "DLL loaded!" << std::endl;
_disconnect = (pdisconnect)GetProcAddress(DLL,disconnect);
if(_disconnect)
{
std::cout << "Successful link to function in DLL!" << std::endl;
}
else
{
std::cout<< "Unable to link to function in DLL!" << std::endl;
}
}
else
{
std::cout<< "DLL failed to load!" << std::endl;
}
FreeLibrary (DLL);
return 0;}
How do i call (for example) the connect member function which has the parameter datatype declared in the dll itself?
Edit
more info:
The DLL comes with an example implementation using Java. The Java example contains a Java wrapper generated using SWIG and a source code.
The documentation lists all the class, their member functions and also their datatypes. According to the doc, the list was generated from the C++ source codes.(??)
No other info was given (no info on what compiler was used to generate the DLL)
My colleague is implementing the interface using Java based on the Java example given, while I was asked to implement using C++. The DLL is from a third party company.
I'll ask them about the compiler. Any other info that i should get from them?
I had a quick read through about JNI but i dont understand how it's implemented in this case.
Update
i'm a little confused... (ok, ok... very confused)
Do i call(GetProcAddress) each public member function separately only when i want to use them?
Do i create a dummy class that imitates the class in the dll. Then inside the class definition, i call the equivalent function from the DLL? (Am i making sense here?) fnieto, is this what you're showing me at the end of your post?
Is it possible to instantiate the whole class from the DLL?
I was trying to use the connect function described in my first post. From the Depends.exe DLL output,
std::map // KeyValueMap has the following member functions: del, empty, get, has_1key,set
std::vector // StringArray has the following member functions: add, capacity, clear, get, isEMPTY, reserve, set, size
which is different from the member functions of map and vector in my compiler (VS 2005)...
Any idea? or am i getting the wrong picture here...
Unless you use a disassembler and try to figure out the paramater types from assemly code, you can't. These kind of information is not stored in the DLL but in a header file coming with the DLL. If you don't have it, the DLL is propably not meant to be used by you.
I would be very careful if I were you: the STL library was not designed to be used across compilation boundaries like that.
Not that it cannot be done, but you need to know what you are getting into.
This means that using STL classes across DLL boundaries can safely work only if you compile your EXE with the same exact compiler and version, and the same settings (especially DEBUG vs. RELEASE) as the original DLL. And I do mean "exact" match.
The C++ standard STL library is a specification of behavior, not implementation. Different compilers and even different revisions of the same compiler can, and will, differ on the code and data implementations. When your library returns you an std::map, it's giving you back the bits that work with the DLL's version of the STL, not necessarily the STL code compiled in your EXE.
(and I'm not even touching on the fact that name mangling can also differ from compiler to compiler)
Without more details on your circumstances, I can't be sure; but this can be a can of worms.
In order to link with a DLL, you need:
an import library (.LIB file), this describes the relation between C/C++ names and DLL exports.
the C/C++ signatures of the exported items (usually functions), describing the calling convention, arguments and return value. This usually comes in a header file (.H).
From your question it looks like you can guess the signatures (#2), but you really need the LIB file (#1).
The linker can help you generate a LIB from a DLL using an intermediate DEF.
Refer to this question for more details: How to generate an import library from a DLL?
Then you need to pass the .lib as an "additional library" to the linker. The DLL must be available on the PATH or in the target folder.