言大漠取色原理與實(shí)戰(zhàn)解析)
我在早期寫自動(dòng)化腳本時(shí)第一次接觸到易語(yǔ)言大漠模塊制作教程里的“獲取顏色 GetColor”這個(gè)命令起因很簡(jiǎn)單做一個(gè)界面狀態(tài)監(jiān)測(cè)小工具需要知道軟件窗口里某個(gè)按鈕是不是已經(jīng)從灰色變成了可點(diǎn)擊狀態(tài)。當(dāng)時(shí)最直接的辦法不是截圖再算像素而是讀取那個(gè)坐標(biāo)點(diǎn)的顏色值。后來(lái)我逐漸發(fā)現(xiàn)如果每寫一個(gè)新腳本都把大漠DLL的注冊(cè)、對(duì)象創(chuàng)建、返回值轉(zhuǎn)換這些流程搬一遍效率實(shí)在太低。于是就有了把整套基礎(chǔ)能力封裝成易語(yǔ)言模塊的想法。這篇文章就以 GetColor 為切入口完整梳理一下我從零封裝大漠模塊的過(guò)程、原理和踩坑記錄給正在研究這類圖色命令的朋友做個(gè)參考。1. 為什么要花功夫封裝模塊而不直接調(diào)用大漠1.1 寫腳本時(shí)最不缺的就是重復(fù)代碼先說(shuō)我一開始的做法新建一個(gè)窗口程序在按鈕事件里直接創(chuàng)建大漠對(duì)象調(diào)用方法用完就扔。這在只有一兩個(gè)腳本的時(shí)候沒什么問(wèn)題但腳本一多麻煩就來(lái)了。比如某天我發(fā)現(xiàn) A 腳本里對(duì)象創(chuàng)建完沒有釋放B 腳本里引用的 DLL 版本和 A 不一致C 腳本里同樣的取色邏輯卻因?yàn)榉祷刂蹈袷讲煌瑢?dǎo)致判斷出錯(cuò)。找原因的時(shí)間比寫代碼還長(zhǎng)。后來(lái)我意識(shí)到反復(fù)復(fù)制粘貼“創(chuàng)建對(duì)象、調(diào)用命令、處理返回結(jié)果”這段樣板代碼本質(zhì)上是在給自己補(bǔ)課。大漠命令這么多如果不做一層封裝項(xiàng)目里就會(huì)到處散落著底層調(diào)用代碼后續(xù)維護(hù)和升級(jí)都很難受。尤其是 GetColor 這類基礎(chǔ)命令幾乎每個(gè)圖色腳本都會(huì)用把它封裝好一勞永逸。1.2 封裝之后接口才像“人話”直接調(diào)用大漠命令寫出來(lái)大概是這樣的對(duì)象.文本方法 (GetColor, 100, 200)。雖然能跑但可讀性一般。封裝成易語(yǔ)言模塊之后外部腳本只需要調(diào)用一個(gè)名字清晰的中文子程序取顏色 (100, 200)內(nèi)部做了什么、返回什么格式全部屏蔽掉。這樣做有三個(gè)直接好處接口簡(jiǎn)化上層腳本只面對(duì)“取顏色”“顏色相似”“初始化”這類清晰接口不用記底層方法名。錯(cuò)誤處理統(tǒng)一對(duì)象沒初始化、坐標(biāo)超范圍、返回空串等異常情況在模塊內(nèi)部統(tǒng)一處理上層只需要判斷返回值。方便擴(kuò)展以后要加 FindColor、FindMultiColor 等高級(jí)命令只需在模塊里繼續(xù)加公開子程序舊代碼不受影響。我自己實(shí)踐下來(lái)的體會(huì)是封裝之后寫新腳本的速度會(huì)明顯變快因?yàn)榛A(chǔ)能力已經(jīng)變成一塊現(xiàn)成的積木。1.3 為什么從 GetColor 入手大漠模塊里命令非常多有找圖、找色、識(shí)字、模擬鍵鼠、窗口操作等等但 GetColor 屬于最底層的那一批。很多高級(jí)命令比如 FindColor、找色區(qū)域判斷底層邏輯都離不開“取某個(gè)點(diǎn)的顏色然后做比較”。把這個(gè)命令的原理、返回格式、坐標(biāo)細(xì)節(jié)徹底搞清楚后面再學(xué)其他命令會(huì)順暢很多。另外 GetColor 命令本身邏輯足夠簡(jiǎn)單非常適合作為造模塊的入門練習(xí)。先用它把“初始化、調(diào)用、返回處理”這一整條鏈路跑通之后再往模塊里添加復(fù)雜功能就是復(fù)制粘貼加改參數(shù)的事了。2. GetColor 到底取回了什么顏色串、坐標(biāo)與DPI的底層邏輯2.1 屏幕上一個(gè)個(gè)像素點(diǎn)背后是RGB三通道先解釋一個(gè)基礎(chǔ)概念。屏幕畫面本質(zhì)上是一個(gè)巨大的像素點(diǎn)陣每個(gè)像素點(diǎn)顯示什么顏色由紅、綠、藍(lán)三個(gè)通道的亮度決定。三個(gè)通道的取值范圍都是 0 到 255對(duì)應(yīng)十六進(jìn)制就是 00 到 FF。比如純紅色是 R255、G0、B0用十六進(jìn)制表示就是 FF0000。純綠色是 00FF00純藍(lán)色是 0000FF。常見顏色都能用 6 位十六進(jìn)制串精確表達(dá)。GetColor 這個(gè)命令做的事情就是讀取指定屏幕坐標(biāo)上那個(gè)像素點(diǎn)的三通道值然后拼成一個(gè)文本串返回給你。這里有個(gè)很重要的認(rèn)知GetColor 返回的是文本串不是整數(shù)。很多新手第一次拿到返回值后試圖用“到整數(shù)”直接轉(zhuǎn)換結(jié)果發(fā)現(xiàn)轉(zhuǎn)出來(lái)亂七八糟就是因?yàn)閷?duì)返回值類型理解錯(cuò)了。2.2 GetColor 返回串與 BGR/RGB 的順序陷阱我在不同版本的模塊上測(cè)試時(shí)發(fā)現(xiàn)同一個(gè)坐標(biāo)點(diǎn)不同版本返回的顏色串順序可能不一樣。有的返回 RGB 順序比如 112233 表示 R11、G22、B33有的返回 BGR 順序比如 112233 表示 B11、G22、R33。這個(gè)坑非常隱蔽。如果腳本里寫死了判斷邏輯一旦換了 DLL 版本顏色匹配直接失效。我的建議是拿到新環(huán)境后先做一次驗(yàn)證方法很簡(jiǎn)單用系統(tǒng)畫圖軟件填充一塊純紅色區(qū)域顏色值設(shè)為 RGB(255,0,0)然后調(diào)用 GetColor 取那個(gè)點(diǎn)的顏色。如果返回 FF0000說(shuō)明當(dāng)前版本返回的是 RGB 順序如果返回 0000FF說(shuō)明是 BGR 順序。在封裝模塊時(shí)我建議不要直接透?jìng)髟挤祷刂刀窃谀K內(nèi)部統(tǒng)一做一次格式轉(zhuǎn)換或命令選擇對(duì)外全部輸出一種約定好的順序通常是 RGB。這樣上層腳本就不會(huì)被版本差異影響。2.3 屏幕坐標(biāo)、窗口偏移與DPI縮放GetColor 接收的參數(shù)是屏幕坐標(biāo)以屏幕左上角為原點(diǎn)向右是 X 軸正方向向下是 Y 軸正方向。所以如果你要取的是某個(gè)軟件窗口內(nèi)部按鈕的顏色不能直接用窗口客戶區(qū)里的相對(duì)坐標(biāo)需要先拿到按鈕在窗口內(nèi)的位置再換算成屏幕坐標(biāo)。舉個(gè)例子窗口客戶區(qū)左上角的屏幕坐標(biāo)是 (winX, winY)按鈕在客戶區(qū)里的位置是 (btnX, btnY)那按鈕的實(shí)際屏幕坐標(biāo)就是 (winX btnX, winY btnY)。很多新手直接拿窗口控件坐標(biāo)去調(diào) GetColor取回來(lái)的自然不是期望的顏色。還有一個(gè)容易忽視的因素是 DPI 縮放?,F(xiàn)在 Windows 系統(tǒng)默認(rèn)可能開了 125% 或 150% 縮放如果程序沒有聲明 DPI 感知系統(tǒng)返回的邏輯坐標(biāo)和實(shí)際物理像素坐標(biāo)就會(huì)對(duì)不上導(dǎo)致取色位置偏移。遇到這種情況可以在程序啟動(dòng)時(shí)調(diào)用系統(tǒng) API 聲明進(jìn)程 DPI 感知或者在模塊里對(duì)坐標(biāo)做一次縮放換算。2.4 單點(diǎn)取色的性能邊界GetColor 是逐點(diǎn)讀取速度不慢每次調(diào)用大約在毫秒級(jí)別但如果你寫一個(gè)循環(huán)去掃描上千個(gè)坐標(biāo)點(diǎn)累計(jì)耗時(shí)就會(huì)很明顯。實(shí)際項(xiàng)目里我用得比較多的是“定時(shí)檢測(cè)某個(gè)關(guān)鍵點(diǎn)顏色變化”比如每 200 毫秒取一次基本不會(huì)影響性能。如果需要在一個(gè)大范圍內(nèi)尋找某個(gè)顏色正確的思路是直接用大漠的 FindColor 命令讓底層去完成區(qū)域查找而不是自己寫雙層循環(huán)配合 GetColor 硬掃。封裝模塊時(shí)也應(yīng)該把命令分層基礎(chǔ)層放 GetColor上層再逐步封裝 FindColor 這類高效命令。3. 動(dòng)手搭模塊骨架大漠對(duì)象的一生與初始化細(xì)節(jié)3.1 建一個(gè)易模塊工程先分清公開與私有在易語(yǔ)言里新建“易模塊”類型會(huì)生成一個(gè) .ec 文件這個(gè)文件就可以被別人引用。模塊里可以存放子程序、全局變量、程序集變量等。關(guān)鍵點(diǎn)是“公開”屬性決定了子程序能否被外部調(diào)用。沒有勾選“公開”的子程序只能模塊內(nèi)部自己用外部項(xiàng)目根本看不到。我在設(shè)計(jì)模塊時(shí)有一個(gè)習(xí)慣內(nèi)部輔助函數(shù)統(tǒng)一加上“內(nèi)部_”前綴比如“內(nèi)部_轉(zhuǎn)換顏色格式”公開接口則使用自然語(yǔ)言命名比如“取顏色”“取顏色RGB”“顏色是否相似”。這樣引用模塊的時(shí)候彈出的命令列表一目了然不會(huì)把內(nèi)部實(shí)現(xiàn)細(xì)節(jié)暴露給使用者。易模塊里還可以放全局變量。對(duì)大漠封裝來(lái)說(shuō)最核心的全局變量就是那個(gè)大漠對(duì)象整個(gè)模塊的所有命令幾乎都要依賴它。3.2 初始化、釋放大漠對(duì)象的生命周期管理大漠作為一個(gè) COM 組件使用前需要先注冊(cè) DLL然后在易語(yǔ)言里通過(guò)“對(duì)象”數(shù)據(jù)類型創(chuàng)建實(shí)例。我把這個(gè)過(guò)程封裝成兩個(gè)公開子程序一個(gè)負(fù)責(zé)初始化一個(gè)負(fù)責(zé)釋放。初始化子程序的主要流程是判斷外部是否傳入了 DLL 路徑如果沒有默認(rèn)取運(yùn)行目錄下的 dm.dll。調(diào)用系統(tǒng)命令注冊(cè) DLL比如regsvr32 /s。等待注冊(cè)完成創(chuàng)建 COM 對(duì)象dm.dmsoft。創(chuàng)建后馬上檢查對(duì)象是否為空如果為空說(shuō)明初始化失敗。返回邏輯型結(jié)果方便外部判斷是否繼續(xù)執(zhí)行。釋放子程序則比較簡(jiǎn)單判斷對(duì)象非空后調(diào)用“釋放”方法把資源還回去。有些腳本寫完后不釋放對(duì)象短時(shí)間運(yùn)行看不出問(wèn)題長(zhǎng)時(shí)間跑或者頻繁創(chuàng)建銷毀時(shí)可能會(huì)遇到類似句柄泄漏的情況所以釋放這一步最好不要省。3.3 初始化失敗時(shí)的分層定位法如果大漠_初始化返回假先別急著懷疑代碼邏輯。按照下面這個(gè)順序排查現(xiàn)象常見原因排查方向注冊(cè)成功但創(chuàng)建對(duì)象失敗DLL 位數(shù)和系統(tǒng)不匹配檢查系統(tǒng)是 32 位還是 64 位更換對(duì)應(yīng) DLL注冊(cè)時(shí)提示權(quán)限不足非管理員權(quán)限運(yùn)行用管理員身份重新運(yùn)行一次DLL 文件存在但注冊(cè)沒反應(yīng)路徑帶了中文或空格把 DLL 放在純英文路徑下殺毒軟件彈出攔截提示DLL 被安全策略攔下確認(rèn)來(lái)源可靠后加入信任區(qū)創(chuàng)建對(duì)象成功但取色全空版本過(guò)老或?qū)ο蟊会尫艙Q一個(gè)穩(wěn)定版本檢查全局對(duì)象狀態(tài)多數(shù)情況下初始化失敗的根子不在代碼而在環(huán)境。尤其是 DLL 位數(shù)問(wèn)題64 位系統(tǒng)上如果不小心拿了 32 位版本的 DLL注冊(cè)可以成功對(duì)象創(chuàng)建卻會(huì)失敗非常迷惑人。4. 取顏色接口的實(shí)現(xiàn)與兩個(gè)高價(jià)值擴(kuò)展4.1 基礎(chǔ)版取顏色子程序的完整代碼模塊骨架搭好后現(xiàn)在來(lái)實(shí)現(xiàn)核心的取顏色子程序。代碼如下.版本 2 .程序集 大漠封裝模塊 .全局變量 大漠對(duì)象, 對(duì)象 .子程序 大漠_初始化, 邏輯型, 公開 .參數(shù) DLL路徑, 文本型, 可空 .局部變量 目標(biāo)路徑, 文本型 目標(biāo)路徑 DLL路徑 如果真 (是否為空 (目標(biāo)路徑)) 目標(biāo)路徑 取運(yùn)行目錄 () “\dm.dll” .如果真結(jié)束 運(yùn)行 (“regsvr32 /s ” 目標(biāo)路徑, 假, 0) 延時(shí) (500) 大漠對(duì)象.創(chuàng)建 (“dm.dmsoft”, ) 如果真 (大漠對(duì)象.是否為空 ()) 返回 (假) .如果真結(jié)束 返回 (真) .子程序 大漠_注銷, 公開 如果真 (大漠對(duì)象.是否為空 () 假) 大漠對(duì)象.釋放 () .如果真結(jié)束 .子程序 取顏色, 文本型, 公開 .參數(shù) 橫坐標(biāo), 整數(shù)型 .參數(shù) 縱坐標(biāo), 整數(shù)型 如果真 (大漠對(duì)象.是否為空 ()) 返回 (“”) .如果真結(jié)束 返回 (大漠對(duì)象.文本方法 (“GetColor”, 橫坐標(biāo), 縱坐標(biāo)))這里有兩個(gè)容易混淆的點(diǎn)。第一為什么用“文本方法”而不是“數(shù)值方法”因?yàn)?GetColor 返回的就是文本串。第二判斷對(duì)象是否創(chuàng)建成功用“是否為空 ()”這是一個(gè)邏輯判斷不是直接比較對(duì)象變量。我習(xí)慣在取色前做一次對(duì)象狀態(tài)檢查如果對(duì)象未初始化就直接返回空文本讓上層可以統(tǒng)一按照“空文本 取色失敗”來(lái)處理。4.2 高價(jià)值擴(kuò)展一統(tǒng)一輸出RGB順序的取顏色RGB前面提到不同版本返回順序可能不同。為了對(duì)外統(tǒng)一我封裝了一個(gè)“取顏色RGB”子程序。如果你的 DLL 版本支持 GetColorRGB 命令直接調(diào)用它最省事.子程序 取顏色RGB, 文本型, 公開 .參數(shù) 橫坐標(biāo), 整數(shù)型 .參數(shù) 縱坐標(biāo), 整數(shù)型 如果真 (大漠對(duì)象.是否為空 ()) 返回 (“”) .如果真結(jié)束 返回 (大漠對(duì)象.文本方法 (“GetColorRGB”, 橫坐標(biāo), 縱坐標(biāo)))如果手上版本不支持這個(gè)命令也可以手動(dòng)轉(zhuǎn)換。比如 GetColor 返回的是 BGR 格式“BBGGRR”要轉(zhuǎn)成 RGB 格式“RRGGBB”可以通過(guò)取文本中間把三個(gè)分量分別拆出來(lái)再重新拼接。這個(gè)轉(zhuǎn)換過(guò)程在模塊內(nèi)部完成外部調(diào)用的人完全無(wú)感。轉(zhuǎn)換代碼大致思路如下 假設(shè)原始顏色串格式為 BBGGRR 藍(lán) 取文本中間 (原始顏色, 1, 2) 綠 取文本中間 (原始顏色, 3, 2) 紅 取文本中間 (原始顏色, 5, 2) 轉(zhuǎn)換結(jié)果 紅 綠 藍(lán)提示轉(zhuǎn)換前最好先確認(rèn)當(dāng)前 DLL 返回的到底是 RGB 還是 BGR不然會(huì)白忙一場(chǎng)。4.3 高價(jià)值擴(kuò)展二帶容差的顏色相似判斷實(shí)際測(cè)試中你會(huì)發(fā)現(xiàn)同一個(gè)坐標(biāo)點(diǎn)的顏色并不是永遠(yuǎn)不變的。屏幕刷新、字體渲染、顯卡色彩管理都可能導(dǎo)致顏色產(chǎn)生輕微波動(dòng)。如果腳本用“顏色值精確相等”來(lái)判斷很容易失靈。所以我封裝了一個(gè)“顏色是否相似”的判斷命令。核心邏輯是把兩個(gè)顏色串分別拆成 RGB 三個(gè)分量計(jì)算每個(gè)分量的差只要三個(gè)分量的差都小于設(shè)定的容差值就認(rèn)為顏色相似。這個(gè)方法比“整串相等”實(shí)用得多。做狀態(tài)監(jiān)測(cè)時(shí)我經(jīng)常用它配合 GetColor 寫等待邏輯取目標(biāo)坐標(biāo)顏色和目標(biāo)顏色比較如果相似就認(rèn)為狀態(tài)到位否則繼續(xù)等待。4.4 模塊接口命名與約定的心得封裝模塊到最后很容易攢出一堆子程序。這里分享一個(gè)我摸索出來(lái)的約定所有公開命令要么以“取”開頭表示讀取要么以“是否”開頭表示判斷保持行為可預(yù)測(cè)。失敗返回統(tǒng)一約定文本型返回空文本邏輯型返回假。所有坐標(biāo)參數(shù)用整數(shù)型不限制為正數(shù)方便處理多顯示器下的負(fù)坐標(biāo)。模塊內(nèi)部每個(gè)子程序都寫上簡(jiǎn)短注釋說(shuō)明參數(shù)含義和返回值約定。這套約定看起來(lái)簡(jiǎn)單但實(shí)際用起來(lái)非常舒服。過(guò)幾個(gè)月再回來(lái)寫新腳本翻模塊命令列表就能快速恢復(fù)記憶不需要重新讀源碼。5. 實(shí)測(cè)驗(yàn)證做個(gè)實(shí)時(shí)取色標(biāo)簽看看顏色到底穩(wěn)不穩(wěn)5.1 準(zhǔn)備一個(gè)最簡(jiǎn)單的取色器窗口光寫完模塊還不夠得實(shí)際跑一下才知道有沒有問(wèn)題。我建了一個(gè)測(cè)試窗口放了三個(gè)控件一個(gè)按鈕、一個(gè)標(biāo)簽、一個(gè)時(shí)鐘。按鈕負(fù)責(zé)手動(dòng)取色標(biāo)簽負(fù)責(zé)展示結(jié)果時(shí)鐘負(fù)責(zé)每 200 毫秒自動(dòng)取一次色觀察顏色變化。窗口創(chuàng)建完畢后先調(diào)用大漠_初始化。如果初始化失敗就彈提示框退出避免后面調(diào)用一路報(bào)錯(cuò)。代碼如下.版本 2 .程序集 窗口程序集_啟動(dòng)窗口 .程序集變量 上次顏色, 文本型 .子程序 __啟動(dòng)窗口_創(chuàng)建完畢 如果真 (大漠_初始化 () 假) 信息框 (“大漠模塊初始化失敗”, 0, ) 返回 () .如果真結(jié)束 上次顏色 “” .子程序 _按鈕_取色_被單擊 .局部變量 顏色值, 文本型 顏色值 取顏色 (200, 200) 如果真 (顏色值 “”) 信息框 (“取色失敗請(qǐng)檢查對(duì)象狀態(tài)”, 0, ) 返回 () .如果真結(jié)束 標(biāo)簽_結(jié)果.標(biāo)題 “當(dāng)前顏色” 顏色值 上次顏色 顏色值 .子程序 _時(shí)鐘_取色_周期事件 .局部變量 當(dāng)前顏色, 文本型 當(dāng)前顏色 取顏色 (200, 200) 如果真 (當(dāng)前顏色 “”) 返回 () .如果真結(jié)束 如果真 (上次顏色 ≠ “” 且 當(dāng)前顏色 ≠ 上次顏色) 標(biāo)簽_結(jié)果.標(biāo)題 “顏色變化舊” 上次顏色 “ 新” 當(dāng)前顏色 .如果真結(jié)束 上次顏色 當(dāng)前顏色5.2 完整測(cè)試代碼與預(yù)期效果運(yùn)行這個(gè)測(cè)試程序點(diǎn)擊按鈕后標(biāo)簽會(huì)顯示坐標(biāo) (200,200) 的顏色值。接著把鼠標(biāo)移動(dòng)到其他區(qū)域再點(diǎn)按鈕標(biāo)簽內(nèi)容會(huì)跟著變化。時(shí)鐘開啟后如果 (200,200) 這個(gè)點(diǎn)的顏色發(fā)生改變標(biāo)簽會(huì)自動(dòng)更新提示。為了驗(yàn)證 BGR/RGB 順序我在系統(tǒng)畫圖里填充了一塊純紅色區(qū)域然后用這個(gè)取色器去取色??吹椒祷刂凳?FF0000 的時(shí)候我就知道當(dāng)前 DLL 版本返回的是 RGB 順序。這個(gè)驗(yàn)證步驟建議每個(gè)新環(huán)境都做一遍。5.3 實(shí)測(cè)中看到的顏色抖動(dòng)現(xiàn)象在我測(cè)試時(shí)標(biāo)簽顯示的顏色偶爾會(huì)在幾個(gè)相近值之間跳動(dòng)。原因很簡(jiǎn)單某些軟件界面會(huì)做漸變或者按鈕 hover 高亮效果鼠標(biāo)輕輕劃過(guò)顏色就變了。另外部分顯卡開啟了色彩抖動(dòng)也會(huì)導(dǎo)致像素點(diǎn)顏色發(fā)生微小波動(dòng)。這個(gè)現(xiàn)象印證了“顏色相似判斷”的必要性。直接比較顏色串是否相等在這種環(huán)境下很容易誤判。使用帶容差的相似判斷比如容差設(shè)為 10 或者 15就可以把這種微小波動(dòng)過(guò)濾掉同時(shí)不會(huì)誤判成完全不同的顏色。6. 封裝備忘錄易語(yǔ)言調(diào)用大漠時(shí)的經(jīng)典坑與排查順序6.1 “創(chuàng)建對(duì)象失敗”明明代碼沒問(wèn)題是環(huán)境問(wèn)題我印象最深的一次排查是這樣的代碼在同事的機(jī)器上運(yùn)行正常換到我這邊就一直報(bào)初始化失敗。折騰了兩小時(shí)最后發(fā)現(xiàn)同事的機(jī)器是 32 位系統(tǒng)而我這邊是 64 位系統(tǒng)兩個(gè)系統(tǒng)需要對(duì)應(yīng)不同位數(shù)的 DLL。所以如果你確認(rèn)代碼邏輯沒問(wèn)題先去檢查系統(tǒng)和 DLL 的位數(shù)匹配情況。另外注冊(cè) DLL 需要管理員權(quán)限如果程序不是以管理員身份運(yùn)行注冊(cè)過(guò)程可能被系統(tǒng)攔截注冊(cè)結(jié)果卻顯示成功等到創(chuàng)建對(duì)象時(shí)才真正報(bào)錯(cuò)。還有一個(gè)小細(xì)節(jié)DLL 路徑里不要有中文或特殊字符。某些系統(tǒng)環(huán)境對(duì)調(diào)用外部命令傳中文路徑的處理不太穩(wěn)定放到純英文目錄下更穩(wěn)妥。6.2 取色結(jié)果全為空先檢查坐標(biāo)和對(duì)象狀態(tài)如果初始化成功但取顏色返回空文本我先檢查兩件事坐標(biāo)是否超出屏幕范圍以及對(duì)象是否在之前的某次操作中被意外釋放了。屏幕坐標(biāo)可以取負(fù)值比如多顯示器場(chǎng)景下副屏在主屏左側(cè)時(shí)副屏上的點(diǎn)是負(fù)坐標(biāo)。如果你的模塊里把坐標(biāo)參數(shù)限定成了非負(fù)整數(shù)型這時(shí)候就會(huì)出問(wèn)題。所以封裝時(shí)坐標(biāo)參數(shù)最好用“整數(shù)型”不要加非負(fù)限制。另一個(gè)情況是對(duì)象被釋放了。有些代碼可能在腳本運(yùn)行中途調(diào)用了“大漠_注銷”后面的取色命令再執(zhí)行時(shí)自然會(huì)失敗。模塊內(nèi)部雖然做了對(duì)象狀態(tài)檢查返回空文本但不代表不會(huì)有“每個(gè)命令都返回空”的現(xiàn)象出現(xiàn)排查方向要放在對(duì)象生命周期上。6.3 多顯示器負(fù)坐標(biāo)與版本差異多顯示器環(huán)境是另一個(gè)高頻故障點(diǎn)。Windows 的主顯示器默認(rèn)坐標(biāo)原點(diǎn)在左上角副屏如果位于主屏左側(cè)或上方它的坐標(biāo)區(qū)域就會(huì)出現(xiàn)負(fù)值。早期有些版本的 DLL 對(duì)負(fù)坐標(biāo)支持不好取色會(huì)直接失敗或返回錯(cuò)誤結(jié)果。遇到這種情況先確認(rèn) DLL 版本是否支持負(fù)坐標(biāo)。如果不支持可以把所有坐標(biāo)整體做一次偏移把副屏的負(fù)坐標(biāo)轉(zhuǎn)換到正坐標(biāo)區(qū)域再取色。不過(guò)這種做法會(huì)犧牲一點(diǎn)直觀性模塊設(shè)計(jì)時(shí)要提前想清楚到底采用“統(tǒng)一偏移”還是“直接支持負(fù)坐標(biāo)”。6.4 位數(shù)、版本和殺毒攔截的組合拳最后說(shuō)一個(gè)比較綜合的坑程序在 32 位模式下編譯一切正常換成 64 位就各種報(bào)錯(cuò)。這通常不是代碼的問(wèn)題而是 DLL 版本對(duì)編譯目標(biāo)不兼容。建議固定一套已經(jīng)測(cè)試通過(guò)的 DLL 版本不要頻繁升級(jí)。安全軟件攔截也是常見問(wèn)題。大漠這類 DLL 因?yàn)橐?cè) COM 組件、寫入注冊(cè)表經(jīng)常觸發(fā)安全軟件的敏感行為提示。如果確認(rèn) DLL 來(lái)源可靠可以把它加入信任區(qū)。這里也想提醒一句這類圖色自動(dòng)化技術(shù)建議用于桌面軟件測(cè)試、界面狀態(tài)監(jiān)測(cè)等正常場(chǎng)景不要用在破壞他人軟件或服務(wù)條款不允許的領(lǐng)域。寫在最后的一點(diǎn)實(shí)戰(zhàn)建議從第一次接觸 GetColor 到現(xiàn)在我最大的體會(huì)是封裝模塊這件事前期多花一小時(shí)后期能省十小時(shí)。GetColor 只是整個(gè)模塊里的一個(gè)小命令但把它吃透之后你會(huì)自然理解大漠底層命令的調(diào)用套路之后再封裝找色、找圖都是順?biāo)浦鄣氖?。最后分享一個(gè)實(shí)用小技巧測(cè)試取色功能時(shí)可以做一個(gè)懸浮小窗口鼠標(biāo)移動(dòng)時(shí)實(shí)時(shí)顯示當(dāng)前坐標(biāo)和顏色值。我后面把這個(gè)取色器做成了自己日常調(diào)試的常備工具比單步調(diào)試舒服太多。等你把模塊的基本命令封裝好建議也做一個(gè)這樣的取色器邊寫邊測(cè)很多問(wèn)題一眼就能看出來(lái)。