我作了一个动态链接库,我只希望用户通过头文件和lib文件来链接我的动态链接库。而不希望用户通过loadlibrary和GetProceAddress来调用我的输出函数。我找遍了MSDN,也没有找到一个合适的办法。
附MSDN关于动态和静态加载的说明:
隐式链接有时称为静态加载或加载时动态链接。显式链接有时称为动态加载或运行时动态链接。在隐式链接下,使用 DLL 的可执行文件链接到此 DLL 的创建者所提供的导入库(.LIB 文件)。使用 DLL 的可执行文件加载时,操作系统加载此 DLL。客户端可执行文件调用 DLL 的导出函数,就好像这些函数包含在可执行文件内一样。在显式链接下,使用 DLL 的可执行文件必须进行函数调用以显式加载和卸载此 DLL,并访问此 DLL 的导出函数。客户端可执行文件必须通过函数指针调用导出函数。可执行文件对两种链接方法可以使用同一个 DLL。另外,由于一个可执行文件可隐式链接到某个 DLL,而另一个可显式附加到此 DLL,故这些机制不是互斥的。为隐式链接到 DLL,可执行文件必须从 DLL 的提供程序获取下列各项: 包含导出函数和/或 C++ 类的声明的头文件(.H 文件)。 
要链接的导入库(.LIB files)。(生成 DLL 时链接器创建导入库。) 
实际的 DLL(.DLL 文件)。 
使用 DLL 的可执行文件必须包括头文件,此头文件包含每个源文件中的导出函数(或 C++ 类),而这些源文件包含对导出函数的调用。从编码的角度讲,导出函数的函数调用与任何其他函数调用一样。若要生成调用可执行文件,必须与导入库链接。如果使用的是外部生成文件,请指定导入库的文件名,此导入库中列出了要链接到的其他对象 (.OBJ) 文件或库。操作系统在加载调用可执行文件时,必须能够定位 .DLL 文件。
在显式链接下,应用程序必须进行函数调用以在运行时显式加载 DLL。为显式链接到 DLL,应用程序必须: 
调用 LoadLibrary(或相似的函数)以加载 DLL 和获取模块句柄。 
调用 GetProcAddress,以获取指向应用程序要调用的每个导出函数的函数指针。由于应用程序是通过指针调用 DLL 的函数,编译器不生成外部引用,故无需与导入库链接。 
使用完 DLL 后调用 FreeLibrary。 
例如:
typedef UINT (CALLBACK* LPFNDLLFUNC1)(DWORD,UINT);
...HINSTANCE hDLL;               // Handle to DLL
LPFNDLLFUNC1 lpfnDllFunc1;    // Function pointer
DWORD dwParam1;
UINT  uParam2, uReturnVal;hDLL = LoadLibrary("MyDLL");
if (hDLL != NULL)
{
   lpfnDllFunc1 = (LPFNDLLFUNC1)GetProcAddress(hDLL,
                                           "DLLFunc1");
   if (!lpfnDllFunc1)
   {
      // handle the error
      FreeLibrary(hDLL);       
      return SOME_ERROR_CODE;
   }
   else
   {
      // call the function
      uReturnVal = lpfnDllFunc1(dwParam1, uParam2);
   }
}

解决方案 »

  1.   

    隐式链接
    应用程序的代码调用导出 DLL 函数时发生隐式链接。当调用可执行文件的源代码被编译或被汇编时,DLL 函数调用在对象代码中生成一个外部函数引用。要解析此外部引用,应用程序必须与 DLL 的创建者所提供的导入库(.LIB 文件)链接。导入库仅包含加载 DLL 的代码和实现 DLL 函数调用的代码。在导入库中找到外部函数后,会通知链接器此函数的代码在 DLL 中。要解析对 DLL 的外部引用,链接器只需向可执行文件中添加信息,通知系统在进程启动时应在何处查找 DLL 代码。系统启动包含动态链接引用的程序时,它使用程序的可执行文件中的信息定位所需的 DLL。如果系统无法定位 DLL,它将终止进程并显示一个对话框来报告错误。否则,系统将 DLL 模块映射到进程的地址空间中。如果任何 DLL 具有(用于初始化代码和终止代码的)入口点函数,操作系统将调用此函数。在传递到入口点函数的参数中,有一个指定用以指示 DLL 正在附带到进程的代码。如果入口点函数没有返回 TRUE,系统将终止进程并报告错误。最后,系统修改进程的可执行代码以提供 DLL 函数的起始地址。与程序代码的其余部分一样,DLL 代码在进程启动时映射到进程的地址空间中,且仅当需要时才加载到内存中。因此,由 .DEF 文件用来在 Windows 的早期版本中控制加载的 PRELOAD 和 LOADONCALL 代码属性不再具有任何意义。显式链接
    大部分应用程序使用隐式链接,因为这是最易于使用的链接方法。但是有时也需要显式链接。下面是一些使用显式链接的常见原因: 直到运行时,应用程序才知道需要加载的 DLL 的名称。例如,应用程序可能需要从配置文件获取 DLL 的名称和导出函数名。 
    如果在进程启动时未找到 DLL,操作系统将终止使用隐式链接的进程。同样是在此情况下,使用显式链接的进程则不会被终止,并可以尝试从错误中恢复。例如,进程可通知用户所发生的错误,并让用户指定 DLL 的其他路径。 
    如果使用隐式链接的进程所链接到的 DLL 中有任何 DLL 具有失败的 DllMain 函数,该进程也会被终止。同样是在此情况下,使用显式链接的进程则不会被终止。 
    因为 Windows 在应用程序加载时加载所有的 DLL,故隐式链接到许多 DLL 的应用程序启动起来会比较慢。为提高启动性能,应用程序可隐式链接到那些加载后立即需要的 DLL,并等到在需要时显式链接到其他 DLL。 
    显式链接下不需将应用程序与导入库链接。如果 DLL 中的更改导致导出序号更改,使用显式链接的应用程序不需重新链接(假设它们是用函数名而不是序号值调用 GetProcAddress),而使用隐式链接的应用程序必须重新链接到新的导入库。 
    下面是需要注意的显式链接的两个缺点: 如果 DLL 具有 DllMain 入口点函数,则操作系统在调用 LoadLibrary 的线程上下文中调用此函数。如果由于以前调用了 LoadLibrary 但没有相应地调用 FreeLibrary 函数而导致 DLL 已经附加到进程,则不会调用此入口点函数。如果 DLL 使用 DllMain 函数为进程的每个线程执行初始化,显式链接会造成问题,因为调用 LoadLibrary(或 AfxLoadLibrary)时存在的线程将不会初始化。 
    如果 DLL 将静态作用域数据声明为 __declspec(thread),则在显式链接时 DLL 会导致保护错误。用 LoadLibrary 加载 DLL 后,每当代码引用此数据时 DLL 就会导致保护错误。(静态作用域数据既包括全局静态项,也包括局部静态项。)因此,创建 DLL 时应避免使用线程本地存储区,或者应(在用户尝试动态加载时)告诉 DLL 用户潜在的缺陷。
      

  2.   

    我曾尝试在DllMain中进程初始化代码中让返回false,结果两者都失败了。
    如果我在DllMain中调用LoadLibrary或者使用__declspec(thread),我觉得这不是一个理想的解决办法。
    请帮我回答,分值可以追加到200分。
      

  3.   

    我作了一个动态链接库,我只希望用户通过头文件和lib文件来链接我的动态链接库。
    =================不理解,为什么不用静态的库呢?
      

  4.   

    这个动态链接库要提交给用户使用,一方面要减少可执行文件大小,另一方面要限制未经授权的访问存取。
    你就当作是一个应聘面试题就行了。
    我只要答案,不需要why not?
      

  5.   

    怎么可能嘛,人家调用fopen,把你的动态库读入内存,修改内存属性为可执行,
    然后jmp或call进去,你能拦的住吗
      

  6.   

    这是一个外企的面试题,我的同事就是灾在这道题上,我不想前蹈复辙。
    不过我猜想反正动态加载是通过调用loadlibrary和getproceaddress实现的。只要这两个函数有一个执行失败,就无法调用了。
       如果实在没有更好的办法的话,我不妨就用dllmain调用loadlibrary,导致循环加载使得loadlibrary返回NULL实现它。不过我敢确信这不是对方要的答案。
      

  7.   

    我觉得系统对静态加载的DLL在内部也是调用LoadLibrary,不会对两种情况采用不同的加载方法的,也就是说在加载过程上打主意应该是没用的。
    关键是你所谓的授权用户和未授权用户的差别在哪里?授权用户用手头有.lib和.h?我的意思是授权用户手里得有别人没有的信息。
      

  8.   

    不过我作了一个控制台类型测试程序以静态方式调用一个动态链接库,结果发现这个测试程序。采用depends工具察看可执行文件,没有发现调用loadlibrary函数。我猜想静态链接已经把函数的定位信息写入到可执行文件内部了,不再需要通过上面两个函数得到DLL输出函数的影像地址了。
       不过我最关心是答案,也就是说在Visual C++的链接选项中,是否存在这样的设置。
      

  9.   

    我说的内部调用LoadLibrary不是指编译器实现调用,而是操作系统在加载EXE时的行为,应该是先检查它的所有依赖项,然后一一加载,而这个加载过程,在系统内部应该是和LoadLibrary等价的。
    所以应该只能在GetProcessAddress上打算,但GetProcessAdress的结果只依赖于DLL文件的导出函数,即DLL文件的结构决定了结果,所以我想我想不出办法了。
      

  10.   

    GetProcessAddress获得函数的入口地址,同时支持字符串,也支持序号调用,比如采用makeinteresource(1)。如果在这个函数调用上作文章的话,只能让这个函数无法得到输出函数的入口地址。那么,DLL最好不要输出函数符号信息。但是如果dll没有输出导出函数符号信息,那么静态调用会不会受影响呢?另外怎么才能实现导出函数符号信息的禁止输出。
      

  11.   

    将该DLL做成扩展的MFCDLL试试。或者这样就不能动态地加载了。通过导出整个类来使用该库也可一试
      

  12.   

    扩展的MFC DLL就不能吗?我查一下,不过对方的面试题上可没有提到是否采用MFC。如果采用SDK或者ATL呢?
      

  13.   

    真的没人会吗?看来这家外企,CSDN中的visual c++高手是没有资格去了?
      

  14.   

    BOOL APIENTRY DllMain( HANDLE hModule, 
                           DWORD  ul_reason_for_call, 
                           LPVOID lpReserved
     )
    {
             MessageBox(0,"真不好意思,这么简单的问题就要了你200分。不过我可花了20分钟建了3个工程做实验,一下就试出来了。","太简单了",MB_OK);
    if(lpReserved) return TRUE; else return FALSE;//答案就是这一句
    }
      

  15.   


    好的,我试验一下,如果满足上面要求,决不食言!
    3Q very much!
      

  16.   

    的确好用,两种链接方式存在着入口参数的差别,先期绑定(静态加载)在DLlMain的入口函数参数中第四个保留的参数不为空,而动态加载(后期绑定)为NULL。
    如果用两个测试程序(分别用两种链接方式)调用同一个动态链接库,将DLL设为启动程序,两个项目的输出文件设为加载调试程序,当跟踪到DLLMain函数时就会发现这一点。
    谢谢 icansaymyabc(学习与进步) ,分先欠着,我一定给您。
    预祝明天面试成功!