dlsym返回NULL,其中GetProcAddress返回有效的IntPtr

时间:2017-06-26 17:41:14

标签: c# dll mono cross-platform

在制作跨平台游戏时,我发现需要一个跨平台的DLL链接器,我的DLL链接器类似乎符合标准"但它只能在Windows机器上运行,而它可以在Window和Linux机器上运行。

这是由于dlsym( IntPtr handle, string symbol )返回IntPtr.ZeroGetProcAddress( 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.ZeroNULL),而Windows"等同于"工作得很好。

1 个答案:

答案 0 :(得分:0)

似乎名称错误/发布差异是问题。 lz4.dll导出LZ4_decompress_default,而liblz4.so导出LZ4_decompress