2020年3月19日 星期四

PYTHON 群益API 2.13.20 錯誤代碼2017 錯誤原因與解法

從2.13.16版本之後,登入前就需要 SKReplyLib_OnReplyMessage 回傳指定值 0xFFFF 才可以登入。

一開始我也是矇了,不管怎麼傳就好像沒有作用?明明照著說明做了啊!

錯誤通常分兩種,第一種就是真的忘記寫到 OnReplyMessage 然後回傳 0xFFFF或寫錯,這種就不在這裡討論。第二種就是都按照說明寫了也確認無誤,但還是不知所以然的錯誤。

到底  2017 SK_WARNING_REGISTER_REPLYLIB_ONREPLYMESSAGE_FIRST 是從何而來?網路上沒有一個明確的解釋與通用的解法,這可以是基於討論的人並不多。

的確,確實回傳值設甚麼都沒有作用,那不是錯覺,那是真的。COM接口的Py檔中 OnReplyMessage 是沒有回傳值。

於是這形成了最怪異的事情,不管怎麼樣登入,由於接口的 OnReplyMessage 沒有回傳值 sConfirmCode,所以 SKCenterLib_Login 永遠都接收不到註冊公告,永遠的錯誤代碼 2017。

問題點是錯在哪裡?本來有個見解,在 2020-03-20 被提醒之後,終於確認是暫存檔的問題。(不是dll

執行以下幾行就能夠刪除掉舊版COM的py檔模型。
import comtypes.client, os
try:
    os.remove(comtypes.client.gen_dir + r"\_75AAD71C_8F4F_4F1F_9AEE_3D41A8C9BA5E_0_1_0.py")
    os.remove(comtypes.client.gen_dir + r"\SKCOMLib.py")
except:
    pass
繼續追根究柢下去。
來講解一下運作機制。

在python中,最初的三行通常是:
import comtypes.client
comtypes.client.GetModule(os.path.split(os.path.realpath(__file__))[0] + r'\SKCOM.dll') #加此行需將API放與py同目錄
import comtypes.gen.SKCOMLib as sk
第二行是從 SKCOM.dll 取得模型,顧名思義。而第三行就是取出第二行建立的模型。

然而 GetModule() 會先從 comtypes.client.gen_dir 路徑上尋找模型,如果路徑上已經有對應的模型,就會優先讀入,沒有才會從COM中生成py檔模型。

如果以前曾經使用過  GetModule(舊版COM),那麼就會優先讀取已存在的舊版COM的py模型,而不是從新版COM去創造一個新的覆蓋掉舊的,這也就是版本差異的所導致的錯誤。

透過 print(comtypes.client.gen_dir) 可以看到模型py檔放在哪裡。

有人會在快取夾 cache,有人會在 anaconda 中 /Lib/site-packages/comtypes/gen裡。複製貼上路徑,可以找到兩個檔案 SKCOMLib.py 及 _75AAD71C-8F4F-4F1F-9AEE-3D41A8C9BA5E.py 這就是從 SKCOM.dll 生出的 .py檔,也是這次的目標。

兩者位置差異在於該專案的解譯器使用甚麼環境,從專案→屬性就能查看到。Visual Studio可供選安裝的環境通常會是在 C:\Users\使用者名稱\AppData\Roaming\Python\ 裡面,如果是官網下載的Anaconda,則會在 anaconda環境/Lib/site-packages/comtypes/gen。

刪了檔案之後,重新執行 GetModule(新版COM),就能產生新版COM的Py檔,這樣便解決到舊版COM的Py檔在OnReplyMessage沒有回傳值( ['out'], c_short, 'sConfirmCode' ),所導致的錯誤代碼2017。

就能順利運行了。

雖然一開始有找到問題所在,但是我卻誤認了真相。被提醒COM版本差異之後,就回頭更注重比對不同COM版本的差異,確實如提醒所指的方向去找就把不確定性解決了。

昨日基於一個忽略,就沒有測驗不同COM版本產生的Py檔模型差異。忘記刪除掉重新產生,只在乎修改,卻沒有發覺使用GetModule並沒有影響到模型py檔,沒有被修改,沒有產生新模型去覆蓋掉舊的,更應該去注意修改時間。

網路上是有人說刪掉gen資料夾,就能順利運作,但是不一定會有這個資料夾啊!當時我用VS的Anaconda,在comtypes裡並沒有gen這個資料夾,當時沒有想知道真相的衝勁,所以也就沒有花時間多思考為什麼可行。

如今可以解釋了,不確定也在提醒之後被解決了,當初是覺得.dll -> .py 錯誤,最後只有.py錯誤。正也因為確認錯誤在哪裡,終於也有了一套滿意的解決方法,能適用於各種的python環境。

思考自己的在尋找答案的過程,一有甚麼想法就去試,如果可行很快就會結束。也就沒有好好釐清各種可能的時間,排序去做確認。

如果先入為主,先設想認為dll問題,所以找解法的過程才不會刪除補好的檔案。
但如果先把其他人的方法整理,自然就會認為是暫存檔的問題,那就會先試刪掉後再用新版COM產生。

兩者同樣都能解決錯誤,但是解決完成度是不一樣的。

官方Documentation並沒有提到會保存檔案且優先讀入已有檔案,或是可供選擇覆寫掉,如果有,那會更快找到答案,直找documentation是優先得順位。所以看來還是不得不進入看看機制為何。

從package裡順一遍邏輯再得出設想,是最硬的,不過也是最根本的,如果當初有更深入去撈,也會發現它會優先讀入已有檔案吧,並不是產生新的覆蓋過去。