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。示例代码片段:

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语言的规则去翻译相应的函数名,即“请保持函数

            名称,不要生成中间函数名"。


你可能感兴趣的:(C++中调用dll的两种方式)