我按照书上的方法试过,出了错,一直不知是设么原因。
源程序:(一部份)
        HINSTANCE  hDll;
        hDll=LoadLibrary("a.DLL");
FARPROC lpFunc;
        lpFunc=GetProcAddress(hDll,"A"); 
错:    c=(*lpFunc)(a,b);error C2197: 'int (__stdcall *)(void)' : too many actual parameters
Error executing cl.exe.

解决方案 »

  1.   

    DLL代码extern "C" _declspec(dllexport)
    int add2int(int x,int y)
    {
         return x+y;
    }
    编译为a.dll.exe
    HINSTANCE  hDll;
    hDll=LoadLibrary("a.DLL");
    assert(hDll);
    typedef int (*pfadd2int)(int x,int y);
    pfadd2int padd2int;
    padd2int=(pfadd2int)GetProcAddress(hDll,"add2int");
    assert(padd2int);
    printf("%d\n",padd2int(2,4));         
        
      

  2.   

    to zsjtop: "a.dll"中的函数是 int A(void arg), 而你调用的时候传了2个参数当然错了.
      

  3.   

    是你的APP的函数指针的原型定义和DLL中的不一样。
      

  4.   

    你出錯的原因是因為dll中的輸出函數的名字被改所致(你在dll中使用了__stdcall修改符,所以你的輸出函數名會被改成類似_FuncName@4的名字),你需用Visual studio 中的Depends工具來查出dll中的實際輸出名字,再在調用GetProcAddress()時傳入正確的函數名字就可以了。
      

  5.   

    摘录:第二节 创建和使用动态链接库本节以非MFC DLL为例来讲解DLL的结构和导出方法,并介绍创建和使用DLL的方法和步骤。13.2.1 DLL的结构和导出方式DLL文件和EXE文件都属于可执行文件,不同的是DLL文件包括了一个导出表,导出表中给出了可以从DLL中导出的所有函数的名字。外部可执行程序只能访问包括在DLL的导出表中的函数,DLL中的其它函数是私有的,不能为外部可执行程序所访问。可以使用Visual C++提供的DUMPBIN实用程序(可以在DevStudio\VC\bin目录下找到这个工具)来查看一个DLL文件的结构。举一个例子,如果需要查看DLL文件msgbox.dll(我们将在本小节的后续内容中创建该DLL)的导出表,可以在命令提示符下键入下面的命令:>dumpbin /exports msgbox.dll运行结果如下:Microsoft (R) COFF Binary File Dumper Version 5.00.7022Copyright (C) Microsoft Corp 1992-1997. All rights reserved.Dump of file msgbox.dllFile Type: DLLSection contains the following Exports for MSGBOX.dll0 characteristics351643C3 time date stamp Mon Mar 23 19:13:07 19980.00 version1 ordinal base1 number of functions1 number of namesordinal hint name1 0 MsgBox (00001000)Summary7000 .data1000 .idata2000 .rdata2000 .reloc17000 .text由上面的结果得知,msgbox.dll中仅包括了一个导出函数MsgBox()。
    注意:
    仅仅知道导出函数的名称并不足以从DLL中导出该函数。若在应用程序中使用显式链接(link explicitly),至少还应该知道导出函数的返回值的类型以及所传递给导出函数的参数的个数、顺序和类型;若使用隐含链接(link implicitly),必须有包括导出函数(或类)的定义的头文件(.H文件)和引入库(import library,.LIB文件),这些文件是由DLL的创建者所提供的。关于显式链接和隐含链接,将在本章的“13.2.2 链接应用程序到DLL”小节中讲述。
    从DLL中导出函数有两种方法:
    在创建DLL时使用模块定义(module DEFinition,.DEF)文件。
    在定义函数时使用关键字__declspec(dllexport)。
    下面我们通过一个简单的例子来分别说明两种方法的使用。在这个例子中,我们将创建一个只包括一个函数MsgBox()的DLL,函数MsgBox()用来显示一个消息框,它和Win32 API函数MessageBox()的功能是一样,只不过在函数MsgBox()中,不需要指定消息的父窗口,而且可以缺省其它所有的参数。(1) 使用模块定义文件模块定义文件是一个文本文件,它包括了一系列的模块语句,这些语句用来描述DLL的各种属性,典型的,模块语句定义了DLL中所导出的函数的名称和顺序值。
    在讲解模块定义文件之前,我们先创建一个Win32 Dynamic-Link Library工程。1. 在Microsoft Developer Studio中选择File菜单下的New命令,在Projects选项卡中选择Win32 Dynamic-Link Library,并为工程取一个名字,如msgbox。单击OK后,Visual C++创建一个Win32 DLL的空白工程,必须手动的将所需要的文件添加到工程中。2. 单击Project菜单下的Add To Project子菜单下的New命令,在Files选项卡中选择Text File,在File文本框中输入DEF文件名,如msgbox.def。3. 双击Workspace窗口的FileView选项卡中的msgbox.def节点,在msgbox.def文件中输入下面的内容:LIBRARY MSGBOXDESCRIPTION "一个DLL的简单例子"EXPORTSMsgBox @1在DEF文件中的第一条语句必须是LIBRARY语句,该语句表明该DEF文件属于一个DLL,在LIBRARY之后是DLL的名称,这个名称在链接时将放到DLL的引入库中。EXPORTS语句下列出了DLL的所有导出函数以及它们的顺序值。函数的顺序值不是必须的,在指定导出函数的顺序值时,我们在函数名后跟上一个@符号和一个数字,该数字即导出函数的顺序值。如果在DEF中指定了顺序值,它必须不小于1,且不大于DLL中所有导出函数的数目。DESCRIPTION语句是可选的,它简单的说明了DLL的用途。4. 下一步是向工程中添加一个头文件,它定义了DLL中的函数的返回值的类型和参数的个数、顺序和类型。单击菜单项Project|Add To Project|New...,在Files选项卡下选择C/C++ Header File,在File文本框中指定头文件名,如msgbox.h(可以省略后缀名.h)。在头文件中输入如下的内容:#include <windows.h>extern "C" int MsgBox(// 消息框的文本LPCTSTR lpText="虽然这个例子有一些幼稚,但它工作得非常的好!",// 消息框的标题LPCTSTR lpCaption="一个简单的例子",// 消息框的样式UINT uType=MB_OK);请注意函数定义前的关键字extern "C",这是由于我们使用了C++语言来开发DLL,为了使C语言模块能够访问该导出函数,我们应该使用C链接来代替C++链接。否则,C++编译器将使用C++的类型安全命名和调用协议,这在使用C调用该函数时就会遇上问题。在本例中并不需要考虑到这个问题,因为我们在开发DLL和应用程序时都是使用C++,但我们仍然强烈建议使用extern "C",以保证在使用C编写的程序调用该DLL的导出函数不会遇上麻烦,在本章后面的内容中我们还会讨论到这个问题。5. 下面要做的事是向工程中添加一个C++源文件,在该文件中实现函数MsgBox()。仿照上面的过程,单击菜单项Project|Add To Project|New...,在Files选项卡下选择C++ Source File,在File文本框中指定源文件名,如msgbox.cpp。在msgbox.cpp文件中添加如下的代码:#include "test1.h"int MsgBox(LPCTSTR lpText,LPCTSTR lpCaption,UINT uType){return MessageBox(NULL,lpText,lpCaption,uType);}编译该工程,在Debug目录下生成文件msgbox.lib和msgbox.dll。在“13.2.2 链接应用程序到DLL”小节中将讲述如何使用在本节中所创建的DLL:msgbox.dll。(2) 使用关键字__declspec(dllexport)从DLL中导出文件的另一种方法是在定义函数时使用__declspec(dllexport)关键字。这种方法不需要使用DEF文件。仍使用前面的例子,在工程中删除msgbox.def文件,将msgbox.h文件修改如下:#include <windows.h>extern "C" __declspec(dllexport) int MsgBox(// 消息框的文本LPCTSTR lpText="虽然这个例子有一些幼稚,但它工作得非常的好!",// 消息框的标题LPCTSTR lpCaption="一个简单的例子",// 消息框的样式UINT uType=MB_OK);msgbox.cpp文件并不需要做任何修改,重新编译该工程,在Debug目录下仍生成两个文件msgbox.lib和msgbox.dll。在下一小节“13.2.2 链接应用程序到DLL”中讲述了如何在应用程序中使用所创建的DLL:msgbox.dll的导出函数MsgBox()。使用__declspec(dllexport)从DLL中导出类的语法如下:class __declspec(dllexport) CDemoClass{...} 
    注意:
    如果在使用__declspec(dllexport)的同时指定了调用协议关键字,则必须将__declspec(dllexport)关键字放在调用协议关键字的左边。如:
    int __declspec(dllexport) __cdacl MyFunc();
    在32位版本的Visual C++中,__declspec(dllexport)和__declspec(dllimport)代替了16版本中使用的__export关键字。因此,在将16位的DLL源代码移植到Win32平台时,需要把每一处__export替换为__declsped(dllexport)。
    如何从这两种导出函数的方法中作出选择,可以从下面的几个方面考虑:
    如果需要使用导出顺序值(export ordinal value),那么应该使用DEF文件来导出函数。只在使用DEF文件导出函数才能指定导出函数的顺序值。使用顺序值的一个好处是当向DLL中添加新的函数时,只要新的导出函数的顺序值大于原有的导出函数,就没有必要重新链接使用隐含链接的应用程序。相反,如果使用__declspec(dllexport)来导出函数,如果向DLL中添加了新的函数,使用隐含链接的应用程序有可以需要重新编译和链接。
    使用DEF文件来导出函数,可以创建具有NONAME属性的DLL。具有NONAME属性的DLL在导出表中仅包含了导出函数的顺序值,这种类型的DLL在包括有大量的导出函数时,其文件长度要小于通常的DLL。
    使用DEF文件从C++文件导出函数,应该在定义函数时使用extern "C"或者在DEF文件中指定导出函数的decorated name。否则,由于编译器所产生的decorated name是基于特定编译器的,链接到该DLL的应用程序也必须使用创建DLL的同一版本的Visual C++来编译和链接。
    由于使用__declspec(dllexport)关键字导出函数不需要编写DEF文件,因此,如果编写的DLL只供自己使用,使用__declspec(dllexport)较为简单。 
    注意:
    MFC本身使用了DEF文件从MFCx0.DLL中导出函数和类。13.2.2 链接应用程序到DLL同样,链接应用程序到DLL也有两种方法:
    隐含链接
    显式链接
    隐含链接有时又称为静态加载。如果应用程序使用了隐含链接,操作系统在加载应用程序的同时加载应用程序所使用的DLL。显式链接有时又称为动态加载。使用动态加载的应用程序必须在代码中明确的加载所使用的DLL,并使用指针来调用DLL中的导出函数,在使用完毕之后,应用程序必须卸载所使用的DLL。同一个DLL可以被应用程序隐含链接,也可以被显式链接,这取决于应用程序的目的和实现。下面我们在分别讲述两种不同的链接方式之后再作对比。(1) 使用隐含链接在使用隐含链接除了需要相应的DLL文件外,还必须具备如下的条件:
    一个包括导出的函数或C++类的头文件
    一个输入库文件(.LIB文件)通常情况下,我们需要从DLL的提供者那里得到上面的文件。输入库文件是在DLL文件被链接时由链接程序生成的。在“13.2.1 DLL的结构和导出方式”中所创建的DLL:msgbox.dll所对应的头文件msgbox.h如下:#include <windows.h>extern "C" __declspec(dllimport) int MsgBox(// 消息框的文本LPCTSTR lpText="虽然这个例子有一些幼稚,但它工作得非常的好!",// 消息框的标题LPCTSTR lpCaption="一个简单的例子",// 消息框的样式UINT uType=MB_OK);需要注意的是,这个msgbox.h文件和创建DLL时所使用msgbox.h是不同的,唯一的差别在于,创建DLL时的msgbox.h中使用的是__declspec(dllexport)关键字,而供应用程序所使用的msgbox.h中使用的是__declspec(dllimport)关键字。无论创建DLL时使用的是DEF文件还是__declspec(dllexport)关键字,均可使用__declspec(dllimport)关键字从DLL中引入函数。引入函数时也可以省略__declspec(dllimport)关键字,但是使用它可以使编译器生成效率更高的代码。
    注意:
    如果需要引入的是DLL中的公用数据和对象,则必须使用__declspec(dllimport)关键字。
    现在使用Microsoft Developer Studio创建一个Win32 Application工程,命名为tester。向工程中添加一个C++源文件,如tester.cpp。在tester.cpp文件中输入下面的代码:#include "msgbox.h" // 应将msgbox.h文件拷贝到工程tester的目录下。int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){return MsgBox();}在上面的代码中,MsgBox()函数的所有参数都使用了缺省值。
    注意:
    在编译之前,将上一步生成的msgbox.lib文件拷贝到tester工程所在的目录下。然后单击菜单项Project|Settings...,将msgbox.lib添加到Link选项卡下的Object/library modules文本框中。如果忽略这一步,链接时将会导致错误。完成之后创建该应用程序。
    如果现在运行该程序,将出现如图13.1所示的对话框。图13.1 未找到DLL时出现的错误上面的对话框说明程序没有在指定的路径未找到所需要的DLL。一般情况下,程序在运行时,系统将按如下的顺序查找程序所使用的动态链接库:
    系统预安装的DLL,如KERNEL32.DLL和USER32.DLL等
    当前目录
    Windows的系统的目录,如WINNT\system32
    Windows所在的目录,如WINNT
    环境变量PATH中所指定的目录图13.2 tester应用程序的运行结果如果Windows在上面的目录中未找到所需要的DLL,则弹出如图13.1所示的对话框。这里,我们把msgbox.dll文件拷贝到tester\Debug目录下,再运行应用程序,则出现如图13.2所示的对话框。(2) 使用显式链接如果没有与DLL相关联的LIB文件,则必须使用显式链接。使用显式链接同样必须知道函数返回值的类型和所传递的参数个数、类型和顺序。与使用隐含链接不同的是,使用显式链接的应用程序在调用DLL中的导出函数前,必须使用LoadLibrary()函数加载DLL并得到一个模块句柄。然后使用该句柄调用GetProcAddress()函数获得所需调用的导出函数的指针,并通过该指针调用DLL中的导出函数,这种模式使用显式链接到DLL的应用程序不再需要相应的LIB文件。在使用完毕之后,还需调用FreeLibrary()函数释放加载的DLL。
    下面我们使用显式链接的方式来实现前面的例子。由于使用指针来调用DLL中的导出函数,所以本例中不再需要msgbox.h文件。在tester.cpp中添加的代码如下所示:#include <windows.h>typedef int (CALLBACK* DLLFUNC)(LPCTSTR lpText="虽然这个例子有一些简单,但它工作得非常的好!",LPCTSTR lpCaption="一个简单的例子",UINT=MB_OK);int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow){HINSTANCE hDLL;DLLFUNC MsgBox;hDLL = LoadLibrary("msgbox");if (hDLL != NULL){MsgBox = (DLLFUNC)GetProcAddress(hDLL,"MsgBox");return MsgBox();}}LoadLibrary()函数的参数是所调用的DLL的名字,这个名字不是放入输入库文件中的名字,而是DLL的文件名。如果文件的扩展名为.DLL,则可以省略。
    这个程序的运行结果同使用隐含链接的前一个程序一样,但它的内部实现是很不相同的。使用显式链接的应用程序加载时,所调用的DLL并不加载,只有当应用程序调用LoadLibray()时系统才加载相应的DLL,并在应用程序调用FreeLibrary()时卸载该DLL。使用隐含链接的应用程序调用DLL中的导出函数时,方法同调用一般的函数一样,而使用显式链接的应用程序必须使用指针来调用。由于使用了指针,因此在编译时不能验证参数的合法性,通过指针使用不合法的参数来调用DLL中的导出函数将会导致不可预料的后果。很明显,使用隐含链接的方式调用DLL中的导出函数要比使用显式链接方便得多。但在某些情况下我们必须使用显式链接。事实上,使用显式链接调用DLL提供了更大的灵活性。尤其在没有与DLL相对应的LIB文件时,我们只能使用显式链接来调用DLL中的导出函数,并且,只要我们使用函数名作参数来调用GetProcAddress(),在更新DLL时,就没有必要重新链接应用程序。另外,使用隐含链接的方式的应用程序加载DLL时如果发生错误(如DLL文件未找到或是DLL中的DllMain()函数初始化失败)时,应用程序将被终止,而使用显式链接的应用程序则可以使用如上面的例子中所给出的方法来避免出现这种情况(可以使用所创建的两个不同版本的tester程序来验证这一点)。由于应用程序调用LoadLibrary()函数时才加载DLL,因此使用显式链接的应用程序的加载速度要比使用隐含链接的应用程序快。使用显式链接的另一个好处是,应用程序可以在运行时决定所加载的DLL。但是要记住,由于使用了指针来传递应用程序的参数,因此编译器在编译时无法确认应用程序所传递的参数类型是否合法。传递不合法的参数给DLL中的导出函数的一件危险的事。在程序调试的过程中我们一定需要注意这一点。第三节 使用动态链接连库扩展MFC我们还可以使用DLL来实现从MFC派生的一些可重用类,这种动态链接库一般称作MFC扩展动态链接库(MFC Extension DLL)。正如这个名称所暗示的那样,通过这种方式我们可以扩展MFC所包括的内容,使得使用MFC编程更加的方便。此外,如果需要在应用程序和DLL之间传递MFC或者由MFC派生的对象的指针的话,我们也必须使用MFC扩展DLL。在本节中,我们使用MFC扩展DLL来创建一个输入通用对话框,如图13.3所示。该对话框很象Visual Basic中的InputBox函数所产生的对话框,使用过Visual Basic的程序员都有印象,函数InputBox非常之好用,这里,我们来使用动态链接库在Visual C++的MFC中也创建这么一个好用的类。图13. 3 输入通用对话框输入通用对话框由类CInputDlg封装,类CInputDlg提供了一个公有成员函数GetInput,该成员函数的原型如下:CString GetInput(CString Title, CString Prompt)第一个参数Title表示输入对话框的标题,在图13.3中为“输入”;第二个参数Prompt代表在输入对话框中显示的简短提示文本,在图13.3中为“请输入对话框的标题:”。函数的返回值为用户在对话框的文本框中输入的字符串。如果用户没有输入任何字符串或者单击了“取消”按钮,返回值为空串""。
    下面我们来介绍该对话框的创建和使用。首先讲述DLL工程ExtDllDemo的创建。该工程实现了类CInputDlg的导出。1. 使用AppWizard创建一个MFC扩展DLL工程,将工程取名为ExtDllDemo。2. 向工程中添加一个对话框资源IDD_INPUT,按图13.3绘制对话框中的各控件。这些控件的资源ID如表所示。表13. 1 对话框资源IDD_INPUT中的控件属性
    控件
     资源ID
     
    提示文本标签
     IDC_PROMPT
     
    输入文本框
     IDC_EDIT
     3. 使用ClassWizard为对话框资源IDD_INPUT创建新的对话框类CInputDlg,该类直接派生于CDialog。按下面的代码修改类CInputDlg的头文件和实现文件。#if !defined(AFX_INPUTDLG_H__02DB98CF_1F76_11D2_971A_0000B4810A31__INCLUDED_)#define AFX_INPUTDLG_H__02DB98CF_1F76_11D2_971A_0000B4810A31__INCLUDED_#if _MSC_VER >= 1000#pragma once#endif // _MSC_VER >= 1000// InputDlg.h : 头文件//// 以下对话框 ID 常量需要手动添加#define IDC_EDIT 1000#define IDC_PROMPT 1001#define IDD_INPUT 129/////////////////////////////////////////////////////////////////////////////// CInputDlg 对话框class __declspec(dllexport) CInputDlg : public CDialog{// 构造public:CString GetInput(CString Title, CString Prompt);CInputDlg(CWnd* pParent = NULL); // 标准构造函数// 对话框数据//{{AFX_DATA(CInputDlg)enum { IDD = IDD_INPUT };CString m_strTitle;CString m_strPrompt;CString m_strInput;//}}AFX_DATA // 重载// 由 ClassWizard 生成的虚函数重载//{{AFX_VIRTUAL(CInputDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持//}}AFX_VIRTUAL// 实现protected:// 生成的消息映射函数//{{AFX_MSG(CInputDlg)virtual BOOL OnInitDialog();//}}AFX_MSGDECLARE_MESSAGE_MAP()};//{{AFX_INSERT_LOCATION}}// Microsoft Developer Studio 将在紧贴上一行之前的位置添加附加的声明#endif // !defined(AFX_INPUTDLG_H__02DB98CF_1F76_11D2_971A_0000B4810A31__INCLUDED_)// InputDlg.cpp : 实现文件//#include "stdafx.h"#include "InputDlg.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endif/////////////////////////////////////////////////////////////////////////////// CInputDlg dialog CInputDlg::CInputDlg(CWnd* pParent /*=NULL*/): CDialog(CInputDlg::IDD, pParent){//{{AFX_DATA_INIT(CInputDlg)m_strInput = _T("");//}}AFX_DATA_INIT} void CInputDlg::DoDataExchange(CDataExchange* pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CInputDlg)DDX_Text(pDX, IDC_EDIT, m_strInput);//}}AFX_DATA_MAP} BEGIN_MESSAGE_MAP(CInputDlg, CDialog)//{{AFX_MSG_MAP(CInputDlg)//}}AFX_MSG_MAPEND_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CInputDlg message handlersCString CInputDlg::GetInput(CString Title, CString Prompt){CString str(""); // 注意:这里对 CString 对象 str 的初始化是必要,否则// 在后面的过程将会出错// 对标题条和提示文本的实际的更新将在消息处理函数 OnInitDialog 中进行m_strTitle=Title;m_strPrompt=Prompt;if (DoModal()==IDOK){// 如果用户单击了确定,则返回所输入的字符串str=m_strInput;}return str;}BOOL CInputDlg::OnInitDialog() {CDialog::OnInitDialog();
    // 使用用户指定的标题字符串SetWindowText(m_strTitle);// 设置提示文本GetDlgItem(IDC_PROMPT)->SetWindowText(m_strPrompt);// 将输入焦点设置为 IDC_EDIT 控件GetDlgItem(IDC_EDIT)->SetFocus();
    // 由于将输入焦点设置为 IDC_EDIT 控件,因此 OnInitDialog 成员函数应该返回假值return FALSE;}在示例程序中,我们使用了__declspec(dllexport)来导出类CInputDlg,最主要的原因是因为这种方法相对比较简单一些。编译DLL工程ExtDllDemo,在Debug目录下生成了动态链接库ExtDllDemo.dll的导入库ExtDllDemo.lib。
    下面我们创建动态链接库ExtDllDemo.dll的测试工程ExtDllTest。1. 使用AppWizard创建基于对话框的EXE工程ExtDllTest,工程ExtDllTest使用了动态MFC链接(这是必须的)。将在前一步中生成的ExtDllDemo.lib拷贝到工程ExtDllTest所在的目录下。完成这一步之后,从Project菜单下选择Settings命令,在工程的设置对话框中选择Link选项卡,在Object/Library Modules框中输入ExtDllDemo.lib。2. 在实现文件ExtDllDemoDlg.cpp的最前面输入下面的代码://///////////////////////////////////////////////////////////////////////////// CInputDlg dialogclass __declspec(dllimport) CInputDlg : public CDialog{public:CString GetInput(CString Title, CString Prompt);CInputDlg(CWnd* pParent = NULL);};上面的代码在工程中定义了类CInputDlg,以便于在以后的代码中使用该类。这里需要注意的是,我们只需给出对我们有意义的那些成员的声明即可,而没有必须在上面的定义中给出完全的成员声明。在OnInitDialog成员函数的// TODO注释下输入下面的代码。该代码在应用程序的主对话框弹出之前询问主对话框的标题文本。CInputDlg dlg;CString str=dlg.GetInput("输入", "请输入对话框的标题:");SetWindowText(str);
    注意:
    如果在CInputDlg::GetInput成员函数中没有对对象变量str进行正确的初始化(如直接使用return m_strInput等),那么上面的代码的Debug版本在运行时将会出现Assertion失败。这种现象的根源在于CString内部所使用的内存分配方式。由于CString所使用的内存分配方式相当的复杂,因此,我们不在这里深入的讨论这一现象,仅仅指出存在这种问题而已。
    编译并生成应用程序ExtDllTest,然后将动态链接库ExtDllDemo.dll拷贝到应用程序ExtDllTest的目录或系统目录中,再运行ExtDllTest,已检验动态链接库ExtDllTest的工作是否正确。