调用托管代码和非托管代码之间以及反之






4.30/5 (31投票s)
2005年3月21日
3分钟阅读

323236

4633
本文介绍如何从非托管代码调用托管代码,以及如何反过来调用。
引言
.NET framework 是当今软件开发和执行的更好的环境之一。但是,已经开发和正在开发的非托管代码软件组件数量非常庞大。因此,需要有一种简单的方法让托管代码调用非托管代码,反之亦然。
我看到过一些关于此的文章,但我没有发现它们提供了我正在寻找的完整解决方案。所以这里有一个。
背景
Microsoft 允许您使用 RCW (Runtime Callable Wrappers) 从 .NET 代码调用 COM 代码。 RCW 是一个托管的包装代码,它包装了 COM 组件。.NET 代码然后与 RCW 交互,而 RCW 又与其中的 COM 组件交互。可以使用 CCW(COM 可调用包装器)完成反向通信。
本文展示了一种手动创建包装器的方法。从托管代码调用非托管代码相当容易,但反之则不然。
代码
我在下面指定的代码包括
- 非托管类:
UnManaged_Class
- 托管包装器类:
Managed_Wrapper_Class
此类包装非托管类。这意味着它“包含”非托管类型的对象,它使用该对象来调用非托管类型中公开的方法。
- 托管代码:
Managed_Class
这就是托管代码如何调用非托管代码
对于非托管类中的每个公开方法,Managed_Wrapper_Class
中都应该有一个对应的方法。托管代码实例化 Managed_Wrapper_Class
的一个对象,并使用此实例调用该类中公开的方法。然后,Managed_Wrapper_Class
中的这些方法调用非托管代码中的对应方法。这是使用代码中显示的 pInner
完成的
//
/*//////////////////////////////////////////////////
//Unmanaged_Class.cpp
//////////////////////////////////////////////////*/
#ifndef UNMANAGED
#define UNMANAGED
class Unmanaged_Class
{
public:
Unmanaged_Class();
/*This is the method that is to be called from Managed code*/
void methodToBeCalledInUnmanaged(int data);
};
#endif
*//////////////////////////////////////////////////
//Unmanaged_Class.cpp
//////////////////////////////////////////////////*/
#include "StdAfx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
Unmanaged_Class::Unmanaged_Class()
{
}
void Unmanaged_Class::methodToBeCalledInUnmanaged(int data)
{
/*Here is the place where the Managed Wrapper code is called. */
scallback(data+1);
}
/*//////////////////////////////////////////////////
//Managed_Wrapper.h
//////////////////////////////////////////////////*/
#pragma once
#include "stdafx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
using namespace System::Runtime::InteropServices;
using namespace System;
namespace Managed_Wrapper
{
/*Managed Wrapper Class */
public __gc class Managed_Wrapper_Class
{
public:
//constructor
Managed_Wrapper_Class();
/* pInner is used to invoke the exposed
methods in the unmanaged class. */
Unmanaged_Class * pInner;
/* An exposed function corresponding
to the exopsed function in Unmanaged*/
void CallUnmanaged(int data);
};
}
/*//////////////////////////////////////////////////
//Managed_Wrapper.cpp
//////////////////////////////////////////////////*/
#include "stdafx.h"
#include "Managed_Wrapper.h"
#using <mscorlib.dll>
namespace Managed_Wrapper
{
Managed_Wrapper_Class::Managed_Wrapper_Class(void)
{
/* define the pInner object */
pInner = new Unmanaged_Class();
}
void Managed_Wrapper_Class::CallUnmanaged(int data)
{
pInner->methodToBeCalledInUnmanaged (data);
}
}
'/*//////////////////////////////////////////////////
'//Managed Code
'//VB.NET code
'//////////////////////////////////////////////////*/
'Import the Managed Wrapper Namespace
Imports Managed_Wrapper
'Create an instance of the Managed Wrapper class.
Dim forwardCaller As Managed_Wrapper_Class = New Managed_Wrapper_Class
'To call a method in the Managed_Wrapper_Class. This method in
'turn will call the method in the unmanaged code
forwardCaller.CallUnmanaged(nudNumber.Value)
这是容易的部分。现在是困难的部分。从非托管代码调用托管代码。
非托管代码具有一个函数指针。该函数指针的地址是托管包装器类中方法的地址。此外,函数指针由包装器类初始化,而不是在非托管代码中初始化。因此,通过这种方式,当调用函数指针时,会调用托管包装器代码中的方法。任务完成了一半。在非托管代码中分配函数指针的方式并不容易,因为该函数必须指向一个托管的方法。因此,我们使用代码中显示的包装器委托 struct
。然后使用 Marshal::StructureToPtr (_Instance_Of_Delegate_Wrapper, &type_unmanaged_functionptr, false);
将此委托 struct
转换为非托管类型的函数指针。
托管包装器代码声明一个委托(函数指针的 .NET 方法)。该委托由托管代码实例化。因此,当调用托管包装器类中的方法(由非托管代码调用)时,它会反过来调用同一类中的委托(由托管代码初始化)。由于委托指向托管代码中的函数,因此调用托管代码中的方法。这就是困难的部分。
/*///////////////////////////////////////////////////
/*Unmanaged_Class.h */
///////////////////////////////////////////////////*/
#ifndef UNMANAGED
#define UNMANAGED
#using <mscorlib.dll>
typedef void (*w_CallBack) (int status);
class Unmanaged_Class
{
public:
Unmanaged_Class();
w_CallBack scallback;
/* To set the callback function. The address in ptr2F will be the
address of a method in the Managed Wrapper class and will be assigned
there. In this case it will be address of ActualMethodInWrapper(int );*/
void setCallBackInUnmanaged(w_CallBack ptr2F);
/*This is the method that is to be called from Managed code*/
void methodToBeCalledInUnmanaged(int data);
};
#endif
/*///////////////////////////////////////////////////
//Unmanaged_Class.cpp
///////////////////////////////////////////////////*/
#include "StdAfx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
Unmanaged_Class::Unmanaged_Class()
{
}
void Unmanaged_Class::setCallBackInUnmanaged(w_CallBack ptr2F)
{
/*scallback now points to ActualMethodInWrapper(int) in
Managed_Wrapper_Class*/
scallback = ptr2F;
}
void Unmanaged_Class::methodToBeCalledInUnmanaged(int data)
{
/*Here is the place where the Managed Wrapper code is called. */
scallback(data+1);
}
/*///////////////////////////////////////////////////
//Managed_Wrapper.h
///////////////////////////////////////////////////*/
#pragma once
#include "stdafx.h"
#using <mscorlib.dll>
#include "Unmanaged.h"
using namespace System::Runtime::InteropServices;
using namespace System;
namespace Managed_Wrapper
{
/*Declare a delegate. It is to be invoked from the unmanaged code*/
public __delegate void CallbackDelegate(int data);
/* Declare a wrapping struct that wraps an object of the above
declared delegate. The delegate that this struct contains will
point to a method that will be called when this delegate is
invoked from the unmanaged code*/
[StructLayoutAttribute( LayoutKind::Sequential, CharSet = CharSet::Ansi )]
public __gc struct Managed_Delegate_Wrapper
{
[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
CallbackDelegate* _Delegate;
};
/*Managed Wrapper Class */
public __gc class Managed_Wrapper_Class
{
public:
//constructor
Managed_Wrapper_Class();
/* pInner is used to invoke the exposed methods in the unmanaged class. */
Unmanaged_Class * pInner;
/* Declare an instance of the wrapping struct */
Managed_Delegate_Wrapper * _Status_Delegate;
/* A method that will be called when the callback function in the unmanaged
code is called. It is this method who’s pointer is passed to the unmanaged
code.*/
void ActualMethodInWrapper(int );
/*A delegate type. To be used for calling managed code from here.*/
__delegate int statusDelegate(int status);
/*An object of the above delagate type is declared.
It will be initialized in the managed code.*/
statusDelegate *statD;
/* An exposed function corresponding to the exopsed function in Unmanaged*/
void CallUnmanaged(int data);
};
}
/*///////////////////////////////////////////////////
//Managed_Wrapper.cpp
///////////////////////////////////////////////////*/
#include "stdafx.h"
#include "Managed_Wrapper.h"
#using <mscorlib.dll>
namespace Managed_Wrapper
{
Managed_Wrapper_Class::Managed_Wrapper_Class(void)
{
/* define the pInner object */
pInner = new Unmanaged_Class();
/* define the wrapping struct instance declared in Managed_Wrapper_Class */
_Status_Delegate = new Managed_Delegate_Wrapper();
/* This is the actual delegate that is contained in the wraping struct */
_Status_Delegate->_Delegate =
new CallbackDelegate(this, &Managed_Wrapper_Class::ActualMethodInWrapper);
/* declare a function pointer of the same type as in unmanaged code */
w_CallBack callback;
/*convert the wrapping struct to a function pointer of the above type. */
Marshal::StructureToPtr (_Status_Delegate, &callback, false);
/* set this function pointer in the unmanaged code using pInner.*/
pInner->setCallBackInUnmanaged(callback);
}
/*This is the method in the Managed_Wrapper_Class that is called
when the function pointer in the unmanaged code is called.*/
void Managed_Wrapper_Class::ActualMethodInWrapper(int status)
{
/*This method in turn calls the delegate in the managed code.
The method that statD is actually poiting is specified in the
managed code itself.*/
statD(status);
}
void Managed_Wrapper_Class::CallUnmanaged(int data)
{
pInner->methodToBeCalledInUnmanaged (data);
}
}
'/*//////////////////////////////////////////////////
'//Managed Code
'//VB.NET code
'//////////////////////////////////////////////////*/
'Import the Managed Wrapper Namespace
Imports Managed_Wrapper
'Create an instance of the Managed Wrapper class.
Dim forwardCaller As Managed_Wrapper_Class = New Managed_Wrapper_Class
'Create an instance of the delegate declared in the Managed Wrapper
'class. Initialize it with the address of the method that is supposed
'to be called when the delegate in the Managed_Wrapper_Class is called
Dim statDelg As New Managed_Wrapper_Class.statusDelegate(AddressOf status_Recd)
'This function gets called when the unmanaged code calls the
'managed wrapper code and which in turn calls the the delegate
'in there
Public Function status_Recd(ByVal status As Integer) As Integer
'use status for something now. It took so much effort to get it :)
MessageBox.Show("Status received is " + status.ToString(), "Status" +
" received from the Unmanaged code")
End Function
'To call a method in the Managed_Wrapper_Class. This method in
'turn will call the method in the unmanaged code
forwardCaller.CallUnmanaged(nudNumber.Value)
'statD is called from the managed code. And statD in turn
'will call the method status_Recd
forwardCaller.statD = statDelg
编译和链接
选择一个 C++ .NET 类库项目来包装您的非托管代码,并编译它以生成 DLL。然后在您的 VB.NET 代码中,使用“添加引用”->“项目”(浏览到 DLL)向此 DLL 添加引用。此外,您还需要将您的项目属性设置为顶部演示项目中的属性。
演示
打开 Managed_VBdotNET.sln 解决方案并启动它。成功。
摘要
我发现这种技术对于我的一个项目特别有用,在这个项目中,我有一些已经用 C++ 编写的代码,用 C++ 编写是个好主意。我需要向其中添加 GUI,为此 VB.NET 是一个非常直接的选择。通过这种方式,我可以分别通过 VB.NET 调用 C++ 方法,并通过 C++ 调用 VB.NET 方法。
欢迎并感谢任何建议。请随时提出任何问题。