[Win10][.NET Desktop] 遷移 Framework 項目至 Core3.1

概述

由於自 netcore 3.0 以來,dotnet 將許多東西都在往 netcore 上般,甚至連 windows gui 的 Winform 和 WPF 都搬了過去,這看起來要放棄framework 的節奏啊,而且最新的vs還在大更新中支持了 netcore 的 windows 窗體設計器,而且 netcore 中的項目組織方式也變化了許多,最主要的就是可以通過配置項目文件同時生成多版本的庫(最近才知道,慚愧啊)。

預覽

這次搬遷的是以前的一個Windows 10 Toast Notification 模塊,這個模塊不是很大,但裏面用的工具類不是很常用,搬遷時就碰到了一個問題:

COM 註冊方法被 Net Core 團隊移除了 !去看 NetCore 團隊的 git 發現 System.Runtime.InteropServices 下的好多類都被刪除了,而且有一個也不知道是誰的回答(link),感覺應該不會再添加回去這個方法了。


var regService = new RegistrationServices();
regService.RegisterTypeForComClients(
    typeof(T),
    RegistrationClassContext.LocalServer,
    RegistrationConnectionType.MultipleUse);

以我目前用到的功能,我覺得這個方法的作用就是將一個類型註冊到COM中,使你可以在其他進程中通過GUID去獲得一個這個類型的實例進行某種操作
當然,上面的回答中也給了代替解決方法 就是用 RunningObjectTable 來實現類型的註冊,也不知道是不是我這個應用環境比較特殊還是怎麼的,

[DllImport("Ole32.dll")]
public static extern void GetRunningObjectTable(int reserved,out IRunningObjectTable pprot);

[DllImport("Ole32.dll")]
static extern int CreateClassMoniker([In] ref Guid rclsid, out IMoniker ppmk);

IMoniker moniker;
IRunningObjectTable rot;
GetRunningObjectTable(0, out rot);
CreateMoniker([你的一個類型的GUID的ref],out moniker);
rot.Register(1, obj, moniker);

//這樣調用成功了以後好像沒有什麼用

好吧,我只能去找 RegisterTypeForComClients 的源碼,找到源碼後我直接呆了(ref):

[System.Security.SecurityCritical]  // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private static extern int RegisterTypeForComClientsExNative(Type t, RegistrationClassContext clsContext, RegistrationConnectionType flags);

//遇見這種方法在 NET 中就很難受了,因爲 framework 的 clr 沒有開源,而這個類剛好又是被core刪掉的類,所以就沒法找了
//不過這裏官方給了個提示 CoRegisterClassObject ,不過很可惜,這個方法的第二個參數沒什麼資料,這個也是卡了最久的地方
//只能在 PInvoke上找到 
[DllImport("ole32.dll")]
static extern int CoRegisterClassObject(
[MarshalAs(UnmanagedType.LPStruct)] Guid rclsid,
[MarshalAs(UnmanagedType.IUnknown)] object pUnk,
uint dwClsContext,
uint flags,
out uint lpdwRegister);

// IUnknown 也是一個 COM 類型

不過在差不多查了一天以後,終於還是有些收穫的 在 excel Dna 的 git 上發現了其一個使用實例,因此就進行了搬遷工作。


        [ComImport]
        [Guid(gstrIClassFactory)]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        internal interface IClassFactory
        {
            // For HRESULTs use 
            [PreserveSig]
            HRESULT CreateInstance([In] IntPtr pUnkOuter,
                                [In] ref IID riid,
                                [Out] out IntPtr ppvObject);

            [PreserveSig]
            HRESULT LockServer([In, MarshalAs(UnmanagedType.VariantBool)] bool fLock);

            //        HRESULT STDMETHODCALLTYPE CreateInstance( 
            //        /* [unique][in] */ IUnknown *pUnkOuter,
            //        /* [in] */ REFIID riid,
            //        /* [iid_is][out] */ void **ppvObject) = 0;

            //    virtual /* [local] */ HRESULT STDMETHODCALLTYPE LockServer( 
            //        /* [in] */ BOOL fLock) = 0;
        }

然後是繼承這個接口實現一個類 並在 CreateInstance 中返回你要創建類型的實例 (ref
最後有一點不同的是在 exceldna 中,最後傳入 CoRegisterClassObject 的是

Marshal.GetIUnknownForObject(factory);

獲得的IUnknown接口,但在我這裏這個不好使,而直接傳入這個工廠類是好使的

最後我的項目 ToastHelper
碼雲
Github
是讓你的 net desktop app 發送win10樣式通知的工具庫

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章