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