http://blogs.msdn.com/b/borisj/archive/2006/09/28/769708.aspx

I apologize for the long delay for this section (although I suppose my average posting frequency is already pretty low), but I was on a much needed vacation. I finished the last chapter with a brief mention of what I would talk about now, which is the native support for interop that C++ provides. In a sense, I hope this is going to appear to be the simplest method even though I will introduce a few new concepts and use C++/CLI, which adds new language constructs to C++ in order to express .NET semantics (e.g. garbage collected types).

As always, let us reprise our original HelloWorld example. I'm going to include it again for sake of making this post depend as little as possible on the previous ones.

// HelloWorld.h

#pragma once

class __declspec(dllexport) HelloWorld

{

public:

HelloWorld();

~HelloWorld();

void SayThis(wchar_t *phrase);

};

// HelloWorld.cpp

void HelloWorld::SayThis(wchar_t *phrase)

{

MessageBox(NULL, phrase, L"Hello World Says", MB_OK);

}

Our goal is to access this type from .NET. As it stands, this piece of code already compiles into a native DLL. The question that stands before us first is what clients will access this code from now on. In other words, are we replacing all existing client code of this DLL with managed code or are we going to maintain some purely native clients. In the first case, we can write our wrapper code directly into the DLL and compile it into a managed assembly (with native code backing it). In the second case, we need to create a second DLL that will be a native client to this one while publishing a managed interface for .NET clients. It is the latter case that we are going to jump into now.

The first thing to do is to create a new CLR project, which we can do with a wizard (look under the Visual C++ > CLR node in the New Project dialog) or simply taking a blank slate and making the project compile with the /clr switch. This switch is the cornerstone of this entire scenario. If you remember the first part in this series, we showed how the C++ compiler is able to generate MSIL and furthermore, it can generate a process image with both a managed and a native section (the only compiler capable of doing so I might add). We have yet to really lay down the bricks for our wrapper so let's make a naïve wrapper for HelloWorld now.

// cppcliwrapper.h

#pragma once

#include "..\interop101\helloworld.h"

namespace cppcliwrapper {

class ManagedHelloWorld

{

private:

HelloWorld hw;

public:

ManagedHelloWorld();

~ManagedHelloWorld();

void SayThis(wchar_t *phrase);

};

}

This piece of code is a native wrapper around our native type using traditional OO encapsulation. Even though this piece of code will compile into MSIL, it does not solve our original problem. Why is that? It's because we're still dealing with a native type. In other words, the ManagedHelloWorld class still obeys the rules of native semantics, namely the fact that it must live on the native heap. Managed languages like C# have no knowledge of the native heap and their new operator only instantiates objects into the CLR's heap. We need to make this wrapper a managed type, which will have the same semantics as a class in C#. Enter C++/CLI. With these additions to the language, we can create two new types of classes: managed value and reference types (the difference is mainly in the way they are implicitly copied). For our wrapper, we simply need to change its declaration from class to ref class. Once we compile the resulting code, we get a pivotal error.

error C4368: cannot define 'hw' as a member of managed 'ManagedHelloWorld': mixed types are not supported

What could this possibly mean? This error is actually directly related to the problem we described just above. In order to be a proper managed reference type that C# and other managed languages can instantiate, we cannot encapsulate native members. Indeed, our wrapper cannot live on the CLR's managed heap as it contains a member that can only live on the native heap. We can resolve this issue by encapsulating a pointer to our native type. Thus we have the following wrapper code.

ref class ManagedHelloWorld

{

private:

HelloWorld *hw;

public:

ManagedHelloWorld();

~ManagedHelloWorld();

void SayThis(wchar_t *phrase);

};

Only three things remain in order to make this wrapper usable. The first is to make it public in accordance with .NET accessibility rules. The second is to change the interface of SayThis such that it uses a managed string. The third is to include the implementation! So here it goes.

// cppcliwrapper.cpp

#include "cppcliwrapper.h"

#include "marshal.h"

using namespace cppcliwrapper;

ManagedHelloWorld::ManagedHelloWorld() : hw(new HelloWorld())

{

}

ManagedHelloWorld::~ManagedHelloWorld()

{

delete hw;

}

void ManagedHelloWorld::SayThis(System::String^ phrase)

{

hw->SayThis(marshal::to<wchar_t*>(phrase));

}

There are two notable elements we have introduced in this final piece code, the managed handle and data marshalling. The handle or "hat" (or "accent circonflexe" even) is part of the C++/CLI language. It represents a pointer to a managed object. Other languages like Java, C# and VB don't use anything like this as they no longer have native semantics. However C++ needs to differentiate between the stack, the native heap and the managed heap and it does so using * and ^. Data marshalling is a huge topic and can eventually become one of the more complex things you have to manage when working with interop. In this example, we need to convert a managed String into a native pointer to wchar_t. In order to do this, a great pattern is to create a library of static template functions, which thus remain stateless and help maintain a certain level of consistency. In this example, we created the following functions:

namespace marshal {

template <typename T>

static T to(System::String^ str)

{

}

template<>

static wchar_t* to(System::String^ str)

{

pin_ptr<const wchar_t> cpwc = PtrToStringChars(str);

int len = str->Length + 1;

wchar_t* pwc = new wchar_t[len];

wcscpy_s(pwc, len, cpwc);

return pwc;

}

}

After this is all said and done, we compile our code into an assembly that 3rd party .NET clients can use as if it were written in C#. So here is our resulting client code, which is eerily similar to the COM example.

using System;

using System.Text;

using cppcliwrapper;

namespace CSharpDirectCaller

{

class Program

{

static void Main(string[] args)

{

ManagedHelloWorld mhw = new ManagedHelloWorld();

mhw.SayThis("I'm a C# application calling native code via C++ interop!");

}

}

}

I have a lot more to say about this, and I promised a performance comparison as well as a 5th part describing doing this in reverse. My next post should not be so long in the making.

Calling C++ code from C# z的更多相关文章

  1. 804. Unique Morse Code Words

    Description International Morse Code defines a standard encoding where each letter is mapped to a se ...

  2. Javascript中的delete

    一.问题的提出 我们先来看看下面几段代码,要注意的是,以下代码不要在浏览器的开发者工具(如FireBug.Chrome Developer tool)中运行,原因后面会说明: 为什么我们可以删除对象的 ...

  3. Object窥探

    /* * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETA ...

  4. Java中hashcode,equals和==

    hashcode方法返回该对象的哈希码值. hashCode()方法可以用来来提高Map里面的搜索效率的,Map会根据不同的hashCode()来放在不同的位置,Map在搜索一个对象的时候先通过has ...

  5. java Object类学习

    /* * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETA ...

  6. (转) Functions

    Functions Functions allow to structure programs in segments of code to perform individual tasks. In ...

  7. Julia is a high-level, high-performance dynamic programming language for technical computing, with syntax that is familiar to users of other technical

    http://julialang.org/ julia | source | downloads | docs | blog | community | teaching | publications ...

  8. The Go Programming Language. Notes.

    Contents Tutorial Hello, World Command-Line Arguments Finding Duplicate Lines A Web Server Loose End ...

  9. 简单读!zookeeper单机模式的启动逻辑

    zk用处如此之多,以至于每个地方都要你理解zk原理! 请按如下操作姿势打开: 1. 打开zk的git仓库地址:https://github.com/apache/zookeeper , 确认过眼神,它 ...

随机推荐

  1. 淘宝(taobao)HSF框架

    一.背景 随着网站访问量增加,仅仅靠增加机器已不能满足系统的要求,于是需要对应用系统进行垂直拆分和水平拆分.在拆分之后,各个被拆分的模块如何通信?如何保证 性能?如何保证各个应用都以同样的方式交互?这 ...

  2. js call apply bind简单的理解

    相同点:JS中call与apply方法可以改变某个函数执行的上下文环境,也就是可以改变函数内this的指向.区别:call与apply方法的参数中,第一个参数都是指定的上下文环境或者指定的对象,而ca ...

  3. 【mysql的编程专题④】存储过程

    类似函数,但是没有返回值,把sql进行封装,便于多次使用或多种应用程序共享使用.不能用在SQL语句中,只能使用CALL调用; 创建存储过程 语法 CREATE PROCEDURE sp_name ([ ...

  4. JavaWeb项目开发案例精粹-第2章投票系统-003Dao层

    1. package com.sanqing.dao; import java.util.List; import com.sanqing.bean.Vote; import com.sanqing. ...

  5. 电容值E系列标称方法

    本节首先介绍常用的E系列标称方法,然后介绍电阻.电容器.电感器.二极管的分类.性能和识别方法,以及简单的实用电路. 一.E系列标称方法 厂家生产的电阻器,并不是包含任何阻值,就像人民币,只有1.2.5 ...

  6. *windows文件显示后缀名

  7. 关于PHP 7你必须知道的五件事

    1.今年的计划表已出.PHP 7时间表RFC投票一直通过, PHP 7将在2015年10月发布.尽管有些延迟,但我们还是很高兴它在今年内发布.PHP 7详细时间表由此查看. 2.PHP 要上太空飞船了 ...

  8. Android权限安全(11)内置计费相关安全要点

    内置计费相关安全要点 1.计费Server接口保密且Transiction 加密 (SSL) 2.仅允许配套的安全本地组件(通常是第三方付费sdk如支付宝)与计费Server通信,且安全本地组件负责与 ...

  9. Android相对布局(RelativeLayout)

    Android相对布局(RelativeLayout) 备注:这里的视图和元素是等同的概念. RelativeLayout是一个允许子视图相对于其他兄弟视图或是父视图显示的视图组(通过ID指定).每个 ...

  10. Android用户界面布局(layouts)

    Android用户界面布局(layouts) 备注:view理解为视图 一个布局定义了用户界面的可视结构,比如activity的UI或是APP widget的UI,我们可以用下面两种方式来声明布局: ...