標籤:
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++靜態函數。它不會工作執行個體成員(方法的類),因為它是不可能來執行個體化一個類,它定義了類的非託管對象從Managed 程式碼。如果你需要讓一個類方法Managed 程式碼,您需要編寫&暴露一些本機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 batchfile he uses for just this purpose:
[ Copy and paste this into a file named"findapi.bat", which you then place this into your AutoCADapplication folder. You will need to run findapi from a command prompt whichknows where to find dumpbin.exe - the Visual Studio Command Prompts created oninstalling VS will help you with this. ]
這個複製並粘貼到一個檔案名稱為“findapi.bat”的檔案中,然後把這個到cad應用程式檔案夾中。你需要從命令提示字元運行findapi知道找到dumpbin.exe - Visual Studio命令提示上建立安裝VS將協助你解決這個問題
@echo offif "%1" == "" goto usage:normalfor %%i IN (*.exe *.dll *.arx *.dbx *.ocx *.ddf) DO dumpbin /exports %%i | findstr "%%i %1"goto end:usageecho findapi "function name":end
You can redirect the output into a text file, of course, for example:
C:\Program Files\AutoCAD 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 [email protected]@[email protected]
A word of warning: the decorated names for functionsaccepting/returning strings changed between AutoCAD 2006 and 2007, because weare now using Unicode for string definition. Here is the previous declarationfor 2004/2005/2006 (which was probably valid for as long as the function wasdefined, 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 [email protected]@[email protected]
This is simply because the function signature has changedfrom taking a char* to an ACHAR* (a datatype which now resolves to a"wide" or Unicode string in AutoCAD 2007). A change in the functionsignature results in a change in the decorated name. This is straightforwardenough, but it is worth bearing in mind the potential migration issue - a heavydependency on decorated function names can lead to substantial migration effortif widespread signature changes are made in a release (as with AutoCAD 2007‘ssupport of Unicode).
這完全是因為函數簽名已經改變了從char *到ACHAR *(資料類型被解析為“寬”或者Unicode字串在AutoCAD 2007)。函數簽名的變化導致修改名稱的改變。這是非常簡單的,但值得注意的是,潛在的歉意問題——嚴重依賴裝飾函數名可能會導致大量的遷移工作在某一次發布之中被改變(與AutoCAD 2007支援Unicode)。
Another warning: you will find a number of other functions exportedfrom the various DLLs/EXEs that do not have corresponding declarations in theObjectARX headers. These functions - while exposed - are not supported. Whichmeans that you may be able to work out how they can be called, but use them atyour 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 isexposed, 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 theprogramming 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 thecompiler into calls to DllImport, which is also used directly in C#.
介紹關鍵字,vb使用declare c#使用DllImport
These declarations should be made at the class level (notwithin an individual function definition).
需要在類級聲明,不要再individual中定義
VB.NETPrivate Declare Auto Function acedGetUserFavoritesDir Lib "acad.exe" Alias "[email protected]@[email protected]"(<MarshalAs(UnmanagedType.LPWStr)> ByVal sDir As StringBuilder) As Boolean
C#[DllImport("acad.exe", EntryPoint = "[email protected]@[email protected]", CharSet = CharSet.Auto)]public static extern boolacedGetUserFavoritesDir([MarshalAs(UnmanagedType.LPWStr)] StringBuildersDir);
Notes:
- It‘sworth specifying the character set as "Auto" - which is not thedefault setting. The compiler does a good job of working out whether to useUnicode or ANSI, so it‘s easiest to trust it to take care of this.
值得指定字元設定為“自動”——這不是預設設定。編譯器很好地判斷是否使用Unicode或ANSI,所以它是可信的。
- Youwill need to use the MarshalAs(UnmanagedType.LPWStr) declaration for Unicodestring variables in 2007. This is true whether using Strings or StringBuilders.
您將需要使用MarshalAs(UnmanagedType.LPWStr)在2007年為Unicode字串變數聲明。這是真的是否使用字串或StringBuilders
- Usea StringBuilder for an output string parameter, as standard Strings areconsidered immutable. Strings are fine for input parameters.
使用StringBuilder輸出字串參數,標準的字串是不可變的。字串是對輸入參數。
Step 3 - Use the function in your code
[ I‘ve omited the standard using/import statements, aswell as the class & function declarations, to improve readability. ]
我省掉了標準使用/匯入語句,以及類和函式宣告,以提高可讀性。
VB.NETDim ed As Editor = Application.DocumentManager.MdiActiveDocument.EditorDim 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: wedeclare the StringBuilder variable (sDir) as being 256 characters long. AutoCADexpects us to provide a sufficiently long buffer for the data to be copied intoit.
我們聲明StringBuilder變數(sDir)是256個字元長。AutoCAD期望我們提供一個足夠長的的資料複製到緩衝區。
On my system both code snippets resulted in the followingbeing sent to AutoCAD‘s command-line:
Your favorites folder is: C:\My Documents\Favorites
So that‘s it: youshould now be able to call global ObjectARX functions from .NET. This techniquecan also be used to call your own functions exposed from DLLs... which is oneway to allow you to create fancy UIs with .NET and leverage existing C++ code (thereare others, such as exposing your own Managed API).
就是這樣:你現在應該可以從.net調用全域ObjectARX函數。這種技術也可以用來從dll調用你的函數暴露…這是一種允許您建立的昂貴的介面利用.NET和利用現有的c++代碼(還有其他,比如暴露自己的管理API)
For additionalinformation on using P/Invoke, particularly with Win32, here is a really greatresource.
2006-7有價值的Kean部落格——Calling ObjectARX functions from a .NET Application(PInvoke)