コマンドプロンプトをGUIで操作するアプリケーション ~Delphiソースコード集
コマンドプロンプトGUIアプリのファイルの準備
本ページの下部のソースコードをコピーして「MamCmdThread.pas」ファイルを作成し、プロジェクトフォルダ内に入れる。ソースコードの記述
プロジェクトを新規作成(VCLアプリケーション)し、フォーム(Form1)にTEdit、TMenuを配置する。Edit1のAlignプロパティを「alTop」に設定する。
Menu1のAlignプロパティを「alClient」に設定する。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
MamCmdThread ;
type
TForm1 = class(TForm)
Edit1: TEdit;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Edit1KeyPress(Sender: TObject; var Key: Char);
procedure FormResize(Sender: TObject);
private
{ Private 宣言 }
cmd:TMamCmdThread;
procedure OnTextChange({Sender:TObject;}text:String);
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
if Key=#13 then
begin
Key:=#0;
cmd.Send(Edit1.Text);
Edit1.Text:='';
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var coordx:integer;
begin
Memo1.ScrollBars:=ssBoth;
Memo1.DoubleBuffered:=True;
Memo1.Font.Name:='MS ゴシック';
Memo1.ReadOnly:=True;
Memo1.Font.Size:=10;
coordx:=trunc(
(Memo1.ClientWidth-10) / ((Abs(Memo1.Font.Height)+1))
)*2-1;
cmd:=TMamCmdThread.Create(self.Handle,OnTextChange,coordx,9000);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
cmd.Terminate;
cmd.Free;
end;
procedure TForm1.FormResize(Sender: TObject);
var coordx:integer;
begin
coordx:=trunc(
(Memo1.ClientWidth-10) / ((Abs(Memo1.Font.Height)+1))
)*2-1;
cmd.SetCoord(coordx,9000);
end;
procedure TForm1.OnTextChange({Sender: TObject;} text: String);
begin
Memo1.Lines.Text:=text;
//一番下までスクロールする
Memo1.Perform(
EM_LINESCROLL, 0, Memo1.Lines.Count);
Memo1.SelStart :=length(Memo1.Lines.Text)-1;
end;
end.
実行する
実行ボタンを押して実行します。(デバッグ実行でもOK)Edit1に「dir」と入力してエンターキーを押すと、GUI上にコマンドプロンプトの実行結果が表示されます。
「MamCmdThread.pas」ファイルのソースコード
unit MamCmdThread;
interface
uses
Winapi.Windows, Winapi.Messages,Winapi.ShellAPI,
system.sysutils,system.classes
,Vcl.Dialogs,system.strutils;
function GetConsoleWindow: HWnd; stdcall;
external 'kernel32.dll' name 'GetConsoleWindow';
function AttachConsole(ProcessId: DWORD): BOOL; stdcall;
external 'kernel32.dll' name 'AttachConsole';
type
TMamCmdThreadEnc=(
MamCmdThreadEncSJIS,
MamCmdThreadEncJIS,
MamCmdThreadEncEUCJP,
MamCmdThreadEncUTF8,
MamCmdThreadEncUTF16
);
TMamCmdThreadOnTextChange=procedure(
{sender:TObject;}text:String) of Object;
TMamCmdThread=class(TThread)
private
fWindowHandle:HWND;
fcmd:String;
fShellExecuteInfo:TShellExecuteInfo;
fBufList:TStringList;
fOnChangeTextFunc:TMamCmdThreadOnTextChange;
procedure wProc(var Msg:TMessage);
function getStdBuf():Boolean;
protected
procedure Execute; override;
public
constructor Create(
ParentHandle:HWND;
OnChangeTextFunc:TMamCmdThreadOnTextChange;
CoordX:Integer=120;CoordY:Integer=9001;
Enc: TMamCmdThreadEnc=TMamCmdThreadEnc.MamCmdThreadEncUTF16
);
destructor Destroy; override;
procedure Send(text: String);
//コードページを設定する
procedure SetEncoding(Enc:TMamCmdThreadEnc);
//コンソールのサイスを設定する
procedure SetCoord(CoordX,CoordY:Integer);
end;
implementation
uses Unit1;
var fCmdWndHandle:hwnd;
function EnumWindowCallbackFunc(h: HWND; lp: NativeInt): boolean;
var processID:Cardinal;
begin
GetWindowThreadProcessId(h, processID);
if lp=processID then
begin
fCmdWndHandle:=h;
end;
result:=True;
end;
{ TMamCmdThread }
constructor TMamCmdThread.Create(
ParentHandle:HWND;
OnChangeTextFunc:TMamCmdThreadOnTextChange;
CoordX:Integer=120;
CoordY:Integer=9001;
Enc: TMamCmdThreadEnc=TMamCmdThreadEnc.MamCmdThreadEncUTF16
);
var ret,pid:Cardinal;
SRect:_SMALL_RECT;
begin
inherited Create(False);
fCmdWndHandle:=0;
fBufList:=TStringList.Create;
FreeOnTerminate:=False;
fOnChangeTextFunc:=OnChangeTextFunc;
//commandプロンプトの実行フルパスを取得
fcmd:=GetEnvironmentVariable('COMSPEC');
fWindowHandle:=ParentHandle;
ZeroMemory(@fShellExecuteInfo,SizeOf(fShellExecuteInfo));
fShellExecuteInfo.cbSize:=SizeOf(fShellExecuteInfo);
//SEE_MASK_NOCLOSEPROCESS プロセスハンドルをクローズしない
fShellExecuteInfo.fMask:=SEE_MASK_NOCLOSEPROCESS;
fShellExecuteInfo.lpFile:=PChar(fCmd);
fShellExecuteInfo.lpVerb:=PChar('');
fShellExecuteInfo.Wnd:=fWindowHandle;
//fShellExecuteInfo.nShow:=SW_SHOW;
fShellExecuteInfo.nShow:=SW_HIDE;
ShellExecuteEx(@fShellExecuteInfo);
//成功すると33以上の値になる
if fShellExecuteInfo.hInstApp>32 then
begin
repeat
//プロセスがアイドル状態になるまで500ms待機する
ret:=WaitForInputIdle(fShellExecuteInfo.hProcess,500);
until (ret<>WAIT_TIMEOUT);
sleep(500);
//プロセスハンドルからプロセスIDを取得
pid:=GetProcessId(fShellExecuteInfo.hProcess);
//プロセスIDからウィンドウハンドルを取得
EnumWindows(@EnumWindowCallbackFunc, NativeInt(pid));
//ウィンドウハンドルが取得できなかった場合
while fCmdWndHandle=0 do
begin
fCmdWndHandle := FindWindow(PChar('ConsoleWindowClass'), nil);
sleep(100);
end;
//コンソールのスレッドにアタッチ
while AttachConsole(pid) do begin end;
//Ctrl+CとCtrl+Breakを無効にする
SetConsoleCtrlHandler(nil, true);
//コンソールウィンドウを小さく設定
SRect.Left:=0;
SRect.Top:=0;
SRect.Right:=1;
SRect.Bottom:=1;
SetConsoleWindowInfo(
GetStdHandle(STD_OUTPUT_HANDLE), True, SRect);
//コードページ設定
SetEncoding(Enc);
//コンソールの幅高さ設定
SetCoord(CoordX,CoordY);
end;
end;
destructor TMamCmdThread.Destroy;
//var pid,hPh:Cardinal;
begin
fOnChangeTextFunc:=nil;
FreeConsole();
PostMessage(fCmdWndHandle,WM_CLOSE,0,0);
fBufList.Free;
inherited;
end;
procedure TMamCmdThread.Execute;
begin
inherited;
while not Terminated do
begin
if getStdBuf() then
begin
if Assigned(fOnChangeTextFunc) then
Synchronize(
procedure
begin
fOnChangeTextFunc({nil,}fBufList.Text);
end
);
end;
sleep(250);
end;
end;
function TMamCmdThread.getStdBuf:Boolean;
var ret:Boolean;
Buf:TConsoleScreenBufferInfo;
pC:PChar;
y:integer;
ReadCount:Cardinal;
st:String;
BufList:TStringList;
flag:boolean;
Cd:TCoord;
begin
result:=False;
//バッファの情報を取得(標準エラーから読みだす)
ret:=GetConsoleScreenBufferInfo(
GetStdHandle(STD_ERROR_HANDLE),Buf);
if ret then
begin
//メモリの確保
GetMem(pC, Buf.dwSize.X*2);
BufList:=TStringList.Create;
try
flag:=False;
for y:=Buf.dwSize.Y-1 downto 0 do
begin
zeromemory(pC,Buf.dwSize.X*2);
Cd.X:=0;
Cd.Y:=y;
if ReadConsoleOutputCharacter(
GetStdHandle(STD_ERROR_HANDLE),
pC, Buf.dwSize.X, Cd, ReadCount) then
begin
st:=String(pC);
st:=LeftStr(st,ReadCount);
if trim(st)<>'' then flag:=True;
if flag then
BufList.Insert(0,st);
end;
end;
if fBufList.Text<>BufList.Text then
begin
fBufList.Text:=BufList.Text;
result:=True;
end;
finally
FreeMem(pC);
BufList.Free;
end;
end;
end;
procedure TMamCmdThread.Send(text: String);
var InputRecord:TInputRecord;
NoEW:Cardinal;
i:Integer;
ca:array of Char;
begin
text:=text+Chr(VK_RETURN);
setlength(ca,length(text));
strCopy(@ca[0],PChar(text));
for i:=0 To Length(ca)-1 do
begin
ZeroMemory(@InputRecord,sizeof(InputRecord));
InputRecord.EventType := KEY_EVENT;
InputRecord.Event.KeyEvent.wRepeatCount:=1;
InputRecord.Event.KeyEvent.UnicodeChar:=ca[i];
InputRecord.Event.KeyEvent.bKeyDown:=True;
WriteConsoleInput(
GetStdHandle(STD_INPUT_HANDLE),
InputRecord,1,NoEW
);
InputRecord.Event.KeyEvent.bKeyDown:=False;
WriteConsoleInput(
GetStdHandle(STD_INPUT_HANDLE),
InputRecord,1,NoEW
);
end;
end;
procedure TMamCmdThread.SetCoord(CoordX, CoordY: Integer);
var cod:TCoord;
begin
if CoordX<20 then CoordX:=20;
if CoordY<20 then CoordY:=20;
if CoordX>32767 then CoordX:=32767;
if CoordY>50000 then CoordY:=50000;
cod.X:=CoordX;
cod.Y:=CoordY;
SetConsoleScreenBufferSize(
GetStdHandle(STD_ERROR_HANDLE),cod);
end;
procedure TMamCmdThread.SetEncoding(Enc: TMamCmdThreadEnc);
begin
if Enc=TMamCmdThreadEnc.MamCmdThreadEncSJIS then
begin
SetConsoleCP(932);
SetConsoleOutputCP(932);
end
else if Enc=TMamCmdThreadEnc.MamCmdThreadEncJIS then
begin
SetConsoleCP(50220);
SetConsoleOutputCP(50220);
end
else if Enc=TMamCmdThreadEnc.MamCmdThreadEncEUCJP then
begin
SetConsoleCP(20932);
SetConsoleOutputCP(20932);
end
else if Enc=TMamCmdThreadEnc.MamCmdThreadEncUTF8 then
begin
SetConsoleCP(65001);
SetConsoleOutputCP(65001);
end
else
begin
//UTF-16
SetConsoleCP(1200);
SetConsoleOutputCP(1200);
end;
end;
procedure TMamCmdThread.wProc(var Msg: TMessage);
begin
end;
end.
