今日のトリビア 64bit Windows10 の32bit OfficeVBAではEnviron("Programfiles")はX86になる

# 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](https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/environ-function)
オペレーティング システムの環境変数に関連付けられた String を返します。 Macintosh では使用できません。
#### 構文

`Environ( { envstring | number } )`

Environ 関数の構文には、次の名前付き引数があります。

|パーツ | 説明|
|:---:|:------|
|envstring |省略可能。 環境変数の名前を含む文字列式です。|
|number |省略可能。 環境文字列テーブル内の環境文字列の数値順序に対応する数式です。 number 引数には任意の数式を使用できますが、評価される前に整数に丸められます。|

#### 解説
 envstring が環境文字列テーブル内にない場合は、長さ 0 の文字列 ("") が返されます。 それ以外の場合、Environ は指定された envstring に割り当てられているテキストを返します。つまり、環境文字列テーブルでその環境変数の等号 (=) の後にあるテキストを返します。
 number を指定した場合は、環境文字列テーブルでその数値位置にある文字列が返されます。 この場合、Environ は envstring を含むすべてのテキストを返します。 指定された位置に環境文字列がない場合、Environ は長さ 0 の文字列を返します。
#### 例
 この例では、Environ 関数を使用して、PATH ステートメントのエントリ番号と長さを環境文字列テーブルから提供しています。 Macintosh では使用できません。

```vb
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は番号でも出せる。しかしどういう法則で番号がついているのかもわからない。

```vb
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](https://stackoverflow.com/questions/8701967/get-path-of-program-files-folder-that-contains-32-bit-programs)
2012年にStackOverflowに回答が書いてあった。
この場合、確実に`C:\Program files\`を得るためには
`Environ("ProgramW6432")`

## 一般的な注意点ではないようだ。
[環境変数の値を取得する（Environ関数）](https://www.moug.net/tech/exvba/0150086.html)
>環境変数の名前は、環境によって、大文字小文字が異なる場合があります。 ここでは、どちらで記述されていても対応できるように、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](https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc749104(v=ws.10)?redirectedfrom=MSDN)
この当時の記事にはこの環境変数は存在しない。

## VBAだけではなく、ほかでも起きる
[Finding the ProgramFiles64 Folder in a 32 Bit App 2019](https://weblog.west-wind.com/posts/2019/Feb/05/Finding-the-ProgramFiles64-Folder-in-a-32-Bit-App)
>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](https://dobon.net/vb/dotnet/file/getfolderpath.html)
この表にもない。

## 発見の端緒
なぜこのような地雷を踏んだかというと、32 bit Office のVBAで 64bitの7zipで圧縮するために
`CreateObject("WScript.Shell").Run Environ("Programfiles")` としていたのがきっかけ。
これをイミディエイトで見ると(x86)になっていた。

## 公式の解説はこれか
[開発者向けWindows > Apps > Win32 > Get Started Programming Guide for 64-bit Windows > WOW64 Implementation Details](https://docs.microsoft.com/ja-jp/windows/win32/winprog64/wow64-implementation-details)
>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回　アプリケーションの互換性](https://www.atmarkit.co.jp/ait/articles/1007/29/news109_3.html)
Windows 7 64bit版の表にはある。環境変数を拡張したことによって追加されたらしい。

## 2016年までは知られていない
[Windowsバッチファイルにおける x86/x64判定](https://qiita.com/Marukaziler/items/3f64a77b3ccef31e6536)
>x86環境で実行すると、環境変数`%ProgramFiles(x86)%`が定義されていないためず'%ProgramFiles(x86)%'という文字列がそのまま入ってきてしまいます。
普通はそんなパスは存在しないので、空の場合は改めて%ProgramFiles%を取得しています。

StackOverFlowもそういう補足があるが一般的にはこの環境変数が指す意味は分かりづらいため、使われていない。

# 結 論
Windows 7以降、絶対にProgram Filesフォルダに行きたい場合は`%ProgramfilesW6432%`を使うこと。
VBAで64bit フォルダにアクセスするときもこれを用いる。
また、この値は2010年頃Windows7 64bit拡張によって導入されたらしい。このため、設定されていないOSのバージョン（Win 7 32bitやVista以前)がある。
このため、後方互換に留意する必要がある場合には空文字の場合の処理が必要。
ということでいいようだ。