在.NET开发中,经常面临选择使用C#还是C++的问题。C#以其简洁易用而广受欢迎,但C++在性能和底层控制方面具有优势。本文将探讨如何将C++的强大框架ACE与.NET C++/CLI结合,实现混合模式编程。
在开始之前,需要确保已经按照ACE的文档编译了ACE框架,并了解如何设置项目的包含路径和库路径,以及如何设置混合模式C++/CLI项目。
首先,需要下载ACE框架。可以通过以下链接下载:
接下来,将创建一个Visual C++ Windows Forms应用程序。为了使用ACE框架,需要在stdafx.h中添加一些头文件。
#ifndef STRICT
#define STRICT
#endif
#pragma managed(push,off)
#include <sdkddkver.h>
#include "ace/Log_Msg.h"
#include "ace/Svc_Handler.h"
#include "ace/Method_Request.h"
#include "ace/Activation_Queue.h"
#include "ace/Future.h"
#include <vector>
#include <string>
#pragma managed(pop)
在ACEDotNetDemo.cpp文件中,需要在main方法之前添加一些代码,以告知链接器需要链接到ace.lib或aced.lib。
#pragma managed(push,off)
#ifndef _DEBUG
#pragma comment(lib,"ace")
#else
#pragma comment(lib,"aced")
#endif
#pragma managed(pop)
main方法相对标准,只需要调用ACE::init()和ACE::fini()来初始化和结束框架。
[STAThreadAttribute]
int main(array<System::String^>^ args) {
int result = ACE::init();
if (result >= 0) {
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
Application::Run(gcnew MainForm());
ACE::fini();
}
return result;
}
在ACEDotNetDemoTask.hpp中,声明了一个非常简单的类ACEDotNetDemoTask,它继承自ACE_Task_Base。这是线程实现,svc方法将在另一个线程中执行。
#pragma once
#pragma managed(push,off)
typedef ACE_Future<int> IntFuture;
class ACEDotNetDemoTask : public ACE_Task_Base {
ACE_Activation_Queue activation_queue_;
public:
ACEDotNetDemoTask(void);
~ACEDotNetDemoTask(void);
virtual int svc(void);
int enqueue(ACE_Method_Request *request);
IntFuture call_exit();
};
typedef ACE_Singleton<ACEDotNetDemoTask, ACE_Null_Mutex> ACEDOTNETDEMOTASK;
class ExitMethodRequest : public ACE_Method_Request {
IntFuture result_;
public:
ExitMethodRequest(IntFuture& result) : result_(result) {
ACE_TRACE("ExitMethodRequest::ExitMethodRequest");
}
virtual ~ExitMethodRequest() {
ACE_TRACE("ExitMethodRequest::~ExitMethodRequest");
}
virtual int call(void) {
ACE_TRACE("ExitMethodRequest::call");
int result = -1;
result_.set(result);
return result;
}
};
#pragma managed(pop)
使用ACE_Singleton声明了一个单例ACEDOTNETDEMOTASK,用于ACEDotNetDemoTask。ACEDotNetDemoTask.cpp包含ACEDotNetDemoTask的实现。
svc方法从激活队列中出列方法请求,并调用出列请求的call方法,直到call方法返回-1,这与标准Windows消息循环非常相似。
int ACEDotNetDemoTask::svc(void) {
ACE_TRACE("ACEDotNetDemoTask::svc");
while (1) {
auto_ptr<ACE_Method_Request> request(this->activation_queue_.dequeue());
if (request->call() == -1) {
break;
}
}
return 0;
}
enqueue方法将请求入列到激活队列中,由svc方法进行处理:
int ACEDotNetDemoTask::enqueue(ACE_Method_Request *request) {
ACE_TRACE("ACEDotNetDemoTask::enqueue");
return this->activation_queue_.enqueue(request);
}
call_exit方法将ExitMethodRequest入列到激活队列中,并返回一个IntFuture,允许调用者从非托管线程中获取结果。
IntFuture ACEDotNetDemoTask::call_exit() {
ACE_TRACE("ACEDotNetDemoTask::call_exit");
IntFuture result;
ExitMethodRequest *request = new ExitMethodRequest(result);
enqueue(request);
return result;
}
这将处理基于ACE_Task_Base的非托管线程实现。
从托管C++/CLI代码与非托管线程进行交互非常简单。
protected:
virtual void OnShown(EventArgs^ e) override {
System::Windows::Forms::Form::OnShown(e);
// 启动非托管线程
ACEDOTNETDEMOTASK::instance()->activate();
}
virtual void OnFormClosing(FormClosingEventArgs^ e) override {
// 调用非托管线程,并告诉它退出
IntFuture futureResult = ACEDOTNETDEMOTASK::instance()->call_exit();
// 并获取结果
int result = 0;
futureResult.get(result);
// 等待非托管线程退出
ACEDOTNETDEMOTASK::instance()->wait();
System::Windows::Forms::MessageBox::Show(
String::Format(L"Exit Result:{0}", result),
L"Call Exit"
);
System::Windows::Forms::Form::OnFormClosing(e);
}
毫无疑问,许多人已经知道使用混合模式C++/CLI集成托管和非托管代码的效果非常好。但想有很多人像一样,认为这是一个诱人的想法,甚至可能,但从未真正去验证它。
实现的是一个简单的非托管代码中的活动对象;并成功地从托管代码中与之交互。