発掘 [AC95] Shell関数を用いて DOS のコマンドを実行する方法

#発掘する趣旨
この項目は
https://qiita.com/Q11Q/items/df5cb314635fb8d6bf14
で取り上げていたが、長くリンク先が不明だったところであるが、最近になって発見されたものである。
##重要な変更 Command.comからcmd.exe
現在（Windows 98 Meの次OS以降）はCommand.comではなく`Cmd.exe` であるが、ほぼそのまま通じる。
したがって、Access95から移植するときは`command.com` を　 `cmd.exe`に変える必要がある。
なお、32bitでしか動かない場合は, `%WIndir%\SysWOW64\Cmd.exe`となる。またSystem32起動（64bitなら64bit,
32bitなら32bit）の通常のCMDは`%COMSPEC%`を使うとよい。また、解説のようなオプションは16bit時代のもので、32ビットになってからはない。レジストリで変えられる指摘があるが、変えないほうが良いであろう。

##Access、いやOffice自体はファイルのコピーはMS-DOSを使うことを本当は意図していたのでは？
また、このサンプルは（おそらくはカレントでもなく開いてもいないしリンクもしていない）mdbファイルを別のドライブに複写している。通常だとFilessystemObjectを使うが、よく考えてみるとこれで十分である。ファイルの操作はDOSで行う。これがMicrsoroft Officeの設計思想だったといっていいと思う。これによりソフトウェア（アプリケーション）自体が軽量化される。OSまで一貫して作るMicrosoftならではの発想だろう。Shellの6についての解説もないが、これは6をデフォルトとしているためであると考えられる。これはDOS窓が最小化して起動され、フォーカスされないという意味になる。
##引数の送り仮名が引き数
また2003年頃までは引数を引き数と記述していたことも分かった。
##Microsoftは末尾のような警告をそういえばいつやめたのだろう

##そのほかShell関数の特徴
非同期実行。つまりVBAの処理はShell関数で複写が終わらなくても次の処理に移る
同期する場合はWscript.shellを使用する。(iwshRuntimeLibraryを参照設定）
ただしWscriptshell.Runは返り値がない。
http://blog.s21g.com/articles/1202
###Shell関数はExcelでもWordでもある。
なぜかShell関数の解説は複数存在する
####Shell 関数 - Access
https://support.microsoft.com/ja-jp/office/shell-%E9%96%A2%E6%95%B0-ff2e4b1b-712d-4e34-aea6-6832eadd3c63
この項目だけ見るとAccess2007以降のように読めてしまうが、実際はAccess95から存在していた。

####Docs Office VBA リファレンス 言語リファレンス リファレンス 関数 Shell
https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/shell-function

#####ちなみに、この電卓を起動するのは現在は誤り。

```vb:RunWin10_64bitCalc
Sub RunWin10_64bitCalc()
' For Windows 10 64 bit
Dim Ret
With CreateObject("WScript.Shell")
Ret = Shell(.ExpandEnvironmentStrings("%WINDIR%") & "\SysWoW64\Calc.exe") '"\SysWoW64\Calc.exe"でも可、どちらも同じものが起動する。
End With
End Sub
```
また名前付き引数なので次の書き方もできる。

```vb:RunWin10_64bitCalc2
Sub RunWin10_64bitCalc2()
Dim Ret
With CreateObject("WScript.Shell")
'Ret = Shell("%WINDIR%" & "\SysWoW64\Calc.exe")
Ret = Shell(Pathname:=.ExpandEnvironmentStrings("%WINDIR%") & "\System32\Calc.exe", windowstyle:=vbNormalNoFocus)
End With
End Sub
```

なお、環境変数をそのまま用いても展開できないらしい。このため、基本はフルパスだが、このように `With CreateObject("WScript.Shell")`を使う。`.ExpandEnvironmentStrings`も長いが、`Program FIles'やCommonはスペースが入らない。また、環境変数を使ったあとにスペースが入ってもうまくいくときがある。ただし、Surfaceは環境変数が使えないらしい。欠陥商品なので無視する。使うほうが悪い。





---

<div class="articleProperty">文書番号: 404917 - 最終更新日: 2003年5月7日 - リビジョン: 1.1</div><h1 class="title">[AC95] Shell関数を用いて DOS のコマンドを実行する方法</h1>

サポート期間が終了した「サポート技術情報」資料に関する免責事項
https://web.archive.org/web/20090524051255if_/http://support.microsoft.com/kb/404917/ja
対象製品
この記事は、以前は次の ID で公開されていました: JP404917
</div><h2 class="subTitle kb_tabs_toggle_closed" id="tocTitle" previewfixed="true"><div class="kb_tabs_toggle kb_tabs_toggle_open"></div><span><a href="javascript:void(0);">目次</a></span><div class="sectionpreview_open"></div></h2>
<ul style="display: block;"><li>概要</span></a></li>
<li>現象</span></a><ul class="tocLine"><li>■ 解説</span></a><ul class="tocLine"></ul></li><li>■ プロシージャの作成</span></a><ul class="tocLine"></ul></li><li>■ サンプルプロシージャ</span></a><ul class="tocLine"></ul></li><li>■ 注 意</span></a><ul class="tocLine"></ul></li></ul></li></ul></div><h2 class="subTitle" id="tocHeadRef">概要</span></H2>
 Shell 関数を利用して、MS - DOS の DIR、COPY、DEL 等のコマンドを実行する方法を紹介します。<br></div>
<h2 class="subTitle" id="tocHeadRef"><span>現象</a></span></h2> Shell 関数は、引き数 commandstring に拡張子が EXE、COM、BAT または PIF の実行形式のファイルを指定しなければなりません。Shell 関数を使用して MS-DOS の コマンドを実行するには、引き数 commandstring に「COMMAND.COM 」を指定します。 COMMAND.COM は、MS-DOS の内部コマンドを実行するためのパラメータを、2 つ持っています。<br><div class="kb_intextwrapper"><div class="kb_intextbody"><pre class="in_text">  COMMAND / C ： 指定されたコマンドを実行し、アプリケーションに戻ります。
                 制御がアプリケーションに戻らない場合は COMMAND.COM の実行
                 ファイルのショートカットメニューよりプロパティを選択し、
                 [プログラム] タブをポイントします。
                 [プログラム終了時にウィンドウを閉じる] チェックボックスが
                 チェックされているか確認して下さい。
  COMMAND / K ： 指定されたコマンドを実行しますが、アプリケーションには
                 戻らずに、DOS プロンプトの状態になります。DOS プロンプト
                 から戻るには、 MS-DOS の EXIT コマンドを実行します。
</pre></div>
<Font color = Gray>編注：この違いは `%COMSPEC% /C``%COMSPEC% /K` と同じである。ただしコマンドプロンプトでオプションを設定する必要はない。
https://qastack.jp/superuser/306167/how-to-prevent-the-command-prompt-from-closing-after-execution
>（16ビットMS-DOSプログラムの場合、Windowsはプロパティダイアログに[終了時に閉じる]オプションを提供しますが、これは上記の動作の例外ではありません。ウィンドウを開いたままにするのはNTVDMプロセスです。再びプログラムごとに。）</Font> </div><h3 id="tocHeadRef">■ プロシージャの作成</h3><script type="text/javascript">
                loadTOCNode(2, 'symptoms');
            </script><ol><li> データベース ウィンドウで [挿入] メニューの [モジュール] をクリックするか、 データベース ウィンドウの&lt;モジュール&gt; タブをクリックし、&lt;新規作成&gt; を クリックします。 </li><li> [挿入] メニューの [プロシージャ] をクリックするか、ツールバーの &lt;プロシージャ挿入&gt;ボタンをクリックします。 </li><li> [新規プロシージャ] ダイアログ ボックスで [種類] で "Function プロシー ジャ"を選択します。[スコープ] で "Public プロシージャ" を選択しま す。[名前] でプロシージャ名 (ShellDOS_Exit) を入力します。</li><li> 以下のサンプルプロシージャを入力します。 </li></ol> MS-DOS の COPY コマンドを実行してファイルのコピーを行い、Access 95 に制御を<br> 戻しています。以下の例では、C ドライブにある DB1.MDB ファイルを、D ドライブ の DB2.MDB ファイルにコピーします。<br>

</br>

```vb
Function ShellDOS_Exit() As Integer
'For Microsoft Access 95 And Later
' 原文は行ラベル Function,EndFunctionは半角2つ分スペース有り
      On Error GoTo ShellDOS_Exit_Err
      Dim MyCommand As String
      Dim TaskId As Long
      'コマンドを MyCommand 引き数に格納します。
       MyCommand = "COMMAND.COM /C COPY C:\DB1.MDB D:\DB2.MDB" ' Windows 95 - Windows 98 MEまで
       MyCommand = "Cmd.exe /C Dir C:\ | Clip" ' この行は原文にない。起動させるとCのルートをイミディエイトウィンドウに表示する
      TaskId = Shell(MyCommand, 6) '原文は6とのみあり、注釈はないが、定数vbMinimizedNoFocusのいみ。つまり最小化フォーカスなし
      ShellDOS_Exit = True
ShellDOS_Exit_End:
      Exit Function 'ここが真のEnd
ShellDOS_Exit_Err:
      MsgBox Error$ 'この場合の$はStringを表し、文字列が返る。ただしNullを渡すとエラーになる。つかない場合はVariantになる。現在ではほとんど使われなくなったコード。 https://vbabeginner.net/vba%E9%96%A2%E6%95%B0%E3%81%AE%E3%83%89%E3%83%AB%E3%83%9E%E3%83%BC%E3%82%AF%E3%81%AE%E6%84%8F%E5%91%B3/
      ShellDOS_Exit = False
      GoTo ShellDOS_Exit_End 'エラーを処理して真のEndに飛ばす。
End Function
```

<div><h3 id="tocHeadRef">■ 注 意</h3></div><br>
<p> MS-DOS レベルで発生するエラー (COPY コマンドの構文が間違っている等) は、Access 95でエラートラップできません。Shell 関数を実行する前に、 MS-DOS のコマンドの構文が正しく記述されているかどうかを確認する必要があります。 </p></br>

<h5>この資料は以下の製品について記述したものです。</h5><ul><li>Microsoft Access 95 Standard Edition</li></ul></div><div class="keywords"><table><tbody><tr><td class="header"><h5>キーワード:&nbsp;
                            </h5></td><td class="text">kbhowto KB404917</td></tr></tbody></table></div<div class="disclaimer text">Microsoft Knowledge Baseに含まれている情報は、いかなる保証もない現状ベースで提供されるものです。Microsoft Corporation及びその関連会社は、市場性および特定の目的への適合性を含めて、明示的にも黙示的にも、一切の保証をいたしません。さらに、Microsoft Corporation及びその関連会社は、本文書に含まれている情報の使用及び使用結果につき、正確性、真実性等、いかなる表明・保証も行ないません。Microsoft Corporation、その関連会社及びこれらの権限ある代理人による口頭または書面による一切の情報提供またはアドバイスは、保証を意味するものではなく、かつ上記免責条項の範囲を狭めるものではありません。Microsoft Corporation、その関連会社 及びこれらの者の供給者は、直接的、間接的、偶発的、結果的損害、逸失利益、懲罰的損害、または特別損害を含む全ての損害に対して、状況のいかんを問わず一切責任を負いません。（Microsoft Corporation、その関連会社 またはこれらの者の供給者がかかる損害の発生可能性を了知している場合を含みます。) 結果的損害または偶発的損害に対する責任の免除または制限を認めていない地域においては、上記制限が適用されない場合があります。なお、本文書においては、文書の体裁上の都合により製品名の表記において商標登録表示、その他の商標表示を省略している場合がありますので、予めご了解ください。</div>
Retired KB Article サポート期間が終了した「サポート技術情報」資料に関する免責事項この資料は、マイクロソフトでサポートされていない製品について記述したものです。そのため、この資料は現状ベースで提供されており、今後更新されることはありません。</div>