Ruby Fiddle dll search Path - ruby

I am trying to load a dll using the following fiddle code: ( If there is a easier way to load a dll and call a function on it that would solve my problem I am happy to hear it )
require 'fiddle' unless defined?(Fiddle)
require 'fiddle/import' unless defined?(Fiddle::Importer)
extend Fiddle::Importer
dlload "Foo.dll"
dlload "C:/Folder/Foo.dll" works like I want. But the problem is that Foo.dll is a c++ dll that requires several other dlls in various locations.
How do I go about just calling dlload "Foo.dll" without a full path. In short how do I add a list of directories to the Ruby dll search path?
I have tried the following:
$LOAD_PATH.unshift("C:/Folder")
ENV['RUBYLIB'] = "C:/Folder"
ENV['LD_LIBRARY_PATH'] = "C:/Folder"
The ruby interpreter is running inside another program (Sketchup), so my environment is restricted.

The only solution I got to work was writing a small kernel32.dll library and calling AddDllDirectory from within it.

Related

Ruby Fiddle - Reload dynamic library

I am using Fiddle to load a function from a dynamic library written in C. During development I need to make changes to the library and reload it for testing. The problem is that dlload does not reload the library unless I relaunch the script. I am developing a plugin in Ruby for SketchUp so "relaunching" the script would actually mean to restart the application.
Sample code to show the issue:
require 'fiddle'
require 'fiddle/import'
require 'fileutils'
module RG
extend Fiddle::Importer
dlload 'utils.dylib'
end
FileUtils.rm 'utils.dylib'
module RG
extend Fiddle::Importer
dlload 'utils.dylib'
end
If you launch the script the first time there is no error. If you launch it the second time you get the error: image not found, as expected.
So it seems that during the execution the library is imported only once.
Any suggestion on how to force Fiddle to reload the library?
I am using macos and Ruby 2.0
Just in case someone needs this in the future. You can use
#handler.handlers.each {|h| h.close unless h.close_enabled? } unless #handler.nil?
GC.start
So you first close all the handlers and then force the garbage collection.

Is there a way when using boost.python on windows to only load pythonXX.dll on demand?

I have a C API DLL we created for a USB product we make that I thought would be nice to be able to import with python without using any wrapping functions like ctypes. Our DLL is already statically linked with boost and the C runtime for other functionality without any problems. Once I add any exported python functions such as this simple test:
#include <boost/python.hpp>
char const* greet()
{
return "hello, world";
}
BOOST_PYTHON_MODULE(mymodule)
{
using namespace boost::python;
def("greet", greet);
}
The DLL that gets built now depends on python34.dll at load time. Normally this would be fine, but the reality is that most users of our DLL currently aren't going to use the python functionality. What I would like to happen is to only have this dependency if the module gets loaded by python, or when a python function gets called for the first time. That way only python users would need to have the python DLL, and our DLL would not get an error loading on systems that lack the python DLL when not being loaded by python code.
On solution I can think of would be to create a separate DLL just for python users that either calls our DLL or includes all of its functionality. I would prefer to just have one binary if possible though. Once there are two or more files involved I question the value over just distributing a wrapper written in python using ctypes.
In my own Windows C/C++ code, I always load whatever DLLs I can on-demand so that I can do better error reporting to the user and maybe allow the program to run with crippled functionality if it is not there. I was a little surprised that boost.python was pulling in python34.dll right at load time.

How does require 'digest/md5' works?

require 'digest/md5' # => true
Digest::Md5.hexdigest('') # => "d41d8cd98f00b204e9800998ecf8427e"
Which file does this require use? Is an actual file being required? Or is it required programmatically?
This is (sorta) explained in the documentation for Kernel#require:
require(name) → true or false
Loads the given name, returning true if successful and false if
the feature is already loaded.
If the filename does not resolve to an absolute path, it will be
searched for in the directories listed in $LOAD_PATH ($:).
If the filename has the extension “.rb”, it is loaded as a source file; if
the extension is “.so”, “.o”, or “.dll”, or the default shared library
extension on the current platform, Ruby loads the shared library as a Ruby
extension. Otherwise, Ruby tries adding “.rb”, “.so”, and so on to the
name until found. If the file named cannot be found, a LoadError will be raised.
By default $LOAD_PATH will include the directories where Ruby's standard library lives. Directories, plural, because native (compiled binary) modules in the standard library live in a different directory than pure-Ruby modules.
Digest is actually a great example because some of its files are native and others are Ruby.
On my Mac $LOAD_PATH includes (among others) these two directories (truncated for readability):
.../usr/lib/ruby/2.0.0
.../usr/lib/ruby/2.0.0/universal-darwin15
The former is where pure-Ruby modules live and the latter is where native modules live.
So when I do require 'digest/md5' Ruby first looks in .../usr/lib/ruby/2.0.0/ for a directory named digest/, and upon finding it, for a file in it named md5.rb. It doesn't find it, so it looks for md5.bundle. Why .bundle and not .so or .o? Because that's the "default shared library extension" that Ruby was configured with when it was compiled for OS X. Go figure.
Ruby doesn't find it there, so next it looks in .../usr/lib/ruby/2.0.0/universal-darwin15/digest/. There's no md5.rb there, but there is an md5.bundle, so Ruby loads it.
It loads a C-extension that can be found in Ruby's source dir - ruby/ext/digest/md5/
It's one of Ruby's standard libraries (stdlib), which means that it's available in pretty much every implementation of Ruby. It does, in fact, load a file. But it's a file that was copied to your computer when you installed Ruby and is pretty much always going to be available to you. Another good example of a standard library is DateTime.
One thing that confused me a lot when I started learning Ruby and Rails was that Rails requires a fair number of Ruby's standard libraries for you. So it's good to be aware of what's actually going on under the hood.

What are the ruby conventions for $LOAD_PATH for non-gems?

I have some code in a ruby project. From what I understand, library code should live in the lib folder.
so I have
/lib
/mymodule.rb
/mymodule
/somefile.rb
I have been using this internal library by calling require 'relative/path/to/lib/mymodule.rb'. What is the best practice to all this library code to the $LOAD_PATH? All I want to do is say
require 'mymodule'
instead of requiring the file directly.
I have been googling around for the last 30mins, and it is not obvious to be what the best practice/convention is.
You can either use require_relative, which might be a bit messy if
you move files around and therefore slightly unstable. Or set
$LOAD_PATH in a startup/init file of your project. Just be aware of
using your own subfolder in that case to avoid name conflicts with
your gems.

IronRuby - how to require .NET assemblies without strong name?

This page on the IronRuby help website talks about being able to 'require' some well-known assemblies such as System.Windows.Forms without needing to crank out the entire 'ah-come-on-gimme-a-break-here-you-cannot-be-serious' strong name of the assembly.
In the docs it says this:
>>> require "System.Windows.Forms"
=> true
But when I try the same 'require', I get this:
>>> require "System.Windows.Forms"
IronRuby.Libraries:0:in 'require': no such file to load -- System.Windows.Forms (LoadError)
from :0:in 'Initialize##1'
What might I be doing wrong? Could it be a setup problem? I can't see this "libs directory on the load path" that gets mentioned in the documentation. Is the documentation wrong?
Thanks.
The way that this works is because the IronRuby guys have written a bunch of wrapper scripts.
Look in <your ironruby install path>\lib\ironruby and you'll see System.Windows.Forms.rb, System.Drawing.rb etc.
What happens when you do require 'System.Windows.Forms' is that IronRuby finds that rb file and runs it. The code inside that file just does the full strong-named require for you.
If you want to load other dll's that they haven't written wrappers for, you have 3 options:
require the full path to the dll (eg c:\program files\whatever\whatever\blah.dll)
require the strong name (this only works if it's in the GAC or somewhere else IronRuby can find dll's in)
use load_assembly - This is the most convenient, but IIRC it loads the dll into the LoadFrom context, not the Load context.
If you don't understand what that means, then basically it's fine in testing, but don't do it in production :-)
Well, it was a setup problem - there were two copies of ir.exe in the IronRuby download, and I was using the wrong one.

Resources