Windowsのしくみ（ど素人編）

今までMac/Linuxしか使ったことのなかった自分が、Windowsしかない職場で生き残るために勉強したHistoryを残します。自分の備忘録＆同様の環境にいる方のTipsになればと思います。

#Windowsの動作モード
* Windows ~~で~~ は、カーネルモードとユーザーモードという2種類の動作モードを使い分けて各種処理を行っている（2019/6/21追記：この考え方はMacでもLinuxでも同じ）
    * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32777/2a675c85-ec2c-13b8-d842-5bd231055aa9.png)
* アプリは全てユーザーモードで動作し、それぞれが独自のアドレス空間や資源を保持する
    * あるアプリが異常になったとしても他のアプリには影響を与えないよう設計されている
* カーネルモードで動作するプログラムは大きく3層に分類できる
    * HAL
        * ハードウェアに最も近い層がこのHAL(Hardware Abstraction Layer)
    * カーネル
        * HALの上にはプロセスやスレッド、仮想メモリ、デバイスドライバなどを管理するカーネルが位置している
        * システムコールの抽象化により、従来のアプリもカーネルの変更点を意識する必要はない
    * System Service Dispatcher
        * カーネルモードで動作するプログラムの最上位に位置する
        * 例えば、ユーザーモードで動作するアプリからkernel32.dllで定義されているCreateFile()などの関数を呼び出す場合、ユーザーモードの窓口であるntdll.dllを通して呼び出しがシステムコールに変換され、カーネルモードの窓口であるSystem Service Dispatherが呼び出される

#WOW64（Windows-On-Windows 64）とは
* 64bit版のWindowsに搭載されている、従来の32bitのアプリを動かす仕組み

#.Netとは
以下のサイトが分かりやすいです。
https://itpropartners.com/blog/8439/

* ライブラリや各種フレームワークをあわせた統合開発環境のこと
* 中核となるのは「.Net Framework」
    * ~~開発者もエンドユーザーもインストールする必要あり~~ (ご指摘いただき以下に修正)
    * Windowsにデフォルトでインストールされている
        * ~~ただしエンドユーザーは何かしらのアプリを入れるときに勝手に入ってくるのであまり意識することはない~~
    * アプリとOSを仲介するもの（OSレベルのAPIコールをラップするもの？）
    * これがあることによって開発者はWindowsの小難しいAPIを理解しなくてよい？？
* 「ASP.NET」とは
    * 「.NET Framework」上で動作するWebアプリケーションフレームワーク
    * こいつがあるお蔭でクライアントのGUIが簡単に作れる
    * サーバーサイドもこのフレームワークにより通信やバリデーションが簡単に作り込める
* 共通言語ランタイム（CLR)とは
    * 言語や環境による差異を吸収する中間言語に変換する環境
    * メモリ管理や型とアセンブリの管理、スレッド管理、例外処理、セキュリティなども担当
        * 「アセンブリの管理」が具体的に何を指しているかはよくわかっていないため、【Read Later】として以下を残す
            * https://itpropartners.com/blog/8439/

#レジストリとは
* サービスやアプリなど、Windowsに使われている全ての設定がこのDBに格納されている
* 格納されているデータはkey-valueで構成されている
    * レジストリエディタで見るとkeyはフォルダ、valueはファイルのように見える
* レジストリには親となるルートキーがある。レジストリ設定にあたっては、このルートキーの種類を理解することが大切
    * HKEY_CLASSES_ROOT
        * ファイルの拡張子と関連付けに関する情報が格納されている
        * 長いので「HKCR」と短縮される
    * HKEY_CURRENT_USER
        * ログイン中のユーザーの設定が格納されている
        * 「HKCU」と略される
        * HKCUでは、ユーザーが変更できるアプリの設定が格納されている
            * HKCUのサブキーにあるvalueはユーザー権限で変更可能
    * HKEY_LOCAL_MACHINE
        * システムについての全ての設定が格納されている
        * 「HKLM」と略される
        * 「HKLM\Software」のサブキーで設定するvalueは、ユーザーのみでなく、PC全体に反映される
        * 外付けのHWのドライバ情報も格納される
    * HKEY_USERS
        * ユーザー別とデフォルト設定を含む設定が格納されている
            * つまり、全てのユーザーのHKCUがここに格納される
        * ユーザー別の設定を確認したいときに便利
        * サブキーはユーザーアカウントがSIDで表示される
            * ユーザーのSIDは以下のように確認できる
            * ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32777/053b998c-73b5-e2b0-092e-0929d2cde07c.png)
    * HKEY_CURRENT_CONFIG
        * 使用中のプリンタとディスプレイの機器についての情報がここに格納される
        * 使用機会は低
    * レジストリの値について
        * レジストリを作成するとき、valueを参照するときは以下の知識が役に立つ
        * ![registry type.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32777/dec0c675-f3bd-8952-2269-218f45fc8903.png)
    
#Windows API
恐らくここは.NET Frameworkを使って開発しているとあまり意識をしないと思われる（Windowsネイティブアプリの開発に疎いので詳細は他を参照いただきたいが）。

##Win32API

* プロセスとは
    * 「プログラム実行中のインスタンス」を意味する
        * プログラムの実行ファイルからメモリ上に実体化されたインスタンス
        * オブジェクト指向で１つのクラスから複数のインスタンスが作れるのと同様に、Windowsにおけるプロセスも複数のインスタンスを作ることができる
    * その時点でのプロセスの状態をプロセスの実行コンテキストもしくはコンテキストと呼ぶ
    * CreateProcess APIでプロセスを作成する
    * エクスプローラーなどからプログラムを起動したときは内部的にこのAPIが叩かれている
    * 「ハンドル」とはWin32 APIで様々なオブジェクトを識別するために頻繁に使われるもの
        * プロセスのハンドルは特定のプロセスを操作する際に必ず必要になる
    * プロセスの作成に成功するとBOOL型で成功／失敗が返される
        * 成功時にはプロおセスが作成され、そのハンドルがPROCESS_INFORMATION構造体に格納される
    * ハンドルは不要になったら必ずクローズすべきもの
        * クローズしないとメモリが開放されず、メモリリークの原因となる
        * クローズの際にはCloseHandle APIと使えばよい
        * プロセスの処理結果を待ってなにかしら処理をしたい場合、「WaitForSingleObject API」を利用し、起動したプロセスが終了するのを待つのが基本
        * 終了したプロセスの終了コードは「GetExitCodeProcess API」を使用できるｊ

    * Windowsには「GUIプロセス」と「コンソールプロセス」の２つのプロセスがある
        * ソースコードで見ると、前者はWinMainという名前の関数から処理が始まり、後者はmain関数から処理が始まる
        * GUIプロセス
            * ユーザーからのI/Oをはじめ、様々ないイベントが「ウィンドウ・メッセージ」と呼ばれるデータ構造としてアプリに通知される
            * こうしたイベントが生じる度にメッセージ・キューというアプリ別に用意された待ち行列にウィンドウ・メッセージを追加する
            * GUIプロセスはメッセージ・キューからメッセージを取り出して処理する
        * コンソールプロセス
            * メッセージ・キューは作成されない
            * コンソール・プロセスはデフォルトでコマンドプロンプトのコンソールをそのまま使う
            * エクスプローラなどGUIから起動された場合は専用のコンソールが用意される
            * GUIプロセスはメッセージ・キューに届いたウィンドウ・メッセージをメッセージ・ループで取り出して処理することを繰り返している
                * そのメッセージのほとんどは、キーを押したりマウスを動かしたりといったユーザーの操作を通知するもの
                * メッセージが届かないGUIプロセスは「待機状態」にある
    * タスク・スイッチ
        * タスク・スイッチはプロセスを切り替えるため、動作中のプロセスを止め、CPUのレジスタの値やプロセスから見えるメモリ上のデータなど、プロセスの環境すべてを退避する高コストな処理
        * CPUの仕組みを使い、効率的に処理している（詳細は省く）
        * Windowsは各プロセスが使うメモリ空間が実メモリ上で独立である（共有部分を除いて重ならない）ようになっている
        * プロセスには優先順位がある
            * タスク・マネージャから優先順位は変更可能
    * スレッド
        * スレッドは処理の流れを表すもの
        * マルチスレッドで処理の待ち合わせをする場合、WaitForSingleObject APIに渡して処理の終了を待ち合わせる
        * プロセスは実行に関わる全てのものを含む概念
            * Windowsはプロセスごとにメモリー空間を用意し、ウィンドウや描画に使用するオブジェクトなどもプロセスごとに管理する
            * プロセスの中で「プログラムの実行」の部分を表すのがスレッド
                * 言い換えればスレッドはプロセスを構成する一要素
        * マルチスレッドのマルチプロセスに比べてのメリット
            * タスク・スイッチのオーバーヘッドが早い
                * プロセスを切り替える場合は、メモリ空間など、プロセスのコンテキストも切り替える必要がある
                * スレッドはメモリ空間などプロセスのコンテキストを共有するために切り替えがない
                * プロセスの場合、データを受け渡す場合、「プロセス間通信」が必要
        * CreateThread APIでスレッドを作成できる


## 障害解析の役に立つかもしれない情報
### イベントログ
- システムとかアプリで発生したイベントを記録したもの
- イベントビューアで閲覧可能
-  C:\Windows\System32\winevt\Logs にある
    -  EVTXという形式で保存されている
    -  内部的にはバイナリだが、イベントビューア経由で見やすくなる
-  イベントトレースログという、プログラムの経過情報のようなものを吐くログもある
    -  C:\Windows\Logs にある
-  イベントログはデフォルトで20MBまで保存し、その後は消される
    -  設定で容量を増やすことも、アーカイブすることもできる
-  イベントログを扱う方法は以下がある
    1. イベントビューアー（GUI）
    2. wevtutil.exe（コマンドライン）
        - コマンドラインからイベントログファイルを扱うためのもの。完全なコマンドラインのツールであるため、スクリプトなどから利用しやすい。また、前述のログファイルが最大値に達したときの設定などもこのコマンドでできる。このため、多くのイベントログの設定を変更するような場合やスクリプトなどから制御する場合に利用可能だ。ただし、出力は、テキストかXMLのどちらかである。
    3. WMIC.EXE（コマンドライン）
       - 同様にコマンドラインツールだが、こちらは、WMI（Windows Management Instrumentation）経由でのアクセスで、他のシステム情報などと同一のコマンドで扱うことができるため、すでにWMIに慣れた人向けだ。WMI自体、ちゃんとWindows 10にも残っているが、推奨される方法ではなく、WMICコマンドは機能が膨大でいまからその使い方を覚えるメリットはあまりないかもしれない。ただし、Wevtutil.exeに比べると検索条件や出力指定が柔軟だ。
    4. PowerShellコマンドレット
       - イベントログを扱うためのコマンドレットが複数ある。PowerShellは、.NET FrameworkのイベントログAPIを利用しており、比較的簡単にイベントログを扱える。ただ、ちょっと使った人ならばわかるが、言語としては非常に癖が強く、PowerShell自体に慣れないと、利用に苦労することがある。もっとも、他人が紹介しているコマンドをコピー＆ペーストで試す程度であれば、一番簡単な方法と思われる。
    5. Win32APIや.NET Frameworkによるソフトウェア開発
      - 究極のログ管理だが、よほど複雑な条件でも指定しない限り、ここまで必要になることはないと思われる。ただ、Win32APIや.NET Frameworkが自由に呼び出せる言語に堪能なら、PowerShellでスクリプトを組むよりは理解しやすいだろう。


### PowerShellでイベントログを扱う
#### イベントログオブジェクトの構造
すべてのイベントは、ログの登録元となるProviderName（イベントビューアーのソースに相当）とIdプロパティ（イベントビュアーではEventId）で区別されている。つまり、ProviderNameごとにIdで何が起こったのかを区別できる。

messageプロパティは、オブジェクトのように見えるが、実際にはevtxの中には記録されておらず、Idをキーにしてどこかにあるメッセージデータベースから入手したテキストとEventDataプロパティに含まれている情報を組みあわせて合成される文字列である。人間には、わかりやすいものの、たとえば、Windows 10のバージョンが違うと、メッセージデータベースが違うため、messageプロパティを作ることができなくなる。

　また、イベントログは、記録された時刻を表す「TimeCreated」プロパティ（イベントビューアーの日付と時刻に相当）と別に記録された順番を示すRecordIdを持っている。時刻はミリ秒単位まで記録されているものの、複数の「ソース」が同時にログを記録しようとしているため、時間がずれる可能性がある。

　このとき、RecordIdは、evtxファイルへの記録順を示すため、ソートするときに利用が可能だ。ただし、RecordIdはevtxファイルごとになるため、複数のevtxファイルを通して見る場合には注意が必要だ。

　結局、evtxファイルから必要な情報を取り出すには、以下の表のプロパティのみを見れば良い。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32777/7fd5185e-fa95-3030-f112-007611f68dcf.png)

※XPathの記法？を学ばないと駄目ですね…

## コマンドプロンプトの使い方

フォルダの中身を表示
```
dir  (option) [フォルダパス] (option) 
```
optionで/aを付けることで表示属性を指定することができる。
例えば、/a:dでディレクトリのみ表示できる。/a:-dでディレクトリ以外を表示できる。
その他にも、
r:読み取り専用のファイル
h:隠しファイル
などがある。

/oは並び順を変えることができる。
/o:nでアルファベット順
/o:sでファイルサイズ順
/o:eで拡張子順
/o:dで更新日の古い順

/tはファイルやフォルダが持つ日付情報によって並び順を指定できる。
/t:aは最終アクセス日の古い順
/t:wは最終更新日の古い順

/qはファイルの所有者表示
/sはサブフォルダ含めたファイルすべて表示

その他、dirの便利な使い方
[dir *.txt]のようにワイルドカードを指定して該当するフォルダやファイルを表示することができる。
※この際、表示結果の上下に余計な結果を表示させたくない場合は「/b」オプションを付けること。

[dir *.txt /b | clip]とすると、結果をクリップボードに保存することができる。

### 環境変数の設定方法
setコマンドを使いますが、これだとコマンドプロンプトを終了すると環境変数が消えてしまい、また、コマンドプロンプト画面ごとに設定が必要になります。
コマンドプロンプト上で永続的に環境変数を有効にする場合は、 `set`ではなく`setx`コマンドを使います。setxを使うと、OSの環境編s縫うとして記録されます。ちなみに、setxを使う場合、あいだの「=」が不要になります。
なお、コマンドプロンプトの場合、一度設定した変数は%で挟むことで再利用できます。

```
set http_proxy=http://proxy.example.com:8000
set https_proxy=%http_proxy%
set HTTP_PROXY=%http_proxy%
set HTTPS_PROXY=%http_proxy%
## setxを使う場合
# setx http_proxy http://proxy.example.com:8000
# setx https_proxy %http_proxy%
# setx HTTP_PROXY %http_proxy%
# setx HTTPS_PROXY %http_proxy%
```
※Windowsの場合、環境変数は大文字と小文字を区別しないので注意が必要

ちなみに、上記の例では、プロキシの通信先として環境変数を設定していますが、もしパスワードを埋め込む必要がある場合は以下のようにします。

```
# 例）ユーザー名/パスワードが kurita/p@sswordのとき
# @のURLエンコーディングは%40なので、次のようになります
# Windowsの場合
set http_proxy=http://kurita:p%40ssword@proxy.example.com:8000以下略
```

ローカル通信用にプロキシ設定を除外したい場合は以下のようになります。

```
## Windowsの場合
set no_proxy=localhost
set NO_PROXY=%no_proxy%

## Macの場合
export no_proxy=localhost
export NO_PROXY=$no_proxy
```

## ユーザー環境変数とシステム環境変数の違い

[Windowsのシステム環境変数とユーザ環境変数 \- Qiita](https://qiita.com/yamap_55/items/9a3f7af47dc879104e71)が参考になります。

ざっくり言うと、システム環境変数がグローバルでユーザ環境変数がローカルな設定、と言えるのではないでしょうか。

どちらか片方に設定されている場合は設定された値が呼び出され、両方に設定されている場合はユーザ環境変数で上書きされます（PATHの場合のみ、システム環境変数＋ユーザ環境変数と連結される）。

## その他、役立つ情報
### findstrはgrep代わりになる
Windowsを使っていてgrepがあればなぁ・・・と思ったことはありませんか？ そんなときはfindstrを使えばコマンドプロンプトでも同等のことができます。

使い方は[【findstr】の使い方～and/or複数条件や否定, 正規表現, エスケープやパイプ等オプション～ \| SEの道標](https://milestone-of-se.nesuke.com/sv-basic/windows-basic/findstr/)を参照ください。

>C:¥> findstr "検索文字列" "ファイル名"
複数ファイル指定することもできます。
C:¥> findstr "検索文字列" test1.txt test2.txt
*によるワイルドカードでの指定もできます。以下はtestを含むファイル全てから検索する場合。
C:¥> findstr "検索文字列" *test*
コマンド結果から特定文字列を含む行だけ抽出したい場合は| (パイプライン) を使います。
C:¥> [標準出力するコマンド] | findstr "検索文字列"
C:¥> findstr "a b" test.txt #OR条件

## トラブルシューティング
### システム環境変数の新規追加、編集ができない
Power Shellで以下のコマンドを打てば可能になります。

```powershell
Start C:\Windows\system32\rundll32.exe sysdm.cpl, EditEnvironmentVariables
```


## 参考
- [Microsoft] Windowsデスクトップ向け業務アプリ開発には何を採用すべきか？ - Qiita https://qiita.com/sengoku/items/fb4948e0d2746e3cc26f