当前位置: 代码迷 >> C# >> .net比较完善的动态注册com组件
  详细解决方案

.net比较完善的动态注册com组件

热度:71   发布时间:2016-05-05 03:36:27.0
.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;	}}

完毕,希望对大家有用!

3楼forhells
其实了,可以不用注册的,在配置文件里面有一个叫无注册调用的方法。
Re: ~Js
@forhells,“配置文件里面有一个叫无注册调用的方法”,这个怎么玩, 求指教。
2楼Pete-Jones
不错.顶一下
1楼民工也Coding
不懂 帮顶。。。
  相关解决方案