2006-7有价值的Kean博客——Calling ObjectARX functions from a .NET Application(PInvoke)

One of the really compelling features of .NET is its ability to call "legacy" unmanaged C++ APIs. I say "legacy", but we use this facility regularly to call APIs that are far from being considered defunct (the C++ version of ObjectARX is alive and kicking, believe me! :-).

真正引人注目的特性之一是.NET调用“遗留”非托管c++ api的能力。我说的“遗产”,但我们使用该机制来调用api远非证明这个东西已经死了(c++版本ObjectARX活蹦乱跳的,相信我!:-)。

Autodesk understands that our development partners have invested many years in application development, and can't afford to throw that investment away to support the latest & greatest (and sometimes "flavor of the month") programming technology. For example, over the years we've made sure it was possible to create a VB or VBA user-interface for an existing LISP application or now a .NET user-interface for an ObjectARX application. Sometimes we expose our own interoperability functions to help with this (such as LISP functions to call ActiveX DLLs), and in other cases we advise people on how best to leverage standard Microsoft platform technologies.

Autodesk明白我们的发展伙伴多年投入在应用程序开发中,并且不能为了支持最新的和最伟大的编程技术而抛弃他们之前的投入。例如,多年来我们尽可能来为现有的LISP程序创建VB或VBA用户接口以保证或是现在为ObjectARX应用程序提供.NET的用户接口。有时我们暴露自己的互操作性的功能来帮助用户(如LISP函数调用ActiveX dll),而在其他情况下,我们建议人们如何利用微软平台技术标准。

So... how do you call an ObjectARX function from VB.NET? The answer is Platform Invoke (or P/Invoke for short). Microsoft has not exposed the full functionality of the Win32 API through the .NET Framework - just as Autodesk has not exposed all of ObjectARX through AutoCAD's Managed API - but P/Invoke helps you get around this.

所以…你怎么从VB.NET调用一个ObjectARX函数?答案是平台调用(简称P / Invoke)。微软尚未经由.NET Framework暴露的Win32 API的完整的功能。就像欧特克并没有暴露ObjectARX通过AutoCAD的所有管理API一样,但P / Invoke帮助你绕过这个限制。

First, some background on what ObjectARX really is, and how P/Invoke can help us.

首先,是一些关于ObjectARX到底是一个东西的背景,以及P / Invoke如何帮助我们。

ObjectARX is a set of APIs that are exported from DLLs or EXEs. Most exported functions get "decorated" or "mangled" during compilation, unless there is a specific compiler directive not to (this is the case for all the old ADS functions, for instance - they are declared as extern "C" and are therefore not mangled). The compiler assigns a unique name based on the function signature, which makes sense: it is quite legal in C++ to have two functions with the same name, but not with identical arguments and return values. The decorated name includes the full function name inside it, which is why the below technique for finding the correct export works.

ObjectARX是一组导出形式为Dll或是Exe的API,大多数输出功能在编译过程总被修饰或破坏,除非有一个特定的编译器指令(这种情况适合于老的ADS功能,他们通过生成一个扩展C以保证没被破坏)编译器分配一个唯一的名称基于函数签名,来保证签名有意义,因为在C++中两个函数具有相同名称且不同签名是完全合法的

修饰名包括完整的函数名在里面,这就是为什么以下技术来找到正确的出口工作

Note: this technique works well for C-style functions, or C++ static functions. It will not work on instance members (methods of classes), as it is not possible to instantiate an unmanaged object of the class that defines the class from managed code. If you need to expose a class method to managed code, you will need to write & expose some native C++ code that instantiates the class, calls the method and returns the result. ]

这种技术适用于C风格的功能,或c++静态函数。它不会工作实例成员(方法的类),因为它是不可能来实例化一个类,它定义了类的非托管对象从托管代码。如果你需要让一个类方法托管代码,您需要编写&暴露一些本机c++代码实例化类,调用该方法,并返回结果。

To demonstrate the procedure we're going to work through the steps needed to call acedGetUserFavoritesDir() from C# and VB.NET.

演示过程我们要完成所需的步骤来实现在从c#VB.NET()中调用acedGetUserFavoritesDir()

This function is declared in the ObjectARX headers as:

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

According to the ObjectARX Reference, "this function provides access to the Windows Favorites directory of the current user."

根据ObjectARX的参考资料,这个功能提供了获取当前用户收藏夹的功能

Step 1 - Identify the location of the export.

Fenton Webb, from DevTech EMEA, provided this handy batch file he uses for just this purpose:

[ Copy and paste this into a file named "findapi.bat", which you then place this into your AutoCAD application folder. You will need to run findapi from a command prompt which knows where to find dumpbin.exe - the Visual Studio Command Prompts created on installing VS will help you with this. ]

这个复制并粘贴到一个文件名为“findapi.bat”的文件中,然后把这个到cad应用程序文件夹中。你需要从命令提示符运行findapi知道找到dumpbin.exe - Visual Studio命令提示上创建安装VS将帮助你解决这个问题

@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

  

You can redirect the output into a text file, of course, for example:

C:Program FilesAutoCAD 2007>findapi acedGetUserFavoritesDir > results.txt

It'll take some time to work, as this batch file chunks through all the DLLs, EXEs, etc. in the AutoCAD application folder to find the results (it doesn't stop when it finds one, either - this enhancement is left as an exercise for the reader ;-).

它会花一些时间去工作,这个批处理文件检查cad应用程序文件夹中所有dll,和exe来找到结果(它不停止当它发现一个,要么,这增强是留给读者作为练习;-)。

Opening the text file will allow you to see where the acedGetUserFavoritesDir() function is exported:

[ from the results for AutoCAD 2007 ]

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

A word of warning: the decorated names for functions accepting/returning strings changed between AutoCAD 2006 and 2007, because we are now using Unicode for string definition. Here is the previous declaration for 2004/2005/2006 (which was probably valid for as long as the function was defined, back in AutoCAD 2000i, if I recall correctly):

函数接受的装饰名称/返回字符串AutoCAD 2006年和2007年之间的变化,因为我们现在使用Unicode字符串的定义。这是以前申报2004/2005/2006(可能是在函数定义,只要有效在AutoCAD 2000我,如果我没记错的话):

[ from the results for AutoCAD 2006 ]

Dump of file acad.exe
        357  161 00335140 ?acedGetUserFavoritesDir@@YAHPAD@Z

This is simply because the function signature has changed from taking a char* to an ACHAR* (a datatype which now resolves to a "wide" or Unicode string in AutoCAD 2007). A change in the function signature results in a change in the decorated name. This is straightforward enough, but it is worth bearing in mind the potential migration issue - a heavy dependency on decorated function names can lead to substantial migration effort if widespread signature changes are made in a release (as with AutoCAD 2007's support of Unicode).

这完全是因为函数签名已经改变了从char *到ACHAR *(数据类型被解析为“宽”或者Unicode字符串在AutoCAD 2007)。函数签名的变化导致修改名称的改变。这是非常简单的,但值得注意的是,潜在的歉意问题——严重依赖装饰函数名可能会导致大量的迁移工作在某一次发布之中被改变(与AutoCAD 2007支持Unicode)。

Another warning: you will find a number of other functions exported from the various DLLs/EXEs that do not have corresponding declarations in the ObjectARX headers. These functions - while exposed - are not supported. Which means that you may be able to work out how they can be called, but use them at your own risk (which can be substantial). Unsupported APIs are liable to change (or even disappear) without notice.

你会发现许多来自不同的dll或exe其他函数的头部并没有相关的声明。这些功能即使暴露也将不会被支持。这意味着你可以找出他们如何可以调用,但使用它们自己的风险(实质)。不受支持的API是很容易在没有通知的条件下发生变化的

Now we've identified where and how the function is exposed, we can create a declaration of this function we can use in our code.

现在我们已经确定在哪里以及如何公开的函数,我们可以创建一个声明这个函数我们可以用在我们的代码。

Step 2 - Declare the function correctly in your code.

This is going to be slightly different depending on the programming language you're using.

这将是略有不同取决于你使用的编程语言

VB developers will be used to using "Declare" to set-up P/Invoke from their projects. This ends up being translated by the compiler into calls to DllImport, which is also used directly in C#.

介绍关键字,vb使用declare c#使用DllImport

These declarations should be made at the class level (not within an individual function definition).

需要在类级声明,不要再individual中定义

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 boolacedGetUserFavoritesDir([MarshalAs(UnmanagedType.LPWStr)] StringBuildersDir);

Notes:

  1. It's worth specifying the character set as "Auto" - which is not the default setting. The compiler does a good job of working out whether to use Unicode or ANSI, so it's easiest to trust it to take care of this.

值得指定字符设置为“自动”——这不是默认设置。编译器很好地判断是否使用Unicode或ANSI,所以它是可信的。

  1. You will need to use the MarshalAs(UnmanagedType.LPWStr) declaration for Unicode string variables in 2007. This is true whether using Strings or StringBuilders.

您将需要使用MarshalAs(UnmanagedType.LPWStr)在2007年为Unicode字符串变量声明。这是真的是否使用字符串或StringBuilders

  1. Use a StringBuilder for an output string parameter, as standard Strings are considered immutable. Strings are fine for input parameters.

使用StringBuilder输出字符串参数,标准的字符串是不可变的。字符串是对输入参数。

Step 3 - Use the function in your code

[ I've omited the standard using/import statements, as well as the class & function declarations, to improve readability. ]

我省掉了标准使用/导入语句,以及类和函数声明,以提高可读性。

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

Note: we declare the StringBuilder variable (sDir) as being 256 characters long. AutoCAD expects us to provide a sufficiently long buffer for the data to be copied into it.

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

On my system both code snippets resulted in the following being sent to AutoCAD's command-line:

Your favorites folder is: C:My DocumentsFavorites

So that's it: you should now be able to call global ObjectARX functions from .NET. This technique can also be used to call your own functions exposed from DLLs... which is one way to allow you to create fancy UIs with .NET and leverage existing C++ code (there are others, such as exposing your own Managed API).

就是这样:你现在应该可以从.net调用全局ObjectARX函数。这种技术也可以用来从dll调用你的函数暴露…这是一种允许您创建的昂贵的界面利用.NET和利用现有的c++代码(还有其他,比如暴露自己的管理API)

For additional information on using P/Invoke, particularly with Win32, here is a really great resource.

原文地址:https://www.cnblogs.com/sinper/p/5097040.html