Mono中如何实现条件性使用DllImport?
在Mono环境下条件性使用DllImport的方案
嘿,针对你给出的这些Windows API的DllImport代码,想要在Mono环境下实现条件性调用,我整理了几个实用的方案,你可以根据项目的跨平台需求来选择:
1. 条件编译:只在Windows平台编译DllImport代码
这是最简单的方式——利用C#的条件编译符号,让DllImport相关的代码只有在目标平台是Windows时才被编译进程序。Mono支持标准的平台编译符号,比如WINDOWS,你可以在项目编译选项里添加这个符号(针对Windows编译时)。
示例代码:
#if WINDOWS using System.Runtime.InteropServices; using System.Drawing; public class Win32ApiWrapper { [DllImport( "kernel32.dll" )] public static extern int GetCurrentThreadId(); [DllImport( "user32.dll" )] private static extern bool GetWindowRect( IntPtr hWnd, ref Rectangle lpRect ); [DllImport( "user32.dll" )] private static extern int MoveWindow( IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint ); [DllImport( "user32.dll" )] [return: MarshalAs( UnmanagedType.Bool )] private static extern bool SetWindowPos( IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags ); } #endif
这样一来,当你用Mono编译非Windows平台的版本时,这段代码会被直接跳过,完全不会出现在最终程序里,也就不会有找不到dll的问题。
2. 运行时平台检测:安全调用Windows API
如果你的项目需要生成一个跨平台的单一二进制文件(同时支持Windows、Linux、macOS),可以在运行时先检测当前平台,只在Windows环境下调用这些DllImport方法,非Windows平台则提供替代逻辑。
示例代码:
using System; using System.Runtime.InteropServices; using System.Drawing; using System.Runtime.Versioning; public class WindowOperations { // 静态声明Windows API方法 [DllImport( "kernel32.dll" )] private static extern int GetCurrentThreadId(); [DllImport( "user32.dll" )] private static extern bool GetWindowRect( IntPtr hWnd, ref Rectangle lpRect ); // 对外暴露的安全调用方法 public static int GetThreadId() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return GetCurrentThreadId(); } else { // 非Windows平台用托管方式获取线程ID return System.Threading.Thread.CurrentThread.ManagedThreadId; } } public static bool TryGetWindowBounds(IntPtr hWnd, out Rectangle bounds) { bounds = Rectangle.Empty; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return GetWindowRect(hWnd, ref bounds); } else { // 非Windows平台返回false或自定义处理逻辑 return false; } } }
注意:只要不在非Windows平台调用这些DllImport方法,就不会触发DllNotFoundException,这种方式兼顾了代码简洁性和跨平台兼容性。
3. 动态加载Native库:最灵活的跨平台方案
如果想要完全避免静态DllImport带来的潜在加载风险(比如某些Mono环境下可能提前检测dll存在性),可以用动态加载的方式——在运行时根据平台加载对应的Native库,再绑定方法委托。
示例代码:
using System; using System.Runtime.InteropServices; using System.Drawing; public class DynamicWin32Wrapper { // 定义与Windows API匹配的委托类型 private delegate int GetCurrentThreadIdDelegate(); private delegate bool GetWindowRectDelegate(IntPtr hWnd, ref Rectangle lpRect); private delegate int MoveWindowDelegate(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); private delegate bool SetWindowPosDelegate(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags); // 存储委托实例 private static GetCurrentThreadIdDelegate _getCurrentThreadId; private static GetWindowRectDelegate _getWindowRect; private static MoveWindowDelegate _moveWindow; private static SetWindowPosDelegate _setWindowPos; static DynamicWin32Wrapper() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { // 动态加载kernel32.dll并绑定方法 IntPtr kernel32 = NativeLibrary.Load("kernel32.dll"); _getCurrentThreadId = Marshal.GetDelegateForFunctionPointer<GetCurrentThreadIdDelegate>( NativeLibrary.GetExport(kernel32, "GetCurrentThreadId")); // 动态加载user32.dll并绑定方法 IntPtr user32 = NativeLibrary.Load("user32.dll"); _getWindowRect = Marshal.GetDelegateForFunctionPointer<GetWindowRectDelegate>( NativeLibrary.GetExport(user32, "GetWindowRect")); _moveWindow = Marshal.GetDelegateForFunctionPointer<MoveWindowDelegate>( NativeLibrary.GetExport(user32, "MoveWindow")); _setWindowPos = Marshal.GetDelegateForFunctionPointer<SetWindowPosDelegate>( NativeLibrary.GetExport(user32, "SetWindowPos")); } else { // 非Windows平台初始化替代实现 _getCurrentThreadId = () => System.Threading.Thread.CurrentThread.ManagedThreadId; _getWindowRect = (hWnd, rect) => { rect = Rectangle.Empty; return false; }; _moveWindow = (hWnd, x, y, w, h, repaint) => false; _setWindowPos = (hWnd, insertAfter, x, y, cx, cy, flags) => false; } } // 对外暴露的统一调用方法 public static int GetCurrentThreadId() => _getCurrentThreadId(); public static bool GetWindowRect(IntPtr hWnd, ref Rectangle lpRect) => _getWindowRect(hWnd, ref lpRect); public static int MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint) => _moveWindow(hWnd, X, Y, nWidth, nHeight, bRepaint); public static bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags) => _setWindowPos(hWnd, hWndInsertAfter, x, y, cx, cy, uFlags); }
这种方式的优势在于完全可控,所有Native库的加载都由你在运行时决定,非Windows平台可以无缝替换成托管实现,不会有任何加载错误。
内容的提问来源于stack exchange,提问作者Mony Armenchev
相关产品推荐
相关产品推荐

