[Python] Windows COM (ActiveX) client 사용 Develop Tip

기존에 어떤 라이브러리가 윈도우에서 Active-X로 작성되어 설치되어 있다고 가정합니다.
그러면 MS의 인터넷 익스플로러를 이용해서 이 ActiveX를 JavaScript에서 
사용해 온 경우가 무척이 많습니다.

그런데 필요에 따라 기존에 MSIE에서 ActiveX 부분만을 따로 떼어 무언가 작업을 하고,
웹 부분은 HTML5 표준으로 만들어야 한다고 생각해 봅니다.

그러면 이 ActiveX 부분을 쉽게 파이썬으로 작업이 가능할까요??
정답은 Yes. 그것도 아주 훌륭하게 말이지요.

다음에 적용해 보았던 방법을 간단하게 설명해 봅니다.

우선 COM을 사용하기 위하여 다음 패키지를 설치합니다.

pip.exe install pywin32

이제 간단한 코드를 확인해 보겠습니다.

우선 다음의 세 줄을 import 에 추가해 줍니다.

import win32com.client as com
import threading
import pythoncom

처음에 ActiveX를 가져오는 부분은 다음과 같이 합니다.

com_obj = com.DispatchWithEvents("PCBiz.PCBizX.1", EvtHandler)

필요에 따라 ActiveX 는 이벤트 콜백을 받을 수도 있는데 위에 EvtHandler 
클래스가 바로 그 콜백을 받는 부분 되겠습니다.

class EvtHandler:
    def OnEventLogin(self, nResult):
        print("EvtHandler.EventLogin: nResult=%s" % nResult)
    def OnEventConnect(self, nResult):
        print("EvtHandler.EventConnect: nResult=%s" % nResult)

콜백 클래스는 위와 같습니다.

ActiveX 설명서에는 EventLogin(...) 이라고 나와있는데 앞에 On 이라고 접두어를
붙여야 콜백이 불리는 경우가 있었으니 참고하십시오.

그 다음 메인 뼈대 입니다.

class OpenAPI(threading.Thread):
    # ==========================================================================
    def __init__(self, authkey, userid, passwd, server_type=1):
        self.authkey = authkey
        self.userid = userid
        self.passwd = passwd
        self.server_type = server_type
        # for internal
        self.b_opened = False
        self.b_break = False
        threading.Thread.__init__(self)
        try:
            self.co = com.DispatchWithEvents("PCBiz.PCBizX.1", EvtHandler)
        except Exception as e:
            raise RuntimeError('Cannot open PCBiz Active-X: %s' % str(e))
        # self.open()

    # ==========================================================================
    def open(self):
        nr = self.co.Login(self.server_type, self.authkey,
                            self.userid, self.passwd)
        print('after Login : %s' % nr)
        self.b_opened = True

    # ==========================================================================
    def close(self):
        if self.b_opened:
            self.co.Logout()
            print('after Logout')
            self.b_opened = False

    # ==========================================================================
    def run(self):
        self.open()
        while not self.b_break:
            # print('OpenAPI.run')
            pythoncom.PumpWaitingMessages()
            time.sleep(0.5)
        self.close()

    # ==========================================================================
    def __del__(self):
        self.close()

    # ==========================================================================
    def __enter__(self):
        self.start()
        return self

    # ==========================================================================
    def __exit__(self, type, value, traceback):
        self.b_break = True
        self.join()
        self.close()

위에서는 context를 이용하여 with 문으로 시작할 때, __enter__() 에서 start()로 개별 쓰레드가 run()을 실행하고
그 안에서 open() 후에 b_break 플래그가 설정되기 전까지 PumpWaitingMessage()를 합니다.

이렇게 개별 쓰레드를 사용한 이유는 밖에 쓰레드에서 해당 COM의 API를 호출할 수도 있지만 개별 콜백이
언제 든지 올 수 있기 때문입니다.

또한 COM이 IO-bound 인 경우가 많으므로 GIL 문제를 생각할 필요 없이 쓰레드를 이용가능할 것입니다.

이제는 호출하는 부분입니다.


    with OpenAPI(auth_key, user_id, passwd) as api:
        for phone in api.GetPhoneList():
            print(phone)
            my_Telnum = phone.Telnum
        for call in api.GetAbsenceCallList():
            print(call)
        print(api.SendCTC(my_Telnum, '010-1111-2222'))
        time.sleep(1000)


r = self.co.aaa()

위와 같이 API를 호출하고 그 결과로 받은 것이 COM 개체인 경우에는
파이썬에서 자동으로 

r.att1
r.att2

와 같은 속성으로 접근이 가능합니다.
(디버거로 r을 확인해 보면 _private_props (?) 와 같은 곳에 접속 가능한 attributes가 있는 것을 볼 수 있었습니다.


어느 분께는 도움이 되셨기를..


덧글

댓글 입력 영역

구글애드텍스트