Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: TMessage__1<UnicodeString> and sending/receiving messages X-platform


This question is answered.


Permlink Replies: 4 - Last Post: Aug 3, 2017 9:51 AM Last Post By: Remy Lebeau (Te...
Sean Hoffman

Posts: 126
Registered: 3/28/99
TMessage__1<UnicodeString> and sending/receiving messages X-platform  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 4, 2017 6:33 AM
First of all, here's the Embarcadero example:

http://docwiki.embarcadero.com/CodeExamples/Berlin/en/System.Messaging_(C%2B%2B)

I would like to handle messages of other types other than TMessage__1<UnicodeString>, but unfortunately whenever I substitute other types (including simple void *) I get link errors:
Unresolved external 'System::Messaging::TMessage__1<void *>::' referenced in blah\blah\Main.o

Has anyone managed to send any other message than a UnicodeString?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: TMessage__1<UnicodeString> and sending/receiving messages X-platform
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 6, 2017 11:35 AM   in response to: Sean Hoffman in response to: Sean Hoffman
Sean wrote:

I would like to handle messages of other types other than
TMessage__1<UnicodeString>, but unfortunately whenever I
substitute other types (including simple void *) I get link errors:

 
Unresolved external 'System::Messaging::TMessage__1<void *>::'
referenced in blah\blah\Main.o
 

Did you read Embarcadero's documentation about this very issue?

How to Handle Delphi Generics in C++
http://docwiki.embarcadero.com/RADStudio/en/How_to_Handle_Delphi_Generics_in_C%2B%2B

In particular:

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**.

So, any Generic type you want to use in C++, like TMessage<void*>, must exist
and be instantiated in Delphi code in order to resolve the C++ linker error.
That does not mean you have to allocate all of your Generic objects in Delphi.
You can allocate them in C++, as long as there is are corresponding type
definitions in Delphi code (ie, like TMessage<Pointer>) and some piece
of Delphi code (ie, like a Pascal function you call at app startup) actually
uses the Generic types at least once so the linker does not optimize them
out of the executable.

--
Remy Lebeau (TeamB)
Sean Hoffman

Posts: 126
Registered: 3/28/99
Re: TMessage__1<UnicodeString> and sending/receiving messages X-platform  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Mar 6, 2017 11:42 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Thank you immensely Remy. I got around the problem in that particular instance, but it's good to know for the future. You're awesome, btw, cheers!
Peter Stefanos

Posts: 5
Registered: 11/30/00
Re: TMessage__1<UnicodeString> and sending/receiving messages X-platform  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 2, 2017 6:46 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:

So, any Generic type you want to use in C++, like TMessage<void*>, must exist
and be instantiated in Delphi code in order to resolve the C++ linker error.
That does not mean you have to allocate all of your Generic objects in Delphi.
You can allocate them in C++, as long as there is are corresponding type
definitions in Delphi code (ie, like TMessage<Pointer>) and some piece
of Delphi code (ie, like a Pascal function you call at app startup) actually
uses the Generic types at least once so the linker does not optimize them
out of the executable.

I don't entirely understand the above but how do you work out which types are instantiated in Delphi code to resolve the C++ linker error?

I am trying to send a blob of binary data as a message from a worker thread to other threads (including the main GUI). Is there another way of doing this? I have tried using my own class but got the same linker errors that Sean got. For what its worth, I did the following:

In the header:
class TBinaryMessage
{
public:
	__fastcall TBinaryMessage(int BufferSize);
	int DataSize;
	uint8_t *Data;
};


In the main file:
__fastcall TForm1::TForm1(TComponent* Owner)
	: TForm(Owner)
{
	TMessageManager::DefaultManager->SubscribeToMessage(__classid(TMessage__1<TBinaryMessage *>), &ProcessBinaryMessage);
}
 
__fastcall TForm1::~TForm1()
{
	TMessageManager::DefaultManager->Unsubscribe(__classid(TMessage__1<TBinaryMessage *>),
		&ProcessBinaryMessage, true);
}


In SocketReadThread(), a TCP socket read worker thread:
	// ...
	TBinaryMessage *BinaryMessage = new TBinaryMessage(100);
	// ...
 
	// ...
	TMessage__1<TBinaryMessage *>* Message = new TMessage__1<TBinaryMessage *>(BinaryMessage);
	// Send a binary data message to main GUI thread.
	MessageManager->SendMessage(SocketReadThread, Message, true);
	// ...
 

... and at the receiving end:
void __fastcall TForm1::ProcessBinaryMessage(System::TObject* const Sender, TMessageBase* const M)
{
	const TMessage__1<TBinaryMessage *>* Message = static_cast<const TMessage__1<TBinaryMessage *>*>(M);
 
	// Do stuff with (TBinaryMessage *)Message->Value;
}


Regards,
Peter.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: TMessage__1<UnicodeString> and sending/receiving messages X-platform  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Aug 3, 2017 9:51 AM   in response to: Peter Stefanos in response to: Peter Stefanos
Peter Stefanos wrote:

I don't entirely understand the above

What don't you understand exactly? If you want to use a Delphi-style
Generic type X in C++, there needs to be a corresponding type X
declared in Delphi, and used in Delphi code at least once at runtime.
This is clearly explained in Embarcadero's documentation.

http://docwiki.embarcadero.com/RADStudio/en/How_to_Handle_Delphi_Generics_in_C%2B%2B

how do you work out which types are instantiated in Delphi code to
resolve the C++ linker error?

You have to code them that way yourself.

I am trying to send a blob of binary data as a message from a worker
thread to other threads (including the main GUI). Is there another
way of doing this?

There are many different ways to pass around data between threads. You
are not required to use TMessageManager for that. For instance, you
could use TThread::Queue() instead:

http://docwiki.embarcadero.com/Libraries/en/System.Classes.TThread.Queue

http://docwiki.embarcadero.com/RADStudio/en/How_to_Handle_Delphi_Anonymous_Methods_in_C%2B%2B

Or,, write your own callback registration system that uses pure C++
function pointers or STL classes like std::function, etc, that doesn't
rely on the Delphi RTL at all.

I have tried using my own class but got the same linker errors that
Sean got.

If you want to go the TMessageManager route, you must add a Delphi .pas
source file to your C++ project and have it define your TBinaryMessage
class and the corresponding TMessage<TBinaryMessage> wrapper. For
example (untested):

unit BinaryMessage;
 
interface
 
type
  TBinaryMessage = class
  public
    Data: TArray<Byte>;
    constructor Create(BufferSize: Integer);
  end;
 
procedure EnsureBinaryMessageExistsInCpp;
 
implementation
 
uses
  System.Messaging;
 
constructor TBinaryMessage.Create(BufferSize: Integer);
begin
  SetLength(Data, BufferSize);
end;
 
procedure EnsureBinaryMessageExistsInCpp;
var
  Data: TBinaryMessage;
  Msg: System.Messaging.TMessage;
begin
  Data := TBinaryMessage.Create(0);
  Msg := TMessage<TBinaryMessage>.Create(Data);
  Msg.Free;
  Data.Free;
end;
 
end.


In the main file:

#include "BinaryMessage.hpp"
 
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
    EnsureBinaryMessageExistsInCpp();
 
 
TMessageManager::DefaultManager->SubscribeToMessage(__classid(TMessage__
1<TBinaryMessage*>), &ProcessBinaryMessage);
}
 
__fastcall TForm1::~TForm1()
{
 
TMessageManager::DefaultManager->Unsubscribe(__classid(TMessage__1<TBina
ryMessage*>), &ProcessBinaryMessage, true);
}


In SocketReadThread():

#include "BinaryMessage.hpp"
 
...
TBinaryMessage *BinaryMessage = new TBinaryMessage(100);
...
TMessage__1<TBinaryMessage*> *Message = new
TMessage__1<TBinaryMessage*>(BinaryMessage);
MessageManager->SendMessage(SocketReadThread, Message, true);
...


At the receiving end:

void __fastcall TForm1::ProcessBinaryMessage(System::TObject* const
Sender, TMessageBase* const M)
{
    const TMessage__1<TBinaryMessage*> *Message = static_cast<const
TMessage__1<TBinaryMessage*>*>(M);
 
    // Do stuff with Message->Value ...
}


--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02