C#后绑定Com库全攻略
本帖最后由 雪山飞狐_lzh 于 2015-4-17 23:06 编辑使用C#做Com调用确实麻烦 比VB要麻烦多了,至于为什么使用后绑定显然是为了程序的通用性
然而Com对象在后绑定时不显示实际类型,只有使用反射
一、使用IDispatch接口
大多数Com类都是基于IDispatch接口,IDispatch接口在.Net中没有定义
下面是IDispatch接口的简易定义,没有Invoke函数的具体定义,那么Com类的方法调用就要获取实际类型
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Security;
namespace TlsCad.Common.Runtime
{
public interface IDispatch
{
int GetTypeInfoCount();
int GetTypeInfo( int index, int lcid, out ITypeInfo pTypeInfo);
int GetIDsOfNames();
int Invoke();
}
}
本帖最后由 雪山飞狐_lzh 于 2015-4-17 23:07 编辑
二、自定义类AcVersion从注册表获取AutoCadCom库在GAC中的.Net程序集,以进行实际的反射,下面的代码主要应用于XP,如果在更高版本,你可能需要做些更改
using System;
using System.Collections.Generic;
using Microsoft.Win32;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace TlsCad.Common.Runtime
{
public class AcVersion
{
public int Major
{ private set; get; }
public int Minor
{ private set; get; }
public double ProgId
{
get { return Major + Minor / 10.0; }
}
public string ProductName
{ private set; get; }
public string ProductRootKey
{ private set; get; }
private string _appAssemblyName;
private string _dbxAssemblyName;
public Assembly AppAssembly
{ private set; get; }
public Assembly DbxAssembly
{ private set; get; }
static string _appClassNameHead = "Autodesk.AutoCAD.Interop.";
static string _dbxClassNameHead = "Autodesk.AutoCAD.Interop.Common.";
/// <summary>
/// 获取Com对象的接口类型
/// </summary>
/// <param name="obj">Com对象</param>
/// <returns>接口类型</returns>
public Type GetType(object obj)
{
IDispatch idisp = obj as IDispatch;
ITypeInfo t;
idisp.GetTypeInfo(0, 0, out t);
string name = Marshal.GetTypeInfoName(t);
return
AppAssembly.GetType(_appClassNameHead + name) ??
DbxAssembly.GetType(_dbxClassNameHead + name);
}
private static List<AcVersion> _versions;
public static List<AcVersion> Versions
{
get
{
if (_versions == null)
{
string[] copys =
Registry.LocalMachine
.OpenSubKey(@"SOFTWARE\Autodesk\Hardcopy")
.GetValueNames();
_versions = new List<AcVersion>();
foreach (var rootkey in copys)
{
Regex r = new Regex(@"Autodesk\\AutoCAD\\R(\d+)\.(\d+)\\.*?");
if (r.IsMatch(rootkey))
{
var gs = r.Match(rootkey).Groups;
var ver =
new AcVersion
{
ProductRootKey = rootkey,
ProductName =
Registry.LocalMachine
.OpenSubKey("SOFTWARE")
.OpenSubKey(rootkey)
.GetValue("ProductName")
.ToString(),
Major = int.Parse(gs[1].Value),
Minor = int.Parse(gs[2].Value),
};
ver.GetAssemblyName();
_versions.Add(ver);
}
}
}
return _versions;
}
}
public static AcVersion FromApp(dynamic app)
{
var gs = Regex.Match(app.Version, @"(\d+)\.(\d+).*?").Groups;
int major = int.Parse(gs[1].Value);
int minor = int.Parse(gs[2].Value);
foreach (var ver in Versions)
{
if (ver.Major == major && ver.Minor == minor)
return ver;
}
return null;
}
public void LoadAssembly()
{
AppAssembly = Assembly.Load(_appAssemblyName);
DbxAssembly = Assembly.Load(_dbxAssemblyName);
}
private void GetAssemblyName()
{
_appAssemblyName = GetAssemblyName("AutoCad.Application", ProgId.ToString());
_dbxAssemblyName = GetAssemblyName("ObjectDBX.AxDbDocument", Major.ToString());
}
private string GetAssemblyName(string name, string id)
{
string clsId =
Registry.ClassesRoot
.OpenSubKey(name + "." + id)
.OpenSubKey("CLSID")
.GetValue("")
.ToString();
return
Registry.ClassesRoot
.OpenSubKey(string.Format("CLSID\\{0}\\InprocServer32", clsId))
.GetValue("Assembly").ToString();
}
public override string ToString()
{
return
string.Format(
"名称:{0}\n版本号:{1}\n注册表位置:{2}\nApp_Com类库:{3}\nDbx_Com类库:{4}",
ProductName,
ProgId,
ProductRootKey,
_appAssemblyName,
_dbxAssemblyName);
}
}
}
三、反射类AcObject继承于DynamicObject,以便在.Net4.0中使用动态特性
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Dynamic;
namespace TlsCad.Common.Runtime
{
public class AcObject : DynamicObject
{
public static dynamic Application
{ private set; get; }
public static dynamic Preferences
{
get { return Application.Preferences; }
}
public static AcVersion Version
{ private set; get; }
public Type Type
{ private set; get; }
public object Value
{ private set; get; }
#region Wrapper
protected AcObject() { }
public static dynamic Wrapper(dynamic obj, Type type)
{
if (obj != null && Marshal.IsComObject(obj))
{
var wrapper =
new AcObject
{
Value = obj,
Type =
Regex.IsMatch(type.Name, "^Acad.*?$") ?
type.GetInterfaces() :
Version.GetType(obj)
};
if (wrapper.Type.GetMethod("Item") != null)
return wrapper.AsItems();
return wrapper;
}
else
{
return obj;
}
}
public static dynamic Wrapper(object obj)
{
return Wrapper(obj, Version.GetType(obj));
}
#endregion
#region Invoke
private dynamic Invoke(MethodInfo mi, object[] args)
{
object obj = mi.Invoke(Value, args);
return Wrapper(obj, mi.ReturnType);
}
public dynamic InvokeMember(string name, params object[] args)
{
try
{
//获取接口函数
MethodInfo mi = Type.GetMethod(name);
//配置参数
var pars = mi.GetParameters();
object[] realargs = new object;
Array.Copy(args, realargs, args.Length);
for (int i = args.Length; i < realargs.Length; i++)
realargs = pars.IsOptional ? Type.Missing : null;
List<dynamic> lst = new List<dynamic> { Invoke(mi, realargs) };
for (int i = args.Length; i < realargs.Length; i++)
{
if (pars.IsOut)
lst.Add(Wrapper(realargs, pars.ParameterType));
}
if (lst.Count == 1)
return lst;
return lst;
}
catch
{
return null;
}
}
#endregion
#region Property
public AcItems AsItems()
{
return
new AcItems
{
Value = Value,
Type = Type
};
}
public dynamic GetMember(string name)
{
try
{
var mi = Type.GetMember("get_" + name) as MethodInfo;
return Invoke(mi, new object);
}
catch
{
return null;
}
}
public void SetMember(string name, object value)
{
try
{
var mi = Type.GetMember("set_" + name) as MethodInfo;
mi.Invoke(Value, new object[] { value });
}
catch
{ }
}
#endregion
#region App
public static bool GetApp()
{
object app;
try
{
app = Marshal.GetActiveObject("AutoCad.Application");
return GetApp(app);
}
catch
{
return false;
}
}
public static bool GetApp(object app)
{
try
{
Version = AcVersion.FromApp(app);
Version.LoadAssembly();
Application = (AcObject)Wrapper(app);
return true;
}
catch
{
return false;
}
}
public static bool OpenApp(AcVersion ver)
{
try
{
var app =
Activator.CreateInstance(Type.GetTypeFromProgID("AutoCad.Application." + ver.ProgId));
return GetApp(app);
}
catch
{
return false;
}
}
#endregion
#region DynamicObject
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = GetMember(binder.Name);
return true;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
SetMember(binder.Name, value);
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = InvokeMember(binder.Name, args);
return true;
}
#endregion
}
}
AcItems类,对应集合类
using System;
using System.Collections.Generic;
using System.Collections;
namespace TlsCad.Common.Runtime
{
public class AcItems : AcObject,IEnumerable<AcObject>
{
internal AcItems() { }
public AcObject this
{
get { return (AcObject)this.InvokeMember("Item", id); }
}
public AcObject this
{
get { return (AcObject)this.InvokeMember("Item", id); }
}
public int Count
{
get { return (int)this.GetMember("Count").Value; }
}
public IEnumerator<AcObject> GetEnumerator()
{
int count = Count;
for (int i = 0; i < count; i++)
yield return (AcObject)this.InvokeMember("Item", i);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
调用例子
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("你安装了如下版本的AutoCad\n" + string.Join("\n", AcVersion.Versions.Select(v => v.ToString()).ToArray()));
if (!AcObject.GetApp())
AcObject.OpenApp(AcVersion.Versions);
var pref = AcObject.Preferences;
var files = pref.Files;
var app = AcObject.Application;
app.Visible = true;
var doc = app.ActiveDocument;
var util = doc.Utility;
var res = util.GetEntity();
if (res != null)
{
var obj = res;
var pt = res;
var p = obj.ObjectName;
}
} 这个很高端啊!给你点个赞!
页:
[1]