xyzzyからのWin32 API SendInput()呼び出しコード
xyzzyからのWin32 API SendInput()呼び出しコード。
最近、xyzzy上で動く作業用のスクリプトを色々と書いています。Win32 APIを叩くこともたまにあって、たいがいは単純なんですが、SendInput()を使ってみようとしたところで手が止まりました。引数にunionで複数の構造体をまとめたメンバーをもった構造体をとるのですね。unionでまとめられたメンバーをどう扱えばいいのか、さっぱりでした。わざわざxyzzyからそんな面倒な関数を呼び出さないでもいいんじゃないかって気もちょっとしたんですが……。まあ、色々と試しまして、なんとか動かすことができました。その結果をまとめたのが以下のコードです。
"SendInput-def.l"をload-fileにて読み込んでから、"SendInput-test-01.l"、"SendInput-test-02.l"をload-fileで読み込んでみてください。なお、"(require "wip/winapi")"は事前になされているものと考えています。
あくまでも、こんな風にしたら動いたっていうサンプルです。こうやればLispとしてきれいに書けるよとか、こういう書き方は危険だよとか、SendInput()の使い方おかしいぞとか、いっぱいあるんじゃなかろうかと思います。そういうのありましたらご指摘ください。下のような面倒な方法使わないでもunion扱えるよって話があれば嬉しいところ。いちおう"winapi.l"や"foreign.l"は眺めたんですが、Lisp見慣れていないので、何が書いてあるのか半分もわからず、僕には見つけられませんでした。もっとも、Cのunionにまで対応した構造体定義マクロを作る必要があるのかなって思いますから、下のようなやり方でベタにchunkを扱うので良いのだろうなとは思うのですが。
ちなみにSJISの2バイト文字とか考えず、単純なキーイベントをおくるだけなら、OLEで"WScript.Shell"のオブジェクトを作って、SendKeysメソッドを使う方がずっと楽です。
最後に、SendInput()は9x系列ではWindows 98以降、NT系列ではWindows NT 4.0 SP3 以降にある関数です。よほど古い環境じゃなければ、下のコードは動くと思います。僕は、Windows XP SP2 / xyzzy 0.2.2.235にて試しました。
参考 MSDN - SendInput:
[SendInput]
;;;; "SendInput-def.l" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; SendInput用構造体、型定義。 (c:define-c-struct MOUSEINPUT (winapi:LONG dx) (winapi:LONG dy) (winapi:DWORD mouseData) (winapi:DWORD dwFlags) (winapi:DWORD time) (winapi:ULONG * dwExtraInfo) ) (c:define-c-struct KEYBDINPUT (winapi:WORD wVk) (winapi:WORD wScan) (winapi:DWORD dwFlags) (winapi:DWORD time) (winapi:ULONG * dwExtraInfo) ) (c:define-c-struct HARDWAREINPUT (winapi:DWORD uMsg) (winapi:WORD wParamL) (winapi:WORD wParamH) ) (setf size-for-INPUT (max (c:c-struct-size-of MOUSEINPUT) (c:c-struct-size-of KEYBDINPUT) (c:c-struct-size-of HARDWAREINPUT))) (c:define-c-struct INPUT (winapi:DWORD type) (winapi:BYTE data size-for-INPUT) ) (c:define-c-type (INPUT *) LPINPUT) ;;; SendInput定義。 (c:define-dll-entry winapi:UINT SendInput (winapi:UINT LPINPUT c:int) "user32" "SendInput")
;;;; "SendInput-test-01.l" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; 実験その1。 ;; KEYBDINPUT (setf kbdinput (make-KEYBDINPUT)) (setf (KEYBDINPUT-wVk kbdinput) 0) (setf (KEYBDINPUT-wScan kbdinput) #x8868) ;U+8868 = "表" (setf (KEYBDINPUT-dwFlags kbdinput) 4) ;KEYBDINPUT.dwFlagsにKEYEVENTF_UNICODEをセットする。 (setf (KEYBDINPUT-time kbdinput) 0) (setf (KEYBDINPUT-dwExtraInfo kbdinput) 0) ;; INPUT (setf input-data (make-INPUT)) (setf (INPUT-type input-data) 1) ;INPUT.typeにINPUT_KEYBOARDをセットする。 (si:copy-chunk kbdinput input-data (si:chunk-size kbdinput) 0 (c:c-struct-offset-of INPUT data)) ;KBDINPUTの内容をINPUTのunion部(slot data)にコピーする。 (SendInput 1 input-data (c:c-struct-size-of INPUT))
;;;; "SendInput-test-02.l" ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; 実験その2。 ;;; SendInputに渡すUnicode文字のリスト。 (setf input-lst '(#x8868 #x8A08 #x7B97 #x30BD #x30D5 #x30C8)) ;U+8868 = "表"/ U+8A08 = "計"/ U+7B97 = "算"/U+30BD = "ソ"/ U+30D5 = "フ"/ U+30C8 = "ト"。 ;; KEYBDINPUT。wScan以外を設定しておく。 (setf kbdinput (make-KEYBDINPUT)) (setf (KEYBDINPUT-wVk kbdinput) 0) (setf (KEYBDINPUT-dwFlags kbdinput) 4) ;KEYBDINPUT.dwFlagsにKEYEVENTF_UNICODEをセットする。 (setf (KEYBDINPUT-time kbdinput) 0) (setf (KEYBDINPUT-dwExtraInfo kbdinput) 0) ;; INPUT。data以外を設定しておく。 (setf input-data (make-INPUT)) (setf (INPUT-type input-data) 1) ;INPUT.typeにINPUT_KEYBOARDをセットする。 ;;; INPUT []。input-lstの個数分INPUTを格納出来るchunkを生成する。 (setf input-data-array (si:make-chunk nil (* (c:c-struct-size-of INPUT) (length input-lst)))) ;要素3個のINPUT配列に相当するchunkを生成する。 ;;; input-lstの文字データをKEYBDINPUTに、。 (let ((destpos 0)) (dolist (inp input-lst) (setf (KEYBDINPUT-wScan kbdinput) inp) ;wScanに文字をセットする。 (si:copy-chunk kbdinput input-data (si:chunk-size kbdinput) 0 (c:c-struct-offset-of INPUT data)) ;KBDINPUTの内容をINPUTのunion部(slot data)にコピーする。 (si:copy-chunk input-data input-data-array (si:chunk-size input-data) 0 destpos) ;INPUT配列としてあつかうchunkにdestposの位置からINPUTの内容をコピーする。 (setf destpos (+ destpos (c:c-struct-size-of INPUT))) ;destposの位置を進める。 ) ) (SendInput (length input-lst) input-data-array (c:c-struct-size-of INPUT))