HSPでコマンドプロンプトを制御する

1.概要

最近は、Excel VBAやPythonなどによる自動化というキーワードを良く耳にしますが、昔からある自動化で最も安定性が あり、比較的単純な手順の記述とコマンド発行といったコマンドプロンプトによる操作およびバッチファイルによる自動処理 があります。システムの運用管理者やネットワーク管理者の人達も多く利用されています。 筆者も一時期は、良く利用していましたがGUIの操作に慣れ過ぎて最近は利用する機会も減ってきています。 今回は、HSP言語によるコマンドプロンプトを操作して外部から文字列(コマンド)を送信する方法を模索して見たいと思います。

2.コマンドプロンプトに文字列送信

コマンドプロンプトにGUI画面上からキー入力された文字列を送信する方法について説明します。
まず、概略の処理の流れを整理します。
(1) コマンドプロンプトを起動させる。
ただし、環境依存を無くすため環境変数'comspec'より取得したパス名を、exec命令のp1パラメータとして渡す。
(2) 入力ボックス等より入力したコマンド( テキスト文字列 )をクリップボードにコピーする。
(3) コマンドプロンプト画面をアクティブにする。
(4) クリップボードよりコマンドを貼り付ける。

整理すると、これらの処理を実現させるためには、
    ・環境変数'comspec'よりパス名を取得する処理
    ・クリップボードにテキスト文字列をコピーする処理
    ・任意の画面をアクティブにする処理
    ・クリップボードよりテキスト文字列を貼り付ける処理
となります。ここで、一番難しいのはコンソールに対してクリップボードよりテキスト文字列を貼り付ける処理です。 [ ALT ]+[ SP ]+[ e ]+[ p ]+[ Enter ] キーという操作を keybd_event APIを 利用 して送り、貼り付け処理をしても良いのですが、メニューの操作をエミュレートするので、[ ALT ]+[ SP ] で メニュー がポップアップし、次に[ e ]で編集がクリックされ、更に [ p ]+[ Enter ]キーといった流れとなって メニューの 操作が丸見えとなります。もちろん、動作的にも遅くスマートではありません。 コマンドプロンプトには、MENU_ID のマジックNoというものがあって、このコードを直接送信することで下記の 機能が実現できます。

MENU機能Magic No
0コピー65520
1貼り付け65521
4検索65524
5全て選択65525

マジックNoの送信は、Windows APIのPostMessageを利用して行います。
(例)コピー操作を実行する場合
PostMessage hDosWin,WM_COMMAND,65520,0 とします。このコード送信でコマンドプロンプトのメニューからコピー操作を実行したことになります。

3.使用方法

コマンドプロンプトの遠隔操作 (cmdctrl.exe)を起動します。画面は、dirコマンドを 実行した様子です。直接、コマンドプロンプト画面にコマンドを入力するか、GUI画面の入力ボックスにコマンドを 入力して、[Enter]キーもしくは、実行ボタンをクリックします。
各操作ボタンの機能は、次の通りです。
    ・[非表示]      : コマンドプロンプト画面を非表示にします。
    ・[表示]          : 非表示状態のコマンドプロンプト画面を表示にします。
    ・[閉じる]      : cmdctrl.exeを終了します。
    ・[全て選択]  : コマンドプロンプト画面の表示を全て選択状態にします。
    ・[複写]          : コマンドプロンプト画面上の内容(選択したもの)をコピーします。
    ・[貼付]          : クリップボード内にコピーしてある内容を貼付します。
    ・[検索]          : コマンドプロンプトの検索ダイアログを表示します。
    ・[Editor]        : メモ帳を表示します。
その他の付属機能として、Left、Top、Width、Heightの各値を変更して、位置サイズ変更ボタンをクリックすると、 値によるポジションと画面サイズが反映されます。

4.ソースコード

ソースコードは下記の通りです。cmdmod.hspは、コマンドプロンプトの遠隔操作 (cmdctrl.hsp)の専用モジュールとして 作成しました。cmdctrl.hspにインクルードして利用します。 cmdmod.hspが本体プログラムです。このプログラムは、コマンドプロンプトには、MENU_ID のマジックNoを利用 して操作する部分です。実際は、cmdmod.hspモジュールにてその機能を実装しています。 コマンドプロンプトに日頃から慣れている人には、まったく必要の無いプログラムですが、GUIからの遠隔操作の題材としては、おもしろいと思い取り上げて見ました。
	;**** (cmdmod.hsp) *****
	#module "cmdmod"
	;***** 必要となるAPIや定数の定義 *****
	#uselib "user32.dll"
	#cfunc global GetForegroundWindow "GetForegroundWindow"
	#cfunc global GetWindowThreadProcessId "GetWindowThreadProcessId" int,int
	#func  global AttachThreadInput "AttachThreadInput" int,int,int
	#func  global SetForegroundWindow "SetForegroundWindow" int
	#func  global SendMessage "SendMessageA" int,int,int,int
	#func  global FindWindow "FindWindowA" int,int
	#func  global MoveWindow "MoveWindow" int,int,int,int,int,int
	#func  global GetWindowRect "GetWindowRect" int,int
	#func  global OpenClipboard "OpenClipboard" int
	#func  global SetClipboardData "SetClipboardData" int,int
	#func  global EmptyClipboard "EmptyClipboard"
	#func  global CloseClipboard "CloseClipboard"
	#func  global ShowWindow "ShowWindow" int,int
	#func  global PostMessage "PostMessageA" int,int,int,int
	#func  global MapVirtualKey "MapVirtualKeyA" int,int
	#func  global keybd_event "keybd_event" int,int,int,int

	#uselib "kernel32.dll"
	#cfunc global GetCurrentThreadId "GetCurrentThreadId"
	#func  global GetEnvironmentVariable "GetEnvironmentVariableA" var,var,int
	#func  global GlobalAlloc "GlobalAlloc" int,int
	#func  global GlobalLock "GlobalLock" int
	#func  global GlobalUnlock "GlobalUnlock" int
	#func  global lstrcpy "lstrcpy" int,int
	#func  global GetVersionEx "GetVersionExA" int

	#define global CF_OEMTEXT		$00000007
	#define global MAX_PATH			260
	#define global WM_SYSCOMMAND	$00000112
	#define global SC_CLOSE			$0000F060
	#define global WM_COMMAND		$00000111
	#define global SW_HIDE			0
	#define global SW_SHOW			5
	#define global SW_RESTORE		9
	#define global EM_SETMARGINS	$000000D3

	;***** アクティブウィンドウの制御 *****
	#deffunc actwin int prm1
	; prm1 : アクティブのウィンドウハンドル
	handle=prm1
	if handle!0 : hsphwnd=handle : else : hsphwnd=hwnd
	if hsphwnd==0 : return -1
	ThreadId1=GetWindowThreadProcessId(GetForegroundWindow(),varptr(PID))
	ThreadId2=GetCurrentThreadId()
	AttachThreadInput ThreadId2,ThreadId1,1
	SetForegroundWindow hsphwnd
	AttachThreadInput ThreadId2,ThreadId1,0
	return 0

	;***** 環境変数の取得 (COMSPEC) *****
	#deffunc getenvval var prm1
	; prm1 : 境変数の取得用変数
	sdim cp,MAX_PATH+2
	ev="Comspec"	;環境変数(COMSPEC)を設定
	GetEnvironmentVariable ev,cp,MAX_PATH+2
	prm1=cp : ret=stat
	sdim cp,0
	return ret

	;***** クリップボードにテキストデータを設定 *****
	#deffunc setcliptext str prm1
	; prm1 : テキストデータ設定用文字列 or 文字列変数
	OpenClipboard
	ret=stat : if ret!0 : EmptyClipboard

	;GMEM_MOVEABLE    2
	ls=strlen(prm1)+1 : sdim strData,ls
	strData = prm1
	GlobalAlloc 2,ls
	lngHwnd=stat
	if lngHwnd!0 {
		GlobalLock lngHwnd
		lngMem=stat
		if lngMem!0 {
			lstrcpy lngMem,varptr(strData) : ret=stat
			if ret!0 {
				SetClipboardData CF_OEMTEXT,lngHwnd
				lngRet=stat
			}
			GlobalUnlock lngHwnd : lngRet=stat
		}
	}
	CloseClipboard : sdim strData,0
	return stat

	;***** コンソール画面の表示・非表示モード制御 *****
	#deffunc dosdspshow int prm1,int prm2
	; prm1 : コンソールのウィンドウハンドル
	; prm2 : 表示モード = 0 , 非表示モード = 1
	hDosWin = prm1 : if hDosWin==0 : return -1
	nMode   = prm2 : if (nMode<=0)|(nMode>1) : nMode=0
	if nMode==0 : dspflg=SW_RESTORE
	if nMode==1 : dspflg=SW_HIDE
	ShowWindow hDosWin,dspflg
	return 0

	;***** 起動しているコンソール画面を閉じる *****
	#deffunc dosclose int prm1
	; prm1 : コンソールのウィンドウハンドル
	hDosWin = prm1 : if hDosWin==0 : return -1
	SendMessage hDosWin,WM_SYSCOMMAND,SC_CLOSE,0
	return 0

	;***** コンソール画面のリモート制御モード設定 *****
	#deffunc doscmd int prm1,int prm2
	; prm1 : コンソールのウィンドウハンドル
	; prm2 : 制御モード
	;        mode=0 : コピー
	;        mode=1 : 貼付
	;        mode=3 : 検索
	;        mode=4 : 全て選択
	hDosWin = prm1 : if hDosWin==0 : return -1
	nMode   = prm2 : if (nMode<=0)|(nMode>4) : nMode=0

	if nMode==0 : PostMessage hDosWin,WM_COMMAND,65520,0
	if nMode==1 : PostMessage hDosWin,WM_COMMAND,65521,0
	if nMode==3 : PostMessage hDosWin,WM_COMMAND,65524,0
	if nMode==4 : PostMessage hDosWin,WM_COMMAND,65525,0
	return 0

	;***** コンソール画面にキーコードを送信 *****
	#deffunc doskeysend int prm1,int prm2,int prm3
	; prm1 : 仮想キーコード(1個目)
	; prm2 : 仮想キーコード(2個目)
	; prm3 : キー送信モード
	;        mode=0 : 単独キー送信, mode=1 : 同時キー送信
	; (例)
	;        doskeysend 13        : Enter Keyのみ送信
	;        doskeysend ,,2       : ハンドルのみを取得
	;        doskeysend 18,70,1   : ALT + F
	nKeyCode1 = prm1 : if (nKeyCode1<=0)|(nKeyCode1>255) : nKeyCode1=0
	nKeyCode2 = prm2 : if (nKeyCode2<=0)|(nKeyCode2>255) : nKeyCode2=0
	nMode     = prm3 : if (nMode<=0)|(nMode>2) : nMode=0

	;OSの種別取得
	dim os_ver_info,37 : os_ver_info(0)=148
	GetVersionEx varptr(os_ver_info)
	if os_ver_info(4)=2 {
		lpClassName="ConsoleWindowClass"
			}else{
				 lpClassName="tty"
	}
	dim os_ver_info,0
	FindWindow varptr(lpClassName),0
	hDosWin=stat : if hDosWin=0 : return -1
	;ハンドルのみを取得
	if nMode==2 : return hDosWin
	SetForegroundWindow hDosWin

	;単独キー送信の場合
	if nMode==0 {
		MapVirtualKey nKeyCode1,0 : convkey=stat
		keybd_event nKeyCode1,convkey,0,0
		keybd_event nKeyCode1,convkey,2,0
	}
	;同時キー送信の場合
	if nMode==1 {
		MapVirtualKey nKeyCode1,0 : convkey=stat
		keybd_event nKeyCode1,convkey,0,0
		MapVirtualKey nKeyCode2,0 : convkey=stat
		keybd_event nKeyCode2,convkey,0,0
		wait 2
		keybd_event nKeyCode1,convkey,2,0
		keybd_event nKeyCode2,convkey,2,0
	}
	return hDosWin
	#global
	;***** コマンドプロンプトの遠隔操作 (cmdctrl.hsp) *****

	;***** 実行ファイル自動作成 ****
	#packopt type 0
	#packopt name "cmdctrl"
	#packopt runtime "hsprt"
	#packopt manifest "app.manifest"
	#packopt icon "sample.ico"
	#packopt hide 1

	#include "cmdmod.hsp"

	#uselib "user32.dll"
	#func   SetFocus "SetFocus" int

	#const WshNormalNoFocus     4
	#const WshHide              0

	;***** 起動ディレクトリ取得 *****
	sdim Startdir,512
	if hspstat&1=0 { Startdir=dir_exe+"¥¥" : chdir dir_exe
	} else {
		Startdir=dir_cur+"¥¥"
	}
	chdir Startdir

	onexit *owari
	screen 0,650,120,0,ginfo(20)-650>>1,(ginfo(21)-120>>1)+200
	title "Command Prompt - KeySendConsole Ver1.0"
	;***** 利用するエディタ *****
	editfile="notepad.exe"

	;***** コマンドプロンプトを起動 *****
	getenvval cp
		newcom WshShell, "WScript.Shell"
	WshShell->"Run" cp,WshHide,0
	wait 50
	gosub *dosmove
	delcom WshShell
	wait 10
	sdim ms,1024
	redraw 0
	font "Meiryo UI",12 : objmode 2
	pos 10,4 : input ms,ginfo(10)-80,22
	PostMessage objinfo(0,2),EM_SETMARGINS,3,3
	objsize 50,22
	pos ginfo(10)-68,3 : button "実行",  *mode01
	objsize 60,24
	pos  10,32 : button "非表示",  *mode02
	pos  72,32 : button "表示",    *mode03
	pos 134,32 : button "閉じる",  *mode04
	pos  10,62 : button "全て選択",*mode05
	pos  72,62 : button "複写",    *mode06
	pos 134,62 : button "貼付",    *mode07
	pos 196,62 : button "検索",    *mode08
	pos 258,62 : button "Editor",  *editor

	objsize 40,20
	pos 210,38 : mes "Left"   : pos 245,34 : input x
	pos 300,38 : mes "Top"    : pos 330,34 : input y
	pos 375,38 : mes "Width"  : pos 415,34 : input w
	pos 460,38 : mes "Height" : pos 500,34 : input h
	objsize 94,24
	pos 544,32 : button "位置サイズ変更",*winposchg
	onkey *kevent
	gsel 0,1
	SetFocus hwnd : actwin hwnd : objsel 0
	redraw 1
	stop

	;***** Enterキーで実行 *****
*kevent
	if (iparam!13)|(lparam>>30) : stop
	objsel -1 : if (stat!0)|(ms="") : actwin hDosWin
	goto *mode01

	;***** コマンドをクリップボードにセットして送信する *****
*mode01
	;ハンドルを再確認して画面が閉じられていたら、起動させる
	gosub *getdoshwnd
	if hDosWin=-1  : exec cp : wait 50
	gosub *getdoshwnd

	;コマンドをクリップボードにセットしてコンソール画面に貼付ける
	;***** 空のEnterキーのみも受け付けるように修正 *****
	ls=strlen(ms) : if ls==0 : actwin hDosWin
	setcliptext ms
	gosub *clipset
	wait 10
	actwin hwnd : objsel 0
	sendmsg objinfo(0,2),$0303,0,0
	stop

	;***** コンソール画面を非表示にする *****
*mode02
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	dosdspshow hDosWin,1
	wait 10
	actwin hwnd : objsel 0
	stop

	;***** コンソール画面を表示する *****
*mode03
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	dosdspshow hDosWin,0
	wait 10
	actwin hwnd : objsel 0
	stop

	;***** コンソール画面を閉じる *****
*mode04
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	dosclose hDosWin
	end

	;***** 全て選択 *****
*mode05
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	doscmd hDosWin,4
	stop

	;***** 複写 *****
*mode06
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	doscmd hDosWin,0
	stop

	;***** 貼付 *****
*mode07
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	doscmd hDosWin,1
	stop

	;***** 検索 *****
*mode08
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	doscmd hDosWin,3
	stop

	;***** コンソール画面のサイズ・位置を変更(起動時のみ) *****
*dosmove
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	dim rc,4
	GetWindowRect hDosWin,varptr(rc)
	w = rc(2)-rc(0)
	h = rc(3)-rc(1)

	; HSPのScreen横幅と同じとする
	MoveWindow hDosWin,ginfo(20)-650>>1,(ginfo(21)-h>>1)-130,650,h,1
	; コンソール画面を表示 (起動時は非表示のものを戻す処理)
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	dosdspshow hDosWin,0
	wait 10
	actwin hwnd
	
	; デフォルトの位置とサイズを再取得
	GetWindowRect hDosWin,varptr(rc)
	w = rc(2)-rc(0)
	h = rc(3)-rc(1)
	x = rc(0)
	y = rc(1)
	return

	;***** コンソール画面のサイズ・位置を変更 *****
*winposchg
	gosub *getdoshwnd
	if hDosWin=-1 : stop
	MoveWindow hDosWin,x,y,w,h,1
	stop

	;***** コンソール画面のハンドル取得 *****
*getdoshwnd
	doskeysend ,,2 : hDosWin=stat
	return

	;***** 貼付処理 *****
*clipset
	doscmd hDosWin,1

	;▼▼▼ 十分なウェイトを確保して下さい。 ▼▼▼
	wait 50
	doskeysend 13
	return

*editor
	if editfile="" : editfile="notepad.exe"
	exec editfile
	stop

	;***** 終了処理 *****
*owari
	;終了時に開いているコンソール画面を閉じる
	gosub *getdoshwnd
	if hDosWin!-1 : dosclose hDosWin
	end

5.ダウンロード

提供するソースコードのライセンスは、CC0 (クレジット表示不要、改変可、商用可) とします。自由に利用して頂いてかまいません。 尚、データの取得やプログラム実行において損害等が生じた場合は、筆者は一切の責任も負いません。全て自己責任でお願いします。

「コマンドプロンプトの遠隔操作(cmdctrl.exe)」は、下記よりダウンロードして下さい。

ダウンロード

コメント

このブログの人気の投稿

Excelアドインで日本語形態素解析

階層構造JSONファイルの作成

TOP