While making a cross-platform game I found the need for a cross-platform DLL linker, my DLL linker class seems to be "up to standards" but it only works on Windows machines, while it is designed to work on both Window and Linux machines.
This is due to dlsym( IntPtr handle, string symbol )
returning IntPtr.Zero
whereas GetProcAddress( IntPtr hModule, string lpProcName )
returns a valid IntPtr
pointing to the desired symbol.
Code
public static class DLL
{
#region DllImport
[DllImport( "kernel32.dll" )]
private static extern IntPtr LoadLibrary( string filename );
[DllImport( "kernel32.dll" )]
private static extern IntPtr GetProcAddress( IntPtr hModule, string procname );
[DllImport( "libdl.so" )]
private static extern IntPtr dlopen( string filename, int flags );
[DllImport( "libdl.so" )]
private static extern IntPtr dlsym( IntPtr handle, string symbol );
const int RTLD_NOW = 2;
#endregion
#region Abstracted
public static bool __linux__
{
get
{
int p = (int)Environment.OSVersion.Platform;
return ( p == 4 ) || ( p == 6 ) || ( p == 128 );
}
}
#endregion
#region Fields
private static Type _delegateType = typeof( MulticastDelegate );
#endregion
#region Methods
public static IntPtr Load( string filename )
{
IntPtr mHnd;
if ( __linux__ )
mHnd = dlopen( filename, RTLD_NOW );
else
mHnd = LoadLibrary( filename );
return mHnd;
}
public static IntPtr Symbol( IntPtr mHnd, string symbol )
{
IntPtr symPtr;
if ( __linux__ )
symPtr = dlsym( mHnd, symbol );
else
symPtr = GetProcAddress( mHnd, symbol );
return symPtr;
}
public static Delegate Delegate( Type delegateType, IntPtr mHnd, string symbol )
{
IntPtr ptrSym = Symbol( mHnd, symbol );
return Marshal.GetDelegateForFunctionPointer( ptrSym, delegateType );
}
public static void LinkAllDelegates( Type ofType, IntPtr mHnd )
{
FieldInfo[] fields = ofType.GetFields( BindingFlags.Public | BindingFlags.Static );
foreach ( FieldInfo fi in fields )
{
if ( fi.FieldType.BaseType == _delegateType )
{
fi.SetValue( null, Marshal.GetDelegateForFunctionPointer( Symbol( mHnd, fi.Name ), fi.FieldType ) );
}
}
}
#endregion
}
I used this custom class to (partially) load LZ4 as such:
public static class LZ4
{
private static string _lib = "lz4.dll";
private static IntPtr _dllHnd;
#region Delegates
public delegate int PFNLZ4_COMPRESS_DEFAULTPROC( IntPtr source, IntPtr dest, int sourceSize, int maxDestSize );
public delegate int PFNLZ4_COMPRESS_FASTPROC( IntPtr source, IntPtr dest, int sourceSize, int maxDestSize, int acceleration );
public delegate int PFNLZ4_COMPRESS_HCPROC( IntPtr src, IntPtr dst, int srcSize, int dstCapacity, int compressionLevel );
public delegate int PFNLZ4_DECOMPRESS_FASTPROC( IntPtr source, IntPtr dest, int originalSize );
public delegate int PFNLZ4_COMPRESSBOUNDPROC( int inputSize );
#endregion
#region Methods
public static void LZ4_Link()
{
if ( DLL.__linux__ )
_lib = "./liblz4.so";
_dllHnd = DLL.Load( _lib );
Console.WriteLine( "LZ4_Link: OS is {0}", DLL.__linux__ ? "Linux" : "Windows" );
Console.WriteLine( "LZ4_Link: Linked {0}", _lib );
Console.WriteLine( "LZ4_Link: _dllHnd -> 0x{0}", _dllHnd.ToString( "X" ) );
DLL.LinkAllDelegates( typeof( LZ4 ), _dllHnd );
}
public static PFNLZ4_COMPRESS_DEFAULTPROC LZ4_compress_default;
public static PFNLZ4_COMPRESS_FASTPROC LZ4_compress_fast;
public static PFNLZ4_COMPRESS_HCPROC LZ4_compress_HC;
public static PFNLZ4_DECOMPRESS_FASTPROC LZ4_decompress_fast;
public static PFNLZ4_COMPRESSBOUNDPROC LZ4_compressBound;
#endregion
}
As said, every dlsym
call returns IntPtr.Zero
(NULL
), whereas the Windows "equivalent" works just fine.