2020年6月24日 星期三

在 linux 下使用wine python 串接群益 SKCOM API

        群益api 是使用 windows 的 COM 技術,在 linux 下要靠 wine 來模擬 windows 的環境。wine 要使用 32 bit 的相容性比較好,因此本篇 linux 系統 , python, skcom.dll 皆是使用 32 bit版。

操作概念是 在 linux 下裝 wine 註冊 SKCOM.dll, 用 wine 安裝 windows 版本的 python,  再用這個windows 版的 python 安裝 comtypes, pywin32 等套件來使用 skcom。 整個環境會是在 wine 底下執行的,操作起來還是有點隔閡,不是很順暢,我也還在摸索 

linux 我使用 lubuntu 18.04 alternative-i386 這個桌面版本, 執行Wine時建議是要在有XWindow的環境下執行,安裝一些 windows 程式,會需要 GUI 的操作 

linux wine 的安裝跟使用是參考這兩篇, 研究的超仔細,大推!! 

安裝 wine, winetricks

  • 建議使用32位元的作業系統來安裝Wine,因為Wine目前64位元的支援很差,它主要是以執行32位元的Windows程式為主!
  • 建議是透過compile Wine原始碼的方式來安裝Wine,也是為了避免32位元的函式庫裝得不夠完整!
  • winetricks 可以幫助安裝 VC++ runtime 2010 ,註冊 SKCOM.dll 時需要用到 
  • 剛裝好的 lubuntu 環境,把 apt 更新至最新,並裝下列開發套件,編譯 Wine 需要用到
$ sudo apt update; apt upgrade
$ sudo apt install openssh-client openssh-server vim software-properties-common gcc g++ vim subversion automake autoconf perl patch build-essential libtool doxygen bison flex zlib1g-dev gperf tofrodos wget curl screen git samba vsftpd bash-completion ftp colordiff libkrb5-dev rar unrar unzip vnstat libssl-dev
  • 補足所需的套件
$ time apt install gnome-devel libx11-dev flex bison qt5-qmake libjpeg-dev libxslt1-dev libglu1-mesa-dev freeglut3-dev prelink libasound2-dev libfreetype6-dev libpng-dev libxml2-dev libxrender-dev libgl1-mesa-dev libgphoto2-dev libmpg123-dev libtiff-dev libpulse-dev liblcms2-dev libcapi20-dev libldap-dev libopenal-dev libv4l-dev libsane-dev libcups2-dev libgsm1-dev libosmesa6-dev libgnutls28-dev libpcap0.8-dev oss4-dev libcdk5-dev libgstreamer-plugins-base1.0-dev libarrayfire-opencl-dev
$ time apt install libsdl2-dev libvulkan-dev
  • 清除一下apt-get抓下來的殘餘檔
$ apt clean
  • 用root帳號登入
  • 先把Wine的git repository上的code拉下來!
$ mkdir -pv /root/packages ; cd /root/packages
$ time git clone https://github.com/wine-mirror/wine

  • 檢查套件是否足夠
$ cd ./wine
$ ./configure 

 #訊息大概剩下下面這樣,就差不多了。
configure: MinGW compiler not found, cross-compiling PE files won't be supported.
configure: libhal development files not found, no legacy dynamic device support.
configure: libFAudio development files not found, XAudio2 won't be supported.
configure: vkd3d development files not found (or too old), Direct3D 12 won't be supported.
  • 接下來編譯並且安裝!編譯會花大約40-60分鐘
$ time make
$ time make install
  • 接下來安裝winetricks!直接從github wget下來就可以用了!
$ mkdir -pv /opt/bin ; cd /opt/bin
$ wget https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks
$ chmod +x ./winetricks
$ ./winetricks -V  

#檢查版本,出現下列訊息就沒問題了
20190615-next - sha256sum: 47304e177f259d6f9c05af01ab42c06531fd8a9716e2751d2fadcd664130feea

Wine 使用 SKCOM api

  • 設定Wine的windows環境,在 XWindow環境下。第一次使用須初始化一個windows 環境,打開Terminal,執行以下指令以設定Wine的環境!我的環境是建立在 ~./home/使用者/.wine 下,.wine 就是windows 虛擬的系統,底下有個 drive_c 資料夾,等於 C:\  槽
$ wineboot  

  • 群益API的SKCOM.dll檔需要 VC++ 2010可轉散發套件, 可透過winetricks來安裝 vcrun2010
$ winetricks vcrun2010 

  • 下載群益API,將API 資料夾 CapitalAPI_2.13.22 下的x86 資料夾搬到 ~/.wine/drive_c/skcom/x86  這等於是存在 windows 的 C:\\skcom\\x86, 之後使用python 呼叫會方便一點
  • cd 到 ~/.wine/drive_c/skcom/x86資料夾下,執行以下指令以註冊群益API:
$ regsvr32 ./SKCOM.dll

drive_c 下路徑的內容,skcom, python, strategy 是我自己創建的,資料夾名稱不要有空格,減少麻煩



wine下的 python環境

  • 用wine 來安裝 windows版的 python
  • 到 python 官網,我選了 Python3.8.3 的 Windows x86 executable installer 這個版本下載
  • 我有嘗試使用miniconda,但 conda 或 pip 位置不在預設路徑上,我搞不定,導致python套件裝不起來  

#使用 wine安裝windows版的python
#會有GUI跳出,可將python裝在 C:\\python 路徑下
$ wine python-3.8.3.exe   

#安裝 comtypes 及 pywin32 套件,其他python套件也可以如此安裝
$ wine pip install comtypes pywine32    


  • wine的windows版的 python 環境建置完成,可以開始 CODING!!
$ wine python



將寫好的 Script.py 交由 window python 執行, 要加wine 不然會呼叫到 linux系統下的 python, 兩個系統還是不相容的

$ wine python Script.py

附上 Script.py 的內容


import pythoncom, time, os
from comtypes.client import GetModule, GetEvents, CreateObject
from datetime import datetime

#第一次使用需要將 SKCOM 包成python 的package
#SKCOM.dll 在linux 下,我是放在 ~./home/yi/.wine/driv_c/skcom/x86/SKCOM.dll
#但在 wine 下,是放在下列路徑, drive_c 就是windows的C槽
GetModule(r'C:\\skcom\x86\SKCOM.dll')
#GetModule會在comtypes.gen 下產生 SKCOMLib package, 之後就用下面方式引用
import comtypes.gen.SKCOMLib as sk

#建立COM物件
if 'skC' not in globals(): skC=CreateObject(sk.SKCenterLib, interface=sk.ISKCenterLib)
if 'skQ' not in globals(): skQ=CreateObject(sk.SKQuoteLib, interface=sk.ISKQuoteLib)
if 'skR' not in globals(): skR=CreateObject(sk.SKReplyLib, interface=sk.ISKReplyLib)

#輸入身分證與密碼
ID=''
PW=''
#股票代號
strStock="TX00" 

print(str(datetime.now())[0:-4], 'ID & PW entered')

#建立事件類別
class skQ_events:                                
    def OnConnection(self, nKind, nCode):
        print(datetime.now(), 'OnConnection', nKind, nCode)
        if nKind == 3003:
            #nKind==3003 表連線報價主機就緒
            print("quoate server connected..., nkind= ", nKind)
            ncode=skQ.SKQuoteLib_RequestStocks(1, strStock)
            if ncode==0:
                print(strStock, 'requested')    
    
    def OnNotifyQuote(self, sMarket, sIndex):
        ts=sk.SKSTOCK()
        skQ.SKQuoteLib_GetStockByIndex(sMarket, sIndex, ts)
        print(ts.bstrStockName, ts.bstrStockNo,  ts.nClose/10**ts.sDecimal)
    
class skR_events:
    def OnReplyMessage(self, bstrUserID, bstrMessage, sConfirmCode=0xFFFF):
        print('OnReplyMessage', bstrMessage)
        return sConfirmCode
                            
#Event sink, 事件實體  
EventQ=skQ_events()
EventR=skR_events()

#make connection to event sink
ConnectionQ = GetEvents(skQ, EventQ)
ConnectionR = GetEvents(skR, EventR)

if __name__=='__main__':
    #登入
    nCode=skC.SKCenterLib_Login(ID, PW)
    print("Login,", skC.SKCenterLib_GetReturnCodeMessage(nCode))
    #連線報價主機
    print(f"{datetime.now()}, EnterMonitor,", skC.SKCenterLib_GetReturnCodeMessage(skQ.SKQuoteLib_EnterMonitor()))
    # Event loop, 持續檢查有沒有event產生
    pythoncom.PumpMessages()



輸出結果,大概類似下圖,夾雜很多 fixme的警告,但不影響使用