Clojure-PyでWin32API
Clojureからネイティブライブラリ
Pythonにはctypesというネイティブライブラリをダイレクトに扱うモジュールがあります。ということはClojure-Pyを使うとClojureからネイティブが呼べるということになりますね。
;;-*- coding:utf-8 -*- (ns sample-ctypes (:require ctypes) (:require [clojure.string :as string])) ;; convert to unicode string (defn u$ ([s codec] (py/unicode s codec)) ([s] (u$ s "utf8"))) ;; get attribute from python-object (defmacro pyattr [obj a] `(py/getattr ~obj ~(name a))) ;; get attribute thread macro (defmacro pyattr.. [obj & attrs] `(-> ~obj ~@(map (fn [a] `(pyattr ~a)) attrs))) ;; Sample user32 MessageBoxW (let [title (u$ "【サンプル CLojure-Py】") message (u$ (string/join "\n\n" ["clojure-py からの〜" "ctypes.windll からの〜" "MessageBoxW を表示"])) message-box (pyattr.. ctypes/windll user32 MessageBoxW)] (message-box 0 message title 0x40))
Python2.7で試しました。
Pythonオブジェクトのメンバへのアクセス
(.attrname obj)
でオブジェクトのメンバにアクセスできると書いてありますが何故かうまくいきませんでした 書き方が違いました、最後に追記しておきます。
(py/getattr obj "attrname")
でメンバにアクセスできることが分かりました。*1
したがってMessageBoxを使うには。
(let [user32 (py/getattr ctypes/windll "user32") message-box (py/getattr user32 "MessageBoxW")] (message-box ...))
のようにする必要がありますが、面倒なのでスレッドマクロにしています。
Clojureの新しいメンバ参照方式(追記)
TLで教えてもらいましたが、Clojure族はこれまでメンバ変数もメソッドも . で参照していましたが、メンバ変数については .- という新しい記法が導入されているそうです。というかReadMe.mdに書いてありました。*2
(.user32 ctypes/windll)
はダメでしたが、
(.-user32 ctypes/windll)
なら行けました。これならユーティリティマクロなど書かなくても。
(let [message-box (-> ctypes/windll .-user32 .-MessageBoxW)] (message-box 0 (u$ "本文") (u$ "タイトル") 0x40))
あるいは少し強引に
(-> ctypes/windll .-user32 .-MessageBoxW (apply [0 (u$ "本文") (u$ "タイトル") 0x40]))
と書けますね。スッキリした。