GetWindowLong

GetWindowLongは、ウィンドウに関する様々なLong値を取得できる。

GetWindowLongA function (winuser.h) - Win32 apps | Microsoft Docs

たとえば、アプリケーションインスタンスハンドルhInstanceは、WinMain(_tWinMain)の引数として渡されるが、それが渡されない他の関数から使いたい場合は、GetWindowLongにGWL_HINSTANCEを指定して取得する。
GetWindowLong.cppで、

引数のほうのhInstanceの値を序盤でメッセージボックスで表示。

WM_PAINTで、GetWindowLongにGWL_HINSTANCEを渡して取得したhInstanceの値はTextOutで描画。

引数のhInstanceは0x00400000
引数のhInstanceは0x00400000

引数のhInstanceは0x00400000。

GetWindowLongで取得したhInstanceも0x00400000
GetWindowLongで取得したhInstanceも0x00400000

GetWindowLongで取得したhInstanceも0x00400000。

Sample/winapi/GetWindowLong/GetWindowLong/src/GetWindowLong at master · bg1bgst333/Sample · GitHub

GetClassName

GetClassNameで、そのウィンドウのウィンドウクラス名を取得する。

GetClassName function (winuser.h) - Win32 apps | Microsoft Docs

GetClassName.cppで、

ウィンドウクラス名は"GetClassName"。

WM_CREATE時に、

GetClassNameで、ウィンドウクラス名をtszClassNameに格納。

WM_PAINT時に、

TextOutでtszClassNameを描画。

取得出来てる
取得出来てる

取得出来てる。

Sample/winapi/GetClassName/GetClassName/src/GetClassName at master · bg1bgst333/Sample · GitHub

CREATESTRUCT

CREATESTRUCTは、CreateWindowによってWM_CREATEが呼ばれた時に、ウィンドウ作成情報を格納している構造体。

CREATESTRUCTA (winuser.h) - Win32 apps | Microsoft Docs

WM_CREATEのlParamは、CREATESTRUCTへのポインタにキャストでき、ウィンドウ作成情報が格納されている。

Window.hで、

ウィンドウ名m_wstrWindowNameを持ち、ウィンドウハンドルをキーとし、CWindowオブジェクトポインタを値とするマップm_mapWindowMapもある。
メンバのWindowProcにウィンドウメッセージ処理を書く。

Window.cppでは、

WM_DESTROYで、ウィンドウ名をメッセージボックス出力するぐらい。
あとはDefWindowProcに任せる。
CREATESTRUCT.cppで、

2つのウィンドウを作成する。
ウィンドウ名を"Window1", "Window2", とし、それぞれCWindowオブジェクトを作成し、そのポインタをCreateWindowの最後の引数に渡す。

WM_CREATEなら、lParamからlpCreateStructを取り出し、lpCreateStruct->lpCreateParamsで、CWindowオブジェクトポインタpWindowを取り出す。
pWindowは、ウィンドウ名m_wstrWindowNameも持っているので、そこにlpCreateStruct->lpszNameをセットする。
hwndをキー、pWindowを値として、ウィンドウマップに登録。
WM_CREATE以外なら、ウィンドウマップからpWindowを取得する。
pWindowが無い場合は、DefWindowProcを呼ぶ。
pWindowがある場合は、pWindow->WindowProcを呼ぶ。

2つのウィンドウが表示
2つのウィンドウが表示

2つのウィンドウが表示。
Window1を閉じると、

WM_DESTROYのウィンドウ名表示にWindow1と出る
WM_DESTROYのウィンドウ名表示にWindow1と出る

WM_DESTROYのウィンドウ名表示にWindow1と出る。

今度はWindow2を閉じる
今度はWindow2を閉じる

最初からやり直して、今度はWindow2を閉じる。

Window2が出る
Window2が出る

Window2が出る。

それぞれのウィンドウごとに、CWindowオブジェクトを割り当てて、内部の状態も独立して別に持てるし、ウィンドウプロシージャも別々にすることができそう。

Sample/winapi/CREATESTRUCT/CREATESTRUCT/src/CREATESTRUCT at master · bg1bgst333/Sample · GitHub

SOCKADDR_IRDA

赤外線通信において、接続相手の情報などは、accept時などにSOCKADDR_IRDA形式で格納される。

SOCKADDR_IRDA (af_irda.h) - Win32 apps | Microsoft Docs

ここから本格的に赤外線通信をしていくが、まずは準備など。

STOLA充電中
STOLA充電中

この日のために、数年前から中古で買っておいたSTOLA。
ようやく今日、日の目を見る。

Sigmatel最後の1本
Sigmatel最後の1本

そしてSigmatelのIrDAドングル。
最後の1本。
もうおそらく製造も販売もしていない。
壊れやすいので、これが壊れたら終わり。

ひたすらアドレス帳に登録していく
ひたすらアドレス帳に登録していく

ひたすらアドレス帳に登録していく。
もちろん架空の情報を。

Windows 10 で赤外線ポート(Irda)を使う方法

当初はここを参考にWindows10で準備。

この時点で赤外線はない
この時点で赤外線はない

この時点で赤外線はない。

コマンドプロンプトの管理者実行
コマンドプロンプトの管理者実行

コマンドプロンプトの管理者実行。

Microsoft Windows [Version 10.0.18363.900]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\WINDOWS\system32>netcfg -u ms_irdanetcfg -c p -I ms_irda
ms_irdanetcfg をアンインストールしようとしています ...

... ms_irdanetcfg はインストールされていません。

... 完了しました。

ms_irda をインストールしようとしています ...

... 失敗しました。エラー コード: 0x80070002。


C:\WINDOWS\system32>

なぜか失敗。

Windows 10 で赤外線ポート(Irda)を使う方法

PowerShellでやり直す。

PS C:\WINDOWS\system32> get-WindowsCapability -online -Name "Network.Irda~~~~0.0.1.0"


Name         : Network.Irda~~~~0.0.1.0
State        : NotPresent
DisplayName  : IrDA 赤外線
Description  : IrDA デバイスのサポート。特別な IrDA ハードウェアが必要です。
DownloadSize : 604101
InstallSize  : 1236233



PS C:\WINDOWS\system32>

して、

PS C:\WINDOWS\system32> Remove-WindowsCapability -online -Name "Network.Irda~~~~0.0.1.0"


Path          :
Online        : True
RestartNeeded : False



PS C:\WINDOWS\system32>

して、

PS C:\WINDOWS\system32> add-WindowsCapability -online -Name "Network.Irda~~~~0.0.1.0"


Path          :
Online        : True
RestartNeeded : False



PS C:\WINDOWS\system32>

これはかかった。
そして、

PS C:\WINDOWS\system32> get-WindowsCapability -online -Name "Network.Irda~~~~0.0.1.0"


Name         : Network.Irda~~~~0.0.1.0
State        : Installed
DisplayName  : IrDA 赤外線
Description  : IrDA デバイスのサポート。特別な IrDA ハードウェアが必要です。
DownloadSize : 604101
InstallSize  : 1236233



PS C:\WINDOWS\system32>

これで使えるのでは。

赤外線が出現
赤外線が出現

赤外線が出現。

クリックして設定
クリックして設定

クリックして設定。

保存先を変更
保存先を変更

保存先を変更。

ドングルを接続していない時はこうなる
ドングルを接続していない時はこうなる

ドングルを接続していない時はこうなる。

接続しているとこうなる
接続しているとこうなる

接続しているとこうなる。

設定の変更とかよくやるけど効果なし
設定の変更とかよくやるけど効果なし

設定の変更とかよくやるけど効果なし。
(9660にすると受信しやすいとか昔あったけどなあ。)
STOLAから1件のアドレス送信をずっと試してたけど、どうも現在のWindows10だと反応もしない。

Windows7だと反応した
Windows7だと反応した

Windows7だと反応した。
ドライバかな・・・。
(Windows7は以前からドライバのインストールとか既に準備済みだった模様。
Windows10の初期のドライバを入れていても、Windows10の最近のバージョンで動くかは不明だし。)

STOLAで赤外線受信にするとPCから送信するための画面も出る
STOLAで赤外線受信にするとPCから送信するための画面も出る

STOLAで赤外線受信にするとPCから送信するための画面も出る。
(この時irftpが出るのかな。)

というわけでなんとか標準の赤外線通信はできそう。
ただし、全件送信時の認証には対応してなかった気がするので1つずつだけ。
さて、これをどうやってプログラムで実現するか。

// ヘッダファイルのインクルード
#include <tchar.h>		// TCHAR型
#include <winsock2.h>	// Windowsソケット
#include <windows.h>	// 標準WindowsAPI
#include <af_irda.h>	// IrDA

// _tmain関数の定義
int _tmain(int argc, TCHAR *argv[]){	// main関数のTCHAR版.

	// 変数の宣言と初期化.
	WSADATA wsaData;	// WinSockの初期化に必要なWSADATA構造体変数wsaData.
	int iRet;	// WSAStartupの結果を格納するiRet.
	int soc;	// ソケットsoc.
	int iBind;	// バインド結果iBind.
	SOCKADDR_IRDA soc_addr = {AF_IRDA, 0, 0, 0, 0, "OBEX"};	// soc_addrをこのように初期化.

	// WinSockの初期化.
	iRet = WSAStartup(MAKEWORD(2,2), &wsaData);	// WSAStartupでWinSockの初期化.
	if (iRet == 0){	// 0なら成功.

		// 結果成功の出力.
		_tprintf(_T("WSAStartup success!\n"));	// "WSAStartup success!"を出力.

		// 赤外線ソケットの作成.
		soc = socket(AF_IRDA, SOCK_STREAM, 0);	// socketでAF_IRDAなsocを作成.
		if (soc != INVALID_SOCKET){	// INVALID_SOCKETでない時.

			// ソケットディスクリプタを出力.
			_tprintf(_T("soc = %d\n"), soc);	// socを出力.

			// バインド
			iBind = bind(soc, (const struct sockaddr *)&soc_addr, sizeof(SOCKADDR_IRDA));	// bindでsocにsoc_addrをバインド.
			if (iBind != SOCKET_ERROR){	// SOCKET_ERRORでなければ.

				// 結果成功の出力.
				_tprintf(_T("bind success!\n"));	// "bind success!"と出力.

			}
			else{	// SOCKET_ERROR.

				// 結果失敗の出力.
				_tprintf(_T("bind failed!\n"));	// "bind failed!"と出力.

			}

			// socを閉じる.
			closesocket(soc);	// closesocketでsocを閉じる.

		}

		// 終了処理.
		WSACleanup();	// WSACleanupで終了処理.

	}

	// プログラムの終了.
	return 0;

} 

SOCKADDR_IRDAのsoc_addrを上記のように初期化し、バインドしてみる。

WSAStartup success!
soc = 128
bind failed!
続行するには何かキーを押してください . . .

失敗する。
標準の"Irmon"が邪魔しているからだ。

Irmonを停止させる
Irmonを停止させる

Irmonを停止させる。

停止状態
停止状態

停止状態。
こうすると、

WSAStartup success!
soc = 128
bind success!
続行するには何かキーを押してください . . .

バインド成功。
(なのでサービス停止とかを扱ったが、プログラムでやる必要もなくなった。
また、PCが送信側の場合はirftpプロセスが邪魔になった記憶があった気がしたので、
プロセス制御も扱ったが、この先で使うことはなかったので、勘違いだったかもしれない。)
ひとまずこの部分をクリアしたので、

		// 赤外線ソケットの作成.
		soc = socket(AF_IRDA, SOCK_STREAM, 0);	// socketでAF_IRDAなsocを作成.
		if (soc != INVALID_SOCKET){	// INVALID_SOCKETでない時.

			// ソケットディスクリプタを出力.
			_tprintf(_T("soc = %d\n"), soc);	// socを出力.

			// バインド
			iBind = bind(soc, (const struct sockaddr *)&soc_addr, sizeof(SOCKADDR_IRDA));	// bindでsocにsoc_addrをバインド.
			if (iBind != SOCKET_ERROR){	// SOCKET_ERRORでなければ.

				// 結果成功の出力.
				_tprintf(_T("bind success!\n"));	// "bind success!"と出力.

				// リッスン
				iListen = listen(soc, 1);	// listenで1つ待ち受ける.
				if (iListen != SOCKET_ERROR){	// SOCKET_ERRORでなければ.

					// 結果成功の出力.
					_tprintf(_T("listen success!\n"));	// "listen success!"と出力.

					// アクセプト
					acc = accept(soc, (struct sockaddr *)&acc_addr, &acc_addr_len);	// acceptで待ち受けて, アクセプトソケットをaccに格納.
					if (soc != INVALID_SOCKET){	// INVALID_SOCKETでない時.

						// アクセプトソケットディスクリプタやアドレス情報を出力.
						_tprintf(_T("acc = %d\n"), acc);	// accを出力.
						_tprintf(_T("acc_addr.irdaAddressFamily = %hu\n"), acc_addr.irdaAddressFamily);	// acc_addr.irdaAddressFamilyを出力.
						_tprintf(_T("acc_addr.irdaDeviceID = %02X %02X %02X %02X\n"), acc_addr.irdaDeviceID[3], acc_addr.irdaDeviceID[2], acc_addr.irdaDeviceID[1], acc_addr.irdaDeviceID[0]);	// acc_addr.irdaDeviceIDを出力.
						iServerNameLen = MultiByteToWideChar(CP_ACP, 0, acc_addr.irdaServiceName, -1, NULL, 0);	// MultiByteToWideCharで長さを取得.
						MultiByteToWideChar(CP_ACP, 0, acc_addr.irdaServiceName, -1, tszServerName, iServerNameLen);	// MultiByteToWideCharで変換.
						_tprintf(_T("acc_addr.irdaServiceName = %s\n"), tszServerName);	// tszServerNameを出力.

						// accを閉じる.
						closesocket(acc);	// closesocketでaccを閉じる.

					}
					
				}
				else{	// SOCKET_ERROR.

					// 結果失敗の出力.
					_tprintf(_T("listen failed!\n"));	// "listen failed!"と出力.

				}

			}
			else{	// SOCKET_ERROR.

				// 結果失敗の出力.
				_tprintf(_T("bind failed!\n"));	// "bind failed!"と出力.

			}

			// socを閉じる.
			closesocket(soc);	// closesocketでsocを閉じる.

		}

接続先情報を出力する。
C# + .NET Frameworkで赤外線通信をする時は、32feet.NETを使うだろう。

CodePlex Archive

これのIrDA Clientのサンプルをビルドして起動すると、

16進数8桁のDeviceIDが出ると思う。
16進数8桁のDeviceIDが出ると思う。

16進数8桁のDeviceIDが出ると思う。
これと同じものが、上のコンソールプログラムでも、

WSAStartup success!
soc = 128
bind success!
listen success!
acc = 132
acc_addr.irdaAddressFamily = 26
acc_addr.irdaDeviceID = CB 42 06 90
acc_addr.irdaServiceName = LSAP-SEL2
続行するには何かキーを押してください . . .

出る。
残念ながらSTOLAは毎回DeviceIDが変更される仕組みになっている模様。
DeviceIDは固定の端末もある。
(1度だけスクショと同じIDが出た時があったのだが、スクショし忘れる失態・・・。)

さて、ここまで来たら、あとは赤外線通信をするだけだ。

https://www.ttc.or.jp/application/files/5015/5417/2604/JF-IR007.10v2.pdf

ここに赤外線通信仕様がある。
(以前のURLリンクから変わった模様。)
PDFにおける66ページ目に例があるので、それを参考にするといい。

最低限、1件のアドレスを受信できるプログラムを作った。
(いろいろ実装すると大変なのであくまで最低限。)

結果はこんな感じ
結果はこんな感じ

結果はこんな感じ。

鈴木一郎.vcfが保存されてる
鈴木一郎.vcfが保存されてる

鈴木一郎.vcfが保存されてる。

こんな感じのvCardが送られてくる
こんな感じのvCardが送られてくる

こんな感じのvCardが送られてくる。
いやはや、WinSockでこれをやるのは大変。
32feet.NETもたいして変わらないけど、これよりはマシ。

Sample/winapi/SOCKADDR_IRDA/SOCKADDR_IRDA/src/SOCKADDR_IRDA at master · bg1bgst333/Sample · GitHub

AF_IRDA

ここまでサービス制御やプロセス制御についてやってきたのも、赤外線通信をするため。
まずは、赤外線通信用のソケットをAF_IRDAを指定して作成する。

IrDA and socket | Microsoft Docs

ws2_32.lib
ws2_32.lib

基本的にWinSockを使うので、ws2_32.libをリンク。

実行すると、

WSAStartup success!
soc = 128
続行するには何かキーを押してください . . .

ソケット自体は、何もしなくても作れる。

Sample/winapi/AF_IRDA/AF_IRDA/src/AF_IRDA at master · bg1bgst333/Sample · GitHub

PathStripPath

PathStripPathで、パスからファイル名部分を取り出す。

PathStripPathA function (shlwapi.h) - Win32 apps | Microsoft Docs
WindowsAPIPath

GetFileTitleとかやり方はいくつかあるけど、コンソールアプリならshlwapi.lib系のほうが何かと近そうなので。
(GetFileTitleはcomdlg32.libなのでウィンドウアプリ系っぽい。)

shlwapi.libをリンク
shlwapi.libをリンク

shlwapi.libをリンク。

ファイル、フォルダ、それからドライブルートフォルダだけの場合、そしてデバイス形式の場合どうなるか。

tszFilePath = test.txt
tszFolderPath = folder
tszDrivePath = C:
tszDeviceName = explorer.exe
続行するには何かキーを押してください . . .

ルートフォルダだとドライブレターだけになるし、デバイス形式でもファイル名を取ることが出来た。

Sample/winapi/PathStripPath/PathStripPath/src/PathStripPath at master · bg1bgst333/Sample · GitHub

GetProcessImageFileName

GetProcessImageFileNameで、そのプロセスのプロセス名というか、実行ファイルのパスを取得できる。

GetProcessImageFileNameA function (psapi.h) - Win32 apps | Microsoft Docs
C++でプロセス名からプロセスIDを取得する - 備忘録
GetProcessImageFileName()で取得できるパス名について – プログラミング – Home

最初、QueryFullProcessImageNameを使ってたのだが、どうも失敗する。
しかもなぜかERROR_INSUFFICIENT_BUFFERで。
管理者権限かなとも思ったが、関係ない模様。
なので、

GetProcessImageFileNameでやってみた。

sizeof(dwProcessIds) = 4096
cbNeeded = 196
dwNum = 49
dwProcessIds[0] = 0, OpenProcess failed!
dwProcessIds[1] = 4, OpenProcess failed!
dwProcessIds[2] = 236, OpenProcess failed!
dwProcessIds[3] = 308, OpenProcess failed!
dwProcessIds[4] = 356, OpenProcess failed!
dwProcessIds[5] = 364, OpenProcess failed!
dwProcessIds[6] = 392, OpenProcess failed!
dwProcessIds[7] = 452, OpenProcess failed!
dwProcessIds[8] = 460, OpenProcess failed!
dwProcessIds[9] = 468, OpenProcess failed!
dwProcessIds[10] = 556, OpenProcess failed!
dwProcessIds[11] = 632, OpenProcess failed!
dwProcessIds[12] = 716, OpenProcess failed!
dwProcessIds[13] = 764, OpenProcess failed!
dwProcessIds[14] = 800, OpenProcess failed!
dwProcessIds[15] = 840, OpenProcess failed!
dwProcessIds[16] = 280, OpenProcess failed!
dwProcessIds[17] = 1056, OpenProcess failed!
dwProcessIds[18] = 1088, OpenProcess failed!
dwProcessIds[19] = 1208, OpenProcess failed!
dwProcessIds[20] = 1348, OpenProcess failed!
dwProcessIds[21] = 1380, OpenProcess failed!
dwProcessIds[22] = 1480, OpenProcess failed!
dwProcessIds[23] = 1524, OpenProcess failed!
dwProcessIds[24] = 1572, OpenProcess failed!
dwProcessIds[25] = 912, hProcess = 56, tszProcessName = \Device\HarddiskVolume2\
Windows\System32\taskhost.exe
dwProcessIds[26] = 2068, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Windows\System32\dwm.exe
dwProcessIds[27] = 2116, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Windows\explorer.exe
dwProcessIds[28] = 2556, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files\TortoiseGit\bin\TGitCache.exe
dwProcessIds[29] = 2700, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\HTC\HTC Sync Manager\HTC Sync\adb.exe
dwProcessIds[30] = 2860, OpenProcess failed!
dwProcessIds[31] = 3504, OpenProcess failed!
dwProcessIds[32] = 3692, OpenProcess failed!
dwProcessIds[33] = 1564, OpenProcess failed!
dwProcessIds[34] = 3056, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\Dropbox\Client\Dropbox.exe
dwProcessIds[35] = 2108, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\Dropbox\Client\Dropbox.exe
dwProcessIds[36] = 2364, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\Dropbox\Client\Dropbox.exe
dwProcessIds[37] = 1468, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\Dropbox\Client\102.4.431\QtWebEngineProcess.exe
dwProcessIds[38] = 1072, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\Dropbox\Client\102.4.431\QtWebEngineProcess.exe
dwProcessIds[39] = 836, hProcess = 56, tszProcessName = \Device\HarddiskVolume2\
Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\devenv.exe
dwProcessIds[40] = 3932, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\TeraPad\TeraPad.exe
dwProcessIds[41] = 4032, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Windows\System32\notepad.exe
dwProcessIds[42] = 3964, OpenProcess failed!
dwProcessIds[43] = 2540, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Program Files (x86)\Microsoft Visual Studio 8\Common7\IDE\mspdbsrv.exe
dwProcessIds[44] = 3624, OpenProcess failed!
dwProcessIds[45] = 2176, OpenProcess failed!
dwProcessIds[46] = 828, hProcess = 56, tszProcessName = \Device\HarddiskVolume2\
Windows\SysWOW64\cmd.exe
dwProcessIds[47] = 4004, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Windows\System32\conhost.exe
dwProcessIds[48] = 1008, hProcess = 56, tszProcessName = \Device\HarddiskVolume2
\Project\Cloud\github.com\Sample\winapi\GetProcessImageFileName\GetProcessImageF
ileName\src\GetProcessImageFileName\release\GetProcessImageFileName.exe
続行するには何かキーを押してください . . .

まあ、開けないのはしょうがないとして、開けるものは取得できた。
ただし、パスがデバイス形式なのは注意。

Sample/winapi/GetProcessImageFileName/GetProcessImageFileName/src/GetProcessImageFileName at master · bg1bgst333/Sample · GitHub