SegeX Automation:VC调用Golden Surfer自动化失败(VC调用Automation失败)原因详解

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

1 简介

在工程领域,Golden Surfer普遍使用,为了提高成图效率,经常使用自动化(Automation)技术调用Surfer完成自动绘制。但网上一般提供的调用方式会有一定机会失败,从而大大影响了用户的体验并给工作带来困难。本文来源于SegeX Automation组件中的技术,旨在分析Surfer调用失败的原因,并给出解决办法。本文中的一些方法也适用于其他软件如AutoCAD、Office等软件的Automation使用。本文的代码基于VC2012及以上。

2 初始化Surfer对象不成功

2.1 一般代码

VC中初始化Surfer对象的一般代码如下(这里使用的是Automation包装类的方法,用接口的调用方法也是一样的。另外里面返回的错误号是SegeX内定,看的时候可直接忽略):

...
#include "CSurferApplication.h"

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

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

		if(!SUCCEEDED(h) || SurfApp.m_lpDispatch == NULL)
			return ERR_AUTOMATION_CAN_NOT_CREATE_OBJECT;
}

这里使用的是Automation包装类的方法,用接口的调用方法也是一样的(关于两种方法详解,可参考我的另一篇文章“SegeX Automation:VC调用AutoCAD自动化的两种方法(包装类、接口)使用详解”)。CSurferApplication是Surfer Automation包装类CApplication重新改了个名字,至于如何得到Surfer Automation的包装类,网上有很多文章,这里不赘述了。运气好的话,上述代码就可以获取或打开Surfer,后面就好办了。但是!往往这里就会失败。咋办?听我道来。

2.1 改进代码

很多时候(在不同的计算机、不同的Windows版本,表现是不一样),我们在获取Surfer的Class ID就失败了!这是失败的一大主因,但问题是不同的计算机反应不同,所以很多时候我们编程人员不知道是哪里出了问题,因为在编程的计算机上是没有问题的。这是微软自动化技术很大的一个弱点,不强壮!不强壮!(重要的事情说两遍),专业术语讲,不鲁棒!别以为我瞎说,作为SegeX的开发人员,任何一句话、一个细节都是很认真的。言归正传,这里失败的原因出在Windows系统函数::CLSIDFromProgID身上。这个函数我认为不完善,为什么这么讲,必须要多啰嗦两句。
首先,你换个平台,别在VC上折腾,先打开Surfer(切记!),然后用Excel的Vba,你试试Excel中的代码,在Excel中随便写个宏,如果没有Excel,用Word、AutoCAD…,实在不行,就用Surfer中的VbScript也行。如下:

Sub a()
	set app = GetObject(, "Surfer.Application")
End Sub

调试运行,我哇!竟然成功(严格讲是大概率会成功,或者说成功率大大高于上面VC中的代码)!为什么?我们MFC洋洋洒洒数十行比不上人家一行,MFC不没落才怪。所以我说::CLSIDFromProgID是有缺陷的,到了这一步,也可以肯定的讲Vba中绝不是简单的使用::CLSIDFromProgID来获取对象CLSID的。

翻开注册表吧!regedit,点开HKEY_CLASSES_ROOT,直接快速打两个字母“su”,会跳到入下图所示的位置:
在这里插入图片描述

看到了吗?明明有Surfer.Application对应的CLSID。可::CLSIDFromProgID就是视而不见,非要到后面很远的地方去读,不知道微软怎么弄的,咱也不敢乱说。解决方法也呼之欲出了,改进代码如下(为节省篇幅,只写::CLSIDFromProgID相关的地方):

		h = ::CLSIDFromProgID(OLESTR("Surfer.Application"), &clsid); 
		if(!SUCCEEDED(h))
		{
			//尝试从注册表读入
			HKEY hStartKey = HKEY_CLASSES_ROOT;
			TCHAR buf[256] = { 0 };
			BOOL b = GetRegKeyValue(hStartKey, _T("Surfer.Application\\CLSID"), NULL, REG_SZ, (LPBYTE)buf, 256, NULL);
			if (b)
				h = CLSIDFromString(buf, &clsid);
			else
			{
				hStartKey = HKEY_CURRENT_USER;
				b = GetRegKeyValue(hStartKey, _T("SOFTWARE\\Classes\\Surfer.Application\\CLSID"), NULL, REG_SZ, (LPBYTE)buf, 256, NULL);
				if (b)
					h = CLSIDFromString(buf, &clsid);
			}

			if (h != S_OK)
				return ERR_AUTOMATION_CAN_NOT_GET_CLSID;
		}

代码里有个函数GetRegKeyValue,是专门用来读取注册表的,代码如下:

BOOL IsWow64()
{
	BOOL bIsWow64 = FALSE;
	typedef BOOL(WINAPI* LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
	LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle(TEXT("kernel32")), "IsWow64Process");
	if (NULL != fnIsWow64Process)
	{
		fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
	}

	return bIsWow64;
}
BOOL GetRegKeyValue(HKEY hKey, LPCTSTR lpszSubKey, LPCTSTR lpszItem, DWORD dwType, LPBYTE pRetData, UINT uRetDataBufSize, LPDWORD pRetSize)
{
	HKEY hKeyResult;
	LONG retval;

	if (IsWow64())
		retval = RegOpenKeyEx(hKey, lpszSubKey, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKeyResult);
	else
		retval = RegOpenKeyEx(hKey, lpszSubKey, 0, KEY_QUERY_VALUE, &hKeyResult);

	if (retval == ERROR_SUCCESS || retval == ERROR_MORE_DATA)
	{
		DWORD size = uRetDataBufSize;
		retval = RegQueryValueEx(hKeyResult, lpszItem, 0, &dwType, pRetData, &size);
		if (retval == ERROR_SUCCESS || retval == ERROR_MORE_DATA)
		{
			if (pRetSize != NULL)
				*pRetSize = size;
			if (dwType == REG_SZ)
				pRetData[size] = 0;
		}
		RegCloseKey(hKeyResult);

		return (retval == ERROR_SUCCESS || retval == ERROR_MORE_DATA);
	}
	return (retval == ERROR_SUCCESS || retval == ERROR_MORE_DATA);
}

3 Windows系统原因

我们下了这么多功夫,应该没问题了吧!呵呵,那你太小看MFC了,它总是会时不时的跳起来,突然出其不意的给你一击。“MFC,永远在路上!”,我给MFC的墓志铭。

不多说了,首先你的程序最好设置为系统管理员权限。在哪里设置?呃。。。,我找找,唉,实在是找不到!还有一句想送的话:”永远别低估VC的难度!“。好了,在”链接器/清单文件/UAC执行级别“,选则:“requireAdministrator”。
这下有预感了吧!事情没有结束,这时我已经没有什么别的好办法了,让用户先打开Surfer,打开方式尝试两种:普通运行和管理员身份运行。可能总有一种适合他的计算机。

4 Surfer原因

事情是没有结束。我们一味指责MFC、Windows也不全对,因为Surfer本身也有问题。不然,为什么你用自动化技术打开Excel、AutoCAD很少出问题呢?第一个问题就是Surfer在注册自动化组件时不完善,也就是注册表中关于自动化项不完善,导致VC调用失败。但这里要说的不是这个。而是你不要安装多个不同的Surfer版本。安装了不同版本之后,可能时之间有冲突,也会导致失败。这时候,请你请求你的用户,让他们删除多余的Surfer,留一个就够了。

这时候就又双叒引申了新的Surfer问题,Surfer删除不干净!也就是说,用户只留了一个Surfer也仍然不行了。这时候已经不是代码问题了, 也不是VC、VB或C#能解决的问题了。请让用户不要嫌麻烦,也不要怪罪开发人员,直接按下面方法干就完了:
1)删除所有的Surfer,重新启动计算机。
2)打开注册表,搜索“Surfer.Application”,见一个删一个,看都不要看。
3)打开Execel的Vba开发代码窗口,别的微软的Vba开发代码窗口也行,在菜单“工具/引用”弹出的对话框中,看看有没有Surfer的影子:
在这里插入图片描述

如果有,看一下下面它定位的位置,比如“C:\Program Files\Golden Software\Surfer 12\Surfer.exe”。回到注册表,从头开始,搜索这个字符串,还是见一个删一个。
4)重新安装一个Surfer。

如果又双叒叕不成功,最后的最后,去建议用户安装一个相对低版本的Surfer,我的建议是11~14。

5 其他问题

在使用surfer接口函数时,也要注意:
1)要考虑到中英文版的区别。这个Surfer也是奇葩,汉化的时候,内核也全部汉化了!!简直就是简直了!内核你去汉化它是个什么鬼思路,想让用户看你里面穿的什么马甲吗?关键是汉化之后,开发人员麻烦了,比如预设的颜色值红色,本来英文版就是“red”,代码中也用字符串“red”读取,但汉化后,这个常量变成“红色”了,也就是你代码中要放两套,我去!而且,而且还有不同的汉化版,一个颜色的汉语两个汉化版用的不是一个词语!更别说还有湾湾的繁体字!坚决让你的用户用英文版!
2)要考虑后续Surfer版本接口函数的参数变化以及执行的差别,否则会导致程序崩溃或调用提前终止。

不说了,如果想更详细了解Surfer自动化处理的细节代码,请看我的另一篇文章“SegeX Automation:VC VB(VBA) C# 调用Golden Surfer自动化(Automation)方法详解”,当然这篇文章还没有写,希望你看这篇文章的时候,那篇文章也写好了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Surfer是一种常用的地质数据可视化软件,而MATLAB是一种编程语言和环境,可以用于数据分析和图形绘制。如果要实现MATLAB调用Surfer来实现电法等值线图的批量绘制,可以按照以下步骤进行: 1. 准备数据:将电法数据整理成适合Surfer读取的格式,通常是以文本文件或网格数据文件的形式保存。 2. 在MATLAB中编写脚本:使用MATLAB编写一个脚本,首先将Surfer软件启动并打开要绘制等值线图的电法数据文件。可以使用MATLAB的COM接口来连接Surfer,并调用相关的功能。 3. 设置绘图参数:在MATLAB中调用Surfer的COM接口,可以设置绘图的参数,如等值线的间隔、颜色填充等。可以根据需要添加标签、标题和图例等。 4. 批量绘制等值线图:可以使用循环结构和文件操作函数,将需要绘制的多个电法数据文件逐个读取并绘制等值线图。 5. 保存输出图片:可以使用MATLAB的图形保存函数将绘制好的等值线图保存为图片格式,如PNG、JPEG等。 需要注意的是,这只是一个基本的思路,具体的实现可能还需要根据具体的数据格式和Surfer的版本来做一些调整。另外,在使用MATLAB调用Surfer绘制等值线图时,还需要确保计算机上已经安装了Surfer软件,并且配置好了MATLAB与Surfer的连接。 总之,通过MATLAB调用Surfer自动化实现电法等值线图的批量绘制,可以提高工作效率,并使绘图结果更加规范和一致。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值