C++中调用dll的两种方式

        Windows下调用C++动态库,即dll文件有两种方式:隐式调用和显式调用。如下以VS2013为编译环境说明。假若有一个项目TestDLL,产生的可执行文件名为TestDLL.exe,在这个工程中调用mydll.dll,mydll.dll对应的.lib文件名为mydll.lib。
        一 隐式调用
        这种调用方式须要编译动态库时用到的头文件、静态库文件(.lib),动态库文件(.dll)。
        这里的.lib文件只包含一些索引,即.dll文件中导出的变量或函数的符号名和可选的标识号,不包含具体的代码实现,具体的代码实现都在.dll文件中, 应用程序中对函数或变量的调用与.lib文件中的符号相匹配,这些符号或标识号被写入最终的可执行文件中,可执行文件运行时,就按照这些符号或标识号到.dll中寻找定义。.lib文件中也包含了对应的.dll文件名,但不是彻底路径,连接程序会把这个.dll文件名写到可执行文件中,当应用程序运行过程当中须要加载.dll文件时, windows根据这些信息发现并加载.dll, 而后经过符号名或标识符实现对dll函数的动态连接。全部被应用程序调用的.dll文件都会在应用程序加载时被加载到内存中。
        若是要完成源代码的编译,只须要.lib文件就能够了;若是要使可执行文件运行起来,只要有.dll文件就能够了,在编译和测试阶段,这两个文件最好都有。
        隐式调用步骤以下:
        1 将编译mydll.dll时用到的头文件拷贝到TestDLL的项目目录下,保证TestDLL项目在编译时能够找到这些头文件。
        在VS2013中,右键点击项目->Properties->Configuration Properties->VC++ Directories->Include Directories,而后将头文件的路径添加到项目配置中。
        2 将mydll.lib的文件路径添加到TestDLL项目的依赖库路径下,保证TestDLL项目在编译时可以找到这个mydll.lib。
        在VS2013中,右键点击项目->Properties->Configuration Properties->VC++ Directories->Library Directories,而后将mydll.lib的文件路径添加到项目配置中。
        3 将mydll.lib文件添加到TestDLL项目的附加依赖中,告诉程序要使用mydll.lib。
        在VS2013中,右键点击项目->Properties->Configuration Properties->Linker->Input->Additional Dependencies, 将mydll.lib添加进去。
        4 将要用到的mydll.dll文件拷贝到可执行文件所在的目录,即TestDLL.exe所在的目录,或系统环境变量配置的目录中(系统环境变量配置可到网上查找),保证TestDLL.exe运行时可以找到mydll.dll。
        在TestDLL项目中,须要用到mydll.dll的地方,包含相应的头文件之后,就可使用mydll.dll中定义的变量或函数了。
        二 显示调用
       
这种方式不须要头文件,也不须要相应的.lib文件,只要.dll文件就能够了。
        这种方式在应用程序编译前并不知道会调用哪些.dll文件,在运行过程当中才肯定,运行过程当中,应用程序调用系统函数,在须要的时候加载.dll文件,而后获取指定变量的址或指定函数的入口地址后,就可使用变量或进行函数调用了。 
        显示式调用步骤以下:   
        
1 调用系统函数LoadLibraryEx加载mydll.dll。示例代码片断:windows

string dllPath = "D:\\Project\\lib\\mydll.dll";                 //mydll.dll的路径
TCHAR dynLibPath[MAX_PATH_LEN];                                 //若是使用的是ANSI字符集,这一步不须要
swprintf(dynLibPath, MAX_PATH_LEN, L"%S", dllPath.c_str());     //若是使用的是ANSI字符集,这一步不须要
//若是使用的是ANSI字符集,LoadLibraryEx的第一个参数,直接使用dllPath.c_str(),即ANSI字符串便可。
HMODULE dllHandler = LoadLibraryEx(dynLibPath, NULL, DONT_RESOLVE_DLL_REFERENCES); 
if(dllHandler)
{
    cout << "Load mydll.dll successfully!" << endl;
}
else
{
    cout << "Load mydll.dll failed!" << endl;
}

        2 调用系统函数GetProcAddress获取变量或函数的地址。示例代码片断:函数

FARPROC pFun = NULL;
pFun = GetProcAddress(dllHandler, "TestFun");    //TestFun是mydll.dll中导出的函数
pFun();                    //调用TestFun
FARPROC pVar = NULL;
pVar = GetProcAddress(dllHandler, "TestVar");    //TestVar是mydll.dll中导出的整型变量
int num = *(int*)pVar;     //将pVar转换成整形
cout<<num<<endl;           //打印TestVar的值

        注意: 有时候,dll中肯定有相应的函数,调用GetProcAddress却返回NULL,例如: mydll.dll中肯定导出了函数“ AnotherTestFun",在Dependency Walker下看到相应的函数名为?AnotherTestFun@3HA,调用GetProcAddress(dllHandler, "AnotherTestFun")始终返回NULL;
        而调用GetProcAddress(dllHandler, "?AnotherTestFun@3HA")却能够返回有效地址值。
        像这种状况,多是由于在定义AnotherTestFun函数中,函数声明前忘了加 extern "C"。
        若是没有加extern "C",编译器在编译AnotherTestFun这个函数时,会按照C++的规则翻译这个函数名,便可能把函数名字改掉,例如改为上面的"?AnotherTestFun@3HA",具体改为什么样的名字视编译器而定,这样一来,以TestDLL项目中再以"AnotherTestFun"为参数获取该函数的地址,确定会失败。
        若是加上了extern "C",则是告诉编译器在编译函数时按照C语言的规则去翻译相应的函数名,即“请保持函数名称,不要生成中间函数名"。测试