Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Problems Loading .dlls

    SDK Help
    0
    31
    16.0k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H
      Helper
      last edited by

      On 01/12/2014 at 18:32, xxxxxxxx wrote:

      I agree with Andreas here.  Your best bet is to go to MSDN and Apple's Development site and peruse their code examples and documentation on how to load dynamic libraries because, as mentioned, these are OS-based operations and not part of C4D.  Also, as noted, there are several ways to specify the location of the dll.  Mine used an environment variable with some appendages (all names changed due to NDA!).  You can use absolute paths (if you can construct them on all systems) or the working directory with appended paths.

      1 Reply Last reply Reply Quote 0
      • H
        Helper
        last edited by

        On 01/12/2014 at 20:24, xxxxxxxx wrote:

        I've been doing that Robert. But it's been rough trying to figure it all out.
        At this point I can now use LoadLibrary() to load the .dll from my plugin folder. And now I'm in the process of learning how to use GetProcAddress() to target the functions in my dll.
        But I still don't know how to get the value from it yet.

        To make it easier.
        I've changed my DLL code and gotten rid of the class. And now I just have a function in it called GetNumber() that returns int 55;
        And I'm loading it like this based on an example I found on the web:

                        typedef double (*LPGETNUMBER)(double Nbr);  
                      LPGETNUMBER lpGetNumber;  
                      lpGetNumber = (LPGETNUMBER)GetProcAddress(dll_Handle, "GetNumber");
        

        It all works without errors.
        But I'm still trying to figure out how to get the value from my dll.
        If I cast it to an int I get a strange number like -112355447.

        I think I'm almost there. But it's slow going.

        -ScottA

        1 Reply Last reply Reply Quote 0
        • H
          Helper
          last edited by

          On 01/12/2014 at 23:57, xxxxxxxx wrote:

          As you said, you are almost there.

          GetProcAddress() does not call your function. What you get from GetProcAddress() is a function pointer. You can then use the function pointer to call your function.
          Like so:

          GePrint(String::FloatToString(lpGetNumber()));
          
          1 Reply Last reply Reply Quote 0
          • H
            Helper
            last edited by

            On 02/12/2014 at 07:05, xxxxxxxx wrote:

            I tried that. But I get an error that it can't convert LPGETNUMBER to a Real.
            So I'm trying to figure out how to convert it to a C4D compatible Real type this morning.
            I'm in the middle of a casting war. !Wink[URL-REMOVED]

            -ScottA


            [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

            1 Reply Last reply Reply Quote 0
            • H
              Helper
              last edited by

              On 02/12/2014 at 07:09, xxxxxxxx wrote:

              Scott,
              you are not supposed to cast LPGETNUMBER into a Float/Real, whatsoever. It is a function pointer! Use it to call the function as I showed in my previous post.

              1 Reply Last reply Reply Quote 0
              • H
                Helper
                last edited by

                On 02/12/2014 at 07:31, xxxxxxxx wrote:

                OK.
                But If GetProcAddress() calls (executes the function) that's in the DLL. How do I get the value it sends out into C4D?
                That function is written in Raw C++ and not able to output any C4D values.
                So how would I connect the two things together so that C4D can read the output?

                -ScottA

                1 Reply Last reply Reply Quote 0
                • H
                  Helper
                  last edited by

                  On 02/12/2014 at 08:56, xxxxxxxx wrote:

                  Scott,
                  GetProcAddress() does NOT execute the library function (i repeat myself). To say it very (perhaps too) simple, it does a table look-up to get the function pointer for a given function name. And then you take the resulting function pointer to call the function.
                  You have done the typedef for the function pointer already. You already defined it to return double, which could be easily cast to a Float64... perhaps you should read something about function pointers. As this concept somehow seems to block you.

                  1 Reply Last reply Reply Quote 0
                  • H
                    Helper
                    last edited by

                    On 02/12/2014 at 09:29, xxxxxxxx wrote:

                    I'm getting closer.
                    I'm now getting values back from the dll.
                    For some reason I can't get the return value from GetNumber(). It's forcing me to put my own value in the params in the program. I'm trying to figure out why.
                    But the MyAdd() function is working properly. And I can use it in my plugin code. Yay!

                    // The DLL code

                    //DLL's .h file  
                    #ifdef SIMPLEDLL_EXPORTS  
                    #define SIMPLEDLL_API __declspec(dllexport)   
                    #else  
                    #define SIMPLEDLL_API __declspec(dllimport)   
                    #endif  
                      
                      
                    //DLL's .cpp file  
                    #include <iostream>  
                    #include "SimpleH.h"  
                    using namespace std;  
                      
                    extern "C"  
                    {  
                      __declspec(dllexport) int GetNumber()  
                      {  
                          return 55;  
                      }  
                      
                      __declspec(dllexport) int MyAdd( int a, int b)  
                      {  
                          return a+b;  
                      }  
                    };
                    

                    // The plugin's main.cpp code without the error checking stuff

                                case C4DPL_PROGRAM_STARTED:  
                              {  
                                  char *sPath = GeGetPluginPath().GetString().GetCStringCopy();  
                      
                                  if(!SetDllDirectory((LPCSTR)sPath)) GePrint("SetDllDirectory FAILED");  
                      
                                  HINSTANCE dll_Handle = NULL;      
                                  dll_Handle = LoadLibrary("SimpleDLL.dll");  
                      
                                  //Get the value from the GetNumber() method in the DLL                  
                                  typedef double (*LPGETNUMBER)(double Nbr);  
                                  LPGETNUMBER lpGetNumber = NULL;   
                                  lpGetNumber = (LPGETNUMBER)GetProcAddress(dll_Handle, "GetNumber");   
                                  double gn = lpGetNumber(6); //<--- For some reason I have to supply a param. value here???  
                                  GePrint(RealToString(gn));  
                      
                                  //Get the value using the MyAdd() method in the DLL  
                                  typedef int (*LPADDNUMBERS)(int a, int b);    
                                  LPADDNUMBERS lpAddNumbers = NULL;  
                                  lpAddNumbers = (LPADDNUMBERS)GetProcAddress(dll_Handle, "MyAdd");   
                                  int addn = lpAddNumbers(6, 6);  
                                  GePrint(LongToString(addn));  
                      
                                  //Free the memory used  
                                  FreeLibrary(dll_Handle);  
                      
                              }break;
                    

                    Almost there I think.
                    I'm not ignoring your advice Andreas. I'm just trying to figure out what you're saying.
                    So don't get mad at me. !Smile[URL-REMOVED]

                    -ScottA

                    *Update:- Found the problem

                    typedef double (*LPGETNUMBER)(double Nbr);
                    should be
                    typedef double (*LPGETNUMBER)();

                    And also
                    __declspec(dllexport) int GetNumber()
                    should be
                    __declspec(dllexport) double GetNumber()

                    Using "int" in the DLL code and "double" in the the plugin's code returned a value of zero.
                    That was tripping me up. So it's very important to keep the types matched properly.

                    I think that does it Andreas. I think I'm done.

                    Thanks again for your help.
                    -ScottA


                    [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

                    1 Reply Last reply Reply Quote 0
                    • H
                      Helper
                      last edited by

                      On 02/12/2014 at 14:27, xxxxxxxx wrote:

                      @Mohamed.

                      Does my code help you out with your question about sub folders?
                      The sPath variable can be used to target the .dll in any folder. Not just the master plugin folder.

                      For example if it's in the "res" folder it would be something like this:

                      //If you want to target the res folder use this instead  
                      //**Just be sure you have actually put the .dll file in the res folder**  
                        
                        Filename fn = GeGetPluginPath() + "res";  
                        char *sPath = fn.GetString().GetCStringCopy();
                      

                      Did that answer your question?

                      -ScottA

                      1 Reply Last reply Reply Quote 0
                      • H
                        Helper
                        last edited by

                        On 02/12/2014 at 16:26, xxxxxxxx wrote:

                        I think I understand how it works with LoadLibrary() now, but using this method you won't need the headers & libraries anymore I guess "so the compiler will give you a function pointer, and will trust you about the usage"

                        is this true?

                        1 Reply Last reply Reply Quote 0
                        • H
                          Helper
                          last edited by

                          On 02/12/2014 at 16:31, xxxxxxxx wrote:

                          Mohamed,
                          yes, you are right. Except for some terminology. "the compiler will give" part. The compiler has nothing to do with it. LoadLibrary() in combination with GetProcAddress() will give you the function pointer. But the later part is right again, the compiler needs to trust you about the usage.

                          1 Reply Last reply Reply Quote 0
                          • H
                            Helper
                            last edited by

                            On 02/12/2014 at 16:39, xxxxxxxx wrote:

                            Oh yeah. Sorry about that.
                            I forgot to post the header section in my plugin's main.cpp file.
                            Yes. I am including the header file for the DLL's project. But that's all. And nothing else.

                            #include <Windows.h>  
                            #include "C:\Program Files\MAXON\Your C4D version\plugins\SimpleDLL\SimpleH.h"  #The DLL's .h file must be here!!  
                              
                            #include "c4d.h"  
                            #include <string.h>
                            

                            I am not setting any of the compiler's options to point at any of the DLL program's files either.
                            That was another mistake I was making.

                            -ScottA

                            1 Reply Last reply Reply Quote 0
                            • H
                              Helper
                              last edited by

                              On 02/12/2014 at 16:49, xxxxxxxx wrote:

                              @Andreas you are right, my terminology was wrong !Tongue[URL-REMOVED] , but you got my point about the trust part.
                              @Scotta you can compile the Cinema4D plugin without the header nor the library linkage , you just need the operating system API to hook the DLL


                              [URL-REMOVED] @maxon: This section contained a non-resolving link which has been removed.

                              1 Reply Last reply Reply Quote 0
                              • H
                                Helper
                                last edited by

                                On 02/12/2014 at 19:01, xxxxxxxx wrote:

                                I'm still trying to figure out how to use classes in my DLL's.
                                But you can mark this thread as Solved if want Andreas. I'll leave that up to you.

                                If anyone has a class based example. They can send it to me at: [email protected]

                                -ScottA

                                1 Reply Last reply Reply Quote 0
                                • H
                                  Helper
                                  last edited by

                                  On 07/12/2014 at 13:08, xxxxxxxx wrote:

                                  Hey guys.
                                  I finally managed to figure out how to use dll's with classes in them.
                                  It requires using a class as an "interface". Which is something I've never done before. And it took me a while to figure it all out.
                                  I'll attempt to write a quick tutorial how to do this here.
                                  Hopefully I won't gloss over anything. And it should be a simple cut and paste operation for you to follow along with.

                                  Start Tutorial
                                  Lets begin by using a simple 64bit C4D CommandData plugin to host the .dll to make it simple.
                                  After you have your C4D CD plugin created and working. Lets write the DLL in a brand new separate project, and separate folder.
                                  *Note: I will put this new DLL project in my C4D plugins folder. But it can go anywhere you want.
                                            You'll just have to change the file paths accordingly if you do that.

                                  -Launch Visual Studio and choose "New project"
                                  -I named my project: "ExportingDllClasses" which will create a .dll file with this same name
                                  -Select the C++ ->Win32 Project wizard
                                  -In the wizard's Applications Settings window. Check "dll" & "Empty project"
                                  -If you're using a 64 bit C4D plugin. Go to the Configuration Manager and change the platform to win64. Using copy setting from win32.
                                  -You should now have a .dll making project consisting of only empty folders at this point
                                  -Add a new .h file called "generic_interface.h" to your Header Files folder
                                  -Add a new .cpp file called "mydll.cpp" to your Source Files folder

                                  Put the following code into these two files. And compile it:

                                  /////generic_interface.h  
                                    
                                  //We create an "Interface" class of virtual functions that will be the home class of our .dll  
                                  //Then we will create another class derrived from this interface...which we will then export so other programs can use it  
                                    
                                  class Iclass   
                                  {  
                                  public:  
                                    virtual void destroy() = 0;  
                                    virtual double myAdd(double a, double b) = 0;  
                                    virtual double GetNumber(double num) = 0;  
                                  };  
                                  ///////////////////////////////////////////////////////////  
                                    
                                    
                                    
                                  /////mydll.cpp  
                                    
                                  #include "generic_interface.h"  
                                  #include <iostream>  
                                  #include <windows.h>  
                                  using namespace std;  
                                    
                                  class MyClass : public Iclass   
                                  {  
                                    private:  
                                        int my_data;  
                                    
                                    public:  
                                    MyClass() : my_data(0)  
                                    {  
                                        cerr << "MyClass constructor\n";  //Outputs message if an error occurs  
                                    }  
                                    
                                    ~MyClass()  
                                    {  
                                        cerr << "MyClass destructor\n";  //Outputs message if an error occurs  
                                    }  
                                    
                                    void destroy()  
                                    {  
                                        delete this;  
                                    }  
                                    
                                    double myAdd(double a, double b)  
                                    {          
                                        return a+b;  
                                    }  
                                    
                                    double GetNumber(double num)  
                                    {          
                                        return num;  
                                    }  
                                    
                                  };  
                                    
                                  //This will export the above MyClass class so other programs can use it from the .dll file  
                                  extern "C" __declspec(dllexport) Iclass* __cdecl create_Iclass()  
                                  {  
                                    return new MyClass;  
                                  }
                                  

                                  //Now we add the code to load the .dll file into our C4D plugin in the plugin's main.cpp file

                                  #include <windows.h>  
                                  #include "C:\\Program Files\\MAXON\\Your C4D Version\\plugins\\ExportingDllClasses\\generic_interface.h"  
                                  #include "c4d.h"  
                                  #include <string.h>  
                                    
                                  ...Your other plugin stuff here...  
                                    
                                    
                                    
                                  Bool PluginStart(void)  
                                  {  
                                    //example of installing a crashhandler  
                                    old_handler = C4DOS.CrashHandler;     // backup the original handler (must be called!)  
                                    C4DOS.CrashHandler = SDKCrashHandler; // insert the own handler  
                                    
                                    
                                    /////////////// Dynamically Loading the DLL section //////////////  
                                    
                                    //Get the folder where the .dll file is located  
                                    Filename fn = "C:\\Program Files\\MAXON\\CINEMA 4D R13\\plugins\\ExportingDllClasses\\x64\\Debug";  
                                    char *path = fn.GetString().GetCStringCopy();  
                                    
                                    //If you want to target the res folder use this instead  
                                    //**Just be sure you have actually put the .dll file in the res folder**  
                                    //Filename fn = GeGetPluginPath() + "res";  
                                    //char *sPath = fn.GetString().GetCStringCopy();  
                                                  
                                    //Use SetDllDirectory to set the directory path where the .dll file exists  
                                    //Stops loading the plugin if the .dll file is not found  
                                    if(!SetDllDirectory((LPCSTR)path)) { GePrint("No .dll file Found!!"); return FALSE; }  
                                    
                                    //Try to Load the .dll...Don't load the plugin if it fails  
                                    HMODULE WINAPI dll_handle = LoadLibrary("ExportingDllClasses.dll");  
                                    if(!dll_handle)  
                                    {  
                                        GePrint("Error loading the .dll with LoadLibrary()!!");  
                                        DWORD err = GetLastError();  
                                        GePrint(LongToString((LONG)err));  
                                        return FALSE;   
                                    }  
                                    
                                    //If we have successfully loaded the .dll file  
                                    //First we create a custom type called: "iclass_factory" (it's called a factory because we can create as many instances as we want)  
                                    //Then we point to it using GetProcAddress()  
                                    typedef Iclass* (__cdecl *iclass_factory)();  
                                    iclass_factory icf = reinterpret_cast<iclass_factory>( ::GetProcAddress(dll_handle, "create_Iclass"));  
                                    if(!icf)   
                                    {  
                                        GePrint("Unable to load create_Iclass from DLL!");  
                                        ::FreeLibrary(dll_handle);  
                                    
                                        //Stop loading the plugin if this failed  
                                        return FALSE;          
                                    }  
                                    
                                    //Then we ask the factory to create a a new instance of the Iclass interface class in the dll program  
                                    //Now this instance is local and can be treated like any other class. So we get it's members the same way  
                                    Iclass *ik = icf();  
                                    
                                    //Then we get the functions from the DLL and use them in C4D  
                                    double sum = ik->myAdd(2.4, 2);  
                                    GePrint(RealToString(sum));  
                                    
                                    double num = ik->GetNumber(55.8);        
                                    GePrint(RealToString(num));  
                                    
                                    //Finally. We destroy the instance, and free the memory used  
                                    ik->destroy();  
                                    ::FreeLibrary(dll_handle);  
                                    
                                    
                                    //Lastly...Register your plugin if the dll was loaded properly  
                                    if(!RegisterSimplePlugin()) return FALSE;  
                                    
                                    return TRUE;  
                                  }  
                                    
                                    
                                  ..The rest of you plugin's main.cpp code here...  
                                  

                                  Hopefully this is all clear enough to follow.
                                  Let me know if I left something out.

                                  -ScottA

                                  1 Reply Last reply Reply Quote 0
                                  • H
                                    Helper
                                    last edited by

                                    On 08/12/2014 at 15:24, xxxxxxxx wrote:

                                    I'm not 100% sure "may someone more experienced than me can confirm this" , I think you got a problem here with your library, it may work only for the same compiler & language.

                                    I think a better option would be "don't return the class" , you need to just create the class, hold its pointer somewhere by the library, and make all your exported functions access that pointer to operate on class internal data/functions. , I think this would let the library be portable to any language that accepts C DLLs.

                                    but as I said this needs confirmation as I didn't test it myself.

                                    1 Reply Last reply Reply Quote 0
                                    • H
                                      Helper
                                      last edited by

                                      On 09/12/2014 at 03:33, xxxxxxxx wrote:

                                      Hi Mohamed,
                                      I'm not quiet sure, what you are referring to?

                                      1 Reply Last reply Reply Quote 0
                                      • H
                                        Helper
                                        last edited by

                                        On 09/12/2014 at 07:16, xxxxxxxx wrote:

                                        Hi Andreas, I'm referring to this:

                                        extern "C" \__declspec(dllexport) Iclass\* \__cdecl create_Iclass()  
                                        {  
                                          return new MyClass;  
                                        }
                                        
                                        here he returns the class, and have the header to show the compiler the class layout, but what if the DLL is compiled with MSVC100 and used in a program which requires MSVC110 or Intel compiler,
                                        or even used in a non C++ program, if it is for C++, the Class layout in the DLL will be different from the Class layout in the executable, which will create a potential crash for an undefined behavior "hard to debug".
                                        
                                        this is all an assumption from what I learnt about C++ and why Classes are hard to export.
                                        
                                        I think a possible solution would be:
                                        
                                        
                                        static MyClass\* theClass = nullptr;
                                        
                                        
                                        
                                        extern "C" \__declspec(dllexport) bool \__cdecl create_Iclass()  
                                        {
                                              if(!theClass)
                                                    theClass = new MyClass;
                                        
                                        
                                            return theClass != nullptr;  
                                        }
                                        

                                        rest of the functions should operate on that pointer, delete that pointer at the end, etc... this is considered an encapsulated class which is inside the DLL and won't need to be exported outside the DLL borders. 
                                        off topic: how to put code in a code brackets on forums.

                                        1 Reply Last reply Reply Quote 0
                                        • H
                                          Helper
                                          last edited by

                                          On 09/12/2014 at 11:05, xxxxxxxx wrote:

                                          after some reading, I noticed what Scotta did is a Class interface, which should be fine across different C++ compilers.

                                          but I think it will be a " C++ DLL " so it is unusable for other languages like C#.

                                          1 Reply Last reply Reply Quote 0
                                          • First post
                                            Last post