Using object->Message()



  • On 30/03/2017 at 13:09, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   16 
    Platform:   Windows  ;   
    Language(s) :     C++  ;

    ---------
    In the python post "Pass data chunks between plugins"   
    (https://plugincafe.maxon.net/topic/9411/12600_pass-data-chunks-between-plugins)

    an object.Message() is used to send data from one plugin to another.
    Now I am trying to do the same in c++, but no success.

    Plugin 1 is a ToolPlugin that sends info to plugin 2,which is a CommandPlugin.
    I get the plugin 2 object and then send a message with an object (BaseObject* object) as data

    BasePlugin* mainPlugin = FindPlugin	(PLUGIN_2, PLUGINTYPE_COMMAND);	
    mainPlugin->Message(105645834, object);
    

    Now, in plugin 2, I expect a message coming in for plugin 1.
    But nothing.

    class C4DO : public CommandData	//plugin 2
    {
    private:
    	MainDialog dlg;
      
    public:
    	virtual Bool Execute(BaseDocument* doc);
    	virtual Bool RestoreLayout(void* secret);
    	virtual Bool Message(Int32 type, void *data);
    };
      
    Bool C4DO::Message(Int32 type, void *data)
    {
    	if (type == 105645834) {                               //message from plugin 1
    			GePrint("ok  message");
    			return TRUE;
    	}
    	return TRUE;	//C4DO::Message(type,data);
    }
    

    I also tried Message() and CoreMessage in the dialog part of the commandplugin, but no success.

    What am I missing?

    -Pim



  • On 30/03/2017 at 23:48, xxxxxxxx wrote:

    In the ToolPlugin you can perform a SpecialEventAdd( <id>, ... ) and in the dialog of the CommandPlugin you then listen in CoreMessage to the incoming <id>



  • On 31/03/2017 at 05:07, xxxxxxxx wrote:

    Hi,

    maybe my posts in the other thread were a bit misleading. Sorry! The Message() suggestion was only meant for NodeData derived plugins, for CommandData Message() behaves a bit differently.
    But C4DS is right, SpecialEventAdd() and CoreMessage() in your GeDialog is most likely the way to go here.
    Also see Custom Messages in the Core Messages manual.



  • On 31/03/2017 at 07:42, xxxxxxxx wrote:

    Ok, that is clear.
    But what if I want to send information about an object to the other plugin?
    SpecialEventAdd() is limited to 2 integers.

    For the SendCoreMessage I tried following code, but I receive nothing in CoreMessage of GeDialog.

    In plugin 1 - send the SendCoreMessage()

    BaseContainer data;
    data.SetLink(100101, oActive);			//set object information in container
    SendCoreMessage(105645834, data);		//send message
    ...
    

    In plugin 2

    Bool MainDialog::CoreMessage(Int32 id,const BaseContainer &msg)
    {
    	if (id == 105645834) {                              //message from PlaceOnObject
    		GePrint("ok corecommand");
    	}
    	...
    


  • On 31/03/2017 at 07:50, xxxxxxxx wrote:

    Actually I wouldn't say SpecialEventAdd() is limited to two integers. Actually it's two UInt parameters, enough space to pass two pointers (simply cast them to UInt and cast back on reception). But be careful though, you have to make sure the memory pointed to still exists, when the message is received. So please don't send a pointer to some local variable, that's bound to fail. Rather have a global variable or even some queue accessible from both ends (depending on your needs).



  • On 31/03/2017 at 08:18, xxxxxxxx wrote:

    Could you give a quick example of a global variable?

    I am doing this, but it does not work:

    In a header file
    extern Int32 externalX;

    But then the linker complains Not defined?

    Using in the header file:
    static Int32 externalX;

    But then the value is always 0.



  • On 31/03/2017 at 08:21, xxxxxxxx wrote:

    With an "extern" declaration, you tell the compiler not to create the variable, but to wait for the variable to be provided by another object file (so the linker will resolve the symbol). Just declare it without extern. If you want to access this variable from another file, then have it declared "extern" there.



  • On 31/03/2017 at 08:52, xxxxxxxx wrote:

    It keeps telling me unresolved or already defined.
    This is my set-up

    header file with Int32 externalX;

    cpp file with the code for plugin1 and plugin 2.
    plugin 1: externalX = 121

    plugin 2: extern Int32 externalX;
    GePrint("ok corecommand" + String::IntToString(externalX));

    It keeps telling already defined?
    Error 14 error LNK1169: one or more multiply defined symbols found



  • On 31/03/2017 at 09:28, xxxxxxxx wrote:

    I tried SpecialEventAdd(), but I cannot get the p2 value correctly.

    //send the message
    // oActive is a BaseObject*
    SpecialEventAdd(PLUGIN_ID_MAIN, PLUGIN_ID_MAIN, (UInt)oActive);  
    ...
    
    //receive the message in CoreMessage cast from UInt back to BaseObject*
    Bool MainDialog::CoreMessage(Int32 id,const BaseContainer &msg)
    {
      
    	if (id == PLUGIN_ID_MAIN) {                              //message from PlaceOnObject
    	   GePrint("ok corecommand");		
    	   UInt p2 = msg.GetUInt32(BFM_CORE_PAR2);	//Cinema crashes here!
    	   BaseObject* nw = (BaseObject* )p2;
    	   ....
    

    How to get the pointer to the object?



  • On 31/03/2017 at 09:37, xxxxxxxx wrote:

      
          Data* data;  
          UInt par1 = (UInt)msg.GetVoid(BFM_CORE_PAR1);  
          UInt par2 = (UInt)msg.GetVoid(BFM_CORE_PAR2);  
          // I use par1 to identify what is passed,  
          // par2 contains the data  
          if (par1 == something)  
                  data = (Data* )par2;  
          else if (par1 == somethingelse)  
                  data = nullptr;  
      
    

    Keep in mind that the message is asynchronous. There can be quite some time between the message being sent by one plugin and actually received by the other.
    It is thus possible that when deleting the memory allocation in the sending plugin, the receiving plugin has not yet been triggered. When it does, and it tries to process the obtained pointer, due to the memory already being unallocated this will result in your plugin crashing .



  • On 01/04/2017 at 04:44, xxxxxxxx wrote:

    Great, thank you!



  • On 03/04/2017 at 02:43, xxxxxxxx wrote:

    And what if I want to pass on a structure like this?

    struct MyStruct
    {
       Int32	index;
       BaseObject* objectLink;
    };
    


  • On 03/04/2017 at 05:28, xxxxxxxx wrote:

    Instantiate the struct (just as you would a class or a POD), and pass the pointer ?



  • On 03/04/2017 at 11:03, xxxxxxxx wrote:

    @Pim: Regarding the declaration of a global variable. Even if you don't need it anymore, we should quickly get this right. The correct place to declare a global variable is in one of your .cpp files (and there without extern). When this file gets compiled, the global variable "gets created/allocated (well not really, but for now)" in the resulting object file. In your header you will declare the global variable "extern", so every other cpp file including the header, will not "create" the global variable again, but will know, it has to wait for the global variable coming from another object file. By directly (without extern) declaring the global variable in the header (and I know, I'm responsible for this, because I didn't take time to explain in detail), the global variable will be "created" in every object file, where the source cpp file includes the header. And then the linker will say "uh... oh... already defined" as it won't know, which of the global variables is actually to be used.
    So, short summary to define a global Int32 variable you'd like to access in two different .cpp files:

    // In sourceA.h:
    extern Int32 walter; // Here we just tell the compiler not to try to resolve symbol walter in the currently compiling source/object, but to wait for the linker to resolve it later
      
    // In sourceA.cpp:
    #include "sourceA.h"
      
    Int32 walter;  // Here the global variable is actually created and it will take up some space in sourceA.o
      
    void funcA()
    {
    	if (walter == 0)
    		return;
    	return;
    }
      
    // In sourceB.cpp
    #include "sourceA.h"
      
    void funcB()
    {
    	if (walter == 0)
    		return;
    	return;
    }
    

    For the struct question, well either you have that structure globally or you allocate the struct and store a pointer to it globally (by globally I mean not necessarily global in the sense of the word, but accessible from both classes). When allocating the struct, don't forget to free it on the receiver side and make sure you do not overwrite the pointer before it got received, otherwise you'll end up with a memory leak.



  • On 03/04/2017 at 11:40, xxxxxxxx wrote:

    Thanks for all the input and clarifications.
    Everything is working now and I understand what I did wrong and why it is now working (global variables and struct).

    To be sure.
    Is this the best way to initialize a struct?

    		MyStruct* myStruct;
    		myStruct = NewMemClear(MyStruct, 1);
    		myStruct->index = 16;
    		myStruct->objectLink = surface_obj;
    


  • On 04/04/2017 at 03:04, xxxxxxxx wrote:

    Yes, though a nullptr check would be appropriate and you should make sure the object is deleted on the receiving end.

    I haven't used SpecialEventAdd() this way myself. @Andreas
    is it garuanteed that every message sent will actually be dispatched
    at some point? Because if it is, say, a queue that swallows events
    of the same ID, it's not garuanteed.



  • On 04/04/2017 at 05:02, xxxxxxxx wrote:

    Valid point, Niklas. I checked the code.
    With SpecialEventAdd() identical events (same messageid and identical parameters p1 and p2) do not get queued, but this shouldn't be a problem in this case. More problematic could be, that events get thrown away, if the event system is overloaded. But I have never seen this under real life conditions.



  • On 04/04/2017 at 07:18, xxxxxxxx wrote:

    Hi,

    What is the benefit of using:  myStruct = NewMemClear(MyStruct, 1);
    Opposed to using something more standard looking like this: myStruct = NewObj(MyStruct);

    -ScottA



  • On 05/04/2017 at 06:00, xxxxxxxx wrote:

    @ScottA: I used NewMemClear(), because NewObj() sounded to me more appropriate for objects and not for structures,but - after reading your question - I am not sure anymore.
    Let's see what the experts say.

    @NiklasR: "and you should make sure the object is deleted on the receiving end."
    Is DeleteMem(myStruct) sufficient?



  • On 05/04/2017 at 10:12, xxxxxxxx wrote:

    @Scott: Well, I wouldn't say there's an advantage. I agree, in general NewObj() is probably preferable, as it can call the constructor for the allocated type. NewMem() on the other hand is mostly used to allocate raw memory (as the docs state) without calling any constructor.

    @Pim: Yes, DeleteMem() would be sufficient. Saying this will all due caution, assuming you do it correctly (just don't blame me, if you call DeleteMem() in the same thread where you allocated the memory right after sending the message. Which I'd regard a wrong usage, as you'd delete the memory before receiving it in the other thread).


Log in to reply