Return string to Python



  • On 30/10/2013 at 07:16, xxxxxxxx wrote:

    User Information:
    Cinema 4D Version:   R14 
    Platform:      
    Language(s) :     C++  ;   PYTHON  ;

    ---------
    Sorry for this newbee in C++ question.
    In my python plugin I call a c++ module to return a string.
    However, the MS visual C++ 2010 express keep complaining about String and the return value in python is an integer and not a string.

    The compiler /linker tells me:
     warning C4190: 'serialnr' has C-linkage specified, but returns UDT 'String' which is incompatible with C

    C++ code

    #include <stdlib.h>  
    #include "c4d.h"
    #include "c4d_symbols.h"
      
    #define DLLEXPORT extern "C" __declspec(dllexport)
      
    DLLEXPORT String serialnr()
    {
    	  int serienr;
    	  String dest;
      
    	  dest = "ABCDEFG";
    	  return dest;	
    }
    

    Python code

    import c4d
    import sys
    import ctypes 
    from ctypes import *
      
      
    def main() :
        
        lib_path = 'D:/Program Files/MAXON/CINEMA 4D R14/plugins/cinema4dsdk/cinema4dsdk.cdl'
        mydll = ctypes.CDLL(lib_path) 
        
        print mydll.serialnr()
      
    
    

    So, the string is not printed, but an integer.

    Perhaps I should add Ctypes like
        #mydll.serialnr.restype = c_char_p
        #mydll.serialnr.argtypes = [c_char_p]



  • On 30/10/2013 at 08:37, xxxxxxxx wrote:

    Hi Pim,

    1. You must make your library a CDL/DYLIB that will be loaded by Cinema 4D (or initialize the
    global function table yourself which is hardly possible from Python). The OS won't load the dynamic
    library twice so the function table will be initialized properly.

    2. Python can't handle Cinema 4D's String class. You need to use a native char pointer and do
    memory allocation/deallocation on your own from Python. Using a small trick (implementing a custom
    ctypes datatype), the memory management can be handled automatically. ( MyLib.c_char_p_c4d )

    Example Project

    Compile the following source code into a Cinema 4D plugin.

    >
    > /**
    > * Copyright (c) 2013, Niklas Rosenstein
    > *
    > * Permission is hereby granted, free of charge, to any person obtaining a copy
    > * of this software and associated documentation files (the "Software"), to deal
    > * in the Software without restriction, including without limitation the rights
    > * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    > * copies of the Software, and to permit persons to whom the Software is
    > * furnished to do so, subject to the following conditions:
    > *
    > * The above copyright notice and this permission notice shall be included in
    > * all copies or substantial portions of the Software.
    > *
    > * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    > * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    > * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    > * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    > * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    > * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    > * THE SOFTWARE.
    > *
    > * main.cpp
    > */
    >
    > #include <c4d.h>
    >
    > #if defined(__PC)
    > #define DLLEXPORT extern "C" __declspec(dllexport)
    > #else
    > #define DLLEXPORT extern "C" __attribute__ ((visibility("default")))
    > #endif
    >
    > #define OUTPUT_CALLS 1
    >
    > /**
    > * Returns the active Cinema 4D license (not as if the GetSerialInfo()
    > * function from Python couldn't do this, it's just for demonstration
    > * purpose).
    > *
    > * The returned pointer must be freed with MemDealloc().
    > */
    > DLLEXPORT char* GetAppLicense() {
    > SerialInfo info;
    > GeGetSerialInfo(SERIALINFO_MULTILICENSE, &info);
    > if (!info.nr.Content()) {
    > GeGetSerialInfo(SERIALINFO_CINEMA4D, &info);
    > }
    >
    > #if OUTPUT_CALLS
    > GePrint("GetAppLicense() : allocating memory for string "" + info.nr + "".");
    > #endif
    >
    > char* output = info.nr.GetCStringCopy();
    > return output;
    > }
    >
    > /**
    > * Deallocate a chunk of memory allocated with the Cinema 4D memory
    > * management API. Will be called from Python.
    > */
    > DLLEXPORT void MemDealloc(void* p) {
    > if (p) {
    > #if OUTPUT_CALLS
    > GePrint("MemDealloc() : deallocating memory at " + LongToString((size_t) p));
    > #endif
    > GeFree(p);
    > }
    > }
    >
    > // Required
    >
    > Bool PluginStart() {
    > return TRUE;
    > }
    >
    > Bool PluginMessage(LONG type, void* pData) {
    > return TRUE;
    > }
    >
    > void PluginEnd() {
    > }

    Once you've compiled the code into a Cinema 4D plugin, run the following script in the Script Manager.
    It will prompt you to select the dynamic library you've just compiled. If you haven't modified the above
    source code and OUTPUT_CALLS is still defined as 1 , you will see two prints in the console.

    >
    > import c4d
    > import ctypes
    >
    > class MyLib:
    >
    > initialized = False
    >
    > class c_char_p_c4d(ctypes.c_char_p) :
    >
    > def __str__(self) :
    > return self.value
    >
    > def __del__(self) :
    > # Deallocate the string.
    > MyLib.MemDealloc(self)
    >
    > @classmethod
    > def init(cls, path) :
    > dll = ctypes.CDLL(path)
    >
    >
    > cls._dll = dll
    >
    > fun = dll.GetAppLicense
    > fun.argtypes = []
    > fun.restype = MyLib.c_char_p_c4d
    > cls.GetAppLicense = fun
    >
    > fun = dll.MemDealloc
    > fun.argtypes = [ctypes.c_void_p]
    > fun.restype = None
    > cls.MemDealloc = fun
    >
    > cls.initialized = True
    >
    > def main() :
    > path = c4d.storage.LoadDialog(title="Please select the dynamic library.")
    > if not path: return
    >
    > try:
    > MyLib.init(path)
    > except OSError:
    > c4d.gui.MessageDialog("Invalid dynamic library selected.")
    > return
    > except AttributeError:
    > c4d.gui.MessageDialog("The selected dynamic library does not provide the "
    > "required interface.")
    > return
    >
    > string = MyLib.GetAppLicense()
    > c4d.gui.MessageDialog("Cinema 4D License (returned by the DLL) : " + str(string))
    >
    > if __name__ == "__main__":
    > main()

    Best,
    -Niklas



  • On 30/10/2013 at 08:58, xxxxxxxx wrote:

    Thanks very much.
    I'll try and let you know.



  • On 30/10/2013 at 09:17, xxxxxxxx wrote:

    Sorry, I get the following error.
    Maybe because I need to change main.cpp?

    1>Main.obj : error LNK2005: "int __cdecl PluginStart(void)" (?PluginStart@@YAHXZ) already defined in niklas.obj
    1>Main.obj : error LNK2005: "void __cdecl PluginEnd(void)" (?PluginEnd@@YAXXZ) already defined in niklas.obj
    1>Main.obj : error LNK2005: "int __cdecl PluginMessage(long,void * )" (?PluginMessage@@YAHJPAX@Z) already defined in niklas.obj
    1>     Creating library D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\obj\cinema4dsdk\Win32_Release\cinema4dsdk.cdl.lib and object D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\obj\cinema4dsdk\Win32_Release\cinema4dsdk.cdl.exp
    1>D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\cinema4dsdk.cdl : fatal error LNK1169: one or more multiply defined symbols found
    ========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========
    


  • On 30/10/2013 at 09:19, xxxxxxxx wrote:

    Note, I am not yet in full control / understanding of all switches in visual studio 2010 Express.



  • On 30/10/2013 at 09:31, xxxxxxxx wrote:

    I now see what I did wrong.
    It is now functioning!
    Thanks, I 'll study it in detail.



  • On 01/11/2013 at 07:48, xxxxxxxx wrote:

    It is progressing.
    I now read the cinema 4d serial number, calculate the sha256 and compare it with the contents of the license file.
    However, when I close cinema 4d, I get a MS C++ runtime error:
    Program: cinema 4d r14\ cinema 4d 64 bit.exe

    R6025
    - pure virtual function call.

    What to do?

    I get only warnings from the compiler / linker

    1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\xlocale(323) : warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
    1>d:\program files\maxon\cinema 4d r14\plugins\cinema4dsdk\niklas 3.cpp(154) : warning C4267: 'argument' : conversion from 'size_t' to 'LONG', possible loss of data
    1>     Creating library D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\obj\GetLicense\x64_Release\GetLicense.cdl64.lib and object D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\obj\GetLicense\x64_Release\GetLicense.cdl64.exp
    1>  cinema4dsdk.vcxproj -> D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\GetLicense.cdl64
    ========== Build: 1 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
    


  • On 01/11/2013 at 09:42, xxxxxxxx wrote:

    At what point is the C string being deallocated? It is possible that it happens at a time the function-table is
    invalid already. Try to create a Python copy of the string and not store it directly.

    > c_string = MyLib . SomeFunction(c4dlicense)
    > string = str(c_string)



  • On 01/11/2013 at 11:32, xxxxxxxx wrote:

    It seems that if I add something outside cinema 4d to your routine, the error is given.
    E.G. when I want to write some debug information to an external file and add 
    ofstream myfile;
    the error is given.
    So without this line, no error, with the line the error is given.
    The same is true when I add my own routine, the error is given.

    DLLEXPORT char* GetAppLicense() { 
      
        ofstream myfile;	       //gives the error!
      
        GePrint ("Start GetAppLicense.");
        SerialInfo info;
        GeGetSerialInfo(SERIALINFO_MULTILICENSE, &info);
        if (!info.nr.Content()) {
            GeGetSerialInfo(SERIALINFO_CINEMA4D, &info);
        }
      
        #if OUTPUT_CALLS
            GePrint("GetAppLicense() : allocating memory for string \"" + info.nr + "\".");
        #endif
      
        char* output = info.nr.GetCStringCopy();   
        return output;
    }
    


  • On 01/11/2013 at 11:44, xxxxxxxx wrote:

    Could you please add the /EHsc compiler option to your project settings and retry?



  • On 01/11/2013 at 12:21, xxxxxxxx wrote:

    I change it using:

    1. Open the project's Property Pages dialog box. For details, see How to: Open Project Property Pages.
    2. Click the C/C++ folder.
    3. Click the Code Generation property page.
    4. Modify the Enable C++ Exceptions property.

    Should I also add some code and if so what code?
    MS gives as an example:
    See also http://msdn.microsoft.com/en-us/library/1deeycx5(v=vs.90).aspx

        
            // compiler_options_EHA.cpp
        // compile with: /EHa
        #include <iostream>
        #include <excpt.h>
        using namespace std;
        
        void fail() {   // generates SE and attempts to catch it using catch(...)
           try {
              int i = 0, j = 1;
              j /= i;   // This will throw a SE (divide by zero).
              printf("%d", j); 
           }
           catch(...) {   // catch block will only be executed under /EHa
              cout<<"Caught an exception in catch(...)."<<endl;
           }
        }
        
        int main() {
           __try {
              fail(); 
           }
        
           // __except will only catch an exception here
           __except(EXCEPTION_EXECUTE_HANDLER) {   
           // if the exception was not caught by the catch(...) inside fail()
              cout << "An exception was caught in __except." << endl;
           }
        }
    


  • On 01/11/2013 at 12:23, xxxxxxxx wrote:

    The warning is gone, but the error is still there.

    1>d:\program files\maxon\cinema 4d r14\plugins\cinema4dsdk\niklas 5.cpp(162) : warning C4267: 'argument' : conversion from 'size_t' to 'LONG', possible loss of data
    1>     Creating library D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\obj\GetLicense\x64_Release\GetLicense.cdl64.lib and object D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\obj\GetLicense\x64_Release\GetLicense.cdl64.exp
    1>  cinema4dsdk.vcxproj -> D:\Program Files\MAXON\CINEMA 4D R14\plugins\cinema4dsdk\GetLicense.cdl64
    ========== Build: 1 succeeded, 0 failed, 1 up-to-date, 0 skipped ==========
    


  • On 01/11/2013 at 12:35, xxxxxxxx wrote:

    Great, the warning was supposed to drop. :) Does it still crash?



  • On 01/11/2013 at 12:44, xxxxxxxx wrote:

    Yes, sorry. The error is still there.
    Also, it does not crash.
    The call to python is working, I do get a return value and the code is executed ok.
    Except when i close down cinema 4d, I get the error message.
    Not while working?



  • On 01/11/2013 at 14:50, xxxxxxxx wrote:

    There is NO error when comp and link in 32 bit debug mode.
    32 release mode give an error.

    I cannot debug in 64 bit because the linker cannot find 'kernel32.lib'?

    1>------ Build started: Project: GetLicense, Configuration: Debug x64 ------
    1>  niklas 5.cpp
    1>d:\program files\maxon\cinema 4d r14\plugins\cinema4dsdk\niklas 5.cpp(160) : warning C4267: 'argument' : conversion from 'size_t' to 'LONG', possible loss of data
    1>LINK : fatal error LNK1104: cannot open file 'kernel32.lib'
    ========== Build: 0 succeeded, 1 failed, 1 up-to-date, 0 skipped ==========
    

    And again, all seems to be functioning correctly.
    Only when I exit cinema, the error pops up?

    Also when debugging I see a lot of:
    "Cannot find or open the PDB file"



  • On 06/11/2013 at 08:39, xxxxxxxx wrote:

    Thanks for sending me the code. I'm currently examining it. What I can say is that the error comes
    not from my original code, but from your use of the standard library. Though I can't tell you at the
    moment why and how to fix it.

    Maxon does not officially support using the standard library, yet you require it. I've successfully
    made use of the standard library myself in plugins, so I am interested in where the issue is.

    Best,
    -Niklas



  • On 08/11/2013 at 10:38, xxxxxxxx wrote:

    Ok, thanks for the support.
    I hope you can solve it quickly.
    We want to bring out a new plugin this month, but we still have to decide how to protect.
    Using easily crack-able python, or a bit more difficult C++ library.

    Did you ever called C++ from python using the standard library?

    Regards, Pim



  • On 10/11/2013 at 05:25, xxxxxxxx wrote:

    Hi Pim,

    if you don't need the Cinema 4D Api, it is really no problem to create a DLL which makes use of the
    C++ standard library. The problem is that the Cinema 4D Api and the use of the std library are
    conflicting sometimes. Unfortunately I don't have a solution to your problem yet (which uses the
    Cinema 4D Api).

    In case you do not need the C4D Api, you can create a new VC++ project from scratch (without
    the cinema4sdk as the original project).

    Best,
    -Niklas



  • On 10/11/2013 at 08:55, xxxxxxxx wrote:

    Hi Niklas,

    Correct, I now created a C++ function with no C4D in it at all and just call it.
    No problem there.

    So like you say " The problem is that the Cinema 4D Api and the use of the std library are
    conflicting sometimes".
    However, it would be nice to mix C4D and standard C++ in one routine and call it from Python.
    If you fix it one day, please let me know.
    I continue on the above road (not mixing C4D and standard C++).
    Thanks for the support.

    Pim


Log in to reply