• Silverlight OOB 获取桌面可视尺寸 F# PInvoke


    潜水潜太久,都快被淹死了。终于鼓起勇气,挣扎着浮出水面。我不知道透一口气后还能坚持多久,总之先给大家一个还凑活的文章吧。

    在说正题之前,先说明一下,我会尽量使用 F# 语言来表述相关的技术,如果对 F# 不熟悉的同学,可以参考MSDN

    http://msdn.microsoft.com/zh-cn/library/dd233154.aspx 

    目前Silverlight OOB环境下虽然可以随意的设置窗体的位置,但是微软似乎还没有提供获取桌面可视范围的参数(屏幕分辨率减去任务栏占用的尺寸后剩下的那部分)。

    微软不支持,不要紧,我们可以 OOB |> Require elevated strust when running outside the browser |> PInvoke Win32 API

    我想,大部分同学玩 SL OOB 都会点上 Use GPU Acceleration 和 Require elevated strust when running outside the browser 吧。如果不知道在哪里,可以在 Solution Explorer 中选中你的 SL 项目,然后按 Alt + Enter 快捷键,然后自己找找吧。

    下面我们开始 DllImport。首先 SL 的 Marshal 是不支持下面这个方法的。

    IntPtr p = Marshal.AllocHGlobal(sizeof(某结构));

    所以我们不得不先用 kernel32.dll 中的 LocalAlloc 来分配内存,不过一定要记得 LocalFree 哦~。~

    [<DllImport("kernel32.dll")>]
    extern IntPtr LocalAlloc(uint32 uFlags, UIntPtr uBytes);

    [<DllImport("kernel32.dll", SetLastError=true)>]
    extern IntPtr LocalFree(IntPtr hMem)

    接下来,uFlags 参数可以有很多的值,一起发出来了,油多不坏菜,代码多不怕硬盘小,不过我们这里只要用到 LPTR,别的留给大家做纪念~。~

    module internal SoftCat.Interop.WinBase

    open System
    open System.Runtime.InteropServices

    #region // Local Memory Flags

    let LMEM_FIXED = 0x0000u
    let LMEM_MOVEABLE = 0x0002u
    let LMEM_NOCOMPACT = 0x0010u
    let LMEM_NODISCARD = 0x0020u
    let LMEM_ZEROINIT = 0x0040u
    let LMEM_MODIFY = 0x0080u
    let LMEM_DISCARDABLE = 0x0F00u
    let LMEM_VALID_FLAGS = 0x0F72u
    let LMEM_INVALID_HANDLE = 0x8000u
    let LHND = LMEM_MOVEABLE ||| LMEM_ZEROINIT
    let LPTR = LMEM_FIXED ||| LMEM_ZEROINIT
    let NONZEROLHND = LMEM_MOVEABLE
    let NONZEROLPTR = LMEM_FIXED

    #endregion

    [<DllImport("kernel32.dll")>]
    extern IntPtr LocalAlloc(uint32 uFlags, UIntPtr uBytes);

    [<DllImport("kernel32.dll")>]
    extern IntPtr LocalFree(IntPtr hMem)

    注意上面的代码,我这里并没有像 C# 通常的做法,定义一个枚举来存放那么多的可选值。并且我也非常建议大家使用 F# Invoke 时直接 let 在 module 中。

    ||| 运算符表示位或运算,但是下面的代码是不符合 F# 目前的语法规范的。

    type A =
    | LPTR = 0x0002u ||| 0x0040u

    好了,能分配内存了,我们接下来需要用 user32.dll 中的 SystemParametersInfo 这个强大的函数来干坏事了,嘿嘿~。~

    [<DllImport("user32.dll")>]
    extern bool SystemParametersInfo(uint32 uiAction, uint32 uiParam, IntPtr pvParam, uint32 fWinIni)

    解释一下几个参数。uiAction 这个是 define 好的,其中我们只需要用到 SPI_GETWORKAREA,而 fWinIni 我们也只用到 0 值。

    如果不想代码太多,像下面这样就可以了

    let SPI_GETWORKAREA = 0x0030u

    pvParam 会返回一个 在 WinDef.h 头文件中的 RECT 结构,我们要预先定义好。 uiParam 则是 RECT 所需的内存尺寸。

    module internal SoftCat.Interop.WinDef

    #nowarn "9"

    open System
    open System.Runtime.InteropServices

    [<StructLayout(LayoutKind.Sequential)>]
    type RECT =
    { left : int; top : int; right : int; bottom : int }
    with
    static member Zero = { left = 0; top = 0; right = 0; bottom = 0 }


    注意,上面的 #nowarn "9",嘿嘿,去掉后报错了吧~。~自己看错误描述~。~不多解释~。~

    RECT 是一个记录,然后添加了一个Zero的静态属性。在F#中,记录实际上是一个不可变类型,实现了好几个接口,比如 IComparable。嘿嘿,这语法糖很甜哦~。~

    有人要问了,这应该创建一个 struct 啊,你创建 class 干吗? 因为 Marshal.PtrToStructure 这个方法有点坑爹,你可以试试改 struct 后它的反应。注意,我们现在是 Silverlight 应用程序。

    ==============================================

    做了这么多准备工作,我们现在开始干活了,先上代码:

    module SoftCat.Windows.Screen

    open System
    open System.Runtime.InteropServices
    open System.Windows
    open SoftCat.Interop

    let getWorkArea () : Rect =
    let size = uint32(Marshal.SizeOf(typeof<WinDef.RECT>))
    let p = WinBase.LocalAlloc(WinBase.LPTR, new UIntPtr(size))
    let r = WinUser.SystemParametersInfo(WinUser.SPI_GETWORKAREA, size, p, 0u)
    let re =
    match r with
    | true ->
    let rect = WinDef.RECT.Zero
    Marshal.PtrToStructure(p, rect) |> ignore
    WinBase.LocalFree p |> ignore
    let point1 = new Point(float(rect.left), float(rect.top))
    let point2 = new Point(float(rect.right), float(rect.bottom))
    new Rect(point1, point2)
    | false ->
    WinBase.LocalFree p |> ignore
    new Rect()
    re


    解释一下。我们只在 SoftCat.Windows.Screen 这个 module 中暴露了一个 getWorkArea 的函数。它返回了 System.Windows.Rect 这个 .net 库中的结构。 个人建议如果 .net 本身有类似的结构,最好不要返回 PInvoke 时自己定义的,因为这样调用者可以偷懒,直接用就好了,不用自己转换一次,嘿嘿。

    let p = WinBase.LocalAlloc(WinBase.LPTR, new UIntPtr(size))

    分配内存,记得要释放

    let r = WinUser.SystemParametersInfo(WinUser.SPI_GETWORKAREA, size, p, 0u)

    取工作区尺寸,数据传给 p 指向的内存空间。

    let rect = WinDef.RECT.Zero
    Marshal.PtrToStructure(p, rect) |> ignore

    从 p 的内存空间中取出数据,然后就可以释放空间了。不得不说一个问题,这里的rect就如前面所说的是一个不可变类型,即使这样,我们也不得不承认直接操作内存的威力。

    剩下的不解释,太容易了,自己看。

        public partial class MainPage : UserControl
    {
    public MainPage()
    {
    InitializeComponent();

    var size = SoftCat.Windows.Screen.getWorkArea();

    MessageBox.Show(string.Format("{0}, {1}, {2}, {3}", size.Left, size.Top, size.Right, size.Bottom));
    }
    }

    调用这个函数,得到下面的结果,有图有真相,呵呵~





     

  • 相关阅读:
    SQL Server用户和角色
    小草手把手教你 LabVIEW 串口仪器控制——VISA 串口配置
    数据库的概念
    域对象
    session
    cookie
    表单
    HttpServletResponse
    JavaWeb核心之Servlet
    Tomcat服务器
  • 原文地址:https://www.cnblogs.com/softcat/p/2338149.html
Copyright © 2020-2023  润新知