2017年11月
      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    
無料ブログはココログ

« ウィンドウアプリから非表示のコンソールウィンドウを開く。(その2) | トップページ | ショートカットにドロップすると、引数と作業フォルダの環境変数が置換されない。(障害?) »

2008年9月16日 (火)

ウィンドウアプリから非表示のコンソールウィンドウを開く。(その3)

SW_HIDEで非表示のコンソールウィンドウを開いて、それをAttachConsole()するサンプル。

vbc /t:winexe sample3.vb

Imports System.Diagnostics
Imports System.Threading

Public Class Class1

Private Declare Function AttachConsole Lib "kernel32" (dwProcessId As Integer) As Integer
Private Declare Function AllocConsole Lib "kernel32" () As Integer
Private Declare Function FreeConsole Lib "kernel32" () As Integer

Private Structure KEY_EVENT_RECORD
Dim bKeyDown As Integer
Dim wRepeatCount As Short
Dim wVirtualKeyCode As Short
Dim wVirtualScanCode As Short
Dim UnicodeChar As Short
Dim dwControlKeyState As Integer
End Structure

Private Structure INPUT_RECORD
Dim EventType As Short
Dim KeyEvent As KEY_EVENT_RECORD
End Structure

Private Declare Function GetStdHandle Lib "kernel32" (ByVal nStdHandle As Integer) As Integer
Private Declare Function WriteConsoleInput Lib "kernel32" Alias "WriteConsoleInputW" (ByVal hConsoleInput As Integer, ByVal lpBuffer() As INPUT_RECORD, ByVal nLength As Integer, ByRef lpNumberOfEventsWritten As Integer) As Integer

Private Const KEY_EVENT As Integer = 1s
Private Const STD_INPUT_HANDLE As Integer = -10

Public Shared Sub Main()
Dim oProcess As New Process()
oProcess.StartInfo.FileName = "cmd"
oProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
oProcess.Start()
Thread.Sleep(100)
Do While AttachConsole(oProcess.Id) = 0
  AllocConsole()
  FreeConsole()
  Thread.Sleep(100)
Loop
KeyIn("exit" + vbCr)
oProcess.WaitForExit()
MsgBox(CreateObject("WScript.Shell").Exec("fc.exe").StdErr.ReadAll())
End Sub

Private Shared Sub KeyIn(s As String)
Dim lpBuffer() As INPUT_RECORD
Dim lpNumberOfEventsWritten As Integer
Dim hConsoleInput As Integer = GetStdHandle(STD_INPUT_HANDLE)
ReDim lpBuffer(Len(s)*2-1)
For k As Integer = 0 To UBound(lpBuffer)
  lpBuffer(k).EventType = KEY_EVENT
  lpBuffer(k).KeyEvent.bKeyDown = (k + 1) Mod 2
  lpBuffer(k).KeyEvent.wRepeatCount = 0
  lpBuffer(k).KeyEvent.wVirtualScanCode = 0
  lpBuffer(k).KeyEvent.wVirtualKeyCode = 0
  lpBuffer(k).KeyEvent.UnicodeChar = AscW(Mid(s,1 + (k \ 2),1))
  lpBuffer(k).KeyEvent.dwControlKeyState = 0
Next
WriteConsoleInput(hConsoleInput, lpBuffer, UBound(lpBuffer)+1, lpNumberOfEventsWritten)
End Sub
End Class

SW_HIDEで作った非表示のコンソールウィンドウはShowWindow()で再表示できます。

コンソールアプリでは、WaitForInputIdle()が使えません。
なので、代わりに、AttachConsole()をリトライループします。
このとき、AttachConsole()のエラーをリセットするために、AllocConsole()+FreeConsole()します。

CMD.EXEを終了するために、exit{Enter}をWriteConsoleInput()します。
コードの大部分は、このための処理です。
もし、面倒なら、横着ですが、Process.Kill()すれば、簡単に終了できます。

« ウィンドウアプリから非表示のコンソールウィンドウを開く。(その2) | トップページ | ショートカットにドロップすると、引数と作業フォルダの環境変数が置換されない。(障害?) »