从 C# 导入 Ruby (Windows)
Importing Ruby from C# (Windows)
我正在尝试创建一个 C# 库,它 "embeds" 一个 Ruby 解释器,使用 DllImport 执行 C-Ruby 函数。
public const string RUBY_DLL = @"msvcrt-ruby18";
[DllImport(RUBY_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern void ruby_init();
// ... Everything in between...
[DllImport(RUBY_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern void ruby_finalize();
这工作得很好,我完全能够导入函数并与 Ruby 交互,但前提是使用 msvcrt-ruby18.dll,这显然已经过时了。我想使用 msvcrt-ruby240.dll,甚至是 msvcrt-ruby19*.dll,但我所做的每一次尝试都失败了。我创建了一个使用 LoadLibrary 和 GetProcAddress 加载函数的变体,这样我就可以使用 Ruby 的任何已安装版本,但一切都失败了。
当使用 DllImport 时,我得到 "DllNotFoundException",这似乎表明 Ruby dll 某处缺少依赖项。我确保我在 x86 下构建,并使用 x86 Ruby 库,所以这不是 BadImageFormat 问题。使用 LoadLibrary 时,我实际上可以在较新版本中调用 ruby_init 而不会出错,但是调用 rb_eval_string 会失败,除了 msvcrt-ruby18.dll。
我对使用 P/Invoke 非常熟悉,不会向他们询问 "how" 到 link。在实际编写 C 代码或准确理解 msvcrt-ruby***.dll、静态库等的构建差异时,我还很新手。
经过广泛的 Google 研究,我找不到一个 links C# 和 Ruby 使用任何比 msvcrt-ruby18.dll 更新的例子,我希望获得一些关于我能做什么以及我必须做什么的见解。如果需要的话,我不反对自己编译 Ruby,但如果需要的话,我会非常感谢任何提示,以及我必须编辑的内容等。
编辑:
这就是我正在做的。
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public class RubyHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32")]
public static extern bool FreeLibrary(IntPtr hModule);
public RubyHandle(string rubyDllPath) : base(true)
{
SetHandle(LoadLibrary(rubyDllPath));
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public override bool IsInvalid
{
get => handle == IntPtr.Zero;
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return FreeLibrary(handle);
}
public static implicit operator IntPtr(RubyHandle rubyHandle)
{
return rubyHandle.DangerousGetHandle();
}
}
并绑定函数...
[SuppressUnmanagedCodeSecurity]
public static class Ruby
{
public const string RUBY_DLL = @"C:\Ruby24\bin\msvcrt-ruby240.dll";
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
private static RubyHandle _rubyLib;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void VoidArgs0();
private static VoidArgs0 _ruby_init;
private static VoidArgs0 _ruby_finalize;
private static VoidArgs0 _ruby_show_version;
private static VoidArgs0 _ruby_show_copyright;
public static void Initialize(string rubyDllPath = null)
{
_rubyLib = new RubyHandle(rubyDllPath ?? RUBY_DLL);
ImportFunctions();
_ruby_init.Invoke();
}
private static void ImportFunctions()
{
_ruby_init = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_init");
_ruby_finalize = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_finalize");
_ruby_show_version = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_show_version");
_ruby_show_copyright = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_show_copyright");
}
private static object ImportFunction<T>(string functionName)
{
var ptr = GetProcAddress(_rubyLib, functionName);
if (ptr == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
}
public static void Release()
{
_ruby_finalize.Invoke();
_rubyLib.Dispose();
}
public static void ShowVersion()
{
_ruby_show_version.Invoke();
}
}
错误一开始就发生了,甚至在它开始调用 "LoadLibrary" 之前,我就收到了 "Specified module was not found" 错误。我还确保 "C:\Ruby24\bin\ruby_builtin_dlls" 和 "C:\Ruby24\bin" 都包含在 PATH 中。
我在用头撞墙。我看不出为什么这不起作用...
好的,所以终于弄明白了,这里 post 可能会遇到类似问题的其他人的答案。
我最终通过 "AddDllDirectory" 为 Ruby 的依赖项手动添加了目录:
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool AddDllDirectory(string lpPathName);
然后使用 "LoadLibraryEx" 反对 "LoadLibrary",并指定 "LOAD_LIBRARY_SEARCH_DEFAULT_DIRS" 标志。
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[System.Flags]
enum LoadLibraryFlags : uint
{
DontResolveDllReferences = 0x00000001,
LoadIgnoreCodeAuthzLevel = 0x00000010,
LoadLibraryAsDatafile = 0x00000002,
LoadLibraryAsDatafileExclusive = 0x00000040,
LoadLibraryAsImageResource = 0x00000020,
LoadLibrarySearchApplicationDir = 0x00000200,
LoadLibrarySearchDefaultDirs = 0x00001000,
LoadLibrarySearchDllLoadDir = 0x00000100,
LoadLibrarySearchSystem32 = 0x00000800,
LoadLibrarySearchUserDirs = 0x00000400,
LoadWithAlteredSearchPath = 0x00000008
}
然后...
public static void Initialize(string rubyDllPath = null)
{
AddDllDirectory(@"C:\Ruby24\bin");
AddDllDirectory(@"C:\Ruby24\bin\ruby_builtin_dlls");
_rubyLib = new RubyHandle(rubyDllPath ?? RUBY_DLL);
ImportFunctions();
_ruby_init.Invoke();
}
显然最终产品会动态执行此操作,但这种方式成功加载了库。
我正在尝试创建一个 C# 库,它 "embeds" 一个 Ruby 解释器,使用 DllImport 执行 C-Ruby 函数。
public const string RUBY_DLL = @"msvcrt-ruby18";
[DllImport(RUBY_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern void ruby_init();
// ... Everything in between...
[DllImport(RUBY_DLL, CallingConvention = CallingConvention.Cdecl)]
private static extern void ruby_finalize();
这工作得很好,我完全能够导入函数并与 Ruby 交互,但前提是使用 msvcrt-ruby18.dll,这显然已经过时了。我想使用 msvcrt-ruby240.dll,甚至是 msvcrt-ruby19*.dll,但我所做的每一次尝试都失败了。我创建了一个使用 LoadLibrary 和 GetProcAddress 加载函数的变体,这样我就可以使用 Ruby 的任何已安装版本,但一切都失败了。
当使用 DllImport 时,我得到 "DllNotFoundException",这似乎表明 Ruby dll 某处缺少依赖项。我确保我在 x86 下构建,并使用 x86 Ruby 库,所以这不是 BadImageFormat 问题。使用 LoadLibrary 时,我实际上可以在较新版本中调用 ruby_init 而不会出错,但是调用 rb_eval_string 会失败,除了 msvcrt-ruby18.dll。
我对使用 P/Invoke 非常熟悉,不会向他们询问 "how" 到 link。在实际编写 C 代码或准确理解 msvcrt-ruby***.dll、静态库等的构建差异时,我还很新手。
经过广泛的 Google 研究,我找不到一个 links C# 和 Ruby 使用任何比 msvcrt-ruby18.dll 更新的例子,我希望获得一些关于我能做什么以及我必须做什么的见解。如果需要的话,我不反对自己编译 Ruby,但如果需要的话,我会非常感谢任何提示,以及我必须编辑的内容等。
编辑: 这就是我正在做的。
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public class RubyHandle : SafeHandleZeroOrMinusOneIsInvalid
{
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32")]
public static extern bool FreeLibrary(IntPtr hModule);
public RubyHandle(string rubyDllPath) : base(true)
{
SetHandle(LoadLibrary(rubyDllPath));
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public override bool IsInvalid
{
get => handle == IntPtr.Zero;
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return FreeLibrary(handle);
}
public static implicit operator IntPtr(RubyHandle rubyHandle)
{
return rubyHandle.DangerousGetHandle();
}
}
并绑定函数...
[SuppressUnmanagedCodeSecurity]
public static class Ruby
{
public const string RUBY_DLL = @"C:\Ruby24\bin\msvcrt-ruby240.dll";
[DllImport("kernel32", SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
private static RubyHandle _rubyLib;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void VoidArgs0();
private static VoidArgs0 _ruby_init;
private static VoidArgs0 _ruby_finalize;
private static VoidArgs0 _ruby_show_version;
private static VoidArgs0 _ruby_show_copyright;
public static void Initialize(string rubyDllPath = null)
{
_rubyLib = new RubyHandle(rubyDllPath ?? RUBY_DLL);
ImportFunctions();
_ruby_init.Invoke();
}
private static void ImportFunctions()
{
_ruby_init = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_init");
_ruby_finalize = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_finalize");
_ruby_show_version = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_show_version");
_ruby_show_copyright = (VoidArgs0) ImportFunction<VoidArgs0>("ruby_show_copyright");
}
private static object ImportFunction<T>(string functionName)
{
var ptr = GetProcAddress(_rubyLib, functionName);
if (ptr == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T));
}
public static void Release()
{
_ruby_finalize.Invoke();
_rubyLib.Dispose();
}
public static void ShowVersion()
{
_ruby_show_version.Invoke();
}
}
错误一开始就发生了,甚至在它开始调用 "LoadLibrary" 之前,我就收到了 "Specified module was not found" 错误。我还确保 "C:\Ruby24\bin\ruby_builtin_dlls" 和 "C:\Ruby24\bin" 都包含在 PATH 中。
我在用头撞墙。我看不出为什么这不起作用...
好的,所以终于弄明白了,这里 post 可能会遇到类似问题的其他人的答案。
我最终通过 "AddDllDirectory" 为 Ruby 的依赖项手动添加了目录:
[DllImport("kernel32", CharSet = CharSet.Unicode, SetLastError = true)]
static extern bool AddDllDirectory(string lpPathName);
然后使用 "LoadLibraryEx" 反对 "LoadLibrary",并指定 "LOAD_LIBRARY_SEARCH_DEFAULT_DIRS" 标志。
[DllImport("kernel32", SetLastError = true)]
static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags);
[System.Flags]
enum LoadLibraryFlags : uint
{
DontResolveDllReferences = 0x00000001,
LoadIgnoreCodeAuthzLevel = 0x00000010,
LoadLibraryAsDatafile = 0x00000002,
LoadLibraryAsDatafileExclusive = 0x00000040,
LoadLibraryAsImageResource = 0x00000020,
LoadLibrarySearchApplicationDir = 0x00000200,
LoadLibrarySearchDefaultDirs = 0x00001000,
LoadLibrarySearchDllLoadDir = 0x00000100,
LoadLibrarySearchSystem32 = 0x00000800,
LoadLibrarySearchUserDirs = 0x00000400,
LoadWithAlteredSearchPath = 0x00000008
}
然后...
public static void Initialize(string rubyDllPath = null)
{
AddDllDirectory(@"C:\Ruby24\bin");
AddDllDirectory(@"C:\Ruby24\bin\ruby_builtin_dlls");
_rubyLib = new RubyHandle(rubyDllPath ?? RUBY_DLL);
ImportFunctions();
_ruby_init.Invoke();
}
显然最终产品会动态执行此操作,但这种方式成功加载了库。