VC调用AutoCAD自动化的两种方法(包装类、接口)使用详解

news2024/12/23 4:52:49

----哆啦刘小洋 原创,转载需说明出处 2022-12-29

VC调用AutoCAD自动化

    • 1 简介
    • 2 AutoCAD的Automation类型库说明文件
    • 3 包装类方式
      • 3.1 VC6
      • 3.2 VC2022
    • 4 接口调用方式
    • 5 两种方式对比
      • 5.1 使用便捷性
      • 5.2 兼容性
      • 5.3 结论
    • 6 后记

1 简介

在工程领域,使用AutoCAD时经常利用自动化(Automation)技术提高工作效率,VC调用AutoCAD自动化时有两种常用的方式,一个是包装类的方式,另一个是接口的方式,当然不只是AutoCAD,一般的支持Automation的软件都可以使用这两种方式,本文就这两种方式给出详细的使用方法,并简要总结各自的优缺点。文中代码基于VC6及以上。

2 AutoCAD的Automation类型库说明文件

不管是用包装类方式还是接口方式,编程时都需要用到类型库说明文件(.tlb),这个文件是微软Com(Component)技术的一个标准文件,有点类似于c++的声明头文件,文件中给出了所有的接口声明、常量定义、枚举声明等等,方便使用者调用,但这个文件是二进制文件,无法直观的查看内容。其实Com还有一种接口声明的标准文件“.tlh”,这种文件是文本格式的,可以直接查看。不过AutoCAD提供的就是tlb格式。

AutoCAD在安装后,提供了组件的接口文件,一般型如:acax16chs.tlb文件,文件名中的16是AutoCAD的内部版本号,比如AutoCAD2016的内部版本号是20;文件名中的chs是语言,chs就是简体中文,我们一般都用这个。这个tlb文件的位置也和Windows版本以及AutoCAD的版本有关,比如“C:\Program Files\Common Files\Autodesk Shared”,不行就先用文件搜索找到位置再来。

3 包装类方式

包装类是VC对Automation组件的接口进行类封装,然后使用时,把这些类当作普通的类使用即可。需要说明,VC不同的版本对同一个tlb文件的包装结果是不一样的,甚至会差别非常大。据我的使用经验,至少有几次版本有断代式的明显差异。这里我就以VC6和VC2022两个版本分别给出使用方法。

3.1 VC6

通过工程中任意一个类进入类向导窗口,在右侧的AddClass里选择“From a type library”,如下图:

在这里插入图片描述
接下来会打开文件对话框让你选择,我们选择acax??chs.tlb,??是AutoCAD的版本号。
在这里插入图片描述
打开后,会让你确认要包装哪些接口类:
在这里插入图片描述
一般全选即可。(先点中第一个,然后滚动到最后按住Shift点中最后一个)。这里可以对h和cpp文件命名或指定目录。再OK,VC6会生成那两个文件。同时,VC6的ClassView中会出现一大堆新的I开头的类,这些就是AutoCAD的接口包装类。

使用时,代码如下:

#include "acax20chs.h"

void OnTest_class_vc6() 
{
	CoInitialize(NULL);
	
	IAcadApplication	app;
	CLSID clsid;
	HRESULT   h=NOERROR;
	IUnknown* pUnknown;
	LPDISPATCH pDispatch;
	BOOL bSuccessget_Object = FALSE;
        
	//获取AutoCAD的Class ID
	h = ::CLSIDFromProgID(OLESTR("AutoCad.Application"), &clsid); 
	if(!SUCCEEDED(h))
		return;
	
	//获取正在运行的AutoCAD对象
	h = GetActiveObject(clsid, NULL, &pUnknown);
	if(SUCCEEDED(h))
	{
		h = pUnknown->QueryInterface(IID_IDispatch, (void**)&pDispatch);
		if(SUCCEEDED(h))
		{
			bSuccessget_Object = TRUE;
			pDispatch->Release();
			app.AttachDispatch(pDispatch);
		}
	}

     //没有正在运行的AutoCAD对象,就创建一个
	if(!bSuccessget_Object)
		h=app.CreateDispatch(clsid);

	if(FAILED(h) || app.m_lpDispatch == NULL)
		return;
	
	app.SetVisible(TRUE);
	
	//...
}

3.2 VC2022

在解决方案资源管理器栏,选中你的目标项目,点击右键,选择“添加 > 新建项”,如果是英文版,在Solution Explore栏,选择“ Add > New Item”:
在这里插入图片描述
注意,在VC2022中,一定要在解决方案资源管理器(Solution Explore)栏,类视图中点击项目右键是没有这个选项的。这和以前一些版本不同。
在添加新项对话框中,选择“已安装”下的“Visual C++”下面的“MFC”,在右面子项中,选择“TypeLib中的MFC类”(英文版:选择“Installed”下的“Visual C++”下面的“MFC”,在右面子项中,选择“MFC Class from Typelib”)。
在这里插入图片描述
选择好后,点击“添加”:
在这里插入图片描述
如上图所示,选择文件方式,再选择tlb文件(如“acax16chs.tlb”),在可用接口栏,一般点击“>>"全部选择,再确定。这时,VC2022会生成各个类的包装文件,和VC6最不一样的是,VC2022每个类对应一个文件,因此会有很多文件。在生成的时候等待时间长一点,不要做动作,否则可能出现文件没有内容的隐含错误。

使用时,也和VC6不太一样了,因为文件多了嘛。代码如下:

#include "CAcadApplication.h"
#include "CAcadDocuments.h"
#include "CAcadDocument.h"
#include "CAcadModelSpace.h"

void OnTest_class_vc2022()
{
	CoInitialize(NULL);

	CLSID clsid;
	HRESULT  h;

	//获取Class ID
	h = CLSIDFromProgID(OLESTR("AutoCad.Application"), &clsid);
	if (!SUCCEEDED(h))
		return;

	CAcadApplication app;
	IUnknown* pUnknown;
	LPDISPATCH pDispatch;
	BOOL bSuccessGetObject;

	//获取正在运行的对象
	bSuccessGetObject = FALSE;
	h = ::GetActiveObject(clsid, NULL, &pUnknown);
	if (SUCCEEDED(h))
	{	
		h = pUnknown->QueryInterface(IID_IDispatch, (void**)&pDispatch);
		if (SUCCEEDED(h))
		{
			bSuccessGetObject = TRUE;
			pDispatch->Release();
			app.AttachDispatch(pDispatch);
		}
	}

	//没有正在运行的对象,就创建一个
	if (!bSuccessGetObject)
		h = app.CreateDispatch(clsid);

	if (!SUCCEEDED(h) || app.m_lpDispatch == NULL)
		return;

	app.put_Visible(TRUE);
	app.put_WindowState(3/*acMax*/);

	CAcadDocuments docs;
	CAcadDocument doc;
	CAcadModelSpace ms;

	//新建一个AutoCAD文档
	docs = (CAcadDocuments)app.get_Documents();
	doc = (CAcadDocument)docs.Add(vtMissing);

	//模型空间
	ms = (CAcadModelSpace)doc.get_ModelSpace();

	//添加一条多义线
	//ms.AddPolyline(...)
}

如果运气不好,编译会出现错误:error C2059: 语法错误:“,” 。神奇吧!竟然是包装类中出了错,比如下面这个函数。而且,不同VC版本,每次生成的文件错误还不一样(但VC6很稳定),所以,这里强调一下,在VC生成类文件的时候一定不要动,等个30秒。

	__int64 get_HWND()
	{
		__int64 result;
		InvokeHelper(0x2c, DISPATCH_PROPERTYGET, , (void*)&result, nullptr);//这一句,两个","间少了参数
		return result;
	}

下面列举了一些我碰到的错误和解决方法:

CAcadAppliction::get_HWND()和CAcadDocument::get_HWND()有错误,类型名添加一个VT_I8。

	InvokeHelper(0x2c, DISPATCH_PROPERTYGET, , (void*)&result, nullptr); //原来
	InvokeHelper(0x2c, DISPATCH_PROPERTYGET, VT_I8, (void*)&result, nullptr);//修改
	
	InvokeHelper(0x40, DISPATCH_PROPERTYGET, , (void*)&result, nullptr); //原来
	InvokeHelper(0x40, DISPATCH_PROPERTYGET, VT_I8, (void*)&result, nullptr);//修改

CAcadDocuments::get__NewEnum()有错误,类型名添加一个VT_UNKNOWN。

	InvokeHelper(0xfffffffc, DISPATCH_PROPERTYGET, , (void*)&result, nullptr); //原来
	InvokeHelper(0xfffffffc, DISPATCH_PROPERTYGET, VT_UNKNOWN, (void*)&result, nullptr);//修改

还可能有别的一些缺少参数类型的错误,修改方法也很简单,比如错误代码如下:

InvokeHelper(0x405, DISPATCH_PROPERTYGET, VT_I8, (void*)&result, nullptr);	__int64 get_ObjectID()
	{
		__int64 result;//看这里
		InvokeHelper(0x405, DISPATCH_PROPERTYGET, , (void*)&result, nullptr);
		return result;
	}

看函数第一行声明的是什么类型,就在第二句缺少类型参数的地方添加相应的类型。如果你不熟悉VARIANT,一般按下面的替换方式:
__int64 -> VT_I8 long -> VT_I4 LPUNKNOWN->VT_UNKNOWN 基本上可以应付了。其实看看别的函数也可以现学现卖解决问题。

此外,每个文件前面有一行类似下面的语句:
#import “C:\Program Files\Common Files\Autodesk Shared\acax20chs.tlb” no_namespace

建议第一,把acax20chs.tlb拷贝到工程文件中来,第二,只保留CAcadApplication.h文件中的这句话,并改成如下所示的代码。其他需要用到的h文件,我的建议是都注释掉或删除。也可以所有文件都删除这句话,只是无法使用接口需要用到的常量和枚举定义。

#import "acax20chs.tlb" no_namespace

4 接口调用方式

好了,包装类的方式总算好了。现在介绍另一种方式,接口调用方式对不同VC版本都是一样的使用。

这个方式不需要先去生成包装类,直接把AutoCAD提供的acax20chs.tlb拷贝到你工程来。拷贝过来的目的是避免AutoCAD被卸载或换了版本,就找不到文件了。代码如下:

#import "acax20chs.tlb" no_namespace named_guids
	
void OnTest_Interface()
{
	CoInitialize(NULL);

	IAcadApplicationPtr		pApp;
	IAcadDocumentsPtr		pDocs;
	IAcadDocumentPtr		pDoc;
	IAcadModelSpacePtr		pMs;

	HRESULT   h = NOERROR;
	CLSID   clsid;

	//获取Class ID
	h = ::CLSIDFromProgID(OLESTR("AutoCad.Application"), &clsid);
	if (FAILED(h))
		return;

	//获取正在运行的对象
	h = pApp.GetActiveObject(clsid);
	if (!SUCCEEDED(h))
	{
		//没有正在运行的对象,就创建一个
		h = pApp.CreateInstance(clsid, NULL, CLSCTX_ALL);
		if (FAILED(h))
			return;
	}

	pApp->WindowState = AcWindowState::acMax;
	pApp->Visible = VARIANT_TRUE;

	//新建一个AutoCAD文档
	pApp->get_Documents(&pDocs);
	pDoc = pDocs->Add();

	//模型空间
	pDoc->get_ModelSpace(&pMs);

	//添加一条多义线
	//pMs->AddPolyline(...)
}

接口方式和包装类的方式差异甚大,这里大概解释一下:

1)为什么简单一句#import,后面的IAcadApplicationPtr类型、AcWindowState::acMax常量定义就都有了呢,也没有看到我们包含什么头文件啊。这是因为#import,VC会生成Com的另外一种标准文件.tlh和.tli,一个是接口声明,一个是接口实现代理,这两个文件是文本文件,在输出目录里面,可以方便的查看各种接口定义、常量定义。所以我们才能方便的使用IAcadApplicationPtr来定义变量等等。
2)型如IAcadApplicationPtr的类型是生成的接口智能指针,这种方式就是用接口指针来调用AutoCAD的Automation接口。

此外,代码中在模型空间添加一条多义线被注释了,是因为使用没有那么简单,这里要牵涉到变体数组的问题,可以参考我的另一篇文章:”SegeX SgxVariant:VC封装支持多维数组的变体类型“,里面提供了变体数组的封装代码。

5 两种方式对比

5.1 使用便捷性

明显接口方式大比分获胜,第一是整个准备工作就是拷贝一个文件,添加一行代码。第二是接口使用便捷,接口方法返回的接口是一个接口指针,可以实现连续指向,比如:

pApp->GetActiveDocument()->GetModelSpace()->AddPolyline(...)

包装类就很难啦!每个函数返回的是一个分发接口LPDISPATCH,因此需要转换,比如下代码,CAcadAppliction类接口函数get_Documents返回的就是一个LPDISPATCH,不信去看代码。而docs呢,是CAcadDocuments类,所以必须要转换,因此很难实现接口的连续指向。

docs = (CAcadDocuments)app.get_Documents(); 

并且,包装类不同版本生成的文件不同,还会出错!可见MFC对OLE的态度。

5.2 兼容性

兼容性恐怕比便捷性重要得多。因为便捷性不好嘛,只是开发人员一时的痛哭,但如果用户使用你的软件,却出现各种打不开、死机,那就要命了,可能职位不保!

接口方式的兼容性差。第一,接口可能是区分32位和64位的(不同软件支持不同),比如程序#import的是一个32位AutoCAD自带的tlb文件,那么一般情况下,你的程序只能在32位AutoCAD中运行良好,为了保持兼容,你需要编写两个版本的代码,根据目标计算机自动做出选择。同时,接口还可能区分AutoCAD版本的,也就是说,如果程序#import的是AutoCAD2012,用户计算机是AutoCAD2023的话,那么可能会出错!为了兼容,你还得编写多个版本的AutoCAD版本,至于哪些兼容,哪些不兼容,我也没有统计过。

5.3 结论

因此,我建议采用包装类的方式。

6 后记

一般认为,Com技术,特别是OLE技术,对于一般开发人员而言过时了,我认为,确实过时了。第一,Com技术仅能良好的用于Windwos,不能适应现在的多平台融合的需求;第二,Com技术理念很好,但使用时要求的技术相对较高,特别是用VC,如果你想编写一个支持OLE和Automation让别人调用你的程序,那是难上加难(也可以看我的文章系列:”SegeX Automation:VC程序的Automation支持(让你的程序既是应用程序,又具备Automation服务器的功能 可被别的程序调用,类似Word AutoCAD)“)(还没写好)。但我想说的不是这个,我想说的是Automation技术有时会大大提高工作效率和图件的准确性,这对于很多工程人员来讲,实在是太重要了。举个真实例子,2007年左右,某单位因为一个施工图件在引用勘探资料时,勘探资料比例尺标注错了,注意只是标注错了,显然是拷贝别的图件没有修改全,从而引起施工图不准确,导致施工中大面积变更,直接经济损失超过1个亿(就是一个数字错了,1写成了2),而如果是AutoCAD图件自动化处理,取代人工成图和过程中的手工转移引用,就可以避免此类错误。回到正题,这么重要的技术,虽然落魄了,但你得给一个新的替代技术呀,不然成天只知道叫过时了,过时了,有什么用呢。当然,话说回来,AutoCAD功能强大,它提供了多种开发手段,不一定用Automation技术,比如script、AutoLisp、Arx,还有基于.Net的开发组件,这个我打算另外写文章再专门介绍。但除了AutoCAD,别的很多软件二次开发最多也就支持Automation。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/141103.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(小程序)后台交互--个人中心

目录 一、微信登录流程简介 二、微信用户获取用户昵称头像和昵称 ① wx.getUserProfile ——> 获取头像 三、微信登录流程代码详解 1.bindgetuserinfo——>把小程序端搭建起来 ① oa-mini 2.登录-小程序 ① wx.checkSession ② wx.login ③ wx.request 3.后台…

Python - 数据容器list(列表)

目录列表的定义列表的下标(索引)列表的常用操作查找某元素的下标index修改特定位置(索引)的元素值插入元素insert追加元素append追加元素方式extend删除元素pop与del删除某元素在列表中的第一个匹配项remove清空列表内容clear()统…

【JavaScript】获取和操作 DOM 节点

文章目录【JavaScript】获取和操作 DOM 节点一. DOM节点分类二. 获取节点(1) 获取元素节点1. 非常规元素节点2. 常规元素节点(2) 获取节点的方式三. 操作节点(1) 创建节点(2) 插入节点(3) 删除节点(4) 替换节点(5) 克隆节点(6) 节点属性四. 案例:微博发布【JavaScri…

使用云开发的垃圾分类、垃圾识别、答题的微信小程序源码+部署教程,文字识别垃圾类型、语音识别垃圾类型、图片识别类型、垃圾类别答题

使用云开发的垃圾分类、垃圾识别、答题的微信小程序 完整代码下载地址:使用云开发的垃圾分类、垃圾识别、答题的微信小程序 前言 项目介绍 小程序使用了云开发,包含文字识别垃圾类型、语音识别垃圾类型、图片识别类型、垃圾类别答题、腾讯机器人对话…

AndroidStudio启动app时闪退问题解决过程

问题描述: Android app在启动时闪退的问题有很多种,大部分可能集中在代码有问题上,譬如StartActivity的时候启动不了。 看提示里一般是有错误出现的,这个很好解决,只要找到对应的错误就可以了。 那么,还有一类问题,就是我这里出现的,启动成功,没有任何错误,查看lo…

Odoo 16 企业版手册 - 库存管理之库存调拨

库存调拨 拥有多个仓库的公司需要管理产品从一个地点转移到另一个地点。在某些情况下,公司可能在不同地点设有不同的分支机构,这需要付出巨大努力才能成功管理产品调拨。公司通常通过分配主要仓库和子仓库进行产品分销来管理这种情况。Odoo中的调拨功能支…

SpringBoot+JWT+Shiro+MybatisPlus实现Restful快速开发后端脚手架

一、背景 前后端分离已经成为互联网项目开发标准,它会为以后的大型分布式架构打下基础。SpringBoot使编码配置部署都变得简单,越来越多的互联网公司已经选择SpringBoot作为微服务的入门级微框架。 Mybatis-Plus是一个 Mybatis 的增强工具,有…

房产管理系统系统与数据中心对接要遵循的标准和规范

数图互通高校房产综合管理系统平台是基于公司自主研发的FMCenterV5.0平台,是针对中国高校房产的管理特点和管理要求,研发的一套标准产品;通过在中国100多所高校的成功实施和迭代,形成了一套成熟、完善、全生命周期的房屋资源管理解…

票务公司网上订票系统

课程设计题目七:票务公司网上订票系统https://download.csdn.net/download/qq_45037155/87364367 点击此链接免费下载原文章! 第1章 绪论 1.1 引言 在当今社会,速度决定了很多商业机构的成败。为了顺应时代发展,提高效率&#x…

2023年山东建筑安全员考试真题题库及答案

百分百题库提供建筑安全员考试试题、安全员证考试真题、安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 51.申请从事建筑施工特种作业的人员,应当具备()基本条件。 A.年满18周岁且符合相关工种规定的年龄要求; B.经医院体检合格…

【Java多线程JUC入门详解】AQS原理详解

AQS 概述 AbstractQuenedSynchronizer抽象的队列式同步器, 在java.util.concurrent.locks包下。 我们常用的ReentrantLock中有一个抽象静态内部类Sync,就继承自AbstractQuenedSynchronizer。 abstract static class Sync extends AbstractQueuedSynchronizer { …

1.4日报

验收了TestMrlController 历经千辛万苦给电脑安装了vpn 因为昨天系统损坏,今天重新配置postman和navicat 遇到的困难及解决 linux安装vpn时官方给的教程: 可是ubnutu里没有yum,我得先下载yum 而默认镜像源里找不到yum软件包,…

单调栈 单调队列 专题

文章目录一、单调栈1、问题模型2、实现过程:3、代码实现4、规律总结5、题目练习二、单调队列1、问题模型2、实现过程:3、代码实现4、规律总结5、题目练习三、总结一、单调栈 1、问题模型 主要解决一类问题: O(n)O(n)O(n) 求数列中每个元素左…

PaddleNLP系列课程一:Taskflow、小样本学习、FasterTransformer

文章目录一、Taskflow1.1 前言1.2 Taskflow应用介绍1.2.1 词法分析1.2.2 命名实体识别1.2.3 文本纠错1.2.4 句法分析1.2.5 情感分析1.2.6 文本生成应用(三行代码体验 Stable Diffusion)1.2.7 使用技巧(保存地址、批量推理)二、 小…

Excel怎么转换成PDF?教你两招轻松搞定

Excel怎么转换成PDF?相信在工作中大家都或多或少需要转换文件的格式,我们会根据工作需求将word、excel、PPT、图片等文件转换成PDF文件。Excel表格是我们经常使用的一款录入数据的文件类型,因为excel文件打开查看时不是很方便,我们…

时序数据库 TDengine 3.0 参数体系使用方式汇总

在日常使用 TDengine 时,参数是用户们无法绕开的重要一环。深入了解参数的属性,生效范围,查询更改方式等会让我们在使用数据库的过程中更加节时高效,也有助于我们更加深入地理解数据库的架构体系。在 3.0 版本中,TDeng…

参数校验(Validator)

为什么要用validator 实战演练 1. Validated 声明要检查的参数 2. 对参数的字段进行注解标注 3. 在全局校验中增加校验异常 4. 测试 自定义参数注解 1. 比如我们来个 自定义身份证校验 注解 2. 然后自定义Validator 3. 使用自定义的注解 4.使用groups的校验 5.restful…

209数组-长度最小的子数组

题目 链接:209. 长度最小的子数组 - 力扣(LeetCode) 思路 可以采用暴力,两个for循环,不断寻找符合条件的子序列。时间复杂度为O(n^2) 代码: class Solution { public:int minSubArrayLen(int target, v…

【BP靶场portswigger-服务端6】信息泄露漏洞-5个实验(全)

目录 一、信息泄露漏洞 1、简述: 2、危害: 3、示例: 4、漏洞的产生 5、影响 6、严重程度判定 二、发现和利用信息泄露漏洞 1、模糊测试 2、使用BP扫描仪 3、使用Burp的相关工具 4、工程信息响应 三、常见来源 1、示例&#xff…

【数据篇】34 # 如何处理多元变量?

说明 【跟月影学可视化】学习笔记。 从数据到图表展现 2014 年北京市的天气历史数据&#xff08;csv 格式&#xff09; 这里使用QCharts 图表库折线图来展示平均气温 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" …