CMD.exe Echo %Programfiles%とEnviron関数の結果が異なる
CMD.Exe コマンドプロンプトを起動する。
Echo %Programfiles%
と入力
すると
C:\Program Files\
が返ってくる。
間違っても
C:\Program Files(x86)\
ではない。
ところが、32bit OfficeのVBAでEnviron関数で
Environ("Programfile")
とすると、
C:\Program Files(x86)\
が返ってくる。つまり同じ環境変数が違う結果を返してしまうのである。
公式のEnvironの解説には記述がない
Docs Office VBA リファレンス 言語リファレンス リファレンス 関数 Environ
オペレーティング システムの環境変数に関連付けられた String を返します。 Macintosh では使用できません。
構文
Environ( { envstring | number } )
Environ 関数の構文には、次の名前付き引数があります。
パーツ | 説明 |
---|---|
envstring | 省略可能。 環境変数の名前を含む文字列式です。 |
number | 省略可能。 環境文字列テーブル内の環境文字列の数値順序に対応する数式です。 number 引数には任意の数式を使用できますが、評価される前に整数に丸められます。 |
解説
envstring が環境文字列テーブル内にない場合は、長さ 0 の文字列 ("") が返されます。 それ以外の場合、Environ は指定された envstring に割り当てられているテキストを返します。つまり、環境文字列テーブルでその環境変数の等号 (=) の後にあるテキストを返します。
number を指定した場合は、環境文字列テーブルでその数値位置にある文字列が返されます。 この場合、Environ は envstring を含むすべてのテキストを返します。 指定された位置に環境文字列がない場合、Environ は長さ 0 の文字列を返します。
例
この例では、Environ 関数を使用して、PATH ステートメントのエントリ番号と長さを環境文字列テーブルから提供しています。 Macintosh では使用できません。
Sub Test_Sample()
Dim EnvString, Indx, Msg, PathLen ' Declare variables.
Indx = 1 ' Initialize index to 1.
Do
EnvString = Environ(Indx) ' Get environment
' variable.
If Left(EnvString, 5) = "PATH=" Then ' Check PATH entry.
PathLen = Len(Environ("PATH")) ' Get length.
Msg = "PATH entry = " & Indx & " and length = " & PathLen
Exit Do
Else
Indx = Indx + 1 ' Not PATH entry,
End If ' so increment.
Loop Until EnvString = ""
If PathLen > 0 Then
MsgBox Msg ' Display message.
Else
MsgBox "No PATH environment variable exists."
End If
End Sub
正直この解説は何一つ書いていないのに等しい。
まず環境文字列テーブルが意味不明である。
ここで言いたいのはEnviron(20)がPathらしいが、その長さが表示されるだけでしか言っていない。
上の公式のサンプルは20にいかずに終了するし、LCaseをかけないとエラーになる。
そこで、次のようにまともなPath数をカウントするようにしてみた。
確かにEnvironは番号でも出せる。しかしどういう法則で番号がついているのかもわからない。
Sub test4()
Dim EnvString, Indx, Msg, PathLen ' Declare variables.
Dim ar
Indx = 1 ' Initialize index to 1.
Do
EnvString = Environ(Indx) ' Get environment variable.
If LCase(Left(EnvString, 5)) = "path=" Then
'If Left(EnvString, 5) = "PATH=" Then ' Check PATH entry.
PathLen = Len(Environ("PATH")) ' Get length.
ar = Split(Environ("PATH"), ";")
Msg = "PATH entry = " & Indx & " and length = " & PathLen & " // Path Counts = " & UBound(ar) + 1
Exit Do
Else
Indx = Indx + 1 ' Not PATH entry,
End If ' so increment.
Debug.Print Indx
Loop Until EnvString = ""
If PathLen > 0 Then
MsgBox Msg ' Display message.
Else
MsgBox "No PATH environment variable exists."
End If
End Sub
マイクロソフトの解説は全く役に立たず、何を考えているのかさっぱりわからない。Environ関数を一切理解できていないのではないか、というレベルである。さらにこの点についての解説は一切説明がなく、謎を深めるだけである。強いて言うなら、環境変数には番号があるということらしい。
想像上の環境文字列テーブル
たぶんこんなものがどこかにあるらしい。
サンプルからこのテーブルは1からはじまることがわかる。0を代入するとエラーになった。
Windows10だと44番あたりまで行くらしい。
EnvironにIndex番号を入れると、環境変数=値で出力される。
Index | 環境変数 | 環境変数と値 |
---|---|---|
1 | %ALLUSERSPROFILE% | ALLUSERSPROFILE=C:\ProgramData |
20 | %Path% | Path=C:\Program Files... |
StackOverFlowに答えがあった
Get path of “Program files” folder that contains 32-bit programs
2012年にStackOverflowに回答が書いてあった。
この場合、確実にC:\Program files\
を得るためには
Environ("ProgramW6432")
一般的な注意点ではないようだ。
環境変数の名前は、環境によって、大文字小文字が異なる場合があります。 ここでは、どちらで記述されていても対応できるように、UCase関数を利用して、取得した環境変数の文字列を大文字に変換してから文字列"USERNAME"と比較しています。
あとCMD(コマンドプロンプト)では%ProgramfilesW%
と表記するが、VBAのEnviron関数は%はいらない。
VISTAまでにはない
Docs > Previous Versions Windows > Windows Vista > Windows Vista Technical Library > Roadmap > Deployment User State Migration Tool 3.0 > XML Reference > Recognized Environment Variables 07/25/2008
この当時の記事にはこの環境変数は存在しない。
VBAだけではなく、ほかでも起きる
Finding the ProgramFiles64 Folder in a 32 Bit App 2019
These days you'd be hard pressed to find a 32 bit version of Windows. Most people run 64 bit versions. So if you run a 32 bit application on a 64 bit version of Windows you can use the following code to get the 'real' Program Files folder:
最近(編注:記事の時点は2019年2月)では、32ビットバージョンのWindowsを見つけるのは難しいでしょう。ほとんどの人は64ビットバージョンを実行しています。したがって、64ビットバージョンのWindowsで32ビットアプリケーションを実行する場合は、次のコードを使用して「実際のProgram Filesフォルダー」を取得できます。
Windwos 7 64bit .Net Flamework4.0までにはない
Top > プログラミング > .NET Tips > ファイル、フォルダ dobon.net
この表にもない。
発見の端緒
なぜこのような地雷を踏んだかというと、32 bit Office のVBAで 64bitの7zipで圧縮するために
CreateObject("WScript.Shell").Run Environ("Programfiles")
としていたのがきっかけ。
これをイミディエイトで見ると(x86)になっていた。
公式の解説はこれか
Environment Variables
When a 32-bit process is created by a 64-bit process, or when a 64-bit process is created by a 32-bit process, WOW64 sets the environment variables for the created process as shown in the following table.
32ビットプロセスが64ビットプロセスによって作成される場合、または64ビットプロセスが32ビットプロセスによって作成される場合、WOW64は、次の表に示すように、作成されたプロセスの環境変数を設定します。
として64bitでも32bit でも ProgramfilesW6432は常にProgramaFileであるとしている。
しかしProgramfile自体は指す場所が64bitと32bitでは変わってしまう。
しかしここまでみていくと過去にCScriptやWScriptで64bit起動を強制するときにこういう表記を見た気がする。
2010年には存在している
第3回 アプリケーションの互換性
Windows 7 64bit版の表にはある。環境変数を拡張したことによって追加されたらしい。
2016年までは知られていない
x86環境で実行すると、環境変数
%ProgramFiles(x86)%
が定義されていないためず'%ProgramFiles(x86)%'という文字列がそのまま入ってきてしまいます。
普通はそんなパスは存在しないので、空の場合は改めて%ProgramFiles%を取得しています。
StackOverFlowもそういう補足があるが一般的にはこの環境変数が指す意味は分かりづらいため、使われていない。
結 論
Windows 7以降、絶対にProgram Filesフォルダに行きたい場合は%ProgramfilesW6432%
を使うこと。
VBAで64bit フォルダにアクセスするときもこれを用いる。
また、この値は2010年頃Windows7 64bit拡張によって導入されたらしい。このため、設定されていないOSのバージョン(Win 7 32bitやVista以前)がある。
このため、後方互換に留意する必要がある場合には空文字の場合の処理が必要。
ということでいいようだ。