sketchucation logo sketchucation
    • Login
    🤑 SketchPlus 1.3 | 44 Tools for $15 until June 20th Buy Now

    SKX Project? Win32::API ?

    Scheduled Pinned Locked Moved Developers' Forum
    33 Posts 3 Posters 1.1k Views 3 Watching
    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.
    • thomthomT Offline
      thomthom
      last edited by

      We've had a few Win32 threads lately:
      http://forums.sketchucation.com/viewtopic.php?f=180&t=31213
      http://forums.sketchucation.com/viewtopic.php?f=180&t=33756

      And one of them has been in regard to Win32API.so and version compatibility:
      http://forums.sketchucation.com/viewtopic.php?f=323&t=33955&p=298665

      In another thread we talked about using a more updated win32 library that supports more features:
      http://forums.sketchucation.com/viewtopic.php?f=180&t=33756#p297106

      Then it's the question of version and compatibility.

      Here's a proposal:
      Make an a plugin [Win32Loader] that will be the main source for the Win32Utils (api.so) library. This will allow users to have control over which version should be loaded. (This should be loaded early.)

      Plugins who want to use Win32Utils check for the existence of this plugin. (Probably be useful with a namespace with some constants to check version etc.)

      If Win32Loader is not present on the system, plugins may load a backup version of Win32Utils bundled with the plugin. (Probably useful to put some info into the Win32Loader namespace to indicate to other plugins it's been loaded by a third party plugin and what version.)

      The idea is that instead of wondering which version has the latest, the user can download Win32Loader which will ensure a given version to be used.
      And with version data plugins can check if a required version is loaded and notify the user if an update is required.

      Thoughts?

      Thomas Thomassen — SketchUp Monkey & Coding addict
      List of my plugins and link to the CookieWare fund

      1 Reply Last reply Reply Quote 0
      • AdamBA Offline
        AdamB
        last edited by

        @thomthom said:

        Thoughts?

        ..That you're asking for a whole heap of trouble if you're not very careful - scratch that, even if you are careful. The whole "DLL versioning nightmare" of cyclic dependencies etc is something nobody has ever really solved.

        SxS assemblies was the best worst option MS finally settled on and they are not a thing of beauty.. ie You demand a specific set of specific versions of libs to run.

        Ultimately ultimately, it comes down to being proscriptive about how people setup and use your software. "No, its not a free country, and yes, you will do it this way." Lacrimus Mundi.

        Of course Thomthom, I know you'll give it a go anyway 😄 but its a major PITA that you might want to avoid.

        Adam

        Developer of LightUp Click for website

        1 Reply Last reply Reply Quote 0
        • thomthomT Offline
          thomthom
          last edited by

          Well, the idea was just to avoid PITA version problems - like we have now with win32api.so where many plugins ship different versions.
          But I don't want to start on something that won't make the situation any better and I'd be more than happy to not doing this. Which is why I figured I'd air it here before I embarked on anything.

          Thomas Thomassen — SketchUp Monkey & Coding addict
          List of my plugins and link to the CookieWare fund

          1 Reply Last reply Reply Quote 0
          • Dan RathbunD Offline
            Dan Rathbun
            last edited by

            @thomthom said:

            Make an a plugin [Win32Loader] that will be the main source for the Win32Utils (api.so) library. This will allow users to have control over which version should be loaded. (This should be loaded early.)

            I see this as Phase 2.

            There are several issues to resolve:

            • Platform editions (mswin32 vs minGW,) for the so files: (api.so, changejournel.so*, daemon.so, etc.so* ) [*deprecated]

            • Compiled version matching: ie, mswin32 pre-built binaries were compiled under VC++ 6 (circa Win2000.) XP may be better using binaries compiled with VC++ 7; Vista/Win7 with VC++ 8. However the user's computer needs the appropriate runtime version(s) installed on their system (via Microsoft Download.)

            • There are two major versions of minGW out there. Not sure about the requirements there.* The proper place to put the binaries (so files,) which should be in a "win32" subfolder of the "#{RUBY_PLATFORM}" subfolder, of the correct lib folder.

            • BUT, what is the correct lib?: ruby, vendor_ruby or site_ruby* The same question for all the pure ruby files, some under the "win32" subfolder, but most under a "windows" subfolder.

            I'm not here much anymore.

            1 Reply Last reply Reply Quote 0
            • Dan RathbunD Offline
              Dan Rathbun
              last edited by


              I see this as Phase 1.


              (A way for you to move forward, and get your plugins out in the world.)

              @thomthom said:

              Plugins who want to use Win32Utils check for the existence of this plugin. (Probably be useful with a namespace with some constants to check version etc.)

              Object.const_defined?(:Win32) && Win32.const_defined?(:API)
              returns true if the library has been loaded at the TOPLEVEL_BINDING (ie, within Object.)

              If the above statement is true, then:
              Win32::API::VERSION returns the api_so version string (currently "1.4.5").

              @thomthom said:

              If Win32Loader is not present on the system, plugins may load a backup version of Win32Utils bundled with the plugin. (Probably useful to put some info into the Win32Loader namespace to indicate to other plugins it's been loaded by a third party plugin and what version.)

              There IS a PROPER way to do this, which is allowed and REQUIRED by the Artistic 2.0 license, under which the win32-api package is released.

              (a) REQUIRED that you do not interfere with a user's running the 'standard' edition.

              It (a) requires also, that you have taken my previous advice (which you are likely weary of hearing me repeat,) that ALL of YOUR libraries, classes and plugin modules (and their encapsulted submodules and classes, ..etc.,) be defined and operate WITHIN YOUR TOPLEVEL namespace.

              For an example.. I will "invent" a toplevel namespace for you Thom.
              I will choose an acronym that is familiar to many, as it is the chemical abbreviation for dynamite, ie: TNT
              In your case it stands for: **T**rondheim **N**orway **T**homassen

              (b) Allowed, that you may distribute a 'modified' edition (provided that you give a link back to the RubyForge win32-api source code files webpage. You need NOT distribute source for your modification if you give the link.)

              So step 1, make a copy of api.c, open it and scroll down to:
              line 876
              VALUE mWin32, cAPI, cCallback, cFunction;
              needs to be:
              VALUE mTNT, mWin32, cAPI, cCallback, cFunction;

              line 881
              mWin32 = rb_define_module("Win32");
              needs to be:
              mWin32 = rb_define_module_under(mTNT, "Win32");

              insert lines at line 879:
              /* The TNT module is Thomas Thomassen's TopLevel namespace */
              mTNT = rb_define_module("TNT");

              Step 2, compile editions for, at least

              • minGW* mswin32(MSVCRT6)
                Or perhaps MSVCRT 7 if you feel all XP should be up to 7 by now.

              You can change the so file name after compilation, like api-mswin-60.so (to follow Dan Berger's convention.) That way you can put them all in one subfolder of YOUR plugins lib subfolder:

              Example:
              sketchup 8/plugins/tnt/win32 (might contain:)
              api-mingw.so
              api-mswin-60.so
              api-mswin-71.so
              api-mswin-80.so


              In the next post, I'll show a Ruby wrapper that conditionally loads the user's 'standard' edition or your 'modified' edition, taking version into account.

              I'm not here much anymore.

              1 Reply Last reply Reply Quote 0
              • thomthomT Offline
                thomthom
                last edited by

                @dan rathbun said:

                There IS a PROPER way to do this, which is allowed and REQUIRED by the Artistic 2.0 license, under which the win32-api package is released.

                I did not intend on modifying the the code in any way. Just wanted to bundle a "backup" version with my TT_Lib2 that I can load if the user haven't already installed it.

                @dan rathbun said:

                Object.const_defined?(:Win32) && Win32.const_defined?(:API)
                returns true if the library has been loaded at the TOPLEVEL_BINDING (ie, within Object.)

                If the above statement is true, then:
                Win32::API::VERSION returns the api_so version string (currently "1.4.5").

                That's nice that it has a VERSION constant - Win32API didn't, just pointed back to the top level constant.

                Thomas Thomassen — SketchUp Monkey & Coding addict
                List of my plugins and link to the CookieWare fund

                1 Reply Last reply Reply Quote 0
                • Dan RathbunD Offline
                  Dan Rathbun
                  last edited by

                  @thomthom said:

                  @dan rathbun said:

                  There IS a PROPER way to do this, which is allowed and REQUIRED by the Artistic 2.0 license, under which the win32-api package is released.

                  I did not intend on modifying the the code in any way. Just wanted to bundle a "backup" version with my TT_Lib2 that I can load if the user haven't already installed it.

                  You don't have a choice.. you must modify it, in order to do what you want.

                  Stay tuned... I have a code example coming in the placeholder post above. (Had to get something to eat.)

                  I'm not here much anymore.

                  1 Reply Last reply Reply Quote 0
                  • Dan RathbunD Offline
                    Dan Rathbun
                    last edited by

                    Here is an example of using a toplevel namespace, to conditionally load a private modified version of api.so :

                    <span class="syntaxdefault"></span><span class="syntaxcomment">#<br />#  module TNT  ; Thomas Thomassen's TopLevel Namespace<br />#<br /><br /></span><span class="syntaxdefault">module TNT  </span><span class="syntaxcomment"># Trondheim Norway Thomassen - Toplevel Namespace<br /><br /></span><span class="syntaxdefault">  unless defined</span><span class="syntaxkeyword">?</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">@@</span><span class="syntaxdefault">loaded_once<br />    </span><span class="syntaxkeyword">@@</span><span class="syntaxdefault">loaded_once</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">false<br />    Win32_api_so_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">false<br />    Win32_api_so_internal_ver</span><span class="syntaxkeyword">=</span><span class="syntaxstring">'n.n.n'<br /></span><span class="syntaxdefault">  end<br /><br /><br />  </span><span class="syntaxcomment">#  Check if win32-api is loaded, or if it can be loaded<br /></span><span class="syntaxdefault">  </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault">  if Object</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">const_defined</span><span class="syntaxkeyword">?(;</span><span class="syntaxdefault">Win32</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment"># true, some version of Win32;;API is loaded<br /></span><span class="syntaxdefault">    Win32_api_so_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">true<br />  else<br />    </span><span class="syntaxcomment"># false, no version of Win32;;API is YET loaded.<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment"># 1. search for an installed version and try to load it;<br /></span><span class="syntaxdefault">    begin<br />      </span><span class="syntaxcomment"># the package should be accessable via $LOAD_PATH<br /></span><span class="syntaxdefault">      result </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> require</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'win32/api'</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">    rescue LoadError<br />      </span><span class="syntaxcomment"># the full package was NOT found (or loaded.)<br /></span><span class="syntaxdefault">      Win32_api_so_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">false<br />    rescue<br />      raise </span><span class="syntaxcomment"># any other exception<br /></span><span class="syntaxdefault">    else<br />      </span><span class="syntaxcomment"># the full package WAS found,<br /></span><span class="syntaxdefault">      </span><span class="syntaxcomment"># Let us recheck that it was LOADED with API class;<br /></span><span class="syntaxdefault">      if Object</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">const_defined</span><span class="syntaxkeyword">?(;</span><span class="syntaxdefault">Win32</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> </span><span class="syntaxkeyword">&&</span><span class="syntaxdefault"> Win32</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">const_defined</span><span class="syntaxkeyword">?(;</span><span class="syntaxdefault">API</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">        </span><span class="syntaxcomment"># it WAS loaded<br /></span><span class="syntaxdefault">        Win32_api_so_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">true<br />      else<br />        </span><span class="syntaxcomment"># it was NOT loaded<br /></span><span class="syntaxdefault">        Win32_api_so_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">false<br />      end<br />    end<br />  end<br /><br />  </span><span class="syntaxcomment">#  Check version of win32-api IF loaded against internal version<br /></span><span class="syntaxdefault">  </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault">  if Win32_api_so_external<br />    if Win32</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">API</span><span class="syntaxkeyword">;;</span><span class="syntaxdefault">VERSION </span><span class="syntaxkeyword">>=</span><span class="syntaxdefault"> Win32_api_so_internal_ver<br />      </span><span class="syntaxcomment"># just use the user's external version<br /></span><span class="syntaxdefault">      Use_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">true<br />    else<br />      Use_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">false<br />    end<br />  else<br />    Use_external</span><span class="syntaxkeyword">=</span><span class="syntaxdefault">false<br />  end<br />  <br />  if not Use_external<br />    </span><span class="syntaxcomment"># just load internal version<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault">    so_file</span><span class="syntaxkeyword">=</span><span class="syntaxstring">'api-mswin.so'</span><span class="syntaxdefault"> if RUBY_PLATFORM</span><span class="syntaxkeyword">.include?(</span><span class="syntaxstring">'mswin'</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">    so_file</span><span class="syntaxkeyword">=</span><span class="syntaxstring">'api-mingw.so'</span><span class="syntaxdefault"> if RUBY_PLATFORM</span><span class="syntaxkeyword">.include?(</span><span class="syntaxstring">'mingw'</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">    </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault">    begin<br />      </span><span class="syntaxcomment"># the package should be accessable via $LOAD_PATH<br /></span><span class="syntaxdefault">      result </span><span class="syntaxkeyword">=</span><span class="syntaxdefault"> require</span><span class="syntaxkeyword">(</span><span class="syntaxstring">'tnt/win32/'</span><span class="syntaxkeyword">+</span><span class="syntaxdefault">so_file</span><span class="syntaxkeyword">)<br /></span><span class="syntaxdefault">    rescue LoadError </span><span class="syntaxkeyword">=></span><span class="syntaxdefault"> e<br />       </span><span class="syntaxcomment"># handle load error gracefully<br /></span><span class="syntaxdefault">      if e</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">message</span><span class="syntaxkeyword">.include?(</span><span class="syntaxstring">'no such file to load'</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> <br />        UI</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">messagebox</span><span class="syntaxkeyword">(</span><span class="syntaxstring">"The file '#{so_file}' could not be found!\nCheck your $LOAD_PATH array.\n"</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> <br />      end <br />      stderr</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">write</span><span class="syntaxkeyword">(</span><span class="syntaxdefault">e</span><span class="syntaxkeyword">.</span><span class="syntaxdefault">message</span><span class="syntaxkeyword">)</span><span class="syntaxdefault"> <br />      </span><span class="syntaxcomment">#<br /></span><span class="syntaxdefault">    rescue<br />      raise </span><span class="syntaxcomment"># any other exception<br /></span><span class="syntaxdefault">    end<br />  end<br /><br />end </span><span class="syntaxcomment"># module TNT  &nbsp;</span><span class="syntaxdefault"></span>
                    

                    beta - it could use cleanup and debugging.

                    So now any submodules or classes etc, can make NORMAL calls to win32-api functions, like:
                    Win32::API.new('GetCurrentDirectory', 'LP', 'L', 'kernel32')

                    Ruby begins looking in the current namespace for a module Win32, and backs out testing each namespace, when it gets to module TNT, it will use your supplied 'nested' edition, IF it is loaded.
                    If it is not loaded inside your TNT module, Ruby contines searching, backing up to the TOPLEVEL_BINDING and will find and use the standard edition if it's loaded.
                    Otherwise, a NameError exception will be raised, ie, 'unknown constant Win32::API' ..same as anytime you try and use a undefined module/class.

                    I'm not here much anymore.

                    1 Reply Last reply Reply Quote 0
                    • Dan RathbunD Offline
                      Dan Rathbun
                      last edited by

                      (bump)

                      Added the code example (above)

                      I'm not here much anymore.

                      1 Reply Last reply Reply Quote 0
                      • Dan RathbunD Offline
                        Dan Rathbun
                        last edited by

                        @thomthom said:

                        Well, the idea was just to avoid PITA version problems - like we have now with win32api.so where many plugins ship different versions.

                        The reason we have this problem.. is because really neither the Plugins nor the Tools folders, can actually be thought of or used as Common Library folders.

                        win32api.so and win32ole.so (as well as any other Standard Ruby library file,) do not belong in either folder.

                        If the user installs a full Ruby install (somewhere on their computer,) and pushes the paths to the various Ruby libraries onto the $LOAD_PATH, the paths to Plugins and Tools come before the Ruby lib paths.

                        The old versions in the plugins folder will always get loaded, short-circuiting the users attempt to have the correct files loaded from the proper place.

                        This is also why it will be wrong to just put Berger's api.so (and other utility files,) in a win32 subfolder of Plugins (or Tools.)

                        If we do, we then force the user to shuffle their $LOAD_PATH array members around in the future, when they wish to load files from a full Ruby library folder heirarchy.

                        It brings us back to the fact.. that Sketchup needs it's OWN full Ruby install...

                        .. should it be under the application folder ?

                        .. or under the user's appdata support folder ?

                        I'm not here much anymore.

                        1 Reply Last reply Reply Quote 0
                        • Dan RathbunD Offline
                          Dan Rathbun
                          last edited by

                          Note that a few changes are in the works, for win32-api, so as to make using it with either 1.8.x or 1.9.x easier.

                          See: [#28841] Why not create fat binary gems for all win32 utils gems?
                          Take note of Daniel Berger's post 3 days ago.

                          I'm not here much anymore.

                          1 Reply Last reply Reply Quote 0
                          • Dan RathbunD Offline
                            Dan Rathbun
                            last edited by

                            Also... Dan Berger has begun pushing his updates onto the RubyGems server first.
                            His file list on the RubyForge site, lags behind.

                            RubyGems for win32-api
                            Ruby Gems for windows-api
                            RubyGems for windows-pr

                            • you can manually open the gems with 7z File Manager, and copy files out to do a manual install, if need be.

                            I'm not here much anymore.

                            1 Reply Last reply Reply Quote 0
                            • thomthomT Offline
                              thomthom
                              last edited by

                              @dan rathbun said:

                              You don't have a choice.. you must modify it, in order to do what you want.

                              From what I understood of that license, if I made no modifications at all it could be redistributed as is.

                              @unknownuser said:

                              Permissions for Redistribution of the Standard Version

                              (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package in any medium without restriction, either gratis or for a Distributor Fee, provided that you duplicate all of the original copyright notices and associated disclaimers. At your discretion, such verbatim copies may or may not include a Compiled form of the Package.

                              (3) You may apply any bug fixes, portability changes, and other modifications made available from the Copyright Holder. The resulting Package will still be considered the Standard Version, and as such will be subject to the Original License.

                              Thomas Thomassen — SketchUp Monkey & Coding addict
                              List of my plugins and link to the CookieWare fund

                              1 Reply Last reply Reply Quote 0
                              • Dan RathbunD Offline
                                Dan Rathbun
                                last edited by

                                @unknownuser said:

                                (2) You may Distribute verbatim copies of the Source form of the Standard Version of this Package ...

                                verbatim copies are gem packages with source (sometimes with pre-compiled binaries, rake files, manifest, gemspec.. etc. etc.

                                Not one file pulled out and installed in the wrong place.

                                You cannot ignore the other major clause that you do not interfer with a user running a standard version (which would be installed in the correct place, which is one of the Ruby Library folders.)

                                I'm not here much anymore.

                                1 Reply Last reply Reply Quote 0
                                • thomthomT Offline
                                  thomthom
                                  last edited by

                                  @dan rathbun said:

                                  You cannot ignore the other major clause that you do not interfer with a user running a standard version (which would be installed in the correct place, which is one of the Ruby Library folders.)

                                  What would interfere?

                                  Thomas Thomassen — SketchUp Monkey & Coding addict
                                  List of my plugins and link to the CookieWare fund

                                  1 Reply Last reply Reply Quote 0
                                  • Dan RathbunD Offline
                                    Dan Rathbun
                                    last edited by

                                    @thomthom said:

                                    @dan rathbun said:

                                    You cannot ignore the other major clause that you do not interfer with a user running a standard version (which would be installed in the correct place, which is one of the Ruby Library folders.)

                                    What would interfere?

                                    I explained it above in this post: http://forums.sketchucation.com/viewtopic.php?f=180&t=34124&p=300656#p300629

                                    I'm not here much anymore.

                                    1 Reply Last reply Reply Quote 0
                                    • thomthomT Offline
                                      thomthom
                                      last edited by

                                      @dan rathbun said:

                                      Step 2, compile editions for, at least

                                      • minGW* mswin32(MSVCRT6)
                                        Or perhaps MSVCRT 7 if you feel all XP should be up to 7 by now.

                                      You can change the so file name after compilation, like api-mswin-60.so (to follow Dan Berger's convention.) That way you can put them all in one subfolder of YOUR plugins lib subfolder:

                                      Ah - I missed this when I was reading your replied the first time.
                                      Compiling it into my own namespace. That's be a safe way of doing it.
                                      Only thing is that I'm not too up to grips with compiling.

                                      Thomas Thomassen — SketchUp Monkey & Coding addict
                                      List of my plugins and link to the CookieWare fund

                                      1 Reply Last reply Reply Quote 0
                                      • Dan RathbunD Offline
                                        Dan Rathbun
                                        last edited by

                                        @thomthom said:

                                        Compiling it into my own namespace. That's be a safe way of doing it.
                                        Only thing is that I'm not too up to grips with compiling.

                                        Me either.. Ruby is confusing.

                                        The minGW dev kit is supposed to make compiling under that easier.
                                        Some where I thought Dan Berger has instructions for compiling using MS VC/C++

                                        A possible Pure Ruby alternative, is to "steal" the Win32 module INTO your namespace, then remove the temporary toplevel one.

                                        1. if Win32 has not yet been loaded, load an edition from your TT_Lib subfolder

                                        2. from within your TT_Lib namespace, call (depending on Ruby ver)

                                        • [v1.8.0] Win32 = Object.const_get(:Win32).clone* ['newer'] Win32 = ::Win32.clone
                                        1. remove the top_level Win32 module, via:
                                          Object.instance_eval('remove_const(:Win32)')

                                        2. run GC.start

                                        Note that the newset ver of win32-api is now 1.4.7 (but you must get it from the RubyGems site. The RubyForge site only has 1.4.5 available.)

                                        I'm not here much anymore.

                                        1 Reply Last reply Reply Quote 0
                                        • thomthomT Offline
                                          thomthom
                                          last edited by

                                          What I'm stuck with is compiling it. all tutorials refer to a make command...

                                          Thomas Thomassen — SketchUp Monkey & Coding addict
                                          List of my plugins and link to the CookieWare fund

                                          1 Reply Last reply Reply Quote 0
                                          • thomthomT Offline
                                            thomthom
                                            last edited by

                                            w00t!
                                            Got it compiled and running!
                                            TT::Win32::API 😄

                                            Thomas Thomassen — SketchUp Monkey & Coding addict
                                            List of my plugins and link to the CookieWare fund

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

                                            Advertisement