首页 文章

从F#消费UWP应用服务

提问于
浏览
5

我使用C#创建通用Windows平台(UWP)应用程序服务提供程序,因为我想要使用的库只能在UWP应用程序中使用 .

但是,我需要在F#中编写一个(控制台)应用程序,然后调用上面的UWP服务提供程序来利用该库 .

根据https://msdn.microsoft.com/en-us/windows/uwp/launch-resume/how-to-create-and-consume-an-app-service,如果服务提供者和消费者都使用UWP编写C#,则可以这样做(因为UWP中不支持F#) .

我的问题是我是否可以从F#项目调用用C#编写的UWP应用程序服务?我可以创建一个包含两个项目的解决方案,其中一个是UWP C#app服务提供商,另一个是F#控制台应用服务消费者吗?

1 回答

  • 3

    这是我上面链接的另一篇文章的代码的改进版本,它还将WinRT异步对象映射到F# Async<_> ,这使得处理某些类型更加愉快 .

    open System.Reflection
    
    type Asyncifier() =
        static let winrtExts = System.Type.GetType("System.WindowsRuntimeSystemExtensions, System.Runtime.WindowsRuntime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
        static let asTask = winrtExts.GetMethods() |> Seq.find(fun m -> m.Name = "AsTask" && m.IsGenericMethodDefinition && m.GetGenericArguments().Length = 1 && m.ReturnType.IsGenericType)
        static member FromTask<'t,'u>(t:System.Threading.Tasks.Task<'t>) =
            async {
                let! t = Async.AwaitTask t
                return box t :?> 'u
            }
        static member FromIAsyncObjThrough<'t>(argTy, o) : Async<'t> =
            typeof<Asyncifier>.GetMethod("FromTask").MakeGenericMethod(argTy, typeof<'t>).Invoke(null, [| asTask.MakeGenericMethod(argTy).Invoke(null, [|o|]) |]) :?> _
    
    let getWindowsType (name:string) = 
        let ns = let parts = name.Split('.') in parts.[0] + "." + parts.[1]
        System.Type.GetType(sprintf "%s, %s, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime" name ns)
    
    let IAsyncOperation = getWindowsType "Windows.Foundation.IAsyncOperation`1"
    
    let (?) (o:obj) s : 'a =
        let rec build ty args =
            if Reflection.FSharpType.IsFunction ty then
                let dom, rng = Reflection.FSharpType.GetFunctionElements ty
                let mkArgs =
                    if dom = typeof<unit> then
                        if Reflection.FSharpType.IsFunction rng then failwith "Unit as non-final argument in curried definition?"
                        fun _ -> args
                    else
                        fun arg -> arg::args
                Reflection.FSharpValue.MakeFunction(ty, fun o -> build rng (mkArgs o))
            else
                let rcvr,ty,flags =
                    match o with
                    | :? System.Type as ty -> null,ty,BindingFlags.Static
                    | _ -> o,o.GetType(),BindingFlags.Instance
                let flags = flags ||| BindingFlags.Public
                let meth =
                    if Reflection.FSharpType.IsFunction typeof<'a> then
                        query {
                            for m in ty.GetMethods(flags) do
                            where (m.Name = s)
                            where (m.GetParameters().Length = args.Length)
                            exactlyOne
                        }                    
                    else
                        ty.GetProperty(s, flags).GetGetMethod()
                let result = meth.Invoke(rcvr, args |> List.toArray)
                let resultTy =
                    let rec resultTy ty = 
                        if Reflection.FSharpType.IsFunction ty then Reflection.FSharpType.GetFunctionElements ty |> snd |> resultTy
                        else ty
                    resultTy typeof<'a>
                let (|GenericTypeInstance|_|) (genTy:System.Type) (ty:System.Type) =
                    if ty.IsGenericType && ty.GetGenericTypeDefinition() = genTy then Some (ty.GetGenericArguments())
                    else None
                let asyncTy = typedefof<Async<_>>
                match meth.ReturnType, resultTy with
                | GenericTypeInstance IAsyncOperation [|winRtTy|], GenericTypeInstance asyncTy [|returnTy|] ->
                    // unwrap to F# async
                    typeof<Asyncifier>.GetMethod("FromIAsyncObjThrough").MakeGenericMethod(returnTy).Invoke(null, [|winRtTy; result|])
                | _ -> result
        build typeof<'a> [] :?> 'a
    

    以下是如何使用它来进行OCR:

    let Language = getWindowsType @"Windows.Globalization.Language"
    let OcrEngine = getWindowsType "Windows.Media.Ocr.OcrEngine"
    let BitmapDecoder = getWindowsType "Windows.Graphics.Imaging.BitmapDecoder"
    let StorageFile = getWindowsType "Windows.Storage.StorageFile"
    
    let enUs = Language.GetConstructor([|typeof<string>|]).Invoke([|"en-US"|])
    let engine : obj = OcrEngine?TryCreateFromLanguage enUs
    
    let getTextAsync (path:string) : Async<string> = async {
        let! file = StorageFile?GetFileFromPathAsync path
        let! stream = file?OpenReadAsync()
        let! decoder = BitmapDecoder?CreateAsync stream
        let! bitmap = decoder?GetSoftwareBitmapAsync()
        let! result = engine?RecognizeAsync bitmap
        return result?Text
    }
    

相关问题