You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:59:00