雪山飞狐_lzh 发表于 2015-4-17 22:36:05

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 22:36:55

本帖最后由 雪山飞狐_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&#91;&#93; 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&#91;1&#93;.Value),
                                    Minor = int.Parse(gs&#91;2&#93;.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&#91;1&#93;.Value);
            int minor = int.Parse(gs&#91;2&#93;.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);
      }


    }
}

雪山飞狐_lzh 发表于 2015-4-17 23:08:17

三、反射类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

    }

}

雪山飞狐_lzh 发表于 2015-4-17 23:09:27

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();
      }



    }
}

雪山飞狐_lzh 发表于 2015-4-17 23:10:05

调用例子
      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;
            }

      }

varwolf2017 发表于 2021-11-6 09:19:26

这个很高端啊!给你点个赞!
页: [1]
查看完整版本: C#后绑定Com库全攻略