Calling C++ code from C# z

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,布布扣,bubuko.com

时间: 2024-10-14 04:22:00

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

Object类的源码分析

JDK 1.8中Object 的源码如下: 1 public class Object { 2 3 private static native void registerNatives(); 4 static { 5 registerNatives(); 6 } 7 8 /** 9 * Returns the runtime class of this {@code Object}. The returned 10 * {@code Class} object is the object tha

Javascript中的delete

一.问题的提出 我们先来看看下面几段代码,要注意的是,以下代码不要在浏览器的开发者工具(如FireBug.Chrome Developer tool)中运行,原因后面会说明: 为什么我们可以删除对象的属性: 复制代码代码如下: var o = { x: 1 }; delete o.x; // true o.x; // undefined 但不以删除像这样声明的变量: 复制代码代码如下: var x = 1; delete x; // false x; // 1 也不能删除像这样定义的函数: 复制

java Object类学习

/* * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package java.lang; /** * Class {@code Object} is the root

Java中hashcode,equals和==

hashcode方法返回该对象的哈希码值. hashCode()方法可以用来来提高Map里面的搜索效率的,Map会根据不同的hashCode()来放在不同的位置,Map在搜索一个对象的时候先通过hashCode()找到相应的位置,然后再根据equals()方法判断这个位置上的对象与当前要插入的对象是不是同一个.若两个对象equals相等,但不在一个区间,根本没有机会进行比较,会被认为是不同的对象. 所以,Java对于eqauls方法和hashCode方法是这样规定的: 1.如果两个对象相同,那么

Android调用JNI本地方法经过有点改变

方法注册好后要经过哪些路 Android一个异常捕获项目 https://github.com/xroche/coffeecatch coffeecatch CoffeeCatch, a tiny native POSIX signal catcher (especially useful for JNI code on Android/Dalvik, but it can be used in non-Java projects) It allows to "gracefully"

Git手册

GitUserManualChinese - Robin Wiki GitUserManualChinese Git 用户手册(1.5.3 及后续版本适用) 翻译: 罗峥嵘 (Robin Steven) < [email protected] > 英文版本: http://www.kernel.org/pub/software/scm/git/docs/user-manual.html Contents Preface 前言 Chapter 1. Repositories and Branch

JavaScript delete用法,属性,特性,执行上下文,激活对象 综合篇

一.问题的提出 我们先来看看下面几段代码,要注意的是,以下代码不要在浏览器的开发者工具(如FireBug.Chrome Developer tool)中运行,原因后面会说明: 为什么我们可以删除对象的属性: var o = { x: 1 }; delete o.x; // true o.x; // undefined 但不以删除像这样声明的变量: var x = 1; delete x; // false x; // 1 也不能删除像这样定义的函数: function x(){} delete

java.lang.String 类源码解读

String类定义实现了java.io.Serializable, Comparable<String>, CharSequence 三个接口:并且为final修饰. public final class String defined String由char[]数组实现 /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the strin

Java基础1----Object类解析

1.Object简介 众所周知,Object类是Java所有类的万类之源,所有Java类都是继承之Object类,而默认就直接忽略了extends Object这段代码. 2.Object类的源码 话不多说,源码先贴为敬,源码如下: 1 package java.lang; 2 3 public class Object { 4 //本地方法,通过JNI调用 5 private static native void registerNatives(); 6 static {//对象初始化时调用