Home  /  TOP  /  ABOUT  /  DTS  /  COMMIC  /  DEV  /  ARC  /  KNOWLEDGE  /  EVENTS  /  FORUMS  /  FAQ  /  LINK  /  ENGISH
非同期的・同期的シェル呼び出し

この項目のより詳しい情報検索のために
キーワード:「Shell関数」,「WSH」,「同期・非同期」など
資料:MSDN,VBAヘルプ,プログラミングを扱っているHP

構築中のプログラムから外部プログラムを呼び出すにはたくさんの方法があります。
実行可能ファイルとして保存されている外部プログラムを呼び出すもっとも簡単な方法は, VB組み込みのShell関数ですが,この関数はVB6.0の段階では非同期にしか処理をしてくれません。
このテキストでは,同期的に外部プログラム呼び出しをする方法を解説しています。

※ 参考
呼び出し相手が実行可能ファイルになっていない場合や,COMとして定義されている場合は,別の方法で呼び出すこともできます。

動作確認環境
1 ウィンドウズ スクリプティング ホスト

Windows Scripting Host(以下WSH といいます。)はMS Office製品に対するVBAと同じように,WindowsOSに対するVBマクロを実行するための環境です。
WSHを利用すると,VBの構文を使用するバッチ処理をWindowsに実行させることができます
WSHは,拡張子vbsを持つテキスト形式のファイルにVBの構文を用いて処理を記述することによって利用できます。
WSHはマクロ言語なので,コンパイルは必要ありません
以下に例を挙げます。

WSHの例
Option Explicit                      ・・・@

Private Sub PopMsg()                 ・・・A
    MsgBox "WSHの呼び出し",,"WSH"

End Sub

PopMsg                               ・・・B
				

@・・・ 変数の宣言強要オプションです。
A・・・ サブプロシージャの記述です。
ここではMsgBoxの表示をしています。
B・・・ WSH特有の,スクリプトする順番を記述しています。
ここではPopMsgサブプロシージャをスクリプトしています。

このテキスト形式ファイルの拡張子を「vbs」としてダブルクリックすると,WSHがメッセージボックスを表示します。
WSHはコンパイルの必要ないマクロ言語であることから,WindowsOS上で行うファイル・フォルダ操作などのバッチ処理を簡単に共有するなどの要件に適しています。

WSHオブジェクトの利用

WSHWSHオブジェクトとしてVBからコントロールが可能なように設計されています。

WSHはWindowsのスクリプト処理を行うように設計されているので,Windowsが標準で行っているOSの基礎に関わる処理を呼び出すのに適しています。

WSHオブジェクトに含まれる様々なサブオブジェクトをOLEオブジェクト(現在はActiveXオブジェクトといいます。)として呼び出すことによって,様々なメソッドを使用することができますす。

ここではWindows シェルの機能を使うためのShellオブジェクトとネットワーク機能を使うためのNetworkオブジェクトを解説します。

WSH,VBA,VBS
WSH:Windows Scripting Host。
Windowsのシェル上で動くVBマクロで,拡張子vbsのファイルでバッチファイルスクリプトを実行できる。
また,vbsファイル,VBAを稼動させる実行エージェントでもあり,Windows98以降既定でインストールされている。
VBA:Visual Basic for Applications,または
Visual Basic Programming System Applications Edition
これら2つは同じものを示す。Office製品上で動くVBマクロ。
VBS:Visual Basic Script。
VB,VBAの技術をWeb上で使用できるようにしたもの。

これらは全てマクロ言語で,VBAに対してWSHが上位階層にあり,VBSは実行されるステージが異なるが基本的な構造は異ならない。
WSHというシステムの上でVBAが稼動する状態にあるが,完全互換でない部分があり,一部WSHでは稼動するがVBAでは稼動しない部分が存在する。
VBSにおいても,扱える変数型や組み込み関数など相違点は存在する

3 WshShellオブジェクト

WshShellオブジェクトWSHオブジェクトの中でWindowsのシェル機能を呼び出すためのサブオブジェクトです。
Shellコマンドの実行やレジストリ情報の操作などをサポートしています。

プロパティ

プロパティ名 内容
Environment システム情報を格納しているEnvironment オブジェクトを返します。
SpecialFolders Windowsの特殊フォルダのパスを返します。


メソッド

メソッド名 内容
CreateShortCut シートカットオブジェクトを作成するメソッドです。
ExpandEnvironmentStrings 環境変数を文字列として返すメソッドです。
Popup メッセージボックスをシェル上から表示するメソッドです。
RegDelete 特定のレジストリエントリを削除するメソッドです。
RegRead 特定のレジストリエントリを返すメソッドです。
RegWrite 特定のレジストリエントリを書き換えるメソッドです。
Run Shellコマンドを実行するメソッドです。

これらのメソッドを利用することによって,通常はAPI関数を呼び出さなければ処理できなかったWindowsの情報にアクセスすることができるようになります。
ここではPopupメソッドとRunメソッドについて解説します。

Popupメソッド

Windows制御のポップアップメッセージを表示します。

構文
WshShellPopup(strText[, natSecondsToWait][, strTitle], [natType]) = intButton

指定項目

指定項目 内容
strText メッセージ内容を指定します。
natSecondsToWait 0以上の値を指定すると,指定した秒数後にメッセージボックスを閉じます。
strTitle メッセージウィンドウのタイトルを指定します。指定項目はMsgBox関数と同様です。
natType メッセージボックスの形式を指定します。指定項目はMsgBox関数と同様です。


PopupメソッドはWSHを介して呼び出すことによって,呼び出し元のアプリケーションとは独立したメッセージボックスを表示することができます。
つまり,呼び出し元アプリケーションのウィンドウがフォーカスを持っていないような場合でもデスクトップの最前面にメッセージを表示することができます。
バックグラウンドで処理を実行するアプリケーションの処理終了を知らせるような場合に有効です。

Runメソッド

Shell関数に実行させることができるコマンドを実行します。

構文
WshShellRun(strCommand[, intWindowStyle][, blnWaitOnReturn])

指定項目

指定項目 内容
strCommand 実行させるコマンドを指定します。コマンド中の環境変数は自動的に展開されて実行されます。
intWindowStyle 実行させるコマンドのウィンドウ状態を指定します。以下の定数値を指定します。
SW_HIDE 非表示のウィンドウ
SW_MINIMIZE 最小化表示をして,Z-Orderが次のウィンドウにフォーカスを送る
SW_RESTORE 位置と大きさを保存するウィンドウに表示
SW_SHOW 現在の位置と大きさでウィンドウを表示
SW_SHOWMAXIMIZED 最大化したウィンドウを表示
SW_SHOWMINIMIZED 最小化したウィンドウを表示
SW_SHOWMINNOACTIVE 最小化したウィンドウとして表示して,フォーカスの移動は行わない
SW_SHOWNA 現在のウィンドウステータスで表示して,フォーカスの移動は行わない
SW_SHOWNOACTIVATE 直前のウィンドウステータスで表示して,フォーカスの移動は行わない
SW_SHOWNORMAL 通常のウィンドウとして表示する 既定値
blnWaitOnReturn Runメソッド以降の処理を呼び出しコマンドの終了まで待つかどうかのフラグを設定します。

Runメソッドは,コマンドやウィンドウを指定して新しいプロセスを作り出すメソッドであり,Shell関数と同様の機能を提供します。
WshShell.Runでは,Shell関数の最大の弱点である起動先プログラムの終了の監視を標準で補っています。
また,起動先のエラーコードを直接に戻り値として受け取ることもできるようになっています。(詳細については後述)

4 WshNetworkオブジェクト

WshShellオブジェクトはWindowsシェル機能を呼び出すためのオブジェクトでしたが,WindowsのNetwork機能を呼び出すオブジェクトがWshNetworkオブジェクトです。
ネットワークコンピューター名の取得や,各種リモートリソースの設定操作などをサポートしています。

プロパティ

プロパティ名 内容
ComputerName コンピューター名を返します。
UserDomain ユーザーの所属するドメイン名を返します。
UserName ユーザー名を返します。

メソッド

メソッド名 内容
AddPrinterConnection リモートプリンターを指定したローカル名で追加します。
EnumNetworkDrives WshCollectionオブジェクトとしてネットワークドライブ一覧を返します。
EnumPrinterConnections WshCollectionオブジェクトとしてプリンター一覧を返します。
MapNetworkDrive 指定した共有リソースを,指定したローカル名で追加します。
RemoveNetworkDrive 指定したネットワークドライブとのコネクションを破棄します。
RemovePrinterConnection 指定したリモートプリンターとのコネクションを破棄します。
SetDefaultPrinter 指定したリモートプリンターをデフォルトプリンターに指定します。

これらのメソッドを利用することによって,ネットワークの設定操作をオブジェクト上から実行することができるようになります。
ここでは,ComputerNameとUserNameについてとりあげます。

ComputerNameプロパティ

構文
WshNetworkComputerName

UserNameプロパティ

構文
WshNetworkUserName

5 WshShell.Popupの例によるWSH呼び出しの基礎

前述のとおりPopupメソッドはWindows Shellにメッセージを表示させることから,メソッド呼び出し元のアプリケーションから独立したメッセージボックスを表示することができます。
これはアプリケーションの制御下でメッセージを表示するMsgBoxと対照的です。
Popupメソッドは,呼び出し元アプリケーションがフォームを持たない場合や,通常フォーカスを持たないバックグラウンドの作業を行うアプリケーションで利用するのが効果的です。

図 5-1 MsgBoxの呼び出し
図 5-1 MsgBoxの呼び出し

図 5-2 Popupの呼び出し
図 5-2 Popupの呼び出し

WSHオブジェクトはメソッドを呼び出すためのオブジェクトのインスタンスを,CreateObject関数を使用して設定することで利用できるようになります。
これはWshShellについてもWshNetworkについても共通です
共通の一連の流れについてPopupメソッドのコーディング例で解説します。

ソース
Dim WshShell As Object   ・・・@

Set WshShell = CreateObject("Wscript.Shell")   ・・・A
WshShell.Popup "アプリケーションとは独立したPopup", , "別制御のウィンドウ", vbInformation   ・・・B

If Not(WshShell Is Nothing) Then Set WshShell = Nothing   ・・・C
				

ソースの解説

@・・・ オブジェクトの宣言
WshShellを格納するオブジェクト変数を宣言します。

A・・・ オブジェクトの動的確保

WshShellにSetステートメントとCreateObject関数を使用してWshShellオブジェクトのインスタンスを動的に確保します。

構文(CreateObject)
CreateObject(class

指定項目

引数classappname.objecttypeの形式で文字列として指定します。

指定項目 内容
appname 必須項目です。
オブジェクトを提供しているアプリケーション名を指定します。
objecttype 必須項目です。
作成するオブジェクトの種類またはクラス名を指定します。

WshShellの場合はclassを"Wscript.Shell"に,WshNetworkの場合は"Wscript.Network"にそれぞれ指定します。

B・・・ Popupメソッドの呼び出し
メソッドの呼び出しを行います。
ここでは図5-2のように動作する呼び出しを行っています。

C・・・ 資源の解放
使い終わったWshShellオブジェクト変数をSet Nothingによって解放します。

オブジェクト変数の確保 → CreateObjectによるオブジェクトインスタンスの確保 → オブジェクトの利用 → 資源の解放という一連の流れは,通常のOLEオートメーションと同様です。
注意すべき点は,ShellオブジェクトとNetworkオブジェクトによって,objecttypeを変える必要があるということだけです。

6 WshNetworkを利用したユーザー名の取得の例

次に,WshShellの呼び出しと同様の手順でComputerNameプロパティとUserNameプロパティを利用した,コンピューター名とユーザー名の取得手順を解説します。

ソース
' 変数宣言
Dim WshNetwork As Object    ・・・@
Dim CompName As String
Dim UsrName As String

' WshNetworkの実行
Set WshNetwork = CreateObject("WScript.Network")    ・・・A
CompName = WshNetwork.ComputerName
UsrName = WshNetwork.UserName

If Not(WshNetwork Is Nothing)  Then Set WshNetwork = Nothing    ・・・B
				

ソースの解説

@・・・ 変数の宣言
WshNetworkオブジェクトを格納するオブジェクト型変数と,それぞれの戻り値を格納するString型変数を宣言します。

A・・・ WshNetworkのプロパティの利用
プロパティのComputerNameによってローカルマシン名を,UserNameによってカレントユーザー名をそれぞれの変数に格納します。

B・・・ 資源の解放
Set Nothingによってオブジェクトの解放を行います。

7 非同期的なShell関数

Shell関数はVBの組み込み関数として外部実行可能ファイルの呼び出しを行える有用な関数ですが,処理を非同期に行う関数です。
つまり,Shell関数に呼び出されたプログラムの終了を待たずにShell関数の次のステートメントは実行されてしまいます。

Shell関数は戻り値として呼び出し先プログラムが問題なく実行された場合にバリアント型(内部処理形式Double)のタスクIDを返します。
実行がエラーとなった場合は0を返します。
この戻り値を受け取った時点でShell関数の実行は終了となって,次のステートメントに処理が移ることになります。
よって,Shell関数は呼び出し先プログラムの起動までと同期することになり,呼び出し先プログラムの終了や,結果の返却を待って同期することありません。

このことから,メインプログラムから特定のURLを表示するためにIEを起動するなどの,呼び出し先から処理結果等を受け取らない処理には適しています。
しかし,コマンドラインプロンプトのコマンド呼び出しなど,呼び出し先プログラムの処理終了を待って次の処理を行いたいような場合には,不適切な動作をすることになると言えます。

(例)
dblShellStat = Shell("c:\winnt\system32\cmd.exe _     ・・・@
                 /C COPY /B c:\test.txt c:\test2.txt", vbHide)

Open "c:\test2.txt" For Input As #1    ・・・A
				

@・・・コマンドプロンプトの呼び出し
cmd.exe(コマンドプロンプト)の呼び出し1が終了し,タスクIDが戻された時点で処理は終わる

A・・・本来は同期させたい次のステートメント
コマンドプロンプト内のCOPYの処理が終わっていない可能性があるため,Openステートメントがエラーを起こす

cmd.exe(コマンドプロンプト)は /Cスイッチにより,以降に与えられたコマンドの終了と同時にcmd.exe自身も終了する。
反対に /Kスイッチにより,コマンド終了後もcmd.exeを終了させないことができる。
同期処理をする場合は,/Cスイッチを通常は使用する。

この例では,コマンドプロンプトのCOPYコマンドを使用してファイルのコピー移動を行った後,コピー先のファイルにOpenステートメントによってアクセスしようとしています。
しかし,COPYコマンドの終了を待たずにOpenが実行されてしまいます
このために,Open先のファイルが存在しない事になり,エラーとなる場合が出てきてしまいます。
このような場合に,COPYコマンドの処理が終了するのを待って,Openステートメントに移行する処理が必要になります。

8 Shell関数の同期化のための方法

以上より,Shell関数は起動先プログラムの起動時点までを請け負い,その起動したプログラムのタスクIDを返却します。
よって,この後のステートメントを起動されたプログラムの終了後に実行するためには,この起動先プログラムの終了を監視すればいいことになります。
起動されたプログラムの状態を監視して終了を待って,終了した場合にShell関数の次のステートメントに制御を移せばよいということです。

前述のCOPYコマンドなどの比較的短期間で終了するようなコマンドに対しては,単純な形で,Shell起動後2秒待って処理を継続するなどとすることで対応することもできます。
しかしコマンドの処理時間はOSのタスク処理能力に依存してしまうため,状況によってまちまちの処理時間になり,この方法は危険です。
そこで,Shell関数が戻すタスクIDを元に,このIDを持つタスクの終了を監視することにします。

終了状態を監視するAPI関数としてkernel32 APIに属するGetExitCodeProcessがあります。
GetExitCodeProcessは指定されたプロセスの終了状態を取得するAPI関数です。
これを利用すると,プロセスが継続している状態(STILL_ACTIVE)の評価ができます。
この関数を利用したループを使用して,プロセスの継続が途切れた時点でShell起動先のプログラムが終了したと判断できます。

このGetExitCodeProcessを使用するために,監視する対象プロセスのハンドルが必要になる。
この為,既存プロセスのIDからハンドルを取得するOpenProcess関数を併せて使用します。
OpenProcess関数もkernel32 APIに属する関数で,オープンするプロセスID,アクセスフラグ,継承フラグを指定することによって,指定されたプロセスのオープンハンドルを返します。

これらから,終了を監視する手順は,

という手順となります。

プロセス:
1つあるいは複数のスレッドと,メモリ上にあるプログラムのコード,データなどのリソースから構成される実行単位。
代表的なプログラムリソースは,オープンされているファイル,セマフォ,動的に割り当てられたメモリなど。

スレッド:
プログラムの実行中にWin32によってスケジュール管理される最小の実行単位。
スレッドは,スタック,CPUレジスタの状態,システムスケジューラの実行リストのエントリで構成される。
それぞれのスレッドはプロセスの全てのリソースを共有する。

ハンドル:
OSによって定義される,重複しない整数値。
フォーム,コントロールなどのオブジェクトを識別したり,切り替えたりするために,プログラムによって使用される。

次は,kernel32 APIを使用したShell同期の為のサブルーチンSyncWait( )のソースと解説です。

ソース
'API宣言    ・・・@
Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _ 
                                           ByVal bInheritHandle As Long, _ 
                                           ByVal dwProcessId As Long) _ 
                                           As Long
Declare Function GetExitCodeProcess Lib "kernel32" (ByVal hProcess As Long, _ 
                                                  lpExitCode As Long) _ 
                                                  As Long

'フラグ定数宣言    ・・・A
Const PROCESS_QUERY_INFORMATION = &H400&
Const SYNCHRONIZE = &H100000
Const STILL_ACTIVE = &H103

Private Sub Main( )    ・・・B
    'Shell呼び出し
    dblShellStat=Shell("c:\winnt\system32\cmd.exe _ 
            /C COPY /B c:\test.txt c:\test2.txt", VBHide)

    '同期処理
    Call SyncWait(dblShellStat)

    '同期後処理
    Open "c:\test2.txt" For Input As #1

End Sub

Private Sub SyncWait(ByVal dblStatShell As Double)
'変数宣言    ・・・C
    Dim lngProcHdle As Long ' 既存プロセスのハンドル格納変数
    Dim lngExitStat As Long ' 関数終了状態戻り値格納変数
    Dim lngExitCode As Long ' プロセス終了状態格納変数

    ' Shellプロセスハンドル取得    ・・・D
    lngProcHdle = OpenProcess(PROCESS_QUERY_INFORMATION Or SYNCHRONIZE, _ 
        True, dblStatShell)

    ' 終了状態評価ループ
    Do    ・・・E
        lngExitStat = GetExitCodeProcess(lngProcHdle, lngExitCode)    ・・・F

    Loop While lngExitCode = STILL_ACTIVE
End Sub
				
			

ソースの解説

@・・・ API宣言
APIの宣言規則によりkernel32 上のGetExitCodeProcess関数とOpenProcess関数の宣言を行う。

A・・・ フラグ定数宣言
OpenProcess関数のdwDesiredAccess(アクセスフラグ)に使用する定数を宣言する。
GetExitCodeProcess関数で監視するSTILL_ACTIVE定数を宣言する。

B・・・ メインサブルーチンの構造
Main( )ではShellの呼び出しと,呼び出したShellの戻り値,タスクIDをSyncWait( )に値渡しで渡す呼び出しを行う。
SyncWait( )の処理からMainに戻る際には,同期が終了しているので,そのまま次の処理を記述することができる。

C・・・ SyncWait( )内部変数の宣言
lngProcHdle 既存プロセスのハンドル格納変数。
OpenProcessの戻り値を格納して,GetExitCodeProcessで使用するための変数。
lngExitStat 関数終了状態戻り値格納変数。
GetExitCodeProcess関数自体の終了コードが格納される変数 特にエラーが起こらない場合,使用しないので形だけの変数。
lngExitCode プロセス終了状態格納変数。
終了状態が実際に格納される変数でこの変数とSTILL_ACTIVEとを評価して,プロセスの状態を監視する。
D・・・ OpenProcessによる起動先プログラムのハンドル取得
構文

lngProcHdle = OpenProess(ByVal dwDesiredAccess As Long , ByVal blnInheritHandle As Long , ByVal dwProcessId As Long

戻り値 ハンドル = OpenProessアクセスフラグ継承フラグプロセスID

戻り値 ハンドル GetExitCodeProcessに渡すためのハンドルを格納するOpenProcessを使用する主目的。
アクセスフラグ PROCESS_QUERY_INFORMATION(GetExitCodeProcessを使えるようにするフラグ)とSYNCHRONIZE(待機関数でプロセスのハンドルが使えるようにするフラグ)とのOR演算(ビットごとの組み合わせ)を指定する。
継承フラグ 返されたハンドルを,プロセス作成時に新しいプロセスに継承させるかどうかを指定。
ここではTrueを設定する。
プロセスID オープンするプロセスのプロセスIDを指定。
つまり,ここではShell関数の戻り値を値渡ししたdblStatShellを渡す。



E・・・ Doループ
後置条件にWhile lngExitCode = STILL_ACTIVE を置くことによって,終了状態がSTILL_ACTIVEの間,つまりShell起動先プログラムが稼動している間,ループを続けることによって次の処理に移ることを防いでいる。
SyncWait( )の要だが,高速にループを回転させるためにCPU負荷がかかり,処理時間のかかるコマンドなどを起動した場合,フォーム内のコントロールの描画が停止したような状態に陥る場合がある。
その際はタイマーイベントなどと組み合わせて,終了状態の評価に遅延を設けるなどすることが考えられる。
または,プログレスバーの描画などと組み合わせることによって,応答状態の表示と共にフォームのリフレッシュをするなどして,視覚的な応答状態を保つことも考えられる。

F・・・ GetExitCodeProcessによる終了状態評価
構文

lngExitStat = GetExitCodeProcess(ByVal hProcess As Long , ByRef lpExitCode As Long)

戻り値 関数状態 = GetExitCodeProcessプロセスハンドル終了状態

戻り値 関数状態 GetExitCodeProcess関数の状態が返される。
エラーの場合0が返され,エラーでない場合は0以外の値が格納される。
プロセスハンドル 終了状態を取得するターゲットプロセスのハンドルを値渡しする。
この場合,OpenProcessによって取得した,Shell起動先プログラムのハンドルを指定する。
終了状態 終了状態を格納するDWORD型変数へのポインタを指定する。
VBでは,Long型変数を参照渡しすることで対応できる。
関数成功時,この変数に終了状態が格納される。

終了状態の取得が済んだら,変数の値をSTILL_ACTIVEと比較し,継続以外の状態が格納されていたらループを抜け,次の処理へと移す。

以上のように,API関数の呼び出しによって,呼び出されたプロセス本体の継続状態を正確に評価することができます。
しかし,SyncWait( )では,比較的高度なkernel32 APIを利用してプロセスの状態を評価する必要があること。
Doループをまわし続けるため,タスク処理に余力のないシステムで稼動させると画面描画が停止すること。
これら2つの問題点をDoEventsステートメントの適切な追加などによって解消しなければならないという課題があります。

WSHによる同期的Shellの実現

これまでの構文の箇所で解説したとおり,WshShell.Runには呼び出しコマンドの終了待ちを行うか行わないかを制御するスイッチが用意されています。
これを利用することによって,上の例の処理を実現できる同期的なShell呼び出しを行うことができます。

ソース
'変数宣言
Dim WshShell As Object    ・・・@
Dim rtnCode As Long

'定数宣言
Const SW_HIDE = 0     ・・・A

Set WshShell = CreateObject("Wscript.Shell")    ・・・B

rtnCode = WshShell.Run("c:\winnt\system32\cmd.exe _ 
    /C COPY /B c:\test1.txt c:\test2.txt", SW_HIDE, True)    ・・・C

If rtnCode <> 0 Then Error rtnCode    ・・・D

Open "c:\test2.txt" For Input As #1    ・・・E

If Not(WshShell Is Nothing) Then Set WshShell = Nothing    ・・・F
				

ソースの解説

構文

WshShellには呼び出し先のエラーコードを戻り値として受け取る場合の構文と,エラーコードを受け取らない構文の両方が用意されています。

a エラーコード返却なし

WshShellRun(strCommand[, intWindowStyle][, blnWaitOnReturn])

b エラーコード返却あり

rtnCodeWshShellRun(strCommand[, intWindowStyle][, blnWaitOnReturn])

Shell関数とRunメソッドとの戻り値の実装上の違いから,コード0の扱いについて差が生まれているので注意が必要です。
Shell関数はDouble型のタスクIDを戻り値とすることから,コード0をエラーとしているのに対し,
Runメソッドは任意のエラーコードを戻り値とすることから,VB標準エラーコードと同様にコード0を正常終了としています。

表 7-1 Shell関数とWshShellの戻り値の違い
戻り値 エラー発生時 正常終了時
Shell関数 タスクID 戻り値 0 呼び出し先のタスクID
WshShell エラーコード 任意のエラーコード 戻り値 0(正常終了)

@・・・ 変数宣言
WshShellとして,WshShellオブジェクトを格納するオブジェクト型変数と,rtnCodeとしてRunメソッドの戻り値を格納するLong型変数を宣言します。

A・・・ 定数宣言
Runメソッド内で使用するウィンドウスタイル用の定数を宣言します。

B・・・ オブジェクトの動的確保
WshShellオブジェクトのインスタンスをCreateObject関数によって動的に確保します。

C・・・ コマンドの実行
WshShellにRunメソッドを適用して,コマンドを実際に実行します。
ここでは,例に挙げたShell関数で使用したものと同じコマンドを使用しています。
ウィンドウスタイルをSW_HIDEとした上でcmd.exeのスイッチを /Cによって同期終了させているので,コマンドがバックグラウンドで実行されて終了します。
また,終了待ちフラグをTrueにしているので,cmd.exeの終了を待って,cmd.exeからのエラーが戻り値としてrtnCodeに設定されます。

D・・・ 擬似エラーの生成
Runメソッドの戻り値として受け取ったrtnCodeを使用して,0(正常終了)以外の場合にErrorステートメントによって擬似的なエラーを発生させています。

E・・・ 次ステートメントの実行
Runメソッドの利用でcmd.exeの終了待ちを行っているので,Shell関数の際と同じOpenステートメントの記述をしても,ファイル不在エラーなどが起こることはありません。

F・・・ 資源の解放
Set Nothingによってオブジェクト変数の解放を行います。

このようにWshShellでは終了待ちフラグ(blnWaitOnReturn)の変更によって,非同期処理・同期処理のスイッチングを行うことができます。
また,ウィンドウスタイルの実装もShell関数より選択肢が幅広くなっています。

(付記) strCommandの記述

WshShell.Runの引数strCommandに送ることができるものは

このような文字列はコマンドとして送出して同期することができます。
以下にいくつかの例を挙げます。

コマンドプロンプト COPYによるテキストファイルの結合コマンド

rtnCode = WshShell.Run("c:\winnt\system32\cmd.exe /C " & _
" ""COPY /B c:\test1.txt + c:\test2.txt c:\test3.txt"" ", 0, True)

cmd.exeには /Cスイッチを指定し,コマンド終了時にプロンプトを終了させます。
cmd.exeのコマンド引数(実際のコマンド)はダブルクォーテーションで囲うので,VBの文法にしたがって,ダブルクォーテーションの2重表記によるエスケープで囲い,コマンドを記述します。

Path %winnt% に格納されている実行可能ファイルの呼び出し

rtnCode = WshShell.Run(" test.exe", 1, True)

パスが通ったフォルダ(例えば,標準ではc:\winnt\ やc:\winnt\system32\ など。)にターゲットファイルが格納されている場合は,ファイル名を指定するのみで呼び出しが可能です。

Path %winnt% に格納されているxlsファイルの呼び出し

rtnCode = WshShell.Run(" test.xls", 1, True)

パスが通ったフォルダに格納されてさえいれば,環境変数に登録された正規の実行可能形式でなくても,ファイルタイプとして実行先アプリケーションが登録されているファイルは登録先親アプリケーションを介して呼び出されます。
ただし,この場合の同期は親アプリケーションの終了と同期します。
例えば,xlsブックを呼び出した場合,ブックを閉じてもWshShellの実行は終了しないで,親アプリケーションであるExcelの終了と共にWshShellが終了することになります。

ノートパッドの呼び出し

rtnCode = WshShell.Run("notepad", 1, True)

Windows標準アクセサリであるNOTEPAD.exeはID名であるNOTEPADのみの指定で起動することができます。
"notepad" を"notepad.exe" とした場合は,%winnt%フォルダに存在する実行可能ファイルを探し当てて実行する形となります。

Excel親アプリケーションの呼び出し

rtnCode = WshShell.Run("Excel", 1, True)

Excel,Word,ACCESSなども標準アクセサリなどと同じように,アプリケーションIDのみによって起動できます。
ExcelのIDは"Excel" ですが,Wordは"WINWord",ACCESSは"MSACCESS"など,実際の実行ファイルのファイル名がIDになっている点に注意しなければなりません。

ファイル名のフルパス指定

通常のファイルのフルパス指定による呼び出しのほかに,以下のような特例が考えられるので列挙しておきます。

空白を含むフルパスの呼び出し

"My Document"のように空白を含むファイルパスは,Shell上ではコマンドの区切りと認識さてしまい,呼び出しに失敗します。

rtnCode = WshShell.Run("c:\My Documents\Local\test.exe ", 1, True)

これを呼び出すためにはコマンド文字列をコマンド内で一つなぎの文字列として認識させるために,ダブルクォーテーションをネストさせる必要があります。

rtnCode = WshShell.Run("""c:\My Documents\Local\test.exe """, 1, True)

ダブルクォーテーションが3つになっているのは,最外の「" 〜 "」内部でダブルクォーテーションを使用するために,「"」を2つ続けて記述してエスケープしなければならないVBの仕様です。

cmd.exeを介して間接的に起動先ファイルを呼び出す

コマンドプロンプト内での直接実行機能を利用して,ターゲットファイルを起動します。

rtnCode = WshShell.Run("c:\winnt\system32\cmd.exe /C ""c:\test.exe""", 0, True)
rtnCode = WshShell.Run("c:\winnt\system32\cmd.exe /C ""c:\test.xls""", 0, True)

cmd.exeがバックグラウンドで起動し,呼び出した先のファイルが終了するまでcmd.exeの起動状態が継続します。
結果的に呼び出し先ファイルの終了と同期をとることができます。