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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
;**** (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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
  ;***** コマンドプロンプトの遠隔操作 (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ファイルの作成

キーボードのキーコードの一覧表