注:環(huán)境搭建到批量自動(dòng)化實(shí)戰(zhàn))
1. 為什么你會(huì)需要C#來(lái)操控圖紙標(biāo)注先從一個(gè)場(chǎng)景說(shuō)起。我接過(guò)一個(gè)挺典型的活客戶那邊有幾十張SolidWorks工程圖需要把所有帶有特定前綴的尺寸標(biāo)注全部替換成新前綴同時(shí)把每張圖紙的標(biāo)注信息尺寸類型、名義值、公差、關(guān)聯(lián)特征匯總成一個(gè)Excel清單。圖紙數(shù)量一大手工改不僅容易漏而且時(shí)間上根本不允許。手動(dòng)操作一張復(fù)雜圖紙的標(biāo)注檢查加修改快則十分鐘慢則半小時(shí)幾十張圖下來(lái)一整天就沒(méi)了關(guān)鍵還容易出錯(cuò)。這時(shí)候就需要用程序來(lái)干了。用C#寫(xiě)SolidWorks標(biāo)注處理程序本質(zhì)上是通過(guò)SolidWorks的API接口和模型打交道。SolidWorks提供了一整套COM接口C#可以通過(guò)Interop程序集訪問(wèn)這些接口實(shí)現(xiàn)在外部程序中創(chuàng)建、讀取、修改模型及工程圖里的標(biāo)注對(duì)象。和SolidWorks自帶的宏VBA相比C#的優(yōu)勢(shì)在于可以方便地寫(xiě)復(fù)雜的業(yè)務(wù)邏輯比如對(duì)接數(shù)據(jù)庫(kù)、做Excel導(dǎo)出、寫(xiě)正則匹配替換規(guī)則可以打包成獨(dú)立工具分發(fā)部署到?jīng)]有VBA開(kāi)發(fā)環(huán)境的機(jī)器上也能跑而且調(diào)試體驗(yàn)比在宏編輯器里寫(xiě)VBA舒服很多。這里要先破除一個(gè)迷思很多人覺(jué)得“標(biāo)注”就是圖紙上那些數(shù)字和符號(hào)不值得用程序處理。但實(shí)際在制造企業(yè)里標(biāo)注是設(shè)計(jì)信息的下游載體圖號(hào)變更、標(biāo)準(zhǔn)件代號(hào)調(diào)整、公差體系切換都會(huì)牽動(dòng)大量標(biāo)注修改。標(biāo)注信息的準(zhǔn)確性和一致性直接影響下游工藝、加工和質(zhì)檢。所以圍繞標(biāo)注的批量化操作、信息提取、模板化生成其實(shí)是很有需求的一塊自動(dòng)化場(chǎng)景。這篇文章我會(huì)從環(huán)境準(zhǔn)備、API對(duì)象模型、讀取標(biāo)注、批量修改標(biāo)注、常見(jiàn)問(wèn)題排查幾個(gè)環(huán)節(jié)把我實(shí)際做過(guò)的C#加SolidWorks標(biāo)注處理項(xiàng)目的思路和踩過(guò)的坑完整寫(xiě)出來(lái)。適合有C#基礎(chǔ)、需要做SolidWorks二次開(kāi)發(fā)的工程師或者被重復(fù)性標(biāo)注工作折磨得想“偷懶”的結(jié)構(gòu)設(shè)計(jì)師。2. 開(kāi)發(fā)環(huán)境搭建與首次連接2.1 環(huán)境清單不止是裝了SolidWorks就行要做C#二次開(kāi)發(fā)本機(jī)環(huán)境有幾個(gè)硬性要求裝有SolidWorks最好是我這次用的2018 SP5及以上版本版本太老API對(duì)象模型差異會(huì)比較明顯。Visual Studio 2019或2022社區(qū)版就行。.NET Framework 4.7.2或.NET 6/8看你的目標(biāo)運(yùn)行環(huán)境如果只是本機(jī)工具建議直接用.NET Framework 4.7.2省心。在項(xiàng)目中引用兩個(gè)Interop程序集SolidWorks.Interop.sldworks.dll 和 SolidWorks.Interop.swconst.dll。這兩個(gè)DLL一般位于SolidWorks安裝目錄下比如C:\Program Files\SOLIDWORKS Corp\SOLIDWORKS\api\redist。添加引用時(shí)直接瀏覽到這個(gè)路徑選DLL即可。版本選擇上有個(gè)實(shí)在的建議如果你們的SolidWorks是2020或2021就下載對(duì)應(yīng)版本的Interop程序集。雖然高版本API基本向下兼容但低版本引用在高版本上會(huì)更穩(wěn)妥反過(guò)來(lái)就偶爾會(huì)出現(xiàn)類型轉(zhuǎn)換問(wèn)題。SolidWorks安裝目錄里自帶的DLL就是和本機(jī)版本最匹配的。2.2 用Interop還是用COM動(dòng)態(tài)調(diào)用實(shí)際開(kāi)發(fā)中有兩種路線強(qiáng)類型Interop引用具體版本的Interop DLL代碼里可以直接使用SldWorks、ModelDoc2等類型編譯期就有智能提示代碼可讀性好。這是我推薦的方式。動(dòng)態(tài)綁定通過(guò)ProgID創(chuàng)建對(duì)象不引用Interop DLL運(yùn)行時(shí)再解析。這種方式部署時(shí)不用管版本但寫(xiě)代碼時(shí)沒(méi)有智能提示報(bào)錯(cuò)也晚非常適合“跑通驗(yàn)證”的場(chǎng)景不適合正式項(xiàng)目。我這里全程用Interop強(qiáng)類型方式。Interop還有一個(gè)好處Visual Studio對(duì)這類COM程序集支持得比較好會(huì)生成對(duì)應(yīng)的互操作程序集遇到方法簽名不明確時(shí)可以直接右鍵“轉(zhuǎn)到定義”查看真實(shí)的參數(shù)約束。2.3 連接SolidWorks的代碼骨架using SolidWorks.Interop.sldworks; using System; using System.Runtime.InteropServices; public class SwConnection : IDisposable { private SldWorks _swApp; public bool Connect(string progId SldWorks.Application) { // 先嘗試獲取已運(yùn)行的實(shí)例 try { _swApp Marshal.GetActiveObject(progId) as SldWorks; } catch { // 沒(méi)有運(yùn)行則創(chuàng)建新實(shí)例 Type swType Type.GetTypeFromProgID(progId); _swApp Activator.CreateInstance(swType) as SldWorks; } if (_swApp null) return false; // 設(shè)為可見(jiàn) _swApp.Visible true; return true; } public void Dispose() { if (_swApp ! null) { Marshal.FinalReleaseComObject(_swApp); _swApp null; } GC.Collect(); GC.WaitForPendingFinalizers(); } }這段代碼有一個(gè)細(xì)節(jié)要提醒Marshal.GetActiveObject如果當(dāng)前沒(méi)啟動(dòng)SolidWorks會(huì)拋異常所以一定要用try-catch包住。Activator.CreateInstance創(chuàng)建實(shí)例時(shí)SolidWorks會(huì)起一個(gè)完整進(jìn)程打開(kāi)速度取決于機(jī)器性能建議開(kāi)發(fā)時(shí)先把SolidWorks開(kāi)著再跑程序。連接后別忘了主動(dòng)釋放COM對(duì)象。COM引用計(jì)數(shù)問(wèn)題在C#里非常隱蔽你以為對(duì)象可以被GC回收了其實(shí)SolidWorks進(jìn)程還占著大量?jī)?nèi)存。不釋放干凈的典型表現(xiàn)是程序跑幾輪后SolidWorks越來(lái)越卡最后蹦出內(nèi)存不足的報(bào)錯(cuò)。3. 標(biāo)注對(duì)象模型搞懂層級(jí)才好下手3.1 從零件到工程圖的完整鏈路SolidWorks中標(biāo)注主要集中在工程圖環(huán)境但模型的尺寸草圖尺寸、特征尺寸也可以被程序訪問(wèn)。對(duì)象層級(jí)大致是SldWorks.Application → ModelDoc2 → Sheet → View → Dimension / Annotation這里有兩個(gè)容易混淆的概念要理清Dimension尺寸標(biāo)注比如直徑標(biāo)注、線性標(biāo)注、角度標(biāo)注在API里對(duì)應(yīng)Dimension對(duì)象。Annotation注釋對(duì)象包括文字注釋Note、表面粗糙度符號(hào)、形位公差符號(hào)等。注釋是一個(gè)更大的概念尺寸標(biāo)注也屬于注釋的范疇。API里它們的關(guān)聯(lián)關(guān)系是Dimension對(duì)象繼承自IAnnotation也就是說(shuō)一個(gè)尺寸本身也是一種注釋。所以遍歷時(shí)通過(guò)Annotation可以拿到所有標(biāo)注類對(duì)象再判斷其類型可以區(qū)分是尺寸、注釋還是其他符號(hào)。先把這個(gè)關(guān)系弄清楚再寫(xiě)遍歷代碼否則容易陷入“為什么我拿到了關(guān)聯(lián)尺寸卻拿不到文字”的困惑。3.2 尺寸標(biāo)注的命名規(guī)則SolidWorks里的每個(gè)尺寸都有一個(gè)唯一名稱最常見(jiàn)的格式是D1草圖1 D1凸臺(tái)-拉伸1 D1工程圖視圖1前綴D就是尺寸的標(biāo)識(shí)后面的符號(hào)后跟所屬特征或視圖名稱。這個(gè)命名規(guī)則對(duì)程序特別重要因?yàn)楹芏郃PI方法比如Parameter、SelectByID2都要靠名稱字符串去定位對(duì)象。實(shí)際項(xiàng)目里我踩過(guò)一個(gè)坑尺寸名稱中會(huì)有變體名稱的情況。SolidWorks允許同一個(gè)特征里存在同名的草圖這時(shí)尺寸名稱會(huì)展示為類似D1草圖1特征1的格式。遍歷時(shí)如果直接用名字匹配容易漏掉這類“重名”尺寸。所以建議優(yōu)先采用遍歷集合的方式而不是用名稱字符串去精確匹配。3.3 三種常用的獲取標(biāo)注方式方式一按名稱獲取尺寸ModelDoc2 swDoc _swApp.ActiveDoc; Dimension swDim swDoc.Parameter(D1草圖1) as Dimension;方式二通過(guò)選擇管理器獲取選中對(duì)象ModelDoc2 swDoc _swApp.ActiveDoc; bool sel swDoc.Extension.SelectByID2( D1工程圖視圖1, DIMENSION, 0, 0, 0, false, 0, null, 0); if (sel) { SelectionMgr selMgr swDoc.SelectionManager; Dimension swDim selMgr.GetSelectedObject6(1) as Dimension; }方式三遍歷視圖內(nèi)所有注解ModelDoc2 swDoc _swApp.ActiveDoc; View swView swDoc.GetFirstView(); while (swView ! null) { Annotation ann swView.GetFirstAnnotation(); swView swView.GetNextView(); }這三種方式各有適用場(chǎng)景按名稱最直接但依賴命名唯一性按選擇管理適合交互式工具遍歷集合適合批量處理。下面實(shí)戰(zhàn)部分主要用遍歷方式最穩(wěn)。4. 實(shí)戰(zhàn)一遍歷工程圖并導(dǎo)出標(biāo)注到Excel4.1 需求定義與方案設(shè)計(jì)這類需求很常見(jiàn)設(shè)計(jì)部門(mén)每季度要出全量圖紙標(biāo)注臺(tái)賬供工藝和采購(gòu)核對(duì)。手工做法是打開(kāi)一張圖逐個(gè)查看標(biāo)注復(fù)制信息到Excel百?gòu)垐D紙工作量巨大。我的實(shí)現(xiàn)思路是打開(kāi)SolidWorks工程圖不顯示界面后臺(tái)運(yùn)行。遍歷所有圖紙Sheet。遍歷每張圖紙的所有視圖View。遍歷視圖內(nèi)的所有注解Annotation。按類型篩選出尺寸標(biāo)注讀取名稱、值、公差、顯示的文字等信息。寫(xiě)入Excel文件我用開(kāi)源的NPOI避免裝Office。4.2 核心代碼遍歷與讀取public ListDimInfo CollectDimensions(IModelDoc2 doc) { var result new ListDimInfo(); int sheetCount doc.SheetCount; for (int i 1; i sheetCount; i) { Sheet sheet doc.GetSheet(i); doc.ActivateSheet(sheet.GetName()); View curView sheet.GetFirstView(); while (curView ! null) { Annotation ann curView.GetFirstAnnotation(); while (ann ! null) { int annType ann.GetType2(); if (annType (int)swAnnotationType_e.swAnnotationDimension) { Dimension dim ann as Dimension; if (dim ! null) { DimInfo info new DimInfo { SheetName sheet.GetName(), ViewName curView.GetName(), DimName dim.GetName(), DimText dim.GetText() }; result.Add(info); } } ann curView.GetNextAnnotation(ann); } curView curView.GetNextView(); } } return result; }這里有一個(gè)關(guān)鍵細(xì)節(jié)GetNextAnnotation的參數(shù)是當(dāng)前Annotation對(duì)象。剛寫(xiě)這個(gè)遍歷時(shí)我犯了個(gè)錯(cuò)誤——只調(diào)GetFirstAnnotation忘記傳參調(diào)用GetNextAnnotation(ann)結(jié)果程序死循環(huán)或者只能拿到第一個(gè)標(biāo)注。視圖遍歷也一樣GetNextView()必須有當(dāng)前視圖作為上下文。SolidWorks API這些遍歷方法基本都是“先把當(dāng)前對(duì)象傳進(jìn)去返回下一個(gè)”的模式和.NET集合的迭代器風(fēng)格完全不同寫(xiě)的時(shí)候很容易轉(zhuǎn)不過(guò)彎來(lái)。4.3 屬性讀取不只是文字尺寸標(biāo)注值得讀取的屬性遠(yuǎn)不止顯示文字。我在項(xiàng)目里主要讀這幾項(xiàng)屬性API方法說(shuō)明名稱dim.GetName()如 D1草圖1顯示文字dim.GetText()實(shí)際顯示的字符串含前綴后綴名義值dim.GetValue()尺寸數(shù)值double公差上下偏差dim.GetToleranceValues()返回上下偏差數(shù)組顯示精度dim.GetDimensionText()相關(guān)小數(shù)位數(shù)信息關(guān)聯(lián)特征dim.GetFeature()返回關(guān)聯(lián)的特征對(duì)象有個(gè)坑要提醒GetText()返回的是“最終顯示文本”SolidWorks在顯示時(shí)會(huì)根據(jù)尺寸值和當(dāng)前單位設(shè)置把數(shù)值格式化。如果你要拿原始表達(dá)式比如形如“D1草圖1 0.5”的驅(qū)動(dòng)表達(dá)式需要調(diào)用更底層的GetDimensionText()之類的方法。我實(shí)際遇到過(guò)一個(gè)場(chǎng)景程序要把所有標(biāo)注的“顯示文本”和“驅(qū)動(dòng)表達(dá)式”分別導(dǎo)出結(jié)果發(fā)現(xiàn)GetText()返回“15”而驅(qū)動(dòng)表達(dá)式其實(shí)是“D1草圖3 0.2”查API文檔查了一會(huì)兒才定位到正確方法。5. 實(shí)戰(zhàn)二批量修改標(biāo)注與自動(dòng)添加注釋5.1 批量修改標(biāo)注前綴的完整案例我在開(kāi)頭提到的那個(gè)任務(wù)——把一批工程圖中所有直徑標(biāo)注的前綴從舊代號(hào)改成新代號(hào)。實(shí)現(xiàn)邏輯其實(shí)不復(fù)雜public int ReplaceDimPrefix(IModelDoc2 doc, string oldPrefix, string newPrefix) { int count 0; foreach (var dim in GetDimensions(doc)) { string text dim.GetText(); if (text.Contains(oldPrefix)) { string newText text.Replace(oldPrefix, newPrefix); bool ok dim.SetText(newText); if (ok) count; } } return count; }SetText方法返回布爾值。這里要特別注意不是每次SetText都會(huì)成功。如果傳入的字符串不符合當(dāng)前系統(tǒng)的標(biāo)注格式比如在直徑標(biāo)注里傳了個(gè)沒(méi)有前綴的數(shù)字SolidWorks可能會(huì)拒絕修改。我實(shí)測(cè)遇到的情況是SetText返回false但沒(méi)有任何異常拋出來(lái)。所以一定要檢查返回值否則你以為改成功了實(shí)際圖紙紋絲不動(dòng)。尺寸文字修改還有一個(gè)陷阱當(dāng)你調(diào)用SetText后尺寸關(guān)聯(lián)的3D模型可能也會(huì)跟著變因?yàn)槌叽缥淖直旧砜赡艹休d了驅(qū)動(dòng)語(yǔ)義尤其是引用方程式或全局變量的尺寸。如果要“只改顯示、不動(dòng)幾何”最安全的方式是操作Annotation的顯示屬性而非直接改尺寸文字。這需要根據(jù)業(yè)務(wù)需求仔細(xì)判斷。5.2 自動(dòng)創(chuàng)建標(biāo)注注釋的實(shí)際操作除了改尺寸有時(shí)候還需要在圖紙上自動(dòng)添加文字注釋比如在圖紙角落寫(xiě)入“設(shè)計(jì)者XXX 審核YYY 日期ZZZ”。通過(guò)API添加注釋的方式public bool AddNoteToSheet(ModelDoc2 doc, string sheetName, string noteText, double x, double y) { bool ok doc.ActivateSheet(sheetName); if (!ok) return false; // 在指定坐標(biāo)插入文字注釋 bool created doc.InsertNote(noteText, x, y); if (!created) return false; // 獲取最后一個(gè)注釋對(duì)象剛創(chuàng)建的 Note swNote doc.GetLastFeatureManagerData() as Note; return swNote ! null; }注意坐標(biāo)系統(tǒng)SolidWorks的插入坐標(biāo)默認(rèn)是圖紙坐標(biāo)系單位是米沒(méi)錯(cuò)API內(nèi)部一律用SI單位哪怕你界面設(shè)置是毫米。我第一版程序在毫米單位下輸入了10, 10結(jié)果注釋跑到了圖紙外找半天沒(méi)找著。換算很簡(jiǎn)單需要處理的數(shù)值直接除以1000即可。關(guān)于定位注釋更穩(wěn)的做法是給注釋添加“定位點(diǎn)”和“引線”而不是用固定坐標(biāo)。因?yàn)楣こ虉D設(shè)計(jì)變量多固定坐標(biāo)在不同比例的視圖里顯示位置不理想。SolidWorks的Note.AddLeader方法可以給注釋添加引線到某個(gè)幾何實(shí)體上這樣注釋跟著特征走位置更靈活。不過(guò)引線相關(guān)API在文檔里描述不夠詳細(xì)需要用試驗(yàn)法摸索。我的建議是先用錄制宏的方式記錄一次手動(dòng)添加注釋的操作然后打開(kāi)VBA編輯窗口查看生成的宏代碼這是了解API調(diào)用最好的學(xué)習(xí)資料。5.3 標(biāo)注樣式的統(tǒng)一性維護(hù)另外一個(gè)實(shí)用場(chǎng)景是統(tǒng)一標(biāo)注樣式。團(tuán)隊(duì)協(xié)作中經(jīng)常出現(xiàn)各人的標(biāo)注字體、箭頭樣式、文字高度不一致。通過(guò)C#可以批量對(duì)齊這些屬性標(biāo)注文字高度dim.GetTextHeight()通過(guò)dim.SetTextHeight()調(diào)整單位是米。箭頭類型遍歷尺寸通過(guò)dim.ArrowHeadStyle相關(guān)屬性調(diào)整。公差顯示方式dim.GetDimTolerance/SetDimTolerance相關(guān)方法可以統(tǒng)一改為“雙邊公差”或“對(duì)稱公差”。這些操作對(duì)API枚舉值比如箭頭樣式用起來(lái)有點(diǎn)繁瑣因?yàn)槲臋n不直觀。我實(shí)際做法是先用宏錄制一遍找到枚舉值的真實(shí)數(shù)值再在C#里用對(duì)應(yīng)枚舉硬編碼或定義常量。比如箭頭樣式在swArrowheadStyle_e里取值有實(shí)心、空心、建筑制圖等好幾種錄一遍宏就知道當(dāng)前命令默認(rèn)用的是哪個(gè)數(shù)值。6. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄6.1 COM對(duì)象泄漏SolidWorks越來(lái)越卡這個(gè)問(wèn)題我說(shuō)過(guò)幾乎是所有COM二次開(kāi)發(fā)都繞不過(guò)去的坎。SolidWorks的API對(duì)象大多派生自COMC#里的GC不會(huì)自動(dòng)釋放COM引用必須手動(dòng)調(diào)用Marshal.FinalReleaseComObject。實(shí)際經(jīng)驗(yàn)是try { // 使用對(duì)象 } finally { Marshal.FinalReleaseComObject(dim); Marshal.FinalReleaseComObject(view); Marshal.FinalReleaseComObject(doc); }這個(gè)做法在遍歷嵌套對(duì)象時(shí)要注意順序先釋放子對(duì)象再釋放父對(duì)象。而且API文檔明確提示不要對(duì)同一個(gè)COM指針調(diào)用FinalReleaseComObject兩次否則會(huì)拋異常。還有一種更隱蔽的泄漏object類型的中間變量。很多API方法返回的是object你把它賦給具體類型后原來(lái)的object被丟棄了但COM引用計(jì)數(shù)還掛在那邊。穩(wěn)妥做法是用try-finally包住或者啟動(dòng)一個(gè)定時(shí)GC兜底。最實(shí)在的技巧是批量處理大量圖紙時(shí)每處理完一張圖紙就調(diào)用一次GC.Collect()配合GC.WaitForPendingFinalizers()雖然粗暴但有效。6.2 后臺(tái)運(yùn)行與界面刷新的取舍處理過(guò)程要不要顯示SolidWorks界面長(zhǎng)期是個(gè)雙難選擇顯示界面速度慢但你能實(shí)時(shí)看到程序在干什么調(diào)試方便。后臺(tái)運(yùn)行_swApp.Visible false速度快出問(wèn)題時(shí)難以定位。我的建議是開(kāi)發(fā)階段用Visible true真實(shí)批量跑時(shí)用false。另外隱藏界面時(shí)SolidWorks可能不刷新視圖導(dǎo)致你讀取的標(biāo)注狀態(tài)是“邏輯上的最新值”而不是“界面顯示值”。補(bǔ)救辦法是在關(guān)鍵操作后調(diào)用doc.ForceRebuild3(true)之類的重繪方式強(qiáng)制刷新。實(shí)際上在批處理流程中我遇到過(guò)一個(gè)罕見(jiàn)情況隱藏界面時(shí)GetCurrentSheet()返回的不是當(dāng)前激活圖紙因?yàn)榻缑鏇](méi)有實(shí)時(shí)同步激活狀態(tài)。這個(gè)坑的解法是不要用GetCurrentSheet老老實(shí)實(shí)用GetSheet(i)按索引遍歷。6.3 版本兼容性不同SolidWorks版本的API差異C#加SolidWorks二次開(kāi)發(fā)的另一個(gè)常見(jiàn)煩惱是版本差異。SolidWorks的API在2016、2018、2020等大版本切換時(shí)部分方法的簽名和枚舉定義會(huì)變化。最典型的是swAnnotationType_e枚舉的值在不同版本間有微小偏移導(dǎo)致在新版本上篩選類型時(shí)意外漏掉某些對(duì)象。應(yīng)對(duì)辦法有兩條鎖版本——只在目標(biāo)版本的SolidWorks上運(yùn)行反正是內(nèi)部工具。類型泛化——不依賴具體枚舉判斷而是通過(guò)“對(duì)象是否能轉(zhuǎn)換為Dimension”來(lái)篩選。后者的寫(xiě)法就算枚舉變了程序也能正常工作if (ann is Dimension dim) { // 這里直接處理 Dimension }這個(gè)寫(xiě)法明顯更穩(wěn)健。6.4 程序偶發(fā)崩潰與異常處理批量運(yùn)行時(shí)最怕的是跑到第37張圖紙時(shí)程序崩潰前面36張白跑了。我的處理方案是把異常捕獲放在單張圖紙粒度f(wàn)or (int i 1; i doc.SheetCount; i) { try { ProcessSheet(doc, i); } catch (Exception ex) { Log($Sheet {i} 處理失敗: {ex.Message}); continue; // 跳過(guò)繼續(xù) } }另外別忘了給AppDomain.CurrentDomain.UnhandledException掛一個(gè)全局處理器把棧信息寫(xiě)進(jìn)日志。COM異常在CLR里往往被包裝成COMException你需要讀取ErrorCode和Source字段才能看到有效信息。我排查過(guò)一個(gè)問(wèn)題表面報(bào)錯(cuò)在SetText實(shí)際根源是前面某次SelectByID2傳入的參數(shù)類型不對(duì)對(duì)象根本沒(méi)被選中后續(xù)操作全部建立在無(wú)效對(duì)象上。這就是“錯(cuò)誤出現(xiàn)在下游根源在上游”的典型COM二次開(kāi)發(fā)案例。6.5 調(diào)試技巧用好SolidWorks的宏錄制器最后一個(gè)技巧是二次開(kāi)發(fā)新人最容易忽略的先錄宏再翻譯成C#。在SolidWorks里打開(kāi)“宏錄制”工具→宏→錄制/暫停手動(dòng)操作一遍你要自動(dòng)化的動(dòng)作比如改一個(gè)標(biāo)注文字、添加一個(gè)注釋、設(shè)置一個(gè)公差錄制出來(lái)的VBA代碼就是API調(diào)用的標(biāo)準(zhǔn)答案。這個(gè)思路的價(jià)值在于錄制宏學(xué)API的效率比翻文檔高太多很多對(duì)象屬性和方法在官方幫助文檔里寫(xiě)得很晦澀但錄制出來(lái)的VBA代碼直白易懂。我基本已經(jīng)把“錄宏→轉(zhuǎn)C#”當(dāng)作SolidWorks二次開(kāi)發(fā)的第一方法論。7. 我的一些實(shí)戰(zhàn)心得做C#加SolidWorks標(biāo)注自動(dòng)化這個(gè)方向前后跑了不少項(xiàng)目最深的體會(huì)有幾點(diǎn)。第一理解業(yè)務(wù)比理解API更重要。標(biāo)注的自動(dòng)化處理表面上是操作對(duì)象實(shí)際是要搞清楚圖紙上的標(biāo)注承擔(dān)了什么信息角色——是工藝標(biāo)識(shí)、尺寸公差還是表注文本。不同的信息角色修改的策略完全不一樣。程序把“該改的漏了不該改的改了”比手工改錯(cuò)還要棘手。第二SolidWorks二次開(kāi)發(fā)本質(zhì)上是一種“講故事”式的編程。你要先連接、再有文檔、再選視圖、再挑標(biāo)注每一層的對(duì)象獲取都依賴上一層的正確狀態(tài)。任何一步出錯(cuò)后面全是空的。所以我的程序里每一步都做了嚴(yán)格的對(duì)象非空檢查寧可多寫(xiě)幾十行判斷也不愿在運(yùn)行時(shí)才看到“對(duì)象未引用”的報(bào)錯(cuò)。第三批量化標(biāo)注處理特別適合做成“工具鏈”而不是單一腳本。從圖紙批量打開(kāi)、標(biāo)注提取、Excel導(dǎo)出、錯(cuò)誤標(biāo)記、修改回寫(xiě)這個(gè)鏈路里的每一環(huán)都可以獨(dú)立復(fù)用到其他任務(wù)上。我后來(lái)把核心的DimensionReader和NoteWriter類抽象出來(lái)新項(xiàng)目直接復(fù)用省下大量重復(fù)編碼的時(shí)間。如果你也想做這類開(kāi)發(fā)建議從最小的場(chǎng)景入手——先寫(xiě)一個(gè)能讀取當(dāng)前工程圖里所有標(biāo)注并輸出Name和Text的小工具跑通了再逐步加功能。把宏錄制、Interop引用、COM釋放這幾個(gè)基礎(chǔ)問(wèn)題先弄清楚后面自然就順了。關(guān)于標(biāo)注的自動(dòng)化處理還有個(gè)延伸方向值得提一下與PLM/ERP系統(tǒng)對(duì)接。通過(guò)C#讀取SolidWorks標(biāo)注信息后直接寫(xiě)入制造執(zhí)行系統(tǒng)的數(shù)據(jù)庫(kù)實(shí)現(xiàn)設(shè)計(jì)到生產(chǎn)的“圖紙標(biāo)注數(shù)據(jù)化”。這個(gè)方向在很多企業(yè)里還處于空白但需求非常明確做出來(lái)價(jià)值不小。最后分享一個(gè)小技巧批量處理前先對(duì)單張圖紙做一次完整走查確認(rèn)你的程序在“標(biāo)準(zhǔn)圖紙”上跑出來(lái)的結(jié)果和你手工核查的結(jié)果完全一致再上量。我在早期項(xiàng)目里就是跳過(guò)這個(gè)環(huán)節(jié)直接跑50張圖紙后來(lái)發(fā)現(xiàn)遍歷視圖時(shí)漏了“圖紙格式”里的注釋導(dǎo)致一批標(biāo)注臺(tái)賬少了幾十行數(shù)據(jù)返工成本比預(yù)想高得多。磨刀不誤砍柴工先驗(yàn)證再鋪開(kāi)這個(gè)習(xí)慣比任何API技巧都更值得養(yǎng)成。