.net比較完美的動態註冊com組件

.net中常常須要使用com組件,怎麼樣註冊com組件呢?

通常想到的固然是直接經過系統cmd 調用regsvr32註冊程序去註冊,以下:css

regsvr32 name.dll

在.net中能夠直接執行cmd命令以下:api

System.Diagnostics.Process.Start("regsvr32.exe","name.dll");

問題來了,那怎麼去檢查一個dll已經註冊了呢?不能每次都註冊吧!咱們知道每個com組件都有一個clsid,若是已經註冊了,那麼在系統註冊表裏面會有註冊信息的。 csharp代碼以下:ssh

private static bool IsExistRegister(Guid guid)
{
    RegistryKey rkTest = Registry.ClassesRoot.OpenSubKey(String.Format("CLSID\\{{{0}}}\\InprocServer32", guid.ToString()));
    if (rkTest != null)
    {
        var val = rkTest.GetValue("");//獲取註冊表中註冊的dll路徑
        if (val != null)
        {
            return System.IO.File.Exists(val.ToString());
        }

    }
    return false;
}

彷佛經過上面的代碼已經解決了全部問題,可是我我的並不知足上面的方案,緣由以下:函數

  1. 不喜歡直接在.net中調用cmd命令
  2. 當檢查是否註冊時,必須預先知道com的clsid。

那麼有麼有方法解決上面2個問題呢?答案固然是確定的! 經過查詢資料得知:
regsvr32 name.dll 實際上就是調用name.dll中的一個方法:DllRegisterServer。
在.net中咱們能夠經過pinvoke直接調用dll這個方法就能夠了,代碼以下:ui

static class NameDll
{
    [DllImport("name.dll")]
    public static extern int DllRegisterServer();

    [DllImport("name.dll")]
    public static extern int DllUnregisterServer();
}

而後在.net中直接經過NameDll.DllRegisterServer();便可完成註冊。 這個辦法不須要在.net中調用cmd命令,可是有個缺點。
每個dll都須要這麼定義下。由於[DllImport("name.dll")]這個路徑不能動態給。因此也不是很好。其實在.net中有能夠動態加載dll並根據須要調用dll中方法的代碼以下:.net

public class Win32DllWrap : IDisposable
{
    [DllImport("kernel32.dll")]
    private extern static IntPtr LoadLibrary(String path);

    [DllImport("kernel32.dll")]
    private extern static IntPtr GetProcAddress(IntPtr lib, String funcName);

    [DllImport("kernel32.dll")]
    private extern static bool FreeLibrary(IntPtr lib);

    private IntPtr hLib;
    public Win32DllWrap(String DLLPath)
    {
        hLib = LoadLibrary(DLLPath);
    }

    /// <summary>
    /// 根據函數名獲取dll中的函數指針,並轉化爲指定的TDelegate類型
    /// </summary>
    /// <typeparam name="TDelegate"></typeparam>
    /// <param name="name"></param>
    /// <returns></returns>
    public TDelegate GetFunc<TDelegate>(String name) where TDelegate : class
    {
        IntPtr api = GetProcAddress(hLib, name);
        return Marshal.GetDelegateForFunctionPointer(api, typeof(TDelegate)) as TDelegate;
    }


    public void Dispose()
    {
        FreeLibrary(hLib);
    }
}


/*
using(var dll = new Win32DllWrap(path)){
     var method = dll.GetFunc<Action>("DllRegisterServer");//根據名字獲取方法,並返回對於的委託
     method();//完成註冊
}
*/

這個方法避免了每個com組件要定義個類的弊端。並且徹底能夠根據com路徑動態註冊。可是你們別忘了,上面還有一個問題沒解決。指針

那就是在檢查com是否註冊時,怎麼動態得知指定路徑com的clsid。 廢話也很少說。代碼以下code

private static List<Guid> GetClsids(string path)
{
    if (!System.IO.File.Exists(path))
    {
        throw new Exception(path + "文件不存在");
    }
    List<Guid> list = new List<Guid>();
    ITypeLib lib;
    IntPtr attrPtr;
    ITypeInfo info;
    LoadTypeLib(path, out lib);
    if (lib == null)
    {
        throw new Exception(path + "不是com組件");
    }
    var n = lib.GetTypeInfoCount();
    for (int i = 0; i < n; i++)
    {
        lib.GetTypeInfo(i, out info);
        if (info != null)
        {
            info.GetTypeAttr(out attrPtr);
            if (attrPtr != null)
            {
                var v = (System.Runtime.InteropServices.ComTypes.TYPEATTR)Marshal.PtrToStructure(attrPtr, typeof(System.Runtime.InteropServices.ComTypes.TYPEATTR));
                if (v.typekind == System.Runtime.InteropServices.ComTypes.TYPEKIND.TKIND_COCLASS)
                {
                    list.Add(v.guid);
                }
                info.ReleaseTypeAttr(attrPtr);
            }
        }
    }
    return list;
}

上面這個方法能夠獲取給定路徑的dll中的clsid列表。 老實講:這個方法我是真的費盡心力,網上幾乎沒.net的資料。
至此全部問題都已經解決。咱們徹底能夠根據指定路徑註冊動態註冊com組件,並能判斷是否已經註冊。我簡單封裝一下代碼:orm

/// <summary>
/// Com組件註冊類
/// </summary>
public class ComRegHelp
{

   

    private delegate void comDelegate();

    /// <summary>
    /// 註冊指定路徑的dll,若是已經註冊,就不註冊
    /// </summary>
    /// <param name="dllPath"></param>
    public static void Registe(string dllPath)
    {
        if (!IsRegistered(dllPath))
        {
            using (var dll = new Win32DllWrap(dllPath))
            {
                dll.GetFunc<comDelegate>("DllRegisterServer")();
            }
        }
    }

    /// <summary>
    /// 取消註冊指定路徑的dll
    /// </summary>
    /// <param name="dllPath"></param>
    public static void UnRegiste(string dllPath)
    {
        using (var dll = new Win32DllWrap(dllPath))
        {
            dll.GetFunc<comDelegate>("DllUnregisterServer")();
        }
    }

    private static List<Guid> GetClsids(string path)
    {
        if (!System.IO.File.Exists(path))
        {
            throw new Exception(path + "文件不存在");
        }
        List<Guid> list = new List<Guid>();
        ITypeLib lib;
        IntPtr attrPtr;
        ITypeInfo info;
        LoadTypeLib(path, out lib);
        if (lib == null)
        {
            throw new Exception(path + "不是com組件");
        }
        var n = lib.GetTypeInfoCount();
        for (int i = 0; i < n; i++)
        {
            lib.GetTypeInfo(i, out info);
            if (info != null)
            {
                info.GetTypeAttr(out attrPtr);
                if (attrPtr != null)
                {
                    var v = (System.Runtime.InteropServices.ComTypes.TYPEATTR)Marshal.PtrToStructure(attrPtr, typeof(System.Runtime.InteropServices.ComTypes.TYPEATTR));
                    if (v.typekind == System.Runtime.InteropServices.ComTypes.TYPEKIND.TKIND_COCLASS)
                    {
                        list.Add(v.guid);
                    }
                    info.ReleaseTypeAttr(attrPtr);
                }
            }
        }
        return list;
    }

    [DllImport("oleaut32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    static extern int LoadTypeLib(string fileName, out ITypeLib typeLib);

    /// <summary>
    /// 判斷指定路徑dll是否已經註冊
    /// </summary>
    /// <param name="path"></param>
    /// <returns></returns>
    public static bool IsRegistered(string path)
    {
        var guids = GetClsids(path);
        foreach (var item in guids)
        {
            if (IsExistRegister(item))
            {
                return true;
            }
        }
        return false;
    }


    private static bool IsExistRegister(Guid guid)
    {
        RegistryKey rkTest = Registry.ClassesRoot.OpenSubKey(String.Format("CLSID\\{{{0}}}\\InprocServer32", guid.ToString()));
        if (rkTest != null)
        {
            var val = rkTest.GetValue("");
            if (val != null)
            {
                return System.IO.File.Exists(val.ToString());
            }

        }
        return false;
    }
}

完畢,但願對你們有用!cmd

相關文章
相關標籤/搜索