くまくまの業務日誌

Markdown記法で徒然に書いてみましょう。

Windowsの型定義情報

WindowsC/C++言語の標準的な型をほぼ使用していません。

Windows開発でよく使う型の情報は以下のようになっています。

■8bit

typedef char CHAR;
typedef char TCHAR, *PTCHAR;
typedef char CCHAR;
typedef char *PSZ;

typedef unsigned char UINT8, *PUINT8;
typedef unsigned char UCHAR;
typedef unsigned char BYTE;
typedef unsigned char *LPBYTE;
typedef unsigned char *PBYTE;
typedef unsigned char uint8_t

16bit、32bit以降は現在環境がないため、後日記載。

■便利(と思われる)マクロ

■■min()、max()は既に宣言済み

NOMINMAX宣言がない場合、以下のマクロが使用可能。

#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif

#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

■■WINDOWSの最大パス長は260まで

#define MAX_PATH 260

■■BOOL変数の実態はintである。

BOOL変数に設定するのは以下の2つ。

#define FALSE 0
#define TRUE  1

■■コードには影響しないが、可読性が上がるマクロ

void hogehoge(
   IN int input,
   OUT int& response,
   OPTIONAL OUT LPVOID lpvResult);

のように、引数の方向を(無害に)示すことができる。

#define IN
#define OUT
#define OPTIONAL

■■いまや、near far は無意味である。

#define far
#define near

https://docs.microsoft.com/ja-jp/cpp/cpp/stdcall?view=vs-2019

Stdcall呼び出し規約は、Win32 API 関数を呼び出すために使用されます。
呼び出し先がスタックを消去するので、コンパイラによって vararg 関数が
cdeclされます。
この呼び出し規則を使用する関数には、関数プロトタイプが必要です。
__Stdcall修飾子は Microsoft 固有です。

#define CALLBACK    __stdcall
#define WINAPI      __stdcall
#define APIENTRY    WINAPI
#define APIPRIVATE  __stdcall
#define PASCAL      __stdcall

https://docs.microsoft.com/ja-jp/cpp/cpp/stdcall?view=vs-2019

cdeclは、C および C++ プログラムの既定の呼び出し規約です。
スタックは呼び出し元によってクリーンアップされるため、機能をvararg実行できます。
cdecl呼び出し規約では、各関数呼び出しにスタック クリーンアップ コードを含める必要があるため、
stdcallよりも大きな実行可能ファイルが作成されます。 次の一覧は、この呼び出し規約の実装例を示しています。
cdecl修飾子は、マイクロソフト固有です。

#define WINAPIV __cdecl

検索しても、ほとんど見つからない。

□検索条件  "WINAPIV"
検索対象       *.*
フォルダ       C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0
除外ファイル   *.msi;*.exe;*.obj;*.pdb;*.ilk;*.res;*.pch;*.iobj;*.ipdb
除外フォルダ   .git;.svn;.vs
    (サブフォルダも検索)
    (英大文字小文字を区別する)
    (正規表現:bregonig.dll Ver.4.20 with Onigmo 6.2.0)
    (文字コードセットの自動判別)
    (一致した行を出力)


C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\minwindef.h(118,9)  [UTF-8]: #define WINAPIV     CDECL
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\minwindef.h(129,9)  [UTF-8]: #define WINAPIV     __cdecl
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\minwindef.h(136,9)  [UTF-8]: #define WINAPIV
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\celib.h(172,5)  [UTF-8]: int WINAPIV ceDbgPrintf(BOOL fDebug, char const *pszfmt, ...);
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\t2embapi.h(139,24)  [UTF-8]: typedef unsigned long( WINAPIV *READEMBEDPROC ) ( void*, void*, const unsigned long );
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\t2embapi.h(140,24)  [UTF-8]: typedef unsigned long( WINAPIV *WRITEEMBEDPROC ) ( void*, const void*, const unsigned long );
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\Vfw.h(59,17)  [UTF-8]: #define VFWAPIV WINAPIV
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\WinUser.h(325,1)  [UTF-8]: WINAPIV
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\WinUser.h(332,1)  [UTF-8]: WINAPIV
9 個が検索されました。

■■ビット操作マクロ

BYTE変数2つからWORD変数を作ったり、WORD変数2つからLONG変数を作ったりするときに使用する。逆のLONGから上半分、下半分を抽出するマクロもある。

Windowsのメッセージ系API(PostMessageやGetMessage、SendMessageなど)で活用していた。 今、この関数を使う人は少ないと思われる。

#define MAKEWORD(a, b)      ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))
#define MAKELONG(a, b)      ((LONG)(((WORD)(((DWORD_PTR)(a)) & 0xffff)) | ((DWORD)((WORD)(((DWORD_PTR)(b)) & 0xffff))) << 16))
#define LOWORD(l)           ((WORD)(((DWORD_PTR)(l)) & 0xffff))
#define HIWORD(l)           ((WORD)((((DWORD_PTR)(l)) >> 16) & 0xffff))
#define LOBYTE(w)           ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w)           ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))

■■各変数の最小値、最大値は既に宣言されている。

流石に、32Bitまで。

#define MINCHAR     0x80        
#define MAXCHAR     0x7f        
#define MINSHORT    0x8000      
#define MAXSHORT    0x7fff      
#define MINLONG     0x80000000  
#define MAXLONG     0x7fffffff  
#define MAXBYTE     0xff        
#define MAXWORD     0xffff      
#define MAXDWORD    0xffffffff  

■■64Bit変数は、__int64がプリミティブ宣言

大半はtypedefされたものを使用する。

typedef unsigned __int64 size_t;
typedef __int64 LONGLONG;
typedef unsigned __int64 ULONGLONG;

■■64bit化させるための構造体もある

WIN32APIとしては、こちらが、多用される。
例えば、QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCounter)など。
演算は.QuadPartでまとめたものを使います。数値が小さいはずだからと、LowPartだけで演算しようとすると、痛い目に遭います。

typedef union _LARGE_INTEGER {
  struct {
    DWORD LowPart;
    LONG  HighPart;
  } DUMMYSTRUCTNAME;
  struct {
    DWORD LowPart;
    LONG  HighPart;
  } u;
  LONGLONG QuadPart;
} LARGE_INTEGER;

typedef LARGE_INTEGER *PLARGE_INTEGER;

typedef union _ULARGE_INTEGER {
  struct {
    DWORD LowPart;
    DWORD HighPart;
  } DUMMYSTRUCTNAME;
  struct {
    DWORD LowPart;
    DWORD HighPart;
  } u;
  ULONGLONG QuadPart;
} ULARGE_INTEGER;

typedef ULARGE_INTEGER *PULARGE_INTEGER;

ハンガリアン記法について

ハンガリアン記法はオワコンなのか?

そんなことはない、現時点でもgithubで検索すると、現在進行中のプロジェクトで、Linux/Windows両対応のソースコードにもまだまだ使用されている。ただ、提唱者の意図していない形になってしまったのは残念ではあるが。

ハンガリアン記法のルールは?

基本は、変数名の先頭(プレフィックス)に付けるキーワードのルールとなる。型が複数(ポインタなど)になってくると、プレフィックスも複数になってくる。

■ルール1:スコープに応じたプレフィックスをつける。

WIN32APIの仮引数で登場した記法なので、グローバル変数に関しては見たことがない。

”m_" は、今でもよく見るルールである。MFCでは、MS製ソースコードもサンプルでよく見たが、ほとんどこれが記載されていた。

  • グローバル変数…"g_"をつける
  • メンバー変数..."m_"をつける
  • ローカル変数...以下で説明
  • 関数の引数...ローカル変数のルールに準ずる。

ただ、もっとシンプルにしたいと願うのも確かである。以下のルールでも十分役目を果たせると思うが、如何だろうか。

  • グローバル変数は "__" アンダーバーを2つ付ける。
  • メンバー変数は "_" アンダーバーを1つ付ける。

■ルール2:ポインタの場合は "lp" ないし、"p" をつける。

今の時代に "near" だの "far" だの、意味があるのだろうかと思うわけだが、 "l" はロングのLで、意味するところは、near/far の far の方を指していたのは、はるか昔のWIN16APIの話となる。現在のVisual Studioでは、near/far は無意味である。だが、Visual Studio で "LPCVOID" と記載して、右クリックして、「定義へ移動」を選択してみて欲しい。"typedef CONST void far LPCVOID;"の行にたどり着いたと思うが、そのもうちょっと上の行にはまだ、「near」の文字が散見される。 "typedef BOOL near PBOOL;" の行を見て、「今どき "far" もないよな。」と "LP" でなく、"P" を使用した場合、もれなく "near" が付いてくることになる。 これを見て、ポインタ宣言の時は "P" ではなく、"LP" で宣言するようにしている。重ねて言うが、"near"は意味がないキーワードである。

昔(WIN16API)の名残で、"lp" と "p" の両方をWindowsのヘッダーファイルは宣言しているが、実は1つないものがある。 LPCVOIDは宣言されているが、PCVOIDはないのである。よく使いそうなのにない。これも、自作関数で "P" に統一できない理由である。

■ルール3:const などの定数の場合は、"c"をつける。

const もWindowsでは、"CONST" と宣言されているので、これを使う。

■ルール4:wide char/multi byteの両方を意識した変数の場合は "t"をつける。

UNICODEの宣言あり/なしで変数(実は関数も変わる)の型が変わる場合、プレフィックスとして "t" を付与する。 予想だが、"transparent:透過的"の t ではなかろうか。 UNICODE関数の場合は、文字数を返したり、wchar_t*型を返してくることになるので、サイズや内容操作時は注意が必要である。

■ルール5:ここからは、変数毎の命名規約になる。

ハンガリアン記法とは全く関係がないが、Windowsはプリミティブな変数に対しても、大文字で再定義しているものが多い。将来を見越しての対処だったのだと思うが、今までスムーズに16bit~32bit~64bitに遷移してこれているので、杞憂だったのではないか。しかし、ほとんどのWIN32APIはこの大文字変数型を使用しているので、これに追従していくしかない。

■■8bit変数の場合

変数名
cSizeCHAR
byBufferUCHAR/BYTE
// winnt.h
typedef char CHAR;
// minwindef.h
typedef unsigned char UCHAR;
typedef unsigned char       BYTE;

■■16bit変数の場合

変数名
sSizeSHORT
wParamUSHORT/WORD
// winnt.h
typedef short SHORT;
// minwindef.h
typedef unsigned short USHORT;
typedef unsigned short      WORD;

■■32bit変数の場合

変数名
iDataSizeINT
uiPathLengthUINT
lTableSizeLONG
ulAreaSizeULONG
dwBufferLenDWORD

Windowsは "long"の宣言でも32ビットである。逆にLinuxは "long" の宣言で64ビットとなる。
https://marycore.jp/prog/c-lang/data-type-ranges-and-bit-byte-sizes/

// minwindef.h
typedef int                 INT;
typedef unsigned int        UINT;
typedef unsigned long ULONG;
typedef unsigned long       DWORD;
// winnt.h
typedef long LONG;

■■64bit変数の場合

  • signed long long の場合、"ll" をプレフィックスに付ける。宣言は LONGLONG を使う。
  • unsigned long long の場合、"ull" をプレフィックスに付ける。宣言はULONGLONG を使う。

※32Bitの時代に生まれたハンガリアン記述のため、64Bitに関しては諸説あり、な状況である。

// winnt.h
typedef __int64 LONGLONG;
typedef unsigned __int64 ULONGLONG;

WIN32で64ビットを扱うために、以下の宣言を使用する場合があるが、これはユニオンである。

LARGE_INTEGER は、ユニオン(_LARGE_INTEGER)をtypedef宣言したもので、以下のようになっている。

typedef union _LARGE_INTEGER {
    struct {
        DWORD LowPart;
        LONG HighPart;
    } DUMMYSTRUCTNAME;
    struct {
        DWORD LowPart;
        LONG HighPart;
    } u;
    LONGLONG QuadPart;
} LARGE_INTEGER;

同様にULARGE_INTEGER も、以下のように宣言されている。

typedef union _ULARGE_INTEGER {
    struct {
        DWORD LowPart;
        DWORD HighPart;
    } DUMMYSTRUCTNAME;
    struct {
        DWORD LowPart;
        DWORD HighPart;
    } u;
    ULONGLONG QuadPart;
} ULARGE_INTEGER;

で、LowとHighをどう使い分けるのか?
これは、入れる場合と、出す場合を統一すれば、問題ない。ここであえて、エンディアンを考える必要はない。
もし、Intel系のビッグエンディアンで作成して、非Intel系のリトルエンディアンで取り出す場合を想像しているなら、それは16ビットの変数から検討しなければならない。

■■実数の場合

※なぜ、doubleに対して"DOUBLE"を用意しなかったのか?

変数名
fLeftPosFLOAT
dMaxWidthdouble

■■bool変数の場合

変数名
bShowWindowBOOL

なお、BOOLはint型である。

// minwindef.h
typedef int                 BOOL;

■■void型の場合

変数名
lpvParamLPVOID

宣言で使用するVOIDは主にアドレスの取得として用いられる。32Bit/64Bitの影響をモロに受ける変数であるが、そもそもサイズ感のない変数なので32→64に換装する場合は要注意変数である。

■■文字列の場合

文字列の場合、基本的には「ヌルターミネーション」であることが、C/C++の標準なので、一位にプレフィックス"sz"が決まる。
ちなみに、string zero termination の省略形である。

通常の文字列とは違う形式としてBSTR型が存在する。BSTR型の文字列の持ち方は特殊で、先頭に文字列長、その後に文字列、という持ち方をしている。この変数の場合、NULLで終端を閉じていないため、'z'は付かないことになる。

変数名
szPathchar [ ]
tszPathwchar_t [ ]

■WIN32APIによく登場する変数宣言

変数名
cSizechar/CHAR
sParamshort/SHORT
iCounterint/INT
lpvParamvoid*
lpcszFilePathchar*/CHAR*
lpctszFilePathconst char*/CONST CHAR*
szBufferchar [ ]
tszBufferwchar_t [ ]

cSize sParam iCounter lpvParam lpcszFilePath lpctszFilePath

ポートフォリオについて

ポートフォリオというのは、株のあれとは違うのか?

■どこで見る?

クラウドワークスでよく見る。全く気にしていなかったが、いつかは本腰入れて調べようと思ってた。

クラウドワークス」に関しては、腕に自信ありの強者が、飯のネタを求めて集う場所。ここで仕事を取り続けられる者は本当に「勇者」だと思う。

■結局これはなに?

自分の実力や能力を、形となるようにして、他者に閲覧してもらい、その能力、実力を認めてもらって契約に結び付ける強力なアイテム。だから、クラウドワークスで見るわけで。

そもそものポートフォリオは、株式購入の際の値の動きをグラフ化したものを指していたはず。 それが「値の動き」ではなく、「己の能力」に変わった感じである。

つまり、「ポートフォリオを見せてください。」と言われたら、すぐにできるものではなく、これまで培ってきたものを自分なりに体系化し、ブラッシュアップしていく事が必要である。

■バックエンドエンジニアには?

自分は全くフロントエンドに縁がなかった。全くと言っては語弊があるが、それはAdobeFlashで業務アプリを作っていたので、今の時代にはノーカウントだと思う。そういう意味では、フロントエンドエンジニアには将来一人でやっていけそうでいいなぁ、と思うわけです。だがしかし、自分のキャリアをまとめ、体系化して後の人々の礎になるのもそれはそれでありかと思うわけでありまして、これから定年までの数年は、この「ポートフォリオ」を築き上げていこうと思う次第な訳です。

出力系(Out-*, Write-*)

出力に関する基本事項のピックアップ&スニペット

Write-Output

出力はパイプラインに送出可能。
出力結果を変数に格納可能。
意識しなくても、Write-Outputするコマンドレットは多い。
配列は改行されて出力される。コンソールもファイルも。

$message = @("This is my long long message.", "How about you?", "I am very fine.")
Write-Output $message
Write-Output $message > temp.log

コンソール出力

This is my long long message.
How about you?
I am very fine.

ファイル出力

This is my long long message.
How about you?
I am very fine.

Write-Host

前景/背景の色指定が可能
コンソール直接出力なので、後で加工することができない。
改行されずに出力されるが、-Separatorで指定は可能。背景色は見ての通り。

$message = @("This is my long long message.", "How about you?", "I am very fine.")
Write-Host $message -ForegroundColor Red -BackgroundColor Blue
Write-Host $message -ForegroundColor Yellow -BackgroundColor Green -Separator "`r`n"
This is my long long message. How about you? I am very fine.
This is my long long message.
How about you?
I am very fine.

Write-Warning

黄色文字で出力され、先頭に"WARNING: "が出力される。
リダイレクトでなにも出力されなかったので、Write-Host -Foreground Yellow と同じ。

$message = "You must shutdown and sleep now. You work too hard."
Write-Warning $message
WARNING: You must shutdown and sleep now. You work too hard.

Write-Error

例外を投げるのは、throw "エラー"のようにthrowで投げる。
出力が例外発生時に似ているが、むしろ例外発生後のエラー出力用と思われる。
see also http://suyamasoft.blue.coocan.jp/PowerShell/Tutorial/index.html#try

function testRaiseError() {
    throw "testRaiseError"
}

try {
    Write-Host "Before testRaiseError()" -ForegroundColor Yellow
    testRaiseError
    Write-Host "After testRaiseError()" -ForegroundColor Yellow
}
catch [System.Net.WebException], [System.IO.IOException] {
    # Never come this line. This is a sample.
}
catch {
    Write-Error -Message $Error[0].Exception.Message -Category InvalidArgument
} finally {
    Write-Host "finally section."
}

出力結果を分かりやすくするために、example.ps1にて実行

Before testRaiseError()
・・・\example.ps1 : testRaiseError
    + CategoryInfo          : InvalidArgument: (:) [Write-Error]、WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,example.ps1
 
finally section.

Write-Progress

プログレスバー的に進捗表示
see also https://www.forsenergy.com/ja-jp/windowspowershellhelp/html/3f2737ad-2984-4e03-ab2f-ecbd14518b3e.htm

for ($i = 1; $i -lt 101; $i++ ) {
    Start-Sleep -Milliseconds 200
    $message = "{0}% Complete:" -F $i
    Write-Progress -activity "Search in Progress" -status $message -percentcomplete $i
}
 Search in Progress
    48% Complete:
    [ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo                                                                   ]

Out-GridView

スクリプトらしからぬ、グリッドウィンドウを使ってデータを表示します。

$message = @{
    "Key1" = "This is my long long message.";
    "Key2" = "How about you?";
    "Key3" = "I am very fine."
}
$message | Out-GridView -Title 連想配列の中身

Out-Host

-Paging があるので、more/less的に使える。(Get-Helpには、なぜか使えなかった)
でも、Qで終了すると例外メッセージが出力される...

Get-Process | Out-Host -Paging
PS C:\Users\User01\Documents\PowerShell\Basic> Get-Process | Out-Host -Paging

Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName
-------  ------    -----      -----     ------     --  -- -----------
    420      19     9660      20744       0.41    440   1 Code
    653     132   289244     279864     593.94   3640   1 Code
    807      46    39904      77788      39.16   6080   1 Code
    221      15     6900       9448       0.23   6528   1 Code
    384      39    47500      66644       4.33   8444   1 Code
    419      33    24980      54944     126.38   9736   1 Code
    321      53    63872      75708      15.17   9828   1 Code
    305      15    11320      12624       0.19   6776   1 CodeHelper
    133       9     4544       7924     127.61   2404   1 conhost
    132       9     4020       6596       0.83   3724   1 conhost
    115       8     5448       7192       0.05  10060   1 conhost
<Space> 次のページ、<CR> 次の行、Q 終了

Out-File

エンコードは、Unicode(既定値),UTF7,UTF8,UTF32,ASCII,OEM
UnicodeUTF-16 LE、UTF8はBOM付き、OEMはSHIFT-JISになる(Windows)。
-Appendで追加モードになります。
-Forceで読み取り専用でも書き込みます。
-NoClobberで既存のファイルを上書きしません。
-Width で、サイズ以降の文字はカットされます。

Get-Help | Out-File -FilePath "Get-Help_OEM.log" -Encoding OEM
Get-Help | Out-File -FilePath "Get-Help_UTF8.log" -Encoding UTF8
Get-Help | Out-File -FilePath "Get-Help_Unicode.log" -Encoding Unicode

Out-Null

なにも出力されない。F8でピンポイント実行しても、ステートメントが出力されない。
> NULL 的な用途。

Get-Process | Out-Null

Out-String

出力されるオブジェクトを文字列配列に変換する場合に使用します。
-Streamを付けると、オブジェクト毎に文字列を送ります。

Get-Alias | Out-String | Select-String "Get-"
Get-Alias | Out-String -Stream | Select-String "Get-"

Date、Time → Get-Date

日付と時刻に関する基本事項のピックアップ&スニペット

MS-DOSバッチからPowerShellに移行するにあたり、一番恩恵を受けるのが「日時の取得と加工」だと思います。

日時の取得

Get-Date
Get-Date -DisplayHint Date
Get-Date -DisplayHint Time
Get-Date -DisplayHint DateTime
コマンドレット 出力結果
Get-Date 2020年6月24日 12:41:47
Get-Date -DisplayHint Date 2020年6月24日
Get-Date -DisplayHint Time 12:41:47
Get-Date -DisplayHint DateTime 2020年6月24日 12:41:47

フォーマットによる出力形式変更

Get-Date -Format "yyyyMMdd_hhmmss"
20200624_124624

変数に格納して使用する

for ($i = 0; $i -lt 10; $i++)
{
    $currentDateTime = "{0}_{1}" -F (Get-Date -Format "yyyyMMdd_hhmmss"), $i
    Write-Output $currentDateTime
    Start-Sleep -Seconds 1
}
20200624_124729_0
20200624_124730_1
20200624_124731_2
20200624_124732_3
20200624_124733_4
20200624_124734_5
20200624_124735_6
20200624_124736_7
20200624_124737_8
20200624_124738_9

DIR → Get-ChildItem

ファイルとディレクトリに関する基本事項のピックアップ&スニペット

エラーの扱い方について

他のプロセスが出力中で、読み込むことができないファイルをスキップする場合は、
" -ErrorAction SilentlyContinue"を付加します。
あるいは、$ErrorActionPreference = "SilentlyContinue"を実行しておきます。
とは言っても、エラー情報を捨てる訳にも行きませんので、エラー情報を変数に格納しておき、後で出力します。

-ErrorVariable errorInfo  # エラー情報を上書きしていく場合 (ここで$を付けない)
-ErrorVariable +errorInfo # エラー情報を追記していく場合 (ここで$を付けない)
Write-Host $errorInfo[0]

ディレクトリ一覧を取得する。

$targetDirectory = "C:\windows\Temp"
Get-ChildItem $targetDirectory
    ディレクトリ: C:\windows\Temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/06/19     22:32                FA345C83-F785-4C40-AB34-2323134FE882-Sigs
d-----       2020/06/19     22:38                MsEdgeCrashpad
-a----       2020/06/19     22:38             53 af397ef28e484961ba48646a5d38cf54.db.ses
-a----       2020/06/07      1:54          12621 CompatibilityList.xml
-a----       2020/06/16     23:01           4554 dd_setup_20200616230105.log
-a----       2020/06/16     23:01              0 dd_setup_20200616230105_errors.log
-a----       2020/06/16     23:07        1398494 dd_setup_20200616230115.log
-a----       2020/06/16     23:01           5392 dd_setup_20200616230115_000_Microsoft.VisualStudio.Product.Community.log
-a----       2020/06/16     23:01         258926 dd_setup_20200616230115_001_Microsoft.VisualStudio.LiveShare.log
-a----       2020/06/16     23:02         726558 dd_setup_20200616230115_002_Microsoft.VisualStudio.LiveShare.log
-a----       2020/06/16     23:06            165 dd_setup_20200616230115_003_Unity3d.x64.log

ディレクトリを再帰しながら、全ての一覧を取得する。 [-Recurse]

+ファイル名にワイルドカードを使用して対象選択+文字列の連結。

$targetDirectory = "C:\windows\Temp"
$targetFileName = "*.log"
Get-ChildItem -Recurse $targetDirectory\$targetFileName
    ディレクトリ: C:\windows\Temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/06/16     23:01           4554 dd_setup_20200616230105.log
-a----       2020/06/16     23:01              0 dd_setup_20200616230105_errors.log
-a----       2020/06/16     23:07        1398494 dd_setup_20200616230115.log
-a----       2020/06/16     23:01           5392 dd_setup_20200616230115_000_Microsoft.VisualStudio.Product.Community.log
-a----       2020/06/16     23:01         258926 dd_setup_20200616230115_001_Microsoft.VisualStudio.LiveShare.log
-a----       2020/06/16     23:02         726558 dd_setup_20200616230115_002_Microsoft.VisualStudio.LiveShare.log
-a----       2020/06/16     23:06            165 dd_setup_20200616230115_003_Unity3d.x64.log
-a----       2020/06/16     23:07          11227 dd_setup_20200616230115_004_Microsoft.VisualStudio.Product.Community.log
-a----       2020/06/16     23:01              0 dd_setup_20200616230115_errors.log

ディレクトリのみをリストアップする。 [-Directory]

$targetDirectory = "C:\windows\Temp"
Get-ChildItem -Directory $targetDirectory
    ディレクトリ: C:\windows\Temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2020/06/19     22:32                FA345C83-F785-4C40-AB34-2323134FE882-Sigs
d-----       2020/06/19     22:38                MsEdgeCrashpad

ファイルのみをリストアップする。 [-File]

$targetDirectory = "C:\windows\Temp"
Get-ChildItem -File $targetDirectory
    ディレクトリ: C:\windows\Temp

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2020/06/19     22:38             53 af397ef28e484961ba48646a5d38cf54.db.ses
-a----       2020/06/07      1:54          12621 CompatibilityList.xml
-a----       2020/06/16     23:01           4554 dd_setup_20200616230105.log
-a----       2020/06/16     23:01              0 dd_setup_20200616230105_errors.log
-a----       2020/06/16     23:07        1398494 dd_setup_20200616230115.log
-a----       2020/06/16     23:01           5392 dd_setup_20200616230115_000_Microsoft.VisualStudio.Product.Community.log

ファイル名を相対パスのみで一覧取得する。[-Name]

$targetDirectory = "C:\windows\Temp"
Get-ChildItem -Recurse -File -Name $targetDirectory
af397ef28e484961ba48646a5d38cf54.db.ses
CompatibilityList.xml
dd_setup_20200616230105.log
dd_setup_20200616230105_errors.log
dd_setup_20200616230115_000_Microsoft.VisualStudio.Product.Community.log
dd_setup_20200616230115_002_Microsoft.VisualStudio.LiveShare.log
VSIXodgqpfok.vsix
MsEdgeCrashpad\metadata
MsEdgeCrashpad\settings.dat

ファイル名を絶対パスのみで一覧取得する。

$targetDirectory = "C:\windows\Temp"
Get-ChildItem -Recurse $targetDirectory | Select-Object -property FullName
FullName
--------
C:\windows\Temp\FA345C83-F785-4C40-AB34-2323134FE882-Sigs
C:\windows\Temp\MsEdgeCrashpad
C:\windows\Temp\af397ef28e484961ba48646a5d38cf54.db.ses
C:\windows\Temp\CompatibilityList.xml
C:\windows\Temp\dd_setup_20200616230105_errors.log
C:\windows\Temp\dd_setup_20200616230115.log
C:\windows\Temp\dd_setup_20200616230115_001_Microsoft.VisualStudio.LiveShare.log
C:\windows\Temp\dd_setup_20200616230115_003_Unity3d.x64.log
C:\windows\Temp\dd_setup_20200616230115_errors.log
C:\windows\Temp\dd_VSIXInstaller_20200616225936_0ff4.log
C:\windows\Temp\mat-debug-7936.log
C:\windows\Temp\MsEdgeCrashpad\settings.dat

Excludeを使用して不要なディレクトリを外して一覧取得する。[-Exclude]

$targetDirectory = "C:\Windows\system32"
Get-ChildItem -Recurse -Exclude @("*.exe", "*.dll", "*.cpl") -Name $targetDirectory
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saMV3B-PropertyBag.bag
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saMV3C-manifest.ini
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saMV3C-PropertyBag.bag
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saMV3D-manifest.ini
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saMV3I-manifest.ini
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saMV5B-manifest.ini
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saMV5B-PropertyBag.bag
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\saSP-pipelineconfig.xml
DriverStore\FileRepository\prnsacl1.inf_amd64_01567720b35ae842\smpclrc2.gpd
DriverStore\FileRepository\prntscl2.inf_amd64_64886520e82c3cce\prntscl2.cat
DriverStore\FileRepository\prntscl2.inf_amd64_64886520e82c3cce\prntscl2.inf

そもそも、Get-ChildItem はなにを返すのか?

出力内容から"Get-Member"を使って、propertyだけを抽出します。

$targetDirectory = ".."
Get-ChildItem $targetDirectory | Get-Member | Where-Object { $_.MemberType -eq "Property"}
   TypeName: System.IO.DirectoryInfo

Name              MemberType Definition
----              ---------- ----------
Attributes        Property   System.IO.FileAttributes Attributes {get;set;}
CreationTime      Property   datetime CreationTime {get;set;}
CreationTimeUtc   Property   datetime CreationTimeUtc {get;set;}
Exists            Property   bool Exists {get;}
Extension         Property   string Extension {get;}
FullName          Property   string FullName {get;}
LastAccessTime    Property   datetime LastAccessTime {get;set;}
LastAccessTimeUtc Property   datetime LastAccessTimeUtc {get;set;}
LastWriteTime     Property   datetime LastWriteTime {get;set;}
LastWriteTimeUtc  Property   datetime LastWriteTimeUtc {get;set;}
Name              Property   string Name {get;}
Parent            Property   System.IO.DirectoryInfo Parent {get;}
Root              Property   System.IO.DirectoryInfo Root {get;}

これだけの情報を返してくれます。これらを駆使して、スクリプトを書いていきます。
また、Get-Menberは「こいつ、なにを返すんだ?」というときによく使います。

再帰しながら、ファイル情報を取得します。

自力で再帰するので、カレントディレクトリから 深い階層に移動するタイミングで状況判断ができます。つまり、制御はディレクトリ移動の際にこちらに戻される訳です。そのタイミングで、サマリを取ったり、プログレスを出したりできます。

function recursiveFileSearch($targetFolderName, $targetExtension) {
    Write-Host $targetFolderName -ForegroundColor Green
    $collection = Get-ChildItem $targetFolderName -ErrorAction SilentlyContinue -ErrorVariable errorInfo
    if ($errorInfo.Length -gt 0) {
        Write-Warning $errorInfo[0]
        Start-Sleep -Seconds 1
    }
    foreach ($item in $collection) {
        if ($item.Extension.trim() -eq $targetExtension) {
            $output = "{0}`t{1}`t{2}" -F $item.FullName, $item.Extension, $item.LastWriteTime
            Write-Output $output
            Start-Sleep -Milliseconds 200
        }
        if ($item.Attributes -eq "Directory") {
            # ディレクトリを再帰調査します。
            recursiveFileSearch $item.FullName $targetExtension
        }
    }
}
recursiveFileSearch "C:\Windows" ".ps1" > ExecuteFileList.log

コンソール出力

C:\Windows\Containers
C:\Windows\Containers\serviced
C:\Windows\CSC
WARNING: パス 'C:\Windows\CSC' へのアクセスが拒否されました。
C:\Windows\Cursors
C:\Windows\debug
C:\Windows\diagnostics

ファイル出力

C:\Windows\diagnostics\scheduled\Maintenance\CL_Utility.ps1  .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\RS_AdminDiagnosticHistory.ps1  .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\RS_MachineWERQueue.ps1 .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\RS_SyncSystemTime.ps1  .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\RS_UserDiagnosticHistory.ps1   .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\RS_UserWERQueue.ps1    .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\TS_DiagnosticHistory.ps1   .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\TS_InaccurateSystemTime.ps1    .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\scheduled\Maintenance\TS_WERQueue.ps1    .ps1    2018/04/12 8:34:38
C:\Windows\diagnostics\system\AERO\CL_AeroFeature.ps1   .ps1    2018/04/12 8:34:37

コマンドプロンプトからWindows PowerShellへ移行準備

cmd.exeのメンテナンスもあまり行われていないということで、本腰入れてPowerShell移行計画を行おうと思います。

cmd.exe(コマンドプロンプト)のヘルプを参照する

コマンドプロンプトから

C>help

と入力して、エンター押下することで、コマンドプロンプトのヘルプが一覧出力されます。

特定のコマンドの詳細情報は、"HELP コマンド名" を入力してください
ASSOC    ファイル拡張子の関連付けを表示または変更します。
ATTRIB   ファイルの属性を表示または変更します。
BREAK    拡張 CTRL+C チェックを設定または解除します。
BCDEDIT  ブート データベースのプロパティを設定して起動時の読み込みを制御します。
CACLS    ファイルのアクセス制御リスト (ACL) を表示または変更します。
CALL     バッチ プログラム中から、別のバッチ プログラムを呼び出します。
CD       現在のディレクトリを表示または変更します。

… 以下、省略

VOL      ディスクのボリューム ラベルとシリアル番号を表示します。
XCOPY    ファイルやディレクトリ構造をコピーします。
WMIC     対話型コマンド シェルで WMI 情報を表示します。

ツールの詳細な情報については、オンライン ヘルプのコマンド ライン リファレンスを参照してください。

この中には、実際はEXEで機能を提供(外部コマンド)されているものも散見されるため、WHEREコマンドで応答がないものを内部コマンドとして調査してみます。

C>where <コマンド名>

whereコマンドで

C:\Users\User01>where dir
情報: 与えられたパターンのファイルが見つかりませんでした。

と返ってくれば、それはCMD.EXEの内部コマンドの説明とみてよいでしょう。

CMD.EXE
内部コマンド
説明
ASSOC ファイル拡張子の関連付けを表示または変更します。
BREAK 拡張 CTRL+C チェックを設定または解除します。
BCDEDIT ブート データベースのプロパティを設定して起動時の読み込みを制御します。
CALL バッチ プログラム中から、別のバッチ プログラムを呼び出します。
CD 現在のディレクトリを表示または変更します。
CHDIR 現在のディレクトリを表示または変更します。
CLS 画面を消去します。
COLOR コンソールの文字と背景の既定の色を設定します。
COPY 1 個以上のファイルを別の場所にコピーします。
DATE 日付を表示または変更します。
DEL 1 個以上のファイルを削除します。
DIR ディレクトリ中のファイルやサブディレクトリの一覧を
表示します。
ECHO メッセージの表示、コマンド エコーのオン、オフの
指定をします。
ENDLOCAL バッチ ファイルで、環境変数のローカル化を終了します。
ERASE 1 個以上のファイルを削除します。
EXIT CMD.EXE プログラム (コマンド インタープリター) を
終了します。
FOR 指定されたコマンドを、ファイルの集合の各ファイルに
対して実行します。
FTYPE ファイル拡張子の関連付けで使われるファイル タイプを
表示または変更します。
GOTO バッチ プログラム中で、ラベルで定義されている行へ
Windows コマンドインタープリターの実行を移します。
GRAFTABL Windows がグラフィック モードで拡張文字セットを
表示できるようにします。
IF バッチ ファイル中で、条件処理を実行します。
MD ディレクトリを作成します。
MKDIR ディレクトリを作成します。
MKLINK シンボリック リンクおよびハード リンクを作成します。
MOVE 1 個以上のファイルをディレクトリから別の
ディレクトリに移動します。
PATH 実行可能ファイルの検索パスを表示または設定します。
PAUSE バッチ ファイルの処理を一時停止し、メッセージを表示します。
POPD 現在のディレクトリを PUSHD で保存したディレクトリに戻します。
PROMPT Windows コマンド プロンプトを変更します。
PUSHD 現在のディレクトリを保存して、変更します。
RD ディレクトリを削除します。
REM バッチ ファイルや CONFIG.SYS の中で、コメント (注釈) を
記録します。
REN ファイルの名前を変更します。
RENAME ファイルの名前を変更します。
RMDIR ディレクトリを削除します。
SET Windows 環境変数を表示、設定、または削除します。
SETLOCAL バッチ ファイルで、環境変数のローカル化を開始します。
SHIFT バッチ ファイルで、置き換え可能パラメーターの位置を
シフトします。
START 別のウィンドウを起動して、指定したプログラム
またはコマンドを実行します。
TIME システム時刻を表示または変更します。
TITLE コマンド プロンプト ウィンドウのタイトルを設定します。
TYPE テキスト ファイルの内容を表示します。
VER Windows のバージョンを表示します。
VERIFY ファイルがディスクへ正しく書き込まれたかを
照合するかどうかWindows へ指定します。
VOL ディスクのボリューム ラベルとシリアル番号を表示します。

次章(?)より、よく使う内部コマンドのPowerShell置き換えスクリプトを検討していきます。