【问题标题】:FMX System messaging to send a pointer(?)FMX 系统消息发送指针(?)
【发布时间】:2023-04-04 01:11:01
【问题描述】:

我正在使用 C++Builder 10.2。

在 Android 中,我想从各种线程(包括主线程)向主 GUI 线程发送消息。在 Windows 中,我可以发布消息并将 LPARAM 或 WPARAM 分配给结构或类的某个实例的地址。

我正在尝试使用System.Messaging.TMessageManager 来做同样的事情,类似于这里的示例:System.Messaging (C++)。但我只能发送“简单”类型,例如UnicodeStringint。我还没有弄清楚如何发送指针,假设它甚至是可能的。

我想发送一个这样的结构/类实例:

class TSendResult
{
public:
    String Message;
    unsigned int Value;
    int Errno;

    __fastcall TSendResult(void);
    __fastcall ~TSendResult();
};

如果可以,我该怎么写?我设法得到一个版本来编译,但得到一个链接器错误:

错误:未定义对'vtable for System::Messaging::TMessage__1'的引用

表单构造函数:

__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    TMessageManager* MessageManager = TMessageManager::DefaultManager;
    TMetaClass* MessageClass = __classid(TMessage__1<TSendResult>);
    TMessageListenerMethod ShowReceivedMessagePointer = &(this->MMReceiveAndCallBack);
    MessageManager->SubscribeToMessage(MessageClass, ShowReceivedMessagePointer);
}

按钮点击处理程序:

void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
    ...

    TSendResult *SPtr = new TSendResult();
    SPtr->Message = "All good";
    SPtr->Value = 10;
    SPtr->Errno = 0;
    TMessageManager* MessageManager = TMessageManager::DefaultManager;
    TMessage__1<TSendResult>* Message = new TMessage__1<TSendResult>(*SPtr); // <-- this doesn't look right...
    MessageManager->SendMessage(Sender, Message, false);
}

捕获消息的函数:

void __fastcall TForm1::MMReceiveAndCallBack(System::TObject* const Sender,
        System::Messaging::TMessageBase* const M)
{
    TMessage__1<TSendResult>* Message = dynamic_cast<TMessage__1<TSendResult>*>(M);
    if (Message) {
        ShowMessage(Message->Value.Message);
    }
}

【问题讨论】:

    标签: android c++ c++builder


    【解决方案1】:

    TMessage__1&lt;T&gt; 是 Delphi Generic TMessage&lt;T&gt; 类的 C++ 类实现。不幸的是,在 C++ 中使用 Delphi Generic 类时有一个记录限制,这就是您收到链接器错误的原因:

    How to Handle Delphi Generics in C++

    Delphi 泛型作为模板暴露给 C++。然而,重要的是要意识到实例化发生在 Delphi 端,而不是在 C++ 中。因此,您只能将这些模板用于在 Delphi 代码中显式实例化的类型

    ...

    如果 C++ 代码尝试对未在 Delphi 中实例化的类型使用 Delphi 泛型,则会在链接时出错。

    这就是为什么TMessage__1&lt;UnicodeString&gt; 有效而TMessage__1&lt;TSendResult&gt; 无效的原因,因为在 Delphi RTL 中存在 TMessage&lt;UnicodeString&gt; 的实例化。编写您正在查看的C++ example 的人可能没有意识到此限制,并且只是按原样翻译Delphi example

    话虽如此,你有两个选择:

    1. 将 Delphi .pas 单元添加到您的 C++ 项目中,将 TSendResult 实现为 Delphi record,并为其定义 TMessage&lt;TSendResult&gt; 的实例化。然后您可以在您的 C++ 代码中使用该单元(在编译 .pas 文件时,C++Builder 会为您生成一个 C++ .hpp 文件),例如:
    unit MyMessageTypes;
    
    interface
    
    uses
      System.Messaging;
    
    type
      TSendResult = record
        Message: String;
        Value: UInt32;
        Errno: Integer;
      end;
    
      TSendResultMsg = TMessage<TSendResult>;
    
    implementation
    
    initialization
      TSendResultMsg.Create.Free;
    finalization
    
    end.
    
    #include "MyMessageTypes.hpp"
    
    __fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
    {
        TMessageManager::DefaultManager->SubscribeToMessage(__classid(TSendResultMsg), &MMReceiveAndCallBack);
    }
    
    void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
    {
        ...
    
        TSendResult Res;
        Res.Message = _D("All good");
        Res.Value = 10;
        Res.Errno = 0;
    
        TSendResultMsg *Message = new TSendResultMsg(Res);
        TMessageManager::DefaultManager->SendMessage(this, Message, true);
    }
    
    void __fastcall TForm1::MMReceiveAndCallBack(System::TObject* const Sender,
        System::Messaging::TMessageBase* const M)
    {
        const TSendResultMsg* Message = static_cast<const TSendResultMsg*>(M);
        ShowMessage(Message->Value.Message);
    }
    
    1. 您可以直接从TMessageBase 派生TSendResult 而不是使用TMessage__1,例如:
    class TSendResultMsg : public TMessageBase
    {
    public:
        String Message;
        unsigned int Value;
        int Errno;
    };
    
    __fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
    {
        TMessageManager::DefaultManager->SubscribeToMessage(__classid(TSendResultMsg), &MMReceiveAndCallBack);
    }
    
    void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
    {
        ...
    
        TSendResultMsg *Message = new TSendResultMsg;
        Message->Message = _D("All good");
        Message->Value = 10;
        Message->Errno = 0;
        TMessageManager::DefaultManager->SendMessage(this, Message, true);
    }
    
    void __fastcall TForm1::MMReceiveAndCallBack(System::TObject* const Sender,
        System::Messaging::TMessageBase* const M)
    {
        const TSendResultMsg* Message = static_cast<const TSendResultMsg*>(M);
        ShowMessage(Message->Message);
    }
    

    【讨论】:

    • 选项 2 似乎是最简单的,所以我尝试了。工作得很好!还有一件事,SendMessage() 的 bool AD​​ispose 参数有什么作用?退出 MMReceiveAndCallBack() 后删除 TSendResultMsg 实例?非常感谢!
    • @PeterStefanos 我相信是的,是的。
    • 我发现的另一件事,除非我不明白,否则它看起来像函数: void __fastcall SendMessage(System::TObject* const Sender, TMessageBase* AMessage, bool AD​​ispose)忽略“ADispose”参数。我在参数设置为 false 的情况下调用此函数,但无论如何实例都会被破坏。不过我已经设法解决它......
    • @PeterStefanos 我没有方便查看的 10.2 的 RTL 源代码,但ADispose 可能仅对非ARC-based platforms 有影响,例如 Android。请注意,Embarcadero 有 moved away from ARC-based object management in 10.4
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-15
    • 2021-11-28
    • 2018-07-18
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多