现在在Windows下的应用程序开发,VS.Net占据了绝大多数的份额。因此很多以前搞VC++开发的人都转向用更强大的VS.Net。在这种情况下,有很多开发人员就面临了如何在C#中使用C++开发好的类的问题。下面就用一个完整的实例来详细说明怎样用托管C++封装一个C++类以提供给C#使用。
    比如,现在有一个工程名为NativeCppDll的由C++编写的DLL,里面输出了一个CPerson类。下面是具体的代码:

// NativeCppDll.h
#pragma once
#ifndef LX_DLL_CLASS_EXPORTS
#define LX_DLL_CLASS __declspec(dllexport)
#else
#define LX_DLL_CLASS __declspec(dllimport)
#endif // hovertree.com
class LX_DLL_CLASS CPerson
{
public:
CPerson();
CPerson(const wchar_t *pName, const wchar_t cSex, int iAge);
void SetName(const wchar_t *pName);
wchar_t * GetName();
void SetSex(const wchar_t cSex);
wchar_t GetSex();
void SetAge(int iAge);
int GetAge();
wchar_t * GetLastError();
private:
wchar_t m_szName[];
wchar_t m_cSex;
int m_iAge;
wchar_t m_szLastError[];
void ShowError();
};
// NativeCppDll.cpp
#include "stdafx.h"
#include "NativeCppDll.h"
#include <iostream>
#include <tchar.h>
using namespace std;
CPerson::CPerson()
{
wcscpy_s(m_szName, _T("No Name"));
m_cSex = 'N';
m_iAge = ;
wcscpy_s(m_szLastError, _T("No Error"));
}
CPerson::CPerson(const wchar_t *pName, const wchar_t cSex, int iAge)
{
wcscpy_s(m_szLastError, _T("No Error"));
SetName(pName);
SetSex(cSex);
SetAge(iAge);
}
void CPerson::SetName(const wchar_t *pName)
{
if ((pName == NULL) || (wcslen(pName) == ) || (wcslen(pName) > ))
{
wcscpy_s(m_szName, _T("No Name"));
wcscpy_s(m_szLastError, _T("The length of the input name is out of range."));
ShowError();
return;
}
wcscpy_s(m_szName, pName);
}
wchar_t * CPerson::GetName()
{
return m_szName;
}
void CPerson::SetSex(const wchar_t cSex)
{
if ((cSex != 'F') && (cSex != 'M') && (cSex != 'm') && (cSex != 'f'))
{
m_cSex = 'N';
wcscpy_s(m_szLastError, _T("The input sex is out of [F/M]."));
ShowError(); return;
}
m_cSex = cSex;
}
wchar_t CPerson::GetSex()
{
return m_cSex;
}
void CPerson::SetAge(int iAge)
{
if ((iAge < ) || (iAge > ))
{
m_iAge = ;
wcscpy_s(m_szLastError, _T("The input age is out of range."));
ShowError();
return;
}
m_iAge = iAge;
}
int CPerson::GetAge()
{
return m_iAge;
}
wchar_t * CPerson::GetLastError()
{
return m_szLastError;
}
void CPerson::ShowError()
{
cerr << m_szLastError << endl;
}

这是一个很典型的由C++开发的DLL,输出一个完整的C++类。如果现在要求开发一个C#工程,需要用到这个DLL中输出的C++类CPerson,该怎么办呢?针对这个例子来说,类CPerson非常小,可以用C#重新写一个跟这个C++类一样的类。可是,如果需要的C++类很大,或者很多的时候,重写工程将非常庞大。而且这样没有对现有的代码进行重用,浪费了现有资源,开发起来费时费力。
    当然,还是有方法解决这个问题的。那就是用托管C++将C++类给封装一下,然后再提供给C#来使用。下面就用代码来详细说明怎样用托管C++来封装上面的那个C++类。
    首先,要创建一个托管C++的DLL工程ManageCppDll,然后在里面添加下面的代码:

// ManageCppDll.h
#pragma once
#define LX_DLL_CLASS_EXPORTS
#include "../NativeCppDll/NativeCppDll.h"
using namespace System;
namespace ManageCppDll
{
public ref class Person
{
// 包装所有类CPerson的公有成员函数
public:
Person();
Person(String ^ strName, Char cSex, int iAge);
~Person();
property String ^ Name
{
void set(String ^ strName);
String ^ get();
}// hovertree.com
property Char Sex
{
void set(Char cSex);
Char get();
}
property int Age
{
void set(int iAge);
int get();
}
String ^ GetLastError();
private:
// 类CPerson的指针,用来调用类CPerson的成员函数
CPerson *m_pImp;
};
};

从这个头文件就能看出来,这是对C++类CPerson的包装。类Person的所有公有成员函数都跟C++类CPerson一样,只不过成员函数的参数和返回值就改成了托管C++的类型,这也是让类Person能在C#中使用的首要条件。当然只需要对公有成员函数进行封装,对于保护成员函数和私有成员函数则不必做任何封装。
    类Person仅有一个私有的成员变量:一个类CPerson的指针。而类Person的所有成员函数的实现都是靠这个CPerson指针来调用类CPerson的相应成员函数来实现。
    下面是具体的实现代码:

// ManageCppDll.cpp
#include "stdafx.h"
#include "ManageCppDll.h"
#include <vcclr.h>
namespace ManageCppDll
{
// 在构造函数中创建类CPerson的对象并在析构函数中将该对象销毁
// 所有的成员函数实现都是通过指针m_pImp调用类CPerson的相应成员函数实现
Person::Person()
{
m_pImp = new CPerson();
}
Person::Person(String ^ strName, Char cSex, int iAge)
{
// 将string转换成C++能识别的指针 hovertree.com
pin_ptr<const wchar_t> wcName = PtrToStringChars(strName);
m_pImp = new CPerson(wcName, cSex, iAge);
}
Person::~Person()
{
// 在析构函数中删除CPerson对象
delete m_pImp;
}
void Person::Name::set(String ^ strName)
{
pin_ptr<const wchar_t> wcName = PtrToStringChars(strName);
m_pImp->SetName(wcName);
}
String ^ Person::Name::get()
{
return gcnew String(m_pImp->GetName());
}
void Person::Sex::set(Char cSex)
{
m_pImp->SetSex(cSex);
}
Char Person::Sex::get()
{
return m_pImp->GetSex();
}
void Person::Age::set(int iAge)
{
m_pImp->SetAge(iAge);
}
int Person::Age::get()
{
return m_pImp->GetAge();
}
String ^ Person::GetLastError()
{
return gcnew String(m_pImp->GetLastError());
}
};

如果要在C#中使用类Person,首先要添加对ManageCppDll.dll的引用,然后就可以像用普通的C#类一样的使用类Person了。比如下面这样的代码:

using ManageCppDll;
Person person = new Person();
person.Name = "StarLee";
person.Sex = 'M';
person.Age = ;
// 何问起

熟悉设计模式的看了上面的代码肯定会发现,这样的设计跟BRIDGE模式如出一辙。其实,上面的方法也算是一种BRIDGE模式,由托管C++充当了C#中使用用C++开发的类的桥梁。另外,这种形式也可以理解为ADAPTER模式,托管C++类Person就是C++类CPerson的一个适配器。通过这个桥梁,可以很容易的重用以前用C++开发的类,让这些C++类继续在C#中发挥它们的效用,让开发变得事半功倍。

http://www.cnblogs.com/roucheng/

http://www.cnblogs.com/roucheng/p/3521864.html

C#引用C++代码的更多相关文章

  1. idea 优先引用项目代码,而非jar包

    1.现有项目,和jar包源码在同一层目录 2.idea直接打开ccccc,右键a,b两个文件中的pom.xml加入maven project,如下: 执行1,2 步骤,在b中引入a,刚引入a在最下方, ...

  2. JVM中强引用,弱引用,软引用和幽灵引用的代码

    上代码: public class ReferenceTest { public static void main(String[] args) { //test1();//软引用 //test2() ...

  3. jmeter进阶之Beanshell引用Java代码

    在Jmeter实际的使用中,根据业务往往需要构造一些数据源,比如虚拟用户的账号信息等,为了快速灵活,较好的方式往往是通过代码来辅助生产数据. 故在Jmeter支持的众多 sampler 或 前/后置处 ...

  4. c++文件中引用C代码

    下面提供一个比较完整的示例程序,一共有四个文件:main.cpp.test.c.test.h.test.hpp main.cpp #include "test.hpp" int m ...

  5. jsp引用servlet生成的验证码代码演示

    此演示代码主要包括以下三部分:1.checkCode.java:用于生成验证码2.checkCodeServler3.check.jsp 验证 下面是checkCode.java的内容: 复制代码代码 ...

  6. .net c# 服务器共享文件夹 windows远程登陆 代码

    一个刚刚开始学习编程的人,如果遇到问题无法解决可能会寻找别的解决方案,如果长此以往可能会放弃这门语言而学习其他的语言... 开源与分享的重要性 使用场景:将网站所有附件上传到指定服务器的共享目录下,首 ...

  7. WSF脚本详解:组合JS和VBS代码

    1.概述 Windows Script Host除了提供一个对象模型之外,还提供了一种脚本框架,这就是WSF脚本.通过WSF约定的标记元素,可以将多种脚本语言写的代码块组合起来,完成任务.除此之外,还 ...

  8. block的循环引用

    什么是循环引用呢? 就是我引用你,你引用我,就会造成循环引用,双方都不会被销毁,导致内存泄漏. _block =  ^{ NSLog(@"%@", self); }; block会 ...

  9. Dapper学习笔记(2)-链接引用

    在研究Dapper源码时发现Dapper NET45类库中的SqlMapper.cs文件前面有个蓝色的箭头图标,发现在Dapper NET45文件夹下根本不存在SqlMapper.cs文件,其文件属性 ...

随机推荐

  1. 每日英语:The Secret About Online Ad Traffic: One-Third Is Bogus

    Billions of dollars are flowing into online advertising. But marketers also are confronting an uncom ...

  2. jQuery Mobile 移动开发中的日期插件Mobiscroll使用说明

    近期在移动方面的开发,使用jQuery Mobile ,移动方面的插件不如Web 方面的插件多,选择的更少,有一些需要自己去封装,但功力尚不足啊. 日期插件JQM也提供了内置的,但样式方面不好看,只好 ...

  3. 修改 上传图片按钮input-file样式。。

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  4. visual studio 2013使用技巧

    去掉 引用提示 文本编辑器=>所有语言=>codelens visual studio 由于以前的函数求值超时,函数求值被禁用.必须继续执行才能重新启用函数求值 visual studio ...

  5. FileZilla FTP Server 高级防火墙例外

    在防火墙中: 在“例外”中,添加端口21,TCP 添加端口50000,TCP (或添加一组端口,一个一个的也行,如果你在软件中选择的是50000-51000,而在这里只打开了50000的单个端口,登录 ...

  6. Go语言实现HashSet

    set.go // set project set.go package set type Set interface { Add(e interface{}) bool Remove(e inter ...

  7. 谈谈对从业IT行业看法

    做后端开发也有五年了,从工厂到IT行业转化很大,当然最后离职的工厂想也没想过会写代码为生. 是什么变动会让我走入这一行呢? 1.思想作怪 *我当时就想,我认为不应该一辈子只做这狗屎事,起码在当时看来就 ...

  8. 【Android】与服务器实现JSON数据通信

    一.前言 作为一名移动端开发人员,具备一定的服务端开发能力也是非常必要的,本篇博客讲述如何在Android和服务器之间实现JSON数据通信交互,博客内容基于另外一篇博客:[Web]Eclipse + ...

  9. 二叉树系列 - 二叉搜索树 - 线性时间内把有序链表转化为BST

    引言 本文来自于Google的一道题目: how to merge two binary search tree into balanced binary search tree. how to me ...

  10. 【转载】 IE/Firefox每次刷新时自动检查网页更新,无需手动清空缓存的设置方法

    [参考了别人的文章]我们做技术,经常在写页面的时候需要多次刷新测试,可是浏览器都有自己的 缓存机制,一般CSS和图片都会被缓存在本地,这样我们修改的CSS就看不到效果 了,每次都去清空缓存,再刷新看效 ...