现在在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
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[128];
wchar_t m_cSex;
int m_iAge;
wchar_t m_szLastError[128];
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 = 0;
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) == 0) || (wcslen(pName) > 127))
{
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 < 0) || (iAge > 150))
{
m_iAge = 0;
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();
}
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++能识别的指针
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 = 28;
熟悉设计模式的看了上面的代码肯定会发现,这样的设计跟BRIDGE模式如出一辙。其实,上面的方法也算是一种BRIDGE模式,由托管C++充当了C#中使用用C++开发的类的桥梁。另外,这种形式也可以理解为ADAPTER模式,托管C++类Person就是C++类CPerson的一个适配器。通过这个桥梁,可以很容易的重用以前用C++开发的类,让这些C++类继续在C#中发挥它们的效用,让开发变得事半功倍。

C#如何调用非托管的C++Dll的更多相关文章

  1. (转)C#调用非托管Win 32 DLL

    转载学习收藏,原文地址http://www.cnblogs.com/mywebname/articles/2291876.html 背景 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使 ...

  2. 在VS2010上使用C#调用非托管C++生成的DLL文件

    背景 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件的时候,通过C#来开发应用软件时,就需要利用DllImport特性进行方法调用.本篇文章将引导你快速理解这个调用 ...

  3. 在VS2017上使用C#调用非托管C++生成的DLL文件(图文讲解)

    原文:在VS2010上使用C#调用非托管C++生成的DLL文件(图文讲解) 背景 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件的时候,通过C#来开发应用软件时,就 ...

  4. 【转】在VS2010上使用C#调用非托管C++生成的DLL文件(图文讲解)

    原文:http://www.cyqdata.com/cnblogs/article-detail-35876# 背景 在项目过程中,有时候你需要调用非C#编写的DLL文件,尤其在使用一些第三方通讯组件 ...

  5. 关于C#调用非托管DLL,报“内存已损坏的”坑,坑,坑

    因客户需求,与第三方对接,调用非托管DLL,之前正常对接的程序,却总是报“内存已损坏的异常”,程序进程直接死掉,折腾到这个点(2018-05-11 00:26),终于尘埃落定,直接上程序. 之前的程序 ...

  6. 关于C#调用非托管动态库方式的性能疑问

    最近的项目中,因为一些原因,需要C#调用非托管(这里为C++)的动态库.网上喜闻乐见的方式是采用静态(DllImport)方式进行调用.偶然在园子里看到可以用动态(LoadLibrary,GetPro ...

  7. .net4.0调用非托管DLL的异常捕获

    转发: 由于有些非托管的DLL内部异常未有效处理,当托管程序调用到这样的DLL时,就引起托管程序意外退出. 托管程序使用通常的捕获try……catch块不起作用.原因是.NET 4.0里新的异常处理机 ...

  8. C#调用非托管dll

    以C#开发周立功CAN举例,在官网下载了周立功的demo 一.C++头文件样子 //接口卡类型定义#define VCI_PCI5121 1 //一些结构体定义 typedef struct tagR ...

  9. 托管程序调用非托管dll问题总结

    托管程序Visual Basic.net, 非托管DLL标准C++程序(使用VC++编译) 函数调用定义 第一种写法: <DllImportAttribute("XXX.dll&quo ...

随机推荐

  1. 推荐一款优雅的jquery手风琴特效

    推荐一款基于 jQuery和 CSS3实现的手风琴效果. 查看demo演示

  2. spring-cloud-netflix集成的服务

    1.Ribbon Ribbon is a client side IPC library that is battle-tested in cloud. It provides the followi ...

  3. Dom4j 查找节点或属性

    Dom4j  查找节点或属性 例如 1 查找下面xml中的student节点的age属性, xpathstr="/students/student/@age"; 2 查找下面xml ...

  4. Eclipse如何新建一个tomcat_server发布web项目

    方法/步骤     通过Eclipse打开一个正在开发阶段的java的web项目,没有项目的,只是为了学习的话,可以新建一个测试的java的web项目即可.在功能窗口找到如图所示的Servers窗口, ...

  5. DENON AVR-X510BT 功放设置记录

    http://manuals.denon.com/avrx510bt/ap/zh/index.php 环绕模式  : Direct:直接 Sttereo:立体声 Dolby PL  声音模式 电影 : ...

  6. VUE里子组件获取父组件动态变化的值

    在VUE里父组件给子组件间使用props方式传递数据,但是希望父组件的一个状态值改变然后子组件也能监听到这个数据的改变来更新子组件的状态. 场景:子组件通过props获取父组件传过来的数据,子组件存在 ...

  7. scp---远程拷贝文件

    scp命令用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,而且scp传输是加密的.可能会稍微影响一下速度.当你服务器硬盘变为只读read onl ...

  8. or in 、Object.keys()以及Object.getOwnPropertyNames有什么区别?

    or in .Object.keys()以及Object.getOwnPropertyNames的区别 var obj= Object.create(parent, { b: { value: 2, ...

  9. HDU——T 1556 Color the ball

    http://acm.hdu.edu.cn/showproblem.php?pid=1556 Time Limit: 9000/3000 MS (Java/Others)    Memory Limi ...

  10. hdu1856 More is better (并查集)

    More is better Time Limit: 5000/1000 MS (Java/Others)    Memory Limit: 327680/102400 K (Java/Others) ...