在.Net程序中调用ObjectARX函数——Through the Interface

原文:Calling ObjectARX functions from a .NET application

.Net有一个非常引人注目的功能就是它调用“传统”的非托管的C++ API。虽然我称之为“传统”,但我们通常利用这个特性来调用远不会被废止的API(相信我,ObjectARX的C++版本现在正活蹦乱跳呢!:-))。

Autodesk公司明白我们的合作伙伴在软件开发上已经进行了多年的投入,不可能抛弃这些投入然后转而支持最新的和最伟大的(有时可能仅仅是“月度精选”)编程技术。例如,过去几年中我们已经确信为已有的LISP程序创建VB或者VBA用户界面是可行的。现在为ObjectARX程序创建.NET的用户界面也是可行的。有时我们发布自己的操作函数来支持这些功能(例如可以调用ActiveX DLL的LISP函数)还有时候我们会建议人们如何最好地利用微软的标准平台技术。

所以…你怎样从VB.NET里调用ObjectARX函数?答案是平台调用(Platform Invoke)(或缩写为P/Invoke)。微软还没有在.NET Framework中发布完整功能的Win32 API。正如Autodesk通过托管API没有完全发布ObjectARX的所有功能,但P/Invoke可以帮助你解决现有的问题。

首先,介绍下背景知识——ObjectARX到底是什么,以及P/Invoke可以怎样帮助我们。

ObjectARX是从DLL或EXE导出的一组API。大多数导出的函数在编译期间都会被“修饰(decorated)”或“重整(mangled)”,除非有特殊的编译指令不进行这样操作(例如,所有ADS函数就是这种情况,他们都被声明为extern "C"所以不会被重整(mangled))。编译器根据函数签名给函数指定一个的唯一的名称是有原因的:在C++中,两个函数具有相同的名字是完全合法的,但它们的参数和返回值却不会相同。修饰过的(decorated)名称包含完整的函数名。这就是为什么下面所说找到函数的正确的导出名称的技术能够有效的原因。

 [注:这种技术可以很好地用于C风格的函数,或C++静态函数。对实例成员(类的方法)不起作用,因为不可能从通过托管代码定义的类定义来实例化一个非托管的实例。如果你需要对托管代码开放一个类的方法,你就需要编写并开放一些C++代码来实例化这个类,然后调用这个方法在返回结果]

为了举例说明这个流程,我们来用C# 和VB.NET一步一步的实现调用acedGetUserFavoritesDir()这个函数在ObjectARX的头文件中被声明如下:

 

extern Adesk::Boolean acedGetUserFavoritesDir( ACHAR* szFavoritesDir );


 

 

根据ObjectARX参考,“该函数能提供对当前用户Windows收藏夹目录的访问。”

第一步——确定的出口位置。

来自DevTech EMEA的Fenton Webb,提供了他用以完成这个目的的批处理文件:

[复制并粘贴到一个名为“findapi.bat”的然后你将它放进入你的AutoCAD应用程序文件夹。从能查找到dumpbin.exe 的命令提示符窗口运行findapi——Visual Studio在安装时创建的命令提示安装和创建将帮你找到dumpbin.exe。]

@echo off
if "%1" == "" goto usage
:normal
for %%i IN (*.exe *.dll *.arx *.dbx *.ocx *.ddf) DO dumpbin /exports %%i | findstr "%%i %1"
goto end
:usage
echo findapi "function name"
:end


当然,你可以将输出重定向到文本文件,例如:

C:\Program Files\AutoCAD 2007>findapi acedGetUserFavoritesDir > results.txt

执行会花费一些时间,这个批处理文件会遍历所有的DLL,EXE等,在AutoCAD文件夹中找到的结果(它有一个Bug就是找不到任何文件时就不停止——这个Bug留给读者作为练习)。

打开文本文件,你将看到 acedGetUserFavoritesDir()函数是在哪里发布的:

[ from the results for AutoCAD 2007 ]

Dump of file acad.exe
        436  1B0 004B4DC0 ?acedGetUserFavoritesDir@@YAHPA_W@Z


 警告:修饰后的函数名的接受/返回字符串从AutoCAD 2006到2007时有了变动。因为我们现在使用Unicode字符串定义。这里是2004 / 2005 / 2006以前的声明(
如果我没记错的话在AutoCAD 2000i这个函数定义可能也是有效的):

[ from the results for AutoCAD 2006 ]
Dump of file acad.exe
        357  161 00335140 ?acedGetUserFavoritesDir@@YAHPAD@Z

原因很简单,是因为函数签名从传入char *改为了ACHAR * , (在AutoCAD 2007的数据类型中现在解析为“宽字符”或Unicode字符串)改变函数签名的改变导致修饰后的函数名也进行了更改。这是非常明显的,但是潜在的迁移问题还是值得铭记在心的——如果某个发行版本有大量的的签名进行了更改(如AutoCAD 2007中对Unicode的支持),过多的依赖修饰后的函数名可能会导致大量潜在的迁移工作。

另一个警告:你会发现从这些DLL/EXE文件导出的许多函数在ObjectARX头文件中找不到相应的声明。这些功能虽然开放了,但是不会被支持,这意味着,你可能能够找到如何调用它们,但使用它们请你自担风险(可以风险会很大) 。不支持的API都可能随时发生改变(甚至消失)而不会另行通知。

现在,我们已经确定函数在哪儿以及如何开放的,我们就可以创建在代码中使用的这个函数的声明。

步骤2 - 在你的代码正确的声明函数。

函数的声明根据你使用的编程语言不同而略有差别。

VB开发人员在他们的项目将用“Declare”来建立P/Invoke。这最终将由编译器翻译成对DllImport的调用,这个调用也会直接在C#中。

这些声明应在类级别(而不是在一个单独的函数的定义)。

VB .NET

Private Declare Auto Function acedGetUserFavoritesDir Lib "acad.exe" Alias "?acedGetUserFavoritesDir@@YAHPA_W@Z" (<MarshalAs(UnmanagedType.LPWStr)> ByVal sDir As StringBuilder) As Boolean

C#

[DllImport("acad.exe", EntryPoint = "?acedGetUserFavoritesDir@@YAHPA_W@Z", CharSet = CharSet.Auto)]
public static extern bool acedGetUserFavoritesDir([MarshalAs(UnmanagedType.LPWStr)] StringBuilder sDir);


注:

  1. 将字符集设置为“自动”是很有必要的——这是不是默认设置。编译器在决定是否使用Unicode或ANSI做的很好,所以交给它来处理最简单。
  2. 在2007中声明Unicode字符串变量时需要用MarshalAs(UnmanagedType.LPWStr) 来声明,不管使用字符串或StringBuilders。
  3. 对输出字符串参数使用StringBuilder,因为标准的字符串都被认为是不可改变的。输入字符串参数则没有关系。

第三步——在你的代码中使用这个函数

[我省略了标准的using/import 语句和类以及函数声明以提高可读性 ]

VB .NET

Dim ed As Editor = Application.DocumentManager.MdiActiveDocument.Editor
Dim sDir As New StringBuilder(256) 
Dim bRet As Boolean = acedGetUserFavoritesDir(sDir)
If bRet And sDir.Length > 0 Then
        ed.WriteMessage("Your favorites folder is: " + sDir.ToString)
End If

C#

Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
StringBuilder sDir = new StringBuilder(256);
bool bRet = acedGetUserFavoritesDir(sDir);
if (bRet && sDir.Length > 0)
        ed.WriteMessage("Your favorites folder is: " + sDir.ToString());

第三步——在你的代码中使用这个函数

[我省略了标准的using/import 语句和类以及函数声明以提高可读性 ]

注:

我们声明的的StringBuilder变量(sDir)为256个字符长。 AutoCAD的期望我们提供一个足够长的缓冲区将数据复制进去。

在我的系统上两段代码都会导致下面的文字被发送到AutoCAD命令行:

Your favorites folder is: C:\My Documents\Favorites

就是这样了:你现在应该能够从.NET中调用ObjectARX的全局函数了。这项技术也可以用来调用你自己在DLL中发布的函数...这种方式能够让你你用.NET创建很酷的用户界面同时也能充分利用现有的C++代码(还有其他的,例如开放自己的托管API)。

对于使用P/ Invoke,特别在Win32上相关的附加信息,这里有很多的资源。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值