通过C/C++LI包装器调用C / Qt类就像在公园散步一样.
但我坚持将C / Qt信号映射到C#事件.
我试图结合一些可用的how-tos / answers但没有得到任何工作结果:
> How to map Qt Signal to Event in Managed C++ (C++/CLI)
> Calling managed code from unmanaged code and vice-versa
>和其他一些不直接相关的……
这里的问题是,这些方法/答案是相当陈旧的.我目前正在使用Qt5.5(很快5.6)和.NET 4.6.我试图使一切都适应当前的技术水平但可能失败了.
可能是因为树木太多而无法看到森林,所以我想问一个能够用当前框架版本完成任务的工作示例,这样我就可以发现差异并从错误中吸取教训.
[编辑]
您可以查看此github仓库,以获取此来源. QtSignal到C#事件的部分已注释掉,以使此代码处于工作状态.
Github回购:https://github.com/qwc/QtSignalToCSharpEvent
对于那些仍然想要阅读所有内容而不玩游戏的人…继续阅读……
这是我目前的非工作代码:
所以我有一个纯Qt5课程
#ifndef PUREQT_H
#define PUREQT_H
#include <QObject>
#include <QString>
#include "PureQt_global.h"
class PUREQTSHARED_EXPORT PureQt : public QObject {
Q_OBJECT
public:
PureQt(QString name);
~PureQt(){}
QString getSomeVar();
void someFunc(const QString & string);
public slots:
signals:
void someFuncWasCalled(const QString &string);
private:
QString someVar;
};
#endif // PUREQT_H
通过以下相当简单的实现:
#include "PureQt.h"
PureQt::PureQt(QString name) {
this->someVar = "ctor("+name+")";
}
QString PureQt::getSomeVar() {
return this->someVar;
}
void PureQt::someFunc(const QString &string) {
this->someVar += "someFunc("+string+")";
emit someFuncWasCalled(this->someVar);
}
然后我用C/C++LI实现了一个托管包装器,以便能够从C#调用非托管代码.请注意,我已经尝试添加代码以获取事件管理信号.
#pragma once
#include "conversion.h"
#include "../pureqt/PureQt.h"
#include "SignalProxy.h"
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
namespace ManagedCppQtSpace {
// different variants... from tinkering around.
delegate void someFuncWasCalled(String^);
delegate void someFuncWasCalledU(QString str);
[StructLayoutAttribute(LayoutKind::Sequential)]
public ref struct DelegateWrapper {
[MarshalAsAttribute(UnmanagedType::FunctionPtr)]
someFuncWasCalledU^ delegate;
};
public ref class ManagedCppQt
{
public:
ManagedCppQt(String^ name){
pureQtObject = new PureQt(StringToQString(name));
proxy = new SignalProxy(pureQtObject);
wrapper = gcnew DelegateWrapper();
wrapper->delegate = gcnew someFuncWasCalledU(this, ManagedCppQt::signalCallback);
signalCallbackProxy callbackproxy;
Marshal::StructureToPtr(wrapper, callbackproxy, false); // currently im stuck here with a compile error, but the problem may lie somewhere else...
proxy->setCallback(callbackproxy);
};
~ManagedCppQt(){
delete pureQtObject;
};
event someFuncWasCalled ^ someFuncCalled;
void someFunc(String^ string){
pureQtObject->someFunc(StringToQString(string));
};
String^ getSomeString() {
return QStringToString(pureQtObject->getSomeVar());
}
void signalCallback(QString str) {
someFuncCalled(QStringToString(str));
}
DelegateWrapper ^ wrapper;
private:
PureQt * pureQtObject;
SignalProxy * proxy;
};
}
因此,为了将Qt中的信号和槽处理与能够在托管代码中引发事件的回调链接,当没有更改基本代码的选项时,某些将需要代理类(因为它也用于其他非托管C项目).
#ifndef SIGNALPROXY_H
#define SIGNALPROXY_H
#include <QObject>
#include "../pureqt/PureQt.h"
typedef void (*signalCallbackProxy) (QString str);
class SignalProxy : public QObject
{
Q_OBJECT
public:
explicit SignalProxy(PureQt* pqt);
~SignalProxy();
void setCallback(signalCallbackProxy callback);
signals:
public slots:
void someFuncSlot(QString str);
private:
PureQt* pureQt;
signalCallbackProxy scallback;
};
#endif // SIGNALPROXY_H
实施:
#include "SignalProxy.h"
SignalProxy::SignalProxy(PureQt* pqt){
pureQt = pqt;
this->connect(pureQt, SIGNAL(PureQt::someFuncWasCalled(QString)), this, SLOT(someFuncSlot(QString)));
}
SignalProxy::~SignalProxy()
{}
void SignalProxy::setCallback(signalCallbackProxy callback){
this->scallback = callback;
}
void SignalProxy::someFuncSlot(QString str){
if(this->scallback != NULL)
this->scallback(str);
}
所以.现在,如何正确地链接这两个世界,Qt信号 – >管理.NET事件?
我也尝试了一些简单的方法,这会导致编译错误,例如:
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, &MangagedCppQtSpace::ManagedCppQt::signalCallback);
而不是代理类,或使用lambda函数:
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled, [] (QString str) {
signalCallback(str);// or ManagedCppQt::signalCallback, but for this the method has to be static, and it isn't possible to raise events from static methods...
}
最佳答案 这里的问题是将Qt与C CLI混合.要获得功能信号和插槽,您需要处理头文件以生成自己的元数据.问题是该工具将无法理解C CLI功能.
要首先解决此问题,您必须回退到C接口并执行安全的C CLI操作.
所以你需要这样的额外类,它不知道.net并创建标准C的桥梁:
class PureQtReceiverDelegate { // this should go to separate header file
virtual void NotifySomeFunc(const char *utf8) = 0;
};
class PureQtReceiver : public QObject {
Q_OBJECT
public:
PureQtReceiver(PureQtReceiverDelegate *delegate, PureQt *parent)
: QObject(parent)
, mDelegate(delegate)
{
bool ok = connect(parent, SIGNAL(PureQt::someFuncWasCalled(QString)),
this, SLOT(someFuncReceiver(QString)));
Q_ASSERT(ok);
}
public slots:
void someFuncReceiver(const QString & string)
{
delegate->NotifySomeFunc(string.toUtf8().data());
}
private:
PureQtReceiverDelegate *delegate;
};
现在你的C CLI类应该实现这个PureQtReceiverDelegate并将字符串转换为.net版本并发布通知.
请注意,您可以/应该在C CLI头文件中转发声明Qt特定的类.
如果您使用Qt4,请不要使用C 11,以上解决方案是好的.
如果你使用Qt 5并且有可用的C 11,那么有更方便的解决方案:你可以在连接到QObject时使用lambda表达式.所以你的ManagedCppQt看起来像这样:
标题:
#pragma once
#include "conversion.h"
using namespace System;
using namespace System::Collections::Generic;
using namespace System::Runtime::InteropServices;
// forward declaration
class PureQt;
namespace ManagedCppQtSpace {
delegate void someFuncWasCalled(String^);
public ref class ManagedCppQt
{
public:
ManagedCppQt(String^ name);
~ManagedCppQt();
event someFuncWasCalled ^ someFuncCalled;
void someFunc(String^ string);
String^ getSomeString();
void signalCallback(QString str);
private:
PureQt * pureQtObject;
};
}
在cpp:
#include "../pureqt/PureQt.h"
using namespace ManagedCppQtSpace;
ManagedCppQt:ManagedCppQt(String^ name) {
pureQtObject = new PureQt(QStringFromString(name));
QObject::connect(pureQtObject, &PureQt::someFuncWasCalled,
[this](const QString &string){
if (this->someFuncCalled) {
String^ s = StringFromQString(string);
this->someFuncCalled(s);
}
});
}
ManagedCppQt::~ManagedCppQt(){
delete pureQtObject;
}
这更容易,更快速,更容易维护.