Turbo HAMLOG/WinをPythonで操作する(1)

前回まではTurbo HAMLOGデータベースを付属のライブラリHAMLOG50.DLLを介してPythonから操作してきました。

ji1jdi.hatenablog.com

HAMLOGにはデータベースを直接アクセスする方法以外にも、Windowsのウィンドウメッセージを使ったアプリケーション間でデータをやり取りする仕組みが提供されています。詳細は次のページをご覧ください。

「HAMLOG50.DLLの仕様/他のアプリからHAMLOGを操作する仕様」 https://hamlog.sakura.ne.jp/mou/thwapi.html

上記のページからダウンロードしたアーカイブの中のファイル「HamlogMs.txt」がその仕様書です。これによると、HAMLOGの交信データを入力するウィンドウに交信データを送ったり、入力ウィンドウに表示されている交信データを受け取れるそうです。

それでは、まずはPythonからTurbo HAMLOGの入力ウィンドウにデータを送ってみます。

PythonからWindows APIを呼び出すには、これまで使用していたctypesからも可能ですが、もっと便利な次のライブラリを使うことにします。

github.com

パッケージをインストールする前に、仮想環境を準備しておきます。

python -m venv .venv
.venv\Scripts\activate


次にpywin32をインストールします。

pip install pywin32

これで win32guiやwin32conといったモジュールが利用できるようになりました。

仕様書によると、Turbo HAMLOGとのデータのやり取りにはウィンドウメッセージ WM_COPYDATA を使うそうです。

WM_COPYDATA メッセージ (Winuser.h) - Win32 apps | Microsoft Learn

データは COPYDATASTRUCT 構造体に入れます。

COPYDATASTRUCT (winuser.h) - Win32 apps | Microsoft Learn

COPYDATASTRUCTの定義は次の通りです。

typedef struct tagCOPYDATASTRUCT {
  ULONG_PTR dwData;
  DWORD     cbData;
  PVOID     lpData;
} COPYDATASTRUCT, *PCOPYDATASTRUCT;


これをPython側で扱えるようにctypesで定義します。

class COPYDATASTRUCT(ctypes.Structure):
    _fields_ = [
        ("dwData", PULONG),
        ("cbData", DWORD),
        ("lpData", LPVOID)
    ]


これでデータ構造の準備はできました。データを送るにはTurbo HAMLOGのウィンドウハンドルが必要です。次のようにウィンドウクラスを指定して、起動中のTurbo HAMLOGのウィンドウハンドルを探します。

hwnd = win32gui.FindWindow("TThwin", None)


今回はTurbo HAMLOGの入力ウィンドウにコールサインを入力してみます。仕様書にはdwDataに設定するコマンドとして「1」を指定するとあります。

* COPYDATASTRUCT 構造体メンバー dwData について

●コマンドを数値で指定し、Turbo HAMLOG/Winの入力ウインドウに文字列を送る。
  1 = コールサインに文字列を送る。
  2 = 日付に文字列を送る。
    ・・1~14まで、Turbo HAMLOG/Winの入力欄の並びのとおり・・
 12 = QTHに文字列を送る。
 13 = Remarks1に文字列を送る。
 14 = Remarks2に文字列を送る。


dwDataに設定するコマンドには、次のようにキーボードからENTERキーを押したときと同じ振る舞いをさせることができます。

(Delphi)
  const THW_ENTER = $10000;  // データ送信後、ENTERキーを押したのと同じ
        THW_FOCUS = $20000;  // データ送信後、編集ボックスにフォーカス


これをPythonで実装すると次のようになります。

THW_ENTER = 0x10000
THW_FOCUS = 0x20000

call = create_string_buffer("JI1JDI".encode())

cmd = 1 | THW_FOCUS | THW_ENTER
cds = COPYDATASTRUCT()
cds.dwData = cast(cmd, PULONG)
cds.cbData = sizeof(call)
cds.lpData = cast(call, LPVOID)

win32gui.SendMessage(hwnd, win32con.WM_COPYDATA, None, cds)


ソースコードは全体で次のようになりました。エラー処理は省略しています。

import win32gui
import win32con
from ctypes import *
from ctypes.wintypes import *

class COPYDATASTRUCT(ctypes.Structure):
    _fields_ = [
        ("dwData", PULONG),
        ("cbData", DWORD),
        ("lpData", LPVOID)
    ]

hwnd = win32gui.FindWindow("TThwin", None)

THW_ENTER = 0x10000
THW_FOCUS = 0x20000

call = create_string_buffer("JI1JDI".encode())

cmd = 1 | THW_FOCUS | THW_ENTER
cds = COPYDATASTRUCT()
cds.dwData = cast(cmd, PULONG)
cds.cbData = sizeof(call)
cds.lpData = cast(call, LPVOID)

win32gui.SendMessage(hwnd, win32con.WM_COPYDATA, None, cds)


実行すると、次のように表示されました(名前はマスクしています)。ユーザー登録しているコールサインは名前とQTHが表示されました。

ここまででPythonからTurbo HAMLOGの入力ウィンドウにデータを送ることができました。次回は入力ウィンドウのデータを取得するところまで試してみたいと思います。

JI1JDI

ゆるく楽しくアマチュア無線とプログラミングを楽しんでいます。 scrapbox.io