注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

gmd20的个人空间

// 编程和生活

 
 
 

日志

 
 

在C++和.Net 中分别如何使用在DLL文件中的类  

2008-11-06 13:25:57|  分类: 程序设计 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

C++中的用法

1) 如果是使用load link (加载时链接)的办法的话,那是很简单的。
2)如果想使用LoadLibray 和GetProcAddress来使用runtime link (运行时链接、加载?)改类的话,则要做点额外的工作,来做到类似的效果。因为LoadLibray 和GetProcAddress 这两个函数本来只能用于获取函数的地址的。

测试如下:

1.建一个 win32 dll工程, 实现一个 widebright的测试类.代码如下:
// widebright_dll.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD ul_reason_for_call,
                       LPVOID lpReserved
        )
{
    return TRUE;
}


class Iwidebright {
public:
virtual void say_hello()=0 ;
virtual void release()=0;
};

class __declspec(dllexport) widebright : public Iwidebright {

public :
void say_hello(){

   MessageBox(NULL,"你好,我是widebright,哈哈!","widebright",MB_OK);
}
void release(){};

};

extern "C" __declspec(dllexport) HRESULT CreateInstanceWidebright(Iwidebright ** ppWidebright)
{
    *ppWidebright = new widebright;
    return TRUE;
}


这里定义了一个Iwidebright类,和   CreateInstanceWidebright 是为了实现运行时加载dll使用里面的类的功能了. 采用virtual 定义的函数是会在类里面插入一个vtable(虚拟表 指针的),这是"多态"的关键吧,运行时,自动会调用相应的继承子类的相应方法.我也是刚刚仔细看了《Thinking in c++》中相关的讲解。__declspec(dllexport) 是把函数或者类声明为导出函数,通知编译器,让生产的dll中包含相应的输出函数。extern "C" 是通知c++编译器不要做函数名字扩展,一般c++为了实现重载等功能是会把 CreateInstanceWidebright 这样名字改成?CreateInstanceWidebright@XXXXX 这样的扩展的,这样dll导出函数中显示的名字就不一样,GetProcAddress的参数也要改。加上extern "C" 就不会扩展啦。


2. 先来看一下load link的用法。

这种情况,因为dll中的函数调用都是在链接的时候就已经固定好的了,所以exe一启动的时候,系统就会自动加载用到的dll。
先把上面那个项目生成的dll和lib文件复制到test工程的目录下。test是一个简单的win32 console 项目,代码如下。

#include "stdafx.h"
#include <windows.h>
#include <string.h>

using namespace std ;

#pragma comment (lib,"./widebright_dll.lib")

class Iwidebright {
public:
virtual void say_hello()=0 ;
virtual void release()=0;
};

class widebright : public Iwidebright{
public :
   void say_hello() ;
              void release();;
};

int _tmain(int argc, _TCHAR* argv[])
{  
    

    widebright me;
    me.say_hello ();


cin.get();   // wait
return 0;

}
---------------------------

#pragma comment (lib,"./widebright_dll.lib") 一句就是通知链接器,包含我们的lib文件,这样链接是才能找到相应的类的实现。我这里直接把类的声明复制到这里来了,其实也可以单独放到一个头文件里面啦,一般都是由dll工程方面提供的。看到了用起来也还是类是在本工程定义的一样的,   编译器自动找到
widebright me;
    me.say_hello ();

所需要的dll,然后生成包含dll调用的exe文件。


3. 再来看看我们动态加载的方法。 动态加载就是说等到你调用LoadLibrary函数,真正用到这个dll的时候,这个dll才被加载进系统内存的。

#include "stdafx.h"
#include <windows.h>
#include <string.h>

using namespace std ;

class Iwidebright {
public:
virtual void say_hello()=0 ;
virtual void release()=0;
};

typedef HRESULT (* pCreateInstanceWidebright) (Iwidebright ** ppWidebright);

int _tmain(int argc, _TCHAR* argv[])
{  
    

HMODULE h = LoadLibrary("widebright_dll.dll");
if(h != NULL) {
pCreateInstanceWidebright CreateInstanceWidebright;
     CreateInstanceWidebright = (pCreateInstanceWidebright) GetProcAddress(h, "CreateInstanceWidebright");
if ( CreateInstanceWidebright!=NULL)
{
      Iwidebright *widebright ;
   if ( CreateInstanceWidebright(&widebright))
   {
                widebright->say_hello();
     widebright->release ();
     delete widebright;
   }
}
     FreeLibrary(h);
}


cin.get();   // wait
return 0;

}

看到代码就知道了,因为GetProcAddress函数是不能用于创建dll中的类,所以用了一种变通 的方法,先通过一个CreateInstanceWidebright函数来返回类的指针,然后我们可以通过Iwidebright这个基类的指针来调用widebright 的具体实现的函数,这样也就是virtual那个关键字的作用了。如果用过COM技术的话,会觉得这里和COM中所说的接口差不多不是吗? 其实COM也是用了类似的思想的,每本COM的书大都会用C++里面的虚函数调用来说明一下原理的。CreateInstanceWidebright 函数和系统提供的CreateInstance函数也有点类似,呵呵。


运行的效果,都会弹出一个对话框

在C++和.Net 中分别如何使用在DLL文件中的类 - widebright - widebright的个人空间


=======================
我们再来看看。Net里面动态加载是怎么用的:

用反射是可以用来动态创建类的,比C++里面要方便一点。
1.创建一个class library 如下

using System;

namespace widebright_dll
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public class widebright
{
   public widebright()
   {
    //
    // TODO: Add constructor logic here
    //

   }
   public void say_hello()
   {
    //好像类库dll项目默认不引入 System.Windows.Forms,需要手工添加一下
     System.Windows.Forms.MessageBox.Show ( "我是widebright,哈哈","dll中的调用哦");
          
   }
}
}


2. ,静态的调用方法 .建一个测试工程如下,
在 “资源浏览器" 引用里面,把上面工程生产dll文件添加进来。
然后添加按钮的代码如下:
   private void button1_Click(object sender, System.EventArgs e)
   {
         widebright_dll.widebright widebrightObj = new widebright_dll.widebright ();
      widebrightObj.say_hello ();


   }

3. 动态 link 方法,把按钮的代码修改如下:


   private void button1_Click(object sender, System.EventArgs e)
   {
            string path = Application.StartupPath;
           
    //使用反射加载,获取dll中的类的type信息
    System.Reflection.Assembly widebright_dll=
          System.Reflection.Assembly.LoadFrom( path+ "/widebright_dll.dll");
      Type widebrightType=widebright_dll.GetType("widebright_dll.widebright");
            System.Reflection.MethodInfo say_hello= widebrightType.GetMethod("say_hello");
            //根据类型,创建对象
    Object widebright=System.Activator.CreateInstance(widebrightType);
            say_hello.Invoke(widebright, null); //调用对象的方法

   }

---------------------
可以看到,.Net比C++就在于他能够动态加载dll通过反射得到里面类信息,然后可以直接根据type创建对象,通过反射调用方法等。应该是把那些结构信息也保存在dll文件中了。这就是.net这类语言的优势了,什么都封装的很好,方便使用。

把生成的dll复制exe的目录下就可以测试了,运行结果如下

在C++和.Net 中分别如何使用在DLL文件中的类 - widebright - widebright的个人空间

  评论这张
 
阅读(854)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017