• Login
sketchucation logo sketchucation
  • Login
πŸ€‘ SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

DLL callback to ruby

Scheduled Pinned Locked Moved Developers' Forum
41 Posts 4 Posters 10.5k Views
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.
  • A Offline
    Alienizer
    last edited by 30 Jul 2011, 04:05

    Anyone knows how to pass a callback proc to a DLL from ruby? When sketchup starts, it loads my .rb file, and in turn it loads my .dll, but I need my .dll to call back a proc in my ruby script!

    Thanks!

    I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

    1 Reply Last reply Reply Quote 0
    • A Offline
      Alienizer
      last edited by 30 Jul 2011, 23:36

      Thanks Dan, but I could use a little more help πŸ˜’

      I found this...
      https://www.ruby-forum.com/topic/83478

      but I can't grasp on how to do it!

      I can get my C code to call ruby functions in the ruby dll but I can't get it to call a user function in my sketchup .rb script!

      I'm not sure how to register the callback, and how to pass the user proc to my dll, if that's how it work!

      I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

      1 Reply Last reply Reply Quote 0
      • D Offline
        Dan Rathbun
        last edited by 31 Jul 2011, 00:30

        @alienizer said:

        I found this...
        https://www.ruby-forum.com/topic/83478

        but I can't grasp on how to do it!

        That's a very advanced concept.. and the author admits it's not working yet.

        Go thru and read the SCF topics listed on my [ info ] post.

        @alienizer said:

        I can get my C code to call ruby functions in the ruby dll but I can't get it to call a user function in my sketchup .rb script!

        I'm not sure how to register the callback, and how to pass the user proc to my dll, if that's how it work!

        #! ruby
        module Alien
        
          @@my_set=[]
        
          def self.callback(a, *b)
            @@my_set = b.collect {|i| i*a }
            puts(@@my_set)
          end
        
        end # module
        
        
        // C
          rb_eval_string("Alien.callback(9, 1, 2, 3)")
        
        

        I'm not here much anymore.

        1 Reply Last reply Reply Quote 0
        • A Offline
          Alienizer
          last edited by 31 Jul 2011, 01:17

          Is that all 😲 😲
          It works like a charm Dan

          Maybe I'll drop programming and go get a job at McDonalds. You are the genius here. Thanks a million.

          I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

          1 Reply Last reply Reply Quote 0
          • D Offline
            Dan Rathbun
            last edited by 31 Jul 2011, 03:11

            @alienizer said:

            Is that all 😲 😲

            Well.. it's the simplest example,... but the slowest.

            Later on when you want speed, you may wish to use one of rb_funcall, rb_funcall2 or rb_funcall3

            Something like (untested):

            // C
            #include "ruby.h"
            
            VALUE modAlien; // C-side id of your module
            
            static VALUE c_add_two(VALUE self, VALUE anInt)
            {
              // make a callback to Ruby
              rb_funcall(modAlien,rb_intern("callback"),4,
                         anInt,
                         INT2NUM(1),
                         INT2NUM(2),
                         INT2NUM(3));
              // now return the arg plus 2
              return INT2NUM( NUM2INT(anInt)+2 );
            }
            
            // other DLL functions
            BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
            {
              return TRUE;
            }
            
            
            // Entry point for Ruby require() method;
            void Init_ExactcaseFilenameofDLL()
            {
              modAlien = rb_define_module("Alien")
              // define the Alien.add_two(arg) method;
              rb_define_singleton_method(modAlien, "add_two", c_add_two, 1);
            }
            
            

            If your DLL file was named KillAliens.DLL
            then the Init function must be named Init_KillAliens
            otherwise require() will puke out an entry point not found error.
            (Also happens if someone changes the case or name of the DLL file after compilation.)

            I'm not here much anymore.

            1 Reply Last reply Reply Quote 0
            • A Offline
              Alienizer
              last edited by 31 Jul 2011, 18:24

              That's what I tried to do at first, but failed with exception errors all over. I wasn't using INT2NUM or anything. I was going the wrong way, not knowing what I was doing. I wish I knew what you know.

              Thanks Dan, your help was more than helpfull, and much appreciated. Without your help, I'd still be in the dark.

              I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

              1 Reply Last reply Reply Quote 0
              • D Offline
                Dan Rathbun
                last edited by 31 Jul 2011, 18:43

                Yes well I was a cubscout.

                If your not going to be calling a method within a loop, where speed is an issue, the rb_eval_string makes more sense. Less code to debug, less things to go wrong.

                I'm not here much anymore.

                1 Reply Last reply Reply Quote 0
                • A Offline
                  Alienizer
                  last edited by 31 Jul 2011, 20:11

                  @unknownuser said:

                  // Entry point for Ruby require() method:
                  void Init_ExactcaseFilenameofDLL()

                  If I put my dll in plugins/test/ along with test.rb, does SK load both or do I have to (require "test.dll") in the .rb script?

                  I did have void Init_test() and exported it too, but I still get this error...

                  Error Loading File test.rb
                  (eval):16:in `initialize': LoadLibrary: C:/Program Files (x86)/Google/Google SketchUp 8/Plugins/test/test.dll

                  which is the following line in the .rb script...

                  $testproc = Win32API.new("test.dll", "test", ["V"], "V")

                  I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

                  1 Reply Last reply Reply Quote 0
                  • D Offline
                    Dan Rathbun
                    last edited by 31 Jul 2011, 20:42

                    @alienizer said:

                    @unknownuser said:

                    // Entry point for Ruby require() method:
                    void Init_ExactcaseFilenameofDLL()

                    If I put my dll in plugins/test/ along with test.rb, does SK load both or do I have to (require "test.dll") in the .rb script?

                    No SK does not load either of them automatically, and does not autoload DLL files.
                    use:
                    require("test.dll")
                    within the Ruby script to load it.

                    @alienizer said:

                    I did have void Init_test() and exported it too, but I still get this error...

                    I don't think you need to export it, unless you use win system calls.

                    @alienizer said:

                    Error Loading File test.rb
                    (eval):16:in `initialize': LoadLibrary: C:/Program Files (x86)/Google/Google SketchUp 8/Plugins/test/test.dll

                    which is the following line in the .rb script...

                    $testproc = Win32API.new("test.dll", "test", ["V"], "V")

                    Why not just expose your C-side methods to Ruby, like shown in the example above?

                    That way the C code can be compiled on both platforms as a .so file. (Windows users won't need to use Win32API calls.)

                    Also... I think a void or empty parameter is called like:
                    $testproc = Win32API.new("test.dll", "test", [], "V")

                    I'm not here much anymore.

                    1 Reply Last reply Reply Quote 0
                    • A Offline
                      Alienizer
                      last edited by 31 Jul 2011, 22:32

                      I have a testloader.rb in the plugins directory that is...

                      $;.push Sketchup.find_support_file("Plugins/test/")
                      Sketchup;;require 'test'
                      

                      ...and then my test.rb loads, and in turn, the alien.dll loads as well!

                      Does the dll load because I have $testproc = Win32API.new("alien.dll", "test", [, "V")] in the rb script?

                      I do not have a require("alien.dll") in the rb script, do I still need to put it in?

                      All goes well in C and in Delphi. But in FPC using the same identical code as Delphi, SK gives me that error I posted last. I do it in C but my wife knows nothing about C and she's learning Pascal, so she use Lazarus/FPC. Any ideas why the FPC dll won't load in SK but it will load using any other test app I made to test it!!

                      I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

                      1 Reply Last reply Reply Quote 0
                      • A Offline
                        Alienizer
                        last edited by 31 Jul 2011, 22:35

                        @unknownuser said:

                        That way the C code can be compiled on both platforms as a .so file. (Windows users won't need to use Win32API calls.)

                        So than, how do you call a function in the dll if you don't use Win32API calls?

                        I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

                        1 Reply Last reply Reply Quote 0
                        • D Offline
                          Dan Rathbun
                          last edited by 1 Aug 2011, 00:34

                          @alienizer said:

                          @unknownuser said:

                          That way the C code can be compiled on both platforms as a .so file. (Windows users won't need to use Win32API calls.)

                          So than, how do you call a function in the dll if you don't use Win32API calls?

                          You expose them to Ruby (which itself is written in C,) by using the C-side functions:
                          rb_define_method, rb_define_module_function, & rb_define_singleton_method

                          I showed you an example of creating a C-side function c_add_two( anInt ), that is exposed to Ruby as the module method Alien.add_two( intarg )

                          You can actually write your entire Ruby module in C.

                          Again, I suggest you take the time to read the "Extending Ruby" from "Programming Ruby: The Pragmatic Programmer's Guide" (aka the "Pick-Axe" book.) The link is #3 in the 2nd post of this thread.

                          I don't have anything to say about writing Ruby extensions in any code except C/C++, other than to say "good luck" and you may wish to look at SWIG. (Links on the [Info] posting, #1 above, in second post.)

                          I'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • A Offline
                            Alienizer
                            last edited by 1 Aug 2011, 00:53

                            oh ok, I get it. So in my loader, I can simply have it load the dll which will do all of what the rb does, and remove the rb alltogether?

                            I am reading up on your links, much to read and learn!

                            I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

                            1 Reply Last reply Reply Quote 0
                            • D Offline
                              Dan Rathbun
                              last edited by 1 Aug 2011, 00:56

                              @alienizer said:

                              I have a testloader.rb in the plugins directory that is...

                              $;.push Sketchup.find_support_file("Plugins/test/")
                              > Sketchup;;require 'test'
                              

                              ...and then my test.rb loads, and in turn, the alien.dll loads as well!

                              Does the dll load because I have $testproc = Win32API.new("alien.dll", "test", [, "V")] in the rb script?

                              Yes, it is loaded by the Ruby Win32API class (which is written in C,) which calls the Windows API C function LoadLibrary. But this is Win32 platform specific, and the functions to be called must have been exported.

                              @alienizer said:

                              I do not have a require("alien.dll") in the rb script, do I still need to put it in?

                              Depends.. yes if you want to write extensions that expose the C-side functions, as Ruby methods; and if you plan to have your extensions be cross-platform (also compiled on Mac Xcode with as few changes as possible.)

                              I'm not here much anymore.

                              1 Reply Last reply Reply Quote 0
                              • D Offline
                                Dan Rathbun
                                last edited by 1 Aug 2011, 01:04

                                @alienizer said:

                                oh ok, I get it. So in my loader, I can simply have it load the dll which will do all of what the rb does, and remove the rb alltogether?

                                Well.. yes. That's what all the Ruby extended libraries do. They are compiled .so (shared object) files that create the Ruby classes and all their methods, that Ruby scripters use.

                                However.. the loader is actually the require() method, and the library is loaded on demand when it's needed.

                                I'm not here much anymore.

                                1 Reply Last reply Reply Quote 0
                                • A Offline
                                  Alienizer
                                  last edited by 1 Aug 2011, 01:39

                                  I see. So if I use require "test.so " in my loader, then I bypass win32api and it can be cross-platform? Can the dll do everything the rb can do? How do you get the Sketchup:: object in the dll? I suppose I need sketchup.h or something?

                                  I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

                                  1 Reply Last reply Reply Quote 0
                                  • D Offline
                                    Dan Rathbun
                                    last edited by 1 Aug 2011, 02:42

                                    @alienizer said:

                                    I see. So if I use require "test.so " in my loader, then I bypass win32api and it can be cross-platform?

                                    Yes.. because an edition of Ruby is compiled for the platform it will run on (or even compiled on the user's platform itself.)
                                    There is a standard platform subdir, beneath the Ruby lib dir, whose name is equal to the Ruby constant RUBY_PLATFORM.
                                    On each platform, the Ruby require() method's C-side code, will use a platform specific API call to load shared library files (.so,.o,.dll,.dylib,...)
                                    So on Windows, require() is likely to be calling LoadLibrary from the kernel32.dll library. On another platform such as OSX, the name of the Lib file, and the load function may differ, but in the Ruby C source, conditional defines will take care of all that.

                                    So your code would also need to be compiled on both platforms. I don't have a Mac, but I imagine a few tweaks may be necessary, such as conditional defines, etc.

                                    @alienizer said:

                                    Can the dll do everything the rb can do?

                                    I would expect so. You must realize the Ruby is written in C, and creates C objects.
                                    A Ruby script is just an invention for human readability and productivity.
                                    The Ruby interpreter DLL (msvcrt-ruby18.dll) reads and converts the script-type definition blocks (for modules, classes and methods,) into C calls using the functions that begin with "rb_"

                                    @alienizer said:

                                    How do you get the Sketchup:: object in the dll? I suppose I need sketchup.h or something?

                                    The Sketchup object is a Ruby module, and you can call any of it's module methods from the C-side using rb_eval_string

                                    //C
                                      int_ver_major = NUM2INT( rb_eval_string("Sketchup.version.to_i") );
                                    

                                    However if you wish to use rb_funcall, you create a C-side identifier for it at the top of your code, and then in the Init_dllname function, you "init" it using rb_define_module("Sketchup"), like so:

                                    //C
                                    
                                    VALUE modSketchup;
                                    
                                    // Entry point for Ruby require() method;
                                    void Init_ExactcaseFilenameofDLL()
                                    {
                                      modSketchup = rb_define_module("Sketchup");
                                    }
                                    

                                    Since the module already exists, it's not re-created, your just making a handle to it. (The Sketchup Ruby API objects are created in a similar way, but are part of the sketchup.exe. When Sketchup loads, the last 3 things it does is, load the Ruby interpreter DLL, define the Ruby API objects, and process the Plugins & Tools folders.)

                                    I'm not here much anymore.

                                    1 Reply Last reply Reply Quote 0
                                    • A Offline
                                      Alienizer
                                      last edited by 1 Aug 2011, 03:05

                                      aaaaaaaaaaah OK, now it's clear to me. I can see exactly what you mean and how this works.

                                      Dan, thank you so much for your time to explain all this to me. I don't know how to thank you enough, and I wish I could return the favor, but you are obviously way above me that there is nothing you can learn from me. I realy appreciate it Dan.

                                      Now I'm gonna go play.........

                                      I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

                                      1 Reply Last reply Reply Quote 0
                                      • A Offline
                                        Alienizer
                                        last edited by 1 Aug 2011, 03:14

                                        Dan, one last question, can the dll be compiled in 64bit or does it have to be 32bit like SK?

                                        Does the handle return by rb_define_module('Sketchup') correspond to the ISkpApplication interface?

                                        I'm from Mars and moved to Earth to alienize you. Unfortunately, I became humanized.

                                        1 Reply Last reply Reply Quote 0
                                        • D Offline
                                          Dan Rathbun
                                          last edited by 1 Aug 2011, 04:33

                                          @alienizer said:

                                          Dan, one last question, can the dll be compiled in 64bit or does it have to be 32bit like SK?

                                          I would say 32bit, because the Ruby that Sketchup loads is also 32bit. There is a 64bit Ruby edition, but it may be in the 1.9.x branch, which Sketchup cannot load even 1.9.x 32bit.

                                          @alienizer said:

                                          Does the handle return by rb_define_module('Sketchup') correspond to the ISkpApplication interface?

                                          I would say "not exactly". There may be some C++ methods from that interface, that are "Ruby wrapped" into the Sketchup module, as well as others from different C++ objects.

                                          Getting a handle on the application object (and from there it's window object, and then it's UI element objects, and so on...) are methods that the current Ruby Sketchup module lacks. You'll have to use a C++ call, as shown in the SDK examples to get the app handle.

                                          I'm not here much anymore.

                                          1 Reply Last reply Reply Quote 0
                                          • 1
                                          • 2
                                          • 3
                                          • 1 / 3
                                          1 / 3
                                          • First post
                                            1/41
                                            Last post
                                          Buy SketchPlus
                                          Buy SUbD
                                          Buy WrapR
                                          Buy eBook
                                          Buy Modelur
                                          Buy Vertex Tools
                                          Buy SketchCuisine
                                          Buy FormFonts

                                          Advertisement