简体   繁体   中英

FMX System messaging to send a pointer(?)

I'm using C++Builder 10.2.

In Android, I would like to send messages from various threads, including the main thread, to the main GUI thread. In Windows, I could post a message and assign an LPARAM or WPARAM to the address of some instance of a struct or class.

I'm trying to use System.Messaging.TMessageManager to do the same thing, similar to the example here: System.Messaging (C++) . But I can only send 'simple' types, like UnicodeString or int . I haven't worked out how to send a pointer, assuming it's even possible at all.

I would like to send a struct/class instance like this:

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

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

If this can be done, how do I write this? I managed to get one version to compile, but got a linker error:

error: undefined reference to 'vtable for System::Messaging::TMessage__1<TSendResult>'

Form constructor:

__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);
}

Button click handler:

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);
}

Function that captures messages:

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);
    }
}

TMessage__1<T> is a C++ class implementation for the Delphi Generic TMessage<T> class. Unfortunately, there is a documented limitation when using Delphi Generic classes in C++, which is why you are getting a linker error:

How to Handle Delphi Generics in C++

Delphi generics are exposed to C++ as templates. However, it is important to realize that the instantiations occur on the Delphi side, not in C++. Therefore, you can only use these template for types that were explicitly instantiated in Delphi code .

...

If C++ code attempts to use a Delphi generic for types that were not instantiated in Delphi, you'll get errors at link time.

Which is why TMessage__1<UnicodeString> works but TMessage__1<TSendResult> does not, as there is an instantiation of TMessage<UnicodeString> present in the Delphi RTL. Whoever wrote the C++ example you are looking at was likely not aware of this limitation and was just translating the Delphi example as-is.

That being said, you have two choices:

  1. Add a Delphi .pas unit to your C++ project, implementing TSendResult as a Delphi record , and defining an instantiation of TMessage<TSendResult> for it. Then you can use that unit in your C++ code (C++Builder will generate a C++ .hpp file for you when the .pas file is compiled), eg:
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. rather than using TMessage__1 at all, you can instead derive TSendResult directly from TMessageBase , eg:
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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM