【发布时间】:2017-11-29 15:13:06
【问题描述】:
在制作跨平台游戏时,我发现需要跨平台 DLL 链接器,我的 DLL 链接器类似乎“符合标准”,但它只适用于 Windows 机器,而它旨在同时适用于两者Window 和 Linux 机器。
这是由于dlsym( IntPtr handle, string symbol ) 返回IntPtr.Zero 而GetProcAddress( IntPtr hModule, string lpProcName ) 返回指向所需符号的有效IntPtr。
代码
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
}
我使用这个自定义类来(部分)加载 LZ4:
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
}
如前所述,每个dlsym 调用都会返回IntPtr.Zero (NULL),而Windows“等效”则可以正常工作。
【问题讨论】:
标签: c# dll mono cross-platform