□文 /張海濤
VBA(Visual Basic for Application)是一種面向對象的編程語言,它的基本語法完全繼承于VB,所以具有語法簡單易懂、編程邏輯清晰的特點,非常適合程序的快速開發。VBA的強大優勢在于將VB的編程環境與被開發程序同時運行,利用面向對象編程方法(OOP),在接口的庫中將軟件中的重要部分對象化、參數化,在VBA中直接調用軟件中豐富的對象進行操作以實現編程目的。因此,VBA作為一種通用型語言,能廣泛應用于Windows下的Office系列軟件、AutoCAD等專業商業軟件的二次開發、Windows腳本編程(VBS)等。
AutoCAD是目前應用最為廣泛的交互式計算機輔助繪圖與設計軟件,其優勢在于通用性、多工業標準和開放的體系結構,廣泛應用于土木建筑、裝飾設計、機械工程等領域。雖然AutoCAD功能強大,但在解決一些專業問題,尤其在需要結合設計計算、數據處理、復雜圖形繪制時就會顯得力不從心,這時候在程序基礎上對AutoCAD進行二次開發成為解決上述問題的有效補充手段[1]。AutoCAD常見的二次開發工具包括Auto LISP/Visual LISP、基于ActiveX Automation技術的VB/VBA、采用C++語言的Object ARX技術、基于微軟.Net技術的VB.Net和C#語言開發。這幾種語言各有優劣,具體特點及使用方法可參考相關介紹[2]。相較而言,VBA具有編程簡單、快速開發、運行效率高、功能強大等特點使其擁有不同于其他二次開發工具的特殊優勢,也使其在各專業工程人員中的推廣和繼承具有了生命力。
基于VBA的這些優勢,二次開發技術在橋梁工程中得以廣泛應用[3]。本文對AutoCAD和Excel進行聯合開發,利用Excel獲取CAD圖形數據生成橋梁工程專業計算軟件橋博的腳本,輔助橋博軟件的建模,提高了工作效率。在此基礎上本文著重介紹了包括VBA理論模塊、程序設計方法和關鍵技術,為相關工程的二次開發提供參考。
首先,二次開發的操作是基于對AutoCAD提供對象(Object)的操作,這些對象的種類非常豐富并且包含各種屬性(Properties)和方法(Methods)。對象本質上是一種特殊的變量,可以進行定義、賦值、釋放。AutoCAD中的對象按性質可以主要劃分為以下幾種[4]。
1)圖形對象,如直線(Line)、文本(Text)、標注(Dimensions)等。
2)樣式設置對象,如線型(Linetype)、文字樣式(TextStyle)。
3)組織結構,如圖層(Layers)、塊(Blocks)。
4)圖形顯示對象,如視圖(View)和視口(Viewport)。
5)AutoCAD 應 用 程 序 (Application) 和 文 檔(Document)。
這些對象基本涉及到了AutoCAD的各方面并可以在VBA的對象瀏覽器中查詢。各對象之間并不是隨意排列,大多數都存在父對象和子對象的樹形衍生關系,常用對象見圖1。

圖1 AutoCAD對象
如圖1所示,AutoCAD中的圖形對象基本都囊括在ModelSpace中,比如常用的各類線、文字等,用Add方法可以向AutoCAD的Document對象中添加各種圖元,完成圖形的繪制,而另外一個重要的對象Utility則集合了一系列的內部實用工具,比如獲取兩點間距(GetDistance)、獲取對象(GetEntity)、獲取坐標(GetPoint)等方法函數,用于獲取圖形中的各類信息。這兩個基本類可以完成對AutoCAD的大部分常用操作。關于其余對象的定義、屬性、方法及示例都可以在AutoCAD提供的官方幫助文件里查詢。
使用VBA開發軟件可以分為內部調用和外部調用兩種方式。AutoCAD內置了VB的開發環境VBE(高版本需要安裝VBA開發包),可以在VBE中直接開發VBA,這樣能夠在程序內部直接運行程序并查看在窗口中的運行結果,這是VBA相較其他開發語言的優勢之一。缺點是內置的VBA沒有辦法進行封裝,只能將代碼部分通過加密的方式進行簡單保護。
由于在AutoCAD中無法通過內部命令的方式直接運行內部VBA,所以通常會用LISP給VBA編寫一個調用命令,可采用如下形式:
(defun c:CM()
(command“_vbarun”“cm_vba”)
)
這樣,在加載這個lsp文件和dvb格式的源文件后,就可以通過在命令行中直接輸入“CM”來內部調用名稱為“cm_vba”的VBA程序。
在另外一些情況下,如果將部分代碼封裝為動態鏈接庫后外部引用或者從其他程序的VBE、VB6.0和Visual Studio等開發工具進行外部控制時,則需要進行外部調用,首先要在窗口的“工具”-“引用”中調用基本庫[5],然后在代碼部分聲明引用對象,獲得軟件的控制權限。
例如,當采用外部創建CAD文件時,可采用以下代碼,依次獲得對軟件和新建文檔的控制權:
DimAcadApp as AcadApplication
DimAcadDoc as AcadDocument
Set AcadApp
=CreateObject(“AutoCAD.Application”)
AcadApp.Visible=True
Set AcadDoc=AcadApp.ActiveDocument
外部調用的缺點是沒有辦法直接在AutoCAD中直接運行程序查看結果且需要考慮程序運行過程中輸入、輸出的接口問題。但是外部調用為AutoCAD與其他程序的交互提供了實施方法。
AutoCAD的優勢在于對圖形的處理,當涉及到其他方面的工作時,單一依賴AutoCAD或者完全靠VB本身的功能與函數去構建復雜的程序會拖累整體的運行效率。所以,與其他專業軟件進行交互,是VBA的一項重要功能,見圖2。

圖2 VBA交互體系
Excel是微軟公司開發的專業辦公軟件,所以Office已經內置了VBA的開發環境[6]。Excel協助AutoCAD能夠完成數據提取、整理、運算、輸出等一系列工作,結合Excel和AutoCAD可以避免大量使用VB窗口及控件,從而節省復雜的窗口設計工作。關于Excel的對象結構此處不多做介紹,具體參閱相關教材與手冊。
在設計程序時,需要選擇VBA基于的軟件主體:如果程序的主要功能是運算和數據處理,需要從Au-toCAD定向獲取、輸出圖形信息,可以選擇從Excel控制AutoCAD,借用Excel的內置功能對數據信息進行處理;如果程序的主要任務是繪圖,只是借助Excel的末端功能完成簡單數據匯總、數據傳遞功能等,可以選擇從AutoCAD控制Excel;如果希望編譯獨立可執行程序,可以直接用VB6.0、Visual Studio等開發工具調用其他VBA兼容軟件。
因為VBA語言本身沒有劃分模塊的語法結構,所以在程序設計初期就應當考慮把程序定義成不同模塊,以方便后期對程序進行定向維護和擴展,有以下幾個基本原則可參考。
1)劃分程序的主次,將具體執行各部分功能的操作歸納于不同的子程序中,而主程序用于控制所有子程序的運算流程,這里將最終腳本輸出函數Genegrate_Script()作為主程序,而子程序負責各個部分腳本信息的統計和生成,見圖3。

圖3 程序模塊
2)將自編的一些通用型功能函數歸納成各類函數模塊,方便在不同的子程序中直接使用,也方便向其他程序進行移植,比如排序函數、插值函數、向量計算函數等。
Sub Array_Shrink(ByRefarr As Variant)
'清除多段線中重復節點
......
For i=2 Tonum-1 Step 2
tmp_array(m)=arr(i)
tmp_array(m+1)=arr(i+1)
Ifarr(i)=arr(i-2)And arr(i+1)=arr(i-1)Then
ReDimPreserve tmp_array(num-2)
Else
m=m+2
End If
Next i
arr=tmp_array
End Sub
3)VB不具備C++/C#等語言方便定義類的功能,但是依然可以利用Type語句在“模塊”中自定義數據類型,可以將一些變量盡量對象化置于模塊中,以方便對同對象變量進行識別,比如此處將箱梁截面定義成對象,包含截面的序號、坐標、插入點屬性。
Public Type bdSection '定義箱梁截面
indexAs Integer '截面序號
Pt_List As Variant'截面組成坐標
InsertPt As Variant'截面插入點坐標
End Type
以Excel為主體,通過VBA外部控制AutoCAD獲取模型信息,自動生成橋博的腳本文件。主程序主要負責匯總各部分信息并輸出為橋博腳本dbs類型文本。型式參照以下總體信息部分代碼,引用的是init_ZTXX()函數
Print#1,"http://總體信息*************"
Print#1,"GEN.PrjTitle=""橋梁博士腳本"";"
Print#1,"GEN.CalType="&init_ZTXX.CalType&";" '函數獲取總體信息
Print#1,"GEN.BPrestress="&init_ZTXX.bPrestress&";"
Print#1,"GEN.SelfHumi=0.8;
Print#1,"GEN.Code="&init_ZTXX.Code&";"
Print#1,"GEN.cU="&init_ZTXX.cU&";"
從Excel內部控制AutoCAD需要識別已打開的CAD文件,使用的是GetObject()函數,型式如下
DimAcadApp as AcadApplication
DimAcadObj As AcadDocument
Set AcadApp=GetObject(,"AutoCAD.Application")
Set AcadObj=AcadApp.ActiveDocument
進入AutoCAD之后,由于模型空間的圖形類型豐富,需要對選定對象類別進行限制,可以用FilterType、FilterData兩個數組限定選擇對象為多段線或者直線等圖元類型
DimFilterType(0)As Integer
DimFilterData(0)As Variant
FilterType(0)=0
FilterData(0)="LWPOLYLINE"
Plset.SelectOnScreen FilterType,FilterData
在AutoCAD中通過鼠標點擊獲取坐標參考原點,使用Utility下的Getpoint()函數,但需要注意由于是外部控制程序,涉及對象語句必須前置主體對象A-cadObj,也就是CAD文件本身:
base_pt=AcadObj.Utility.GetPoint(,"選擇參考基點:")
這里程序會返回一個三維雙精度數組,表示點在三個方向的坐標值。
通過選擇獲得多段線對象后,即可以通過其Coordinates屬性得到多段線上全部的節點坐標,返回值為一個數組,然后再返回給Excel
Num=Plset.count
For i=0 Tonum-1
Pl_cdn=plObj(i).Coordinates
Call Array_Shrink(Pl_cdn)
'自定義函數刪除多段線上重復節點
Pl_num= (UBound(Pl_cdn)+1)/2
For j=0 ToPl_num-1
Cells(j+12,4*i+1).Value=j+1
Cells(j+12,4*i+2).Value=Round((Pl_cdn(2*j)-base_pt(0))/1000,3)
Cells(j+12,4*i+3).Value=Round((Pl_cdn(2*j+1)-base_pt(1))/1000,3)
Cells(j+12,4*i+4).Value=0
Next j
Next i
從AutoCAD中得到的圖形信息本質就是節點坐標,利用Excel的數據統計功能可以方便的對這些坐標做自動化處理
DimSec_numAs Integer'截面類型數量
DimSections()As bdSection'截面類型
Sec_num=WorksheetFunction.CountA (Range("B4:B100"))
ReDimSections(1 ToSec_num)
這樣就定義了一個截面對象數組,包含全部類型截面的坐標信息。
VBA編程主要具有以下幾個優點:
1)語法簡單清晰、邏輯性強,能夠協助工程人員快速掌握并開發程序,不需要花費大量時間去學習復雜的語法和數據結構原理方面的知識;
2)VBA具有通用性,可以用于二次開發Auto-CAD、Office等一系列軟件,更可以用來聯合不同功能的軟件進行組合編程,實現功能上的互補;
3)利用二次開發技術,在面對需要批量化、重復性、數據處理類的工作時,能夠明顯提高完成速度。
當然,VBA的一些缺點限制了它的應用和發展,比如大部分使用者由于是非編程專業領域出身的工程人員,加之VB語法本身不夠緊湊,很容易寫出龐大繁冗的程序。VBA的類定義功能不全,它的優勢在于對接口提供的類庫進行直接應用而并不是深化創造,使得它在二次開發領域的應用擴展性比較受限。
但總而言之,對于工程領域的從業者來說,VBA實現的部分功能是一種極為高效簡便的解決方案,本文簡單介紹了其在橋博建模中的一種應用方法,可為在其他更廣泛領域內的使用提供參考。