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


.net中經常需要使用com組件,怎么樣注冊com組件呢?

一般想到的當然是直接通過系統cmd 調用regsvr32注冊程序去注冊,如下:

regsvr32 name.dll

在.net中可以直接執行cmd命令如下:

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

問題來了,那怎么去檢查一個dll已經注冊了呢?不能每次都注冊吧!我們知道每一個com組件都有一個clsid,如果已經注冊了,那么在系統注冊表里面會有注冊信息的。 csharp代碼如下:

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這個方法就可以了,代碼如下:

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中方法的代碼如下:

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。 廢話也不多說。代碼如下

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組件,並能判斷是否已經注冊。我簡單封裝一下代碼:

/// <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;
	}
}

完畢,希望對大家有用!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM