前の10件 5  6  7  8  9  10  11  12  13  14  15

記事一覧

2007年03月18日

HRESULT型とは?

HRESULTとはlong型の数値だ。この型はCOMインターフェース関連の関数の戻り値として利用されることが多い。

関数の戻り値として使われることの多いBOOL型が成功を意味するのは「1」(=TRUE)で失敗が「0」(=FALSE)なのと似ている。

大きな違いはBOOL型がTRUEとFALSEの2値しか取らないのに対して、HRESULT型では0x00000000~0xFFFFFFFFまでの多くの値が利用される。そのためBOOL型では失敗の原因を取得するためにGetLastError()を利用する必要があったが、HRESULT型では値を見るだけで失敗の原因まで分かる。

HRESULT型として使われる値は下のような規則に則って決定されている。これは「winerror.h」のコメント内に記述されているものだ。

//  HRESULTs are 32 bit values layed out as follows:
//
//   3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
//   1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
//  +-+-+-+-+-+---------------------+-------------------------------+
//  |S|R|C|N|r|    Facility         |               Code            |
//  +-+-+-+-+-+---------------------+-------------------------------+
//
//  where
//
//      S - Severity - indicates success/fail
//
//          0 - Success
//          1 - Fail (COERROR)
//
//      R - reserved portion of the facility code, corresponds to NT's
//              second severity bit.
//
//      C - reserved portion of the facility code, corresponds to NT's
//              C field.
//
//      N - reserved portion of the facility code. Used to indicate a
//              mapped NT status value.
//
//      r - reserved portion of the facility code. Reserved for internal
//              use. Used to indicate HRESULT values that are not status
//              values, but are instead message ids for display strings.
//
//      Facility - is the facility code
//
//      Code - is the facility's status code


要約すると

・上位1bitが「1」なら"失敗"、「0」なら"成功"
・16~26bitまでの11ビットの値はカテゴリ
・0~15bitまでの下位WORDの値は詳細情報

のように値が決定されている。つまり、あるHRESULT型の値が"成功"を示しているか、それとも"失敗"を示しているのかは

	HRESULT	hr;

	hr = function_example();

	if(hr & 0x80000000)
	{
		//上位1bitが「1」なので成功
	}
	else
	{
		//失敗
	}


というように判定できる。

この判別を簡単にするために「winerror.h」内では以下の2つのマクロが定義されている。

#define SUCCEEDED(hr) (((HRESULT)(hr)) >= 0)
#define FAILED(hr) (((HRESULT)(hr)) < 0)


これを利用すると成功と失敗の判別は以下のようにできる。

	HRESULT	hr;

	hr = function_example();

	if(SUCCEEDED(hr))
	{
		//成功
	}
	else
	{
		//失敗
	}

	if(SUCCEEDED(hr))
	{
		//成功
	}

	if(FAILED(hr))
	{
		//失敗
	}


また、前に述べたようにHRESULT型では値を参照することで失敗の原因を知ることができる。例えば...

	HRESULT	hr;

	hr = function_example();

	switch(hr)
	{
	case	S_OK:
		//成功
		break;

	case	E_FAIL:
		//一般的な失敗
		break;

	case	E_OUTOFMEMORY:
		//メモリー不足による失敗
		break;

	case	E_INVALIDARG:
		//引数不正による失敗
		break;

	case	E_UNEXPECTED:
		//予期せぬ失敗
		break;

	case	E_OUTOFMEMORY:
		//による失敗
		break;

	case	E_POINTER:
		//不正なポインターによる失敗
		break;

	(...省略...)

	default:
		//その他(の失敗時もしくは成功時)
		break;
	}


のようにできる。ここで、例えば「E_OUTOFMEMORY」が示す数値はWin32の場合は「0x8007000E」だがMacの場合は「0x80000002」のようにプラットフォームによって値が変わることがある。そのため具体的な数値をソースコードに記述してはいけない。

定義されている値から推測できるように「S_」から始まるものは"成功"を示し、「E_」から始まるものは"失敗"を示す。また、多くの場合成功時は「S_OK」が返る。しかし、成功時は必ずS_OKというわけではない。そのため先に述べた「SUCCEEDED()」マクロによって判定すること。

2007年03月17日

Vistaのバグ(その4) デスクトップにコピーや移動したファイルが表示されない!

vista4_1.gif
これもよく生じるちょっと不便なバグ。

任意のフォルダーからファイルをデスクトップにコピーや移動したとき、もしくは使っているアプリケーションがデスクトップ上にファイルを作成したときに起きる。このバグも必ず起きるというわけではないが頻発する。

vista4_2.gif
ここでは画像ファイルをデスクトップへ移動したにも関わらずデスクトップ上にアイコンが表示されていない。もちろん移動したので元のフォルダからはファイルのアイコンは消えている。

vista4_3.gif
バグでファイルが失われたというわけではない。

デスクトップ上で右クリックをして現れるメニューから「最新の情報に更新」を選択して画面表示を更新する。

vista4_4.gif
するとファイルのアイコンが現れる。

「Excelなどで編集をして、デスクトップに保存。そしてExcelを閉じてからデスクトップを見たら保存したはずのファイルがない!」というように焦ることもあるだろう(少なくとも私はこのようなことがあった)。

また、このバグではデスクトップのアイコンが自動的に整列されるようにしていても、新しく移動したファイルなどは「コンピュータ」と「ごみ箱」のアイコンの間に現れる。つまりアイコンの位置が勝手に変わってしまう。そのためアイコンを表示してからアイコンを手動で動かさなければならず二度手間なのが不便だ。

Vistaのバグ(その3) ファイルサイズがマイナスの値になる!

vista3.gif
Windows VistaのエクスプローラーはWindows XPの頃と比べて非常に多くのバグがある。さすがに致命的なものは"少ない"(無いわけでない)のであまり問題はないのだが不便なことが多い。

そんな致命的ではないバグの1つがこれ。エクスプローラーのステータスバー部分に表示されているファイルサイズがマイナスになっている。残念ながら「最新の情報に更新」をしても正常な値は表示されない。正常なファイルサイズを知りたい場合は右クリックして「プロパティ」を表示する。

このバグは必ず起きるというわけではない。発生条件はよく分からないものの頻繁に発生する。


TOSHIBA RDシリーズからネットdeダビング経由で映像を取得する

test127.JPG
TOSHIBA RD-H1などのHDDレコーダーには「ネットdeダビング」という機能により機器間で録画した映像をやりとりできる。ここではこのネットdeダビングを利用してTOSHIBA RDシリーズからPCへ映像をダビング(コピー)する。

今回のソースコードにはFTPサーバー部しか実装されていない。そのため実際にTOSHIBA RDシリーズから映像データをダビングするには「NetBIOSの名前問い合わせに返答する 」を起動しておく。

■「ネットdeダビング」での処理の流れ

1.(RD上で操作)赤外線リモコンの「ネットdeダビング」ボタンを押す
2.(RD上で操作)ダビング先として「ネットワーク」を選択する
3.(RD→PC)NetBIOS(UDP/137)を利用した名前の問い合わせが送信される
4.(PC→RD)NetBIOS(UDP/137)による名前を応答する
5.(RD上で表示)「ネットワーク機器名」としてNetBIOSで応答のあった機器が列挙される
6.(RD上で操作)PCを示すダビング先を選択する
7.(RD⇔PC)PCがFTPサーバー、RDがFTPクライアントとして動作して映像がPCにアップロードされる

前回の「NetBIOSの名前問い合わせに返答する 」により1~6の処理が可能になる。今回は7の処理を実装した。
ただし実装はシングルスレッドで、かつ、ネットワーク遅延やパケット分割、各種エラー発生時の処理は省略している。そのため実際に流用する場合はそれらを考慮したものに変更する必要がある。


■「ネットdeダビング」でのFTPの流れ

1.(PC)(TCP/21で受信待機する)
2.(RD→PC)(FTPサーバー(PC)へTCP/21で接続する)
3.(RD←PC)220
4.(RD→PC)USER b2b404fc9e0344e64c40d549d0c21219
5.(RD←PC)331
6.(RD→PC)PASS b2b404fc9e0344e64c40d549d0c21219
7.(RD←PC)230
8.(RD→PC)TYPE I
9.(RD←PC)200

10.(RD→PC)PASV
11.(PC)(データ転送用に任意のポートを開ける)
12.(RD←PC)227 Entering Passive Mode (x,x,x,x,y,y). (xは自分のIPアドレス、yはデータ転送用のポート番号)
13.(PC)(データ転送用のポートで受信待機する)
14.(RD→PC)(データ転送用のポートにアクセスする)
15.(RD→PC)STOR $netdubbing$dubbinginfo.xml
16.(RD←PC)150
17.(RD→PC)(データ転送用のポートにXMLが送られる。内容は以下のようなもの)

<?xml version="1.0" encoding="shift_jis"?>
<dubbinginfo>
<titlesize>2246</titlesize>
<device>0</device>
</dubbinginfo>

18.(RD←PC)226

19.(RD→PC)PASV
20.(PC)(データ転送用に任意のポートを開ける)
21.(RD←PC)227 Entering Passive Mode (x,x,x,x,y,y). (xは自分のIPアドレス、yはデータ転送用のポート番号)
22.(PC)(データ転送用のポートで受信待機する)
23.(RD→PC)(データ転送用のポートにアクセスする)
24.(RD→PC)RETR $xml$status.xml
25.(RD←PC)150
26.(RD←PC)(データ転送用のポートにXMLを送る。内容は以下のようなもの)

<?xml version=\"1.0\" encoding=\"shift_jis\"?>
<status>
<dubbing>ready</dubbing>
<accept_dubbing>ready</accept_dubbing>
<device0_remain>167772160</device0_remain>
<device0_recorded>0</device0_recorded>
<device0_title_remain>350</device0_title_remain>
<device1_remain>167772160</device1_remain>
<device1_recorded>0</device1_recorded>
<device1_title_remain>350</device1_title_remain>
</status>

27.(RD←PC)226

28.(RD→PC)PASV
29.(PC)(データ転送用に任意のポートを開ける)
30.(RD←PC)227 Entering Passive Mode (x,x,x,x,y,y). (xは自分のIPアドレス、yはデータ転送用のポート番号)
31.(PC)(データ転送用のポートで受信待機する)
32.(RD→PC)(データ転送用のポートにアクセスする)
33.(RD→PC)STOR $netdubbing$dev0.dat
34.(RD←PC)150
35.(RD→PC)(データ転送用のポートにmpegファイルの内容が送られる。内容は以下のようなもの)
36.(RD←PC)226

37.(RD→PC)PASV
38.(PC)(データ転送用に任意のポートを開ける)
39.(RD←PC)227 Entering Passive Mode (x,x,x,x,y,y). (xは自分のIPアドレス、yはデータ転送用のポート番号)
40.(PC)(データ転送用のポートで受信待機する)
41.(RD→PC)(データ転送用のポートにアクセスする)
42.(RD→PC)STOR $netdubbing$dubbinginfo.xml
43.(RD←PC)150
44.(RD→PC)(データ転送用のポートにXMLが送られる。内容は以下のようなもの)

<?xml version="1.0" encoding="shift_jis"?>
<dubbinginfo>
<titlesize>0</titlesize>
<device>0</device>
</dubbinginfo>

45.(RD←PC)226

46.(RD→PC)PASV
47.(PC)(データ転送用に任意のポートを開ける)
48.(RD←PC)227 Entering Passive Mode (x,x,x,x,y,y). (xは自分のIPアドレス、yはデータ転送用のポート番号)
49.(PC)(データ転送用のポートで受信待機する)
50.(RD→PC)(データ転送用のポートにアクセスする)
51.(RD→PC)RETR $xml$status.xml
52.(RD←PC)150
53.(RD←PC)(データ転送用のポートにXMLを送る。内容は以下のようなもの)

<?xml version=\"1.0\" encoding=\"shift_jis\"?>
<status>
<dubbing>ready</dubbing>
<accept_dubbing>ready</accept_dubbing>
<device0_remain>167772160</device0_remain>
<device0_recorded>0</device0_recorded>
<device0_title_remain>350</device0_title_remain>
<device1_remain>167772160</device1_remain>
<device1_recorded>0</device1_recorded>
<device1_title_remain>350</device1_title_remain>
</status>

54.(RD←PC)226

55.(RD→PC)RETR QUIT

※PCからRDへの送信例は適当に作成しています。本来はきちんとHDD容量や転送デバイスを考慮した値を入れる必要があります。

依存環境:ATL
#include <winsock2.h>
#pragma	comment(lib,"Ws2_32.lib")

bool	Send(SOCKET sock,const char* pszMessage)
{
	int		nRet;

	nRet = ::send(sock,pszMessage,(int)::strlen(pszMessage),0);

	return (nRet == SOCKET_ERROR) ? false : true;
}


bool	SendResponseCode(SOCKET sock,int nCode,const char* pszMessage=NULL)
{
	char	pszData[128];

	if(pszMessage == NULL)
		::sprintf_s(pszData,128,"%d \r\n",nCode);		//TOSHIBA RDシリーズでは%d直後のスペースが必須!
	else
		::sprintf_s(pszData,128,"%d %s\r\n",nCode,pszMessage);

	return	Send(sock,pszData);
}




bool	FTPServer()
{
	int		nRet;
	SOCKET	sock;
	SOCKET	sockDataConnection;
	sockaddr_in	addr;

	sock = ::socket(PF_INET,SOCK_STREAM,0);
	if(sock == SOCKET_ERROR)
		return	false;

	sockDataConnection = SOCKET_ERROR;


	addr.sin_family		= PF_INET;
	addr.sin_port		= ::htons(21);		//FTPポート
	addr.sin_addr.s_addr= INADDR_ANY;

	nRet = ::bind(sock,(sockaddr*)&addr,sizeof(sockaddr_in));
	if(nRet == SOCKET_ERROR)
	{
		::closesocket(sock);
		return	false;
	}

	nRet = ::listen(sock,SOMAXCONN);
	if(nRet == SOCKET_ERROR)
	{
		::closesocket(sock);
		return	false;
	}

	while(1)
	{
		int			nAddrLen;
		SOCKET		sockAccept;
		sockaddr_in	addrAccept;

		ATLTRACE("FTP Server Start...\n");
		nAddrLen = sizeof(sockaddr_in);
		sockAccept = ::accept(sock,(sockaddr*)&addrAccept,&nAddrLen);
		if(sockAccept == SOCKET_ERROR)
			break;

		//FTPサーバーへのコネクト開始メッセージ
		SendResponseCode(sockAccept,220);
		ATLTRACE("FTP Connected\n");

		while(1)
		{
			char	pBuffer[2048];

			nRet = ::recv(sockAccept,pBuffer,sizeof(pBuffer),0);
			if(nRet == SOCKET_ERROR)
				break;
			if(nRet == 0)
				continue;

			pBuffer[nRet] = NULL;
			ATLTRACE("%s\n",pBuffer);

			if(nRet < 4)
				break;

			if(::StrCmpNIA(pBuffer,"USER",4) == 0)
			{
				SendResponseCode(sockAccept,331);
				continue;
			}
			if(::StrCmpNIA(pBuffer,"PASS",4) == 0)
			{
				SendResponseCode(sockAccept,230);
				continue;
			}
			if(::StrCmpNIA(pBuffer,"TYPE",4) == 0)
			{
				SendResponseCode(sockAccept,200);
				continue;
			}
			if(::StrCmpNIA(pBuffer,"PASV",4) == 0)
			{
				//データ転送用ソケットを作って、そのポートをRDに知らせる

				int		i;
				int		nPort;
				char	pszMessage[1024];

				SOCKET		sockData;
				sockaddr_in	sDataAddr;

				sockData = ::socket(PF_INET,SOCK_STREAM,0);
				if(sockData == SOCKET_ERROR)
					break;

				nPort = 22222;
				for(i = 0; i < 100; i++)
				{
					::ZeroMemory(&sDataAddr,sizeof(sockaddr_in));
					sDataAddr.sin_family		= AF_INET;
					sDataAddr.sin_addr.s_addr	= INADDR_ANY;
					sDataAddr.sin_port			= ::htons(nPort);

					nRet = ::bind(sockData,(sockaddr*)&sDataAddr,sizeof(sockaddr_in));
					if(nRet != SOCKET_ERROR)
						break;

					//ポート番号を変えて再試行
					nPort++;
				}
				if(nRet == SOCKET_ERROR)
					break;
				::listen(sockData,1);

				int			nAddrLen;
				sockaddr	addr;

				nAddrLen = sizeof(sockaddr);
				::getsockname(sockAccept,&addr,&nAddrLen);

				::sprintf_s(pszMessage,1024,"Entering Passive Mode (%u,%u,%u,%u,%u,%u).\r\n"
					,addr.sa_data[2] & 0xFF
					,addr.sa_data[3] & 0xFF
					,addr.sa_data[4] & 0xFF
					,addr.sa_data[5] & 0xFF
					,(nPort >> 8) & 0x00FF
					,(nPort >> 0) & 0x00FF);

				//待機アドレス、ポートを知らせる
				SendResponseCode(sockAccept,227,pszMessage);

				//RDからの接続を待つ
				nAddrLen = sizeof(sockaddr_in);
				sockDataConnection = ::accept(sockData,(sockaddr*)&sDataAddr,&nAddrLen);
				::closesocket(sockData);
				if(sockDataConnection == SOCKET_ERROR)
					break;

				continue;
			}
			if(::StrCmpNIA(pBuffer,"STOR $netdubbing$dev0.dat",(int)::strlen("STOR $netdubbing$dev0.dat")) == 0)
			{
				//MPEGデータの受信

				char	pBuffer[2048];
				HANDLE	hFile;

				//ファイル決め打ちで保存
				hFile = ::CreateFile(_T("c:\\_Down\\test2.mpg"),GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
				if(hFile == INVALID_HANDLE_VALUE)
					break;

				ATLTRACE("Receiving mpeg file...\n");

				//転送開始
				SendResponseCode(sockAccept,150);

				while(1)
				{
					::Sleep(0);

					nRet = ::recv(sockDataConnection,pBuffer,sizeof(pBuffer),0);
					if(nRet == SOCKET_ERROR)
						break;
					if(nRet == 0)
						break;

					BOOL	ret;
					DWORD	dwWrittenSize;

					ret = ::WriteFile(hFile,pBuffer,nRet,&dwWrittenSize,NULL);
					if(ret == FALSE)
					{
						SendResponseCode(sockAccept,450);
						break;
					}
				}

				//転送終了
				SendResponseCode(sockAccept,226);

				::CloseHandle(hFile);
				ATLTRACE("Received\n");

				::closesocket(sockDataConnection);
				sockDataConnection = SOCKET_ERROR;

				continue;
			}
			if(::StrCmpNIA(pBuffer,"STOR",4) == 0)
			{
				//mpegファイル以外のデータ受信
				//dubbinginfoやコマンドなどを処理

				char	pBuffer[2048];

				//転送開始
				SendResponseCode(sockAccept,150);

				while(1)
				{
					::Sleep(0);

					nRet = ::recv(sockDataConnection,pBuffer,sizeof(pBuffer),0);
					if(nRet == SOCKET_ERROR)
						break;
					if(nRet == 0)
						break;
					pBuffer[nRet] = NULL;

					ATLTRACE("%s\n",pBuffer);
				}

				//転送終了
				SendResponseCode(sockAccept,226);

				::closesocket(sockDataConnection);
				sockDataConnection = SOCKET_ERROR;

				continue;
			}
			if(::StrCmpNIA(pBuffer,"RETR",4) == 0)
			{
				//ステータス情報の送信
				//本来ならdubbinginfoを見て返答すべきだが決め打ち

				char	pBuffer[4096];

				strcpy_s(pBuffer,4096,
					"<?xml version=\"1.0\" encoding=\"shift_jis\"?>\r\n"
					"<status>\r\n"
					"<dubbing>ready</dubbing>\r\n"
					"<accept_dubbing>ready</accept_dubbing>\r\n"
					"<device0_remain>167772160</device0_remain>\r\n"
					"<device0_recorded>0</device0_recorded>\r\n"
					"<device0_title_remain>350</device0_title_remain>\r\n"
					"<device1_remain>167772160</device1_remain>\r\n"
					"<device1_recorded>0</device1_recorded>\r\n"
					"<device1_title_remain>350</device1_title_remain>\r\n"
					"</status>\r\n");

				//転送開始
				SendResponseCode(sockAccept,150);

				Send(sockDataConnection,pBuffer);

				//転送終了
				SendResponseCode(sockAccept,226);

				::closesocket(sockDataConnection);
				sockDataConnection = SOCKET_ERROR;

				continue;
			}
			if(::StrCmpNIA(pBuffer,"QUIT",4) == 0)
				break;


			ATLTRACE("unknown cmd\n");
		}

		::shutdown(sockAccept,SD_BOTH);
		::closesocket(sockAccept);
	}


	if(sockDataConnection != SOCKET_ERROR)
	{
		::shutdown(sockDataConnection,SD_BOTH);
		::closesocket(sockDataConnection);
	}

	::shutdown(sock,SD_BOTH);
	::closesocket(sock);

	return	true;
}




bool	Test()
{
	WSAData wsaData;

	//Winsock2.2
	::WSAStartup(MAKEWORD(2,2),&wsaData);

	FTPServer();

	::WSACleanup();

	return	true;
}

プロジェクトファイルをダウンロード

2007年03月16日

NetBIOSの名前問い合わせに返答する

test126.JPG
TOSHIBAのHDDレコーダー「RD-H1」などは「ネットdeダビング」という機能によりLAN経由による映像データのコピー(ダビング)に対応している。

この「ネットdeダビング」ではダビング先を探す際にNetBIOSの名前解決を利用している。ここではUDPのポート137を使ったNetBIOSの名前問い合わせに対してRDシリーズ向けの返答を返している。ここで返す応答はNetBIOSに則っているようで反したものになっている。そのため実際のNetBIOS機器に流用する際は注意が必要だ。

#include <mstcpip.h>
#include <winsock2.h>
#pragma	comment(lib,"Ws2_32.lib")



//////////////////////////////////////////////////////
//以下の構造体はWindows SDKのサンプルに含まれる「iphdr.h」内からコピー
//

#pragma	pack(push,1)

//
// IPv4 Header (without any IP options)
//
typedef struct ip_hdr
{
    unsigned char  ip_verlen;        // 4-bit IPv4 version
                                     // 4-bit header length (in 32-bit words)
    unsigned char  ip_tos;           // IP type of service
    unsigned short ip_totallength;   // Total length
    unsigned short ip_id;            // Unique identifier 
    unsigned short ip_offset;        // Fragment offset field
    unsigned char  ip_ttl;           // Time to live
    unsigned char  ip_protocol;      // Protocol(TCP,UDP etc)
    unsigned short ip_checksum;      // IP checksum
    unsigned int   ip_srcaddr;       // Source address
    unsigned int   ip_destaddr;      // Source address
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;

//
// Define the UDP header 
//
typedef struct udp_hdr
{
    unsigned short src_portno;       // Source port no.
    unsigned short dest_portno;      // Dest. port no.
    unsigned short udp_length;       // Udp packet length
    unsigned short udp_checksum;     // Udp checksum
} UDP_HDR, *PUDP_HDR;

#pragma	pack(pop)


//////////////////////////////////////////////////////

#pragma	pack(push,1)

typedef	struct ip_hdr_udp
{
	IPV4_HDR	sHdrIp;
	UDP_HDR		sHdrUdp;
	BYTE		pData[1];

} UDP_IP_HDR;



typedef	struct netbios_name_reply
{
	USHORT	nTransactionID;
	USHORT	nFlags1;
	USHORT	nQuestion;
	USHORT	nAnswer;
	USHORT	nAuthority;
	USHORT	nAdditional;
	char	cbX;
	char	pszName[32];
	char	cbY;
	USHORT	nType;
	USHORT	nClass;
	USHORT	nTTL1;
	USHORT	nTTL2;
	USHORT	nLen;
	USHORT	nFlags2;
	UINT	nSrcAddr;
} NETBIOS_NAME_REPLY;


#pragma	pack(pop)


enum	RD_DEVICE_ID
{
	DEVICE_HDD			= 1,
	DEVICE_DVD			= 2,
	DEVICE_HDD_DVD		= 3,
	DEVICE_HDD2			= 4,
	DEVICE_HDD_HDD2		= 5,
	DEVICE_DVD_HDD2		= 6,
	DEVICE_HDD_DVD_HDD2	= 7,
	DEVICE_4			= 8,
	DEVICE_HDD_4		= 9,
	DEVICE_DVD_4		= 10,
	DEVICE_HDD_DVD_4	= 11,
	DEVICE_HDD2_4		= 12,
	DEVICE_HDD_HDD2_4	= 13,
	DEVICE_DVD_HDD2_4	= 14,
	//DEVICE_HDD_DVD_HDD2	= 15,
	DEVICE_5			= 16,
	DEVICE_HDD_5		= 17,
};




//
//	TOSHIBA RDシリーズ向けの名前問い合わせ返答処理
//
//【目的】
//TOSHIBA RDシリーズではネットDEダビングなどのためにダビング先機器を検索する際に
//UDP/137(NetBIOSの名前問い合わせ)を送信する。
//ここではその問い合わせに対して返答を返す。
//
//【処理の流れ】
//WinSock2のRAWソケットで全パケットをキャプチャする。
//キャプチャデータのうちUDP/137(NetBIOS名前問い合わせ)以外は無視
//NetBIOS名前問い合わせに対する返答をTOSHIBAのRDシリーズ用に送信
//
bool	ReplyNetBiosNS()
{
	int		nRet;
	SOCKET	sock;
	DWORD	dwReturned;

	//RAW SOCKETを作る
	sock = ::WSASocket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(sock == INVALID_SOCKET)
		return false;

	{
		SOCKADDR_IN	sSockAddrIn;

		//パケットを監視するインターフェースを選ぶ
		//ここでは簡易のため最初に見つかったインターフェースを利用している
		//PCに複数のインターフェース(無線LANと有線LANなど)がある場合は適宜変更すること
		//無線LANが何番目に見つかるかなど、物理的なNICとの関連付けをしたい場合は、GetIfTableを利用する
		{
			int		nNo;
			BYTE	pBuff[1024];
			SOCKET_ADDRESS_LIST* pSockAddrList;

			//何番目に見つかったインターフェースを利用するか
			//2番目のインターフェースを利用したい場合は「1」に変更
			nNo = 0;

			// アダプタの列挙
			nRet = ::WSAIoctl(sock,SIO_ADDRESS_LIST_QUERY,NULL,0,pBuff,1024,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR || dwReturned == 0)
			{
				::closesocket(sock);
				return false;
			}

			pSockAddrList = (SOCKET_ADDRESS_LIST*)pBuff;

			if(nNo >= pSockAddrList->iAddressCount)
			{
				::closesocket(sock);
				return false;
			}
			sSockAddrIn.sin_addr.s_addr= ((SOCKADDR_IN*)pSockAddrList->Address[nNo].lpSockaddr)->sin_addr.s_addr;
		}


		//バインドとキャプチャ-の有効化
		//WSAIoctlに渡すコマンドによってキャプチャ-種などを変更できる
		{
			BOOL	bOption;

			//sSockAddrIn.sin_addr.s_addr	= INADDR_ANY; <-これはNG.必ずSIO_ADDRESS_LIST_QUERYで求めた値を利用する
			sSockAddrIn.sin_family	= AF_INET;
			sSockAddrIn.sin_port	= htons(0);

			nRet = ::bind(sock,(sockaddr*)&sSockAddrIn,sizeof(SOCKADDR_IN));
			if(nRet == SOCKET_ERROR) 
			{
				::closesocket(sock);
				return false;
			}

			//パケットキャプチャの有効化(Windows 2000以降対応)
			bOption = TRUE;
			nRet = ::WSAIoctl(sock,SIO_RCVALL,&bOption,sizeof(BOOL),NULL,0,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR)
			{
				::closesocket(sock);
				return false;
			}
		}
	}





	//キャプチャの実行とNetBIOS名前問い合わせへの返答
	{
		char	pBuff[204800];
		WSABUF	sWsaBuf;
		DWORD	dwFlags;
		DWORD	dwTick;

		dwTick = ::GetTickCount();

		while(1)
		{
			::Sleep(0);

			//処理時間を10秒間に制限
			if(::GetTickCount() - dwTick > 10*1000)
				break;


			dwFlags = 0;
			::ZeroMemory(pBuff,sizeof(pBuff));
			sWsaBuf.buf = pBuff;
			sWsaBuf.len = sizeof(pBuff);

			//パケット受信
			nRet = ::WSARecv(sock,&sWsaBuf,1,&dwReturned,&dwFlags,NULL,NULL);
			if(nRet == SOCKET_ERROR)
				continue;


			//受信データの解析
			{
				UDP_IP_HDR*	pHdr;

				//UDP分のデータ長がなければ処理しない
				if(dwReturned < sizeof(UDP_IP_HDR))
					continue;

				pHdr = (UDP_IP_HDR*)pBuff;

				//IPv4、かつ、IPヘッダーサイズが5×32bit以外のときは処理しない
				if(pHdr->sHdrIp.ip_verlen != 0x45)
					continue;

				//UDP/139以外は処理しない
				if(pHdr->sHdrIp.ip_protocol != IPPROTO_UDP || pHdr->sHdrUdp.dest_portno != ::htons(137))
					continue;


				//NetBIOSの名前問い合わせに返答する。。。ただしTOSHIBAのHDDレコーダーRDシリーズ形式で返す
				{
					UINT	i;
					BYTE	cbDeviceID;
					BYTE	cbTransactionID;
					NETBIOS_NAME_REPLY	sReply;
					char	pszName[] = "usefullcode.net";	//機器名、最大15文字

					//TOSHIBA RDシリーズではトランザクションIDの1バイト目がデバイス種別になっている
					cbDeviceID		= DEVICE_HDD;			//enum RD_DEVICE_ID内の各値を使用可能
					cbTransactionID = pHdr->pData[1];

					::ZeroMemory(&sReply,sizeof(NETBIOS_NAME_REPLY));
					sReply.nTransactionID = ::htons((cbDeviceID << 8) + cbTransactionID);
					sReply.nFlags1		= ::htons(0x8500);
					sReply.nQuestion	= 0;
					sReply.nAnswer		= ::htons(0x0001);
					sReply.cbX			= 0x32;

					//NetBIOS形式で名前をエンコード
					for(i = 0;i < 15; i++)
					{
						if(i < ::strlen(pszName))
						{
							sReply.pszName[i*2+0] = ((pszName[i] & 0xF0) >> 4) + 'A';
							sReply.pszName[i*2+1] = ((pszName[i] & 0x0F) >> 0) + 'A';
						}
						else
						{
							sReply.pszName[i*2+0] = ((' ' & 0xF0) >> 4) + 'A';
							sReply.pszName[i*2+1] = ((' ' & 0x0F) >> 0) + 'A';
						}
					}

					sReply.nType		= ::htons(0x0020);
					sReply.nClass		= ::htons(0x0001);
					sReply.nLen			= 0;
					sReply.nSrcAddr		= pHdr->sHdrIp.ip_srcaddr;


					SOCKET		sock;
					sockaddr_in	addr;

					sock = ::socket(AF_INET,SOCK_DGRAM,0);

					addr.sin_family		= AF_INET;
					addr.sin_port		= ::htons(137);
					addr.sin_addr.s_addr= pHdr->sHdrIp.ip_srcaddr;

					//返答の送信
					::sendto(sock,(char*)&sReply,sizeof(NETBIOS_NAME_REPLY),0,(sockaddr*)&addr,sizeof(sockaddr));

					::closesocket(sock);
				}
			}
		}
	} 

	::closesocket(sock);

	return	true;
}


bool	Test()
{
	WSAData wsaData;

	//Winsock2.2
	::WSAStartup(MAKEWORD(2,2),&wsaData);

	ReplyNetBiosNS();

	::WSACleanup();

	return	true;
}

プロジェクトファイルをダウンロード

ネットワークに流れるパケットをキャプチャする

test125.gif
Windows 2000以降ではかなり簡単にネットワークのパケットモニターを作れる。

ここではWinSock2.2のRAWソケットを利用してパケットデータを取得している。取得結果はVisual Studioの「出力」ウインドウに表示している。

依存環境:ATL
#include <mstcpip.h>
#include <winsock2.h>
#pragma	comment(lib,"Ws2_32.lib")



//////////////////////////////////////////////////////
//以下の構造体はWindows SDKのサンプルに含まれる「iphdr.h」内からコピー
//

#pragma	pack(push,1)

//
// IPv4 Header (without any IP options)
//
typedef struct ip_hdr
{
    unsigned char  ip_verlen;        // 4-bit IPv4 version
                                     // 4-bit header length (in 32-bit words)
    unsigned char  ip_tos;           // IP type of service
    unsigned short ip_totallength;   // Total length
    unsigned short ip_id;            // Unique identifier 
    unsigned short ip_offset;        // Fragment offset field
    unsigned char  ip_ttl;           // Time to live
    unsigned char  ip_protocol;      // Protocol(TCP,UDP etc)
    unsigned short ip_checksum;      // IP checksum
    unsigned int   ip_srcaddr;       // Source address
    unsigned int   ip_destaddr;      // Source address
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;

//
// Define the UDP header 
//
typedef struct udp_hdr
{
    unsigned short src_portno;       // Source port no.
    unsigned short dest_portno;      // Dest. port no.
    unsigned short udp_length;       // Udp packet length
    unsigned short udp_checksum;     // Udp checksum
} UDP_HDR, *PUDP_HDR;

//
// Define the TCP header
//
typedef struct tpc_hdr				// <- スペルミス?動作に問題なし
{
    unsigned short src_portno;       // Source port no.
    unsigned short dest_portno;      // Dest. port no.
    unsigned long  seq_num;          // Sequence number
    unsigned long  ack_num;          // Acknowledgement number;
    unsigned short lenflags;         // Header length and flags
    unsigned short window_size;      // Window size
    unsigned short tcp_checksum;     // Checksum
    unsigned short tcp_urgentptr;    // Urgent data?
} TCP_HDR, *PTCP_HDR;

#pragma	pack(pop)


//////////////////////////////////////////////////////

#pragma	pack(push,1)

typedef	struct ip_hdr_tcp
{
	IPV4_HDR	sHdrIp;
	TCP_HDR		sHdrTcp;
	BYTE		pData[1];

} TCP_IP_HDR;

typedef	struct ip_hdr_udp
{
	IPV4_HDR	sHdrIp;
	UDP_HDR		sHdrUdp;
	BYTE		pData[1];

} UDP_IP_HDR;


#pragma	pack(pop)


#define	SWAP_BYTE_ALIGNMENT(data)	\
{									\
	int		i;						\
	int		nSize;					\
	int		nLoop;					\
	BYTE*	pData;					\
	BYTE	cbTmp;					\
									\
	pData = (BYTE*)&data;			\
	nSize = sizeof(data);			\
	nLoop = nSize / 2;				\
	for(i = 0; i < nLoop; i++)		\
	{								\
		cbTmp = *(pData + i);		\
		*(pData + i) = *(pData + nSize - i - 1);	\
		*(pData + nSize - i - 1) = cbTmp;			\
	}								\
}


bool	PacketCapture()
{
	int		nRet;
	SOCKET	sock;
	DWORD	dwReturned;

	//RAW SOCKETを作る
	sock = ::WSASocket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);
	if(sock == INVALID_SOCKET)
		return false;

	{
		SOCKADDR_IN	sSockAddrIn;

		//パケットを監視するインターフェースを選ぶ
		//ここでは簡易のため最初に見つかったインターフェースを利用している
		//PCに複数のインターフェース(無線LANと有線LANなど)がある場合は適宜変更すること
		//無線LANが何番目に見つかるかなど、物理的なNICとの関連付けをしたい場合は、GetIfTableを利用する
		{
			int		nNo;
			BYTE	pBuff[1024];
			SOCKET_ADDRESS_LIST* pSockAddrList;

			//何番目に見つかったインターフェースを利用するか
			//2番目のインターフェースを利用したい場合は「1」に変更
			nNo = 0;

			// アダプタの列挙
			nRet = ::WSAIoctl(sock,SIO_ADDRESS_LIST_QUERY,NULL,0,pBuff,1024,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR || dwReturned == 0)
			{
				::closesocket(sock);
				return false;
			}

			pSockAddrList = (SOCKET_ADDRESS_LIST*)pBuff;

			if(nNo >= pSockAddrList->iAddressCount)
			{
				::closesocket(sock);
				return false;
			}
			sSockAddrIn.sin_addr.s_addr= ((SOCKADDR_IN*)pSockAddrList->Address[nNo].lpSockaddr)->sin_addr.s_addr;
		}


		//バインドとキャプチャ-の有効化
		//WSAIoctlに渡すコマンドによってキャプチャ-種などを変更できる
		{
			BOOL	bOption;

			//sSockAddrIn.sin_addr.s_addr	= INADDR_ANY; <-これはNG.必ずSIO_ADDRESS_LIST_QUERYで求めた値を利用する
			sSockAddrIn.sin_family	= AF_INET;
			sSockAddrIn.sin_port	= htons(0);

			nRet = ::bind(sock,(sockaddr*)&sSockAddrIn,sizeof(SOCKADDR_IN));
			if(nRet == SOCKET_ERROR) 
			{
				::closesocket(sock);
				return false;
			}

			//パケットキャプチャの有効化(Windows 2000以降対応)
			bOption = TRUE;
			nRet = ::WSAIoctl(sock,SIO_RCVALL,&bOption,sizeof(BOOL),NULL,0,&dwReturned,NULL,NULL);
			if(nRet == SOCKET_ERROR)
			{
				::closesocket(sock);
				return false;
			}
		}
	}





	//キャプチャの実行とキャプチャデータの表示
	{
		char	pBuff[204800];
		WSABUF	sWsaBuf;
		DWORD	dwFlags;
		DWORD	dwTick;

		dwTick = ::GetTickCount();

		while(1)
		{
			::Sleep(0);

			//キャプチャ時間を10秒間に制限
			if(::GetTickCount() - dwTick > 10*1000)
				break;


			dwFlags = 0;
			::ZeroMemory(pBuff,sizeof(pBuff));
			sWsaBuf.buf = pBuff;
			sWsaBuf.len = sizeof(pBuff);

			//パケット受信
			nRet = ::WSARecv(sock,&sWsaBuf,1,&dwReturned,&dwFlags,NULL,NULL);
			if(nRet == SOCKET_ERROR)
				continue;

			//受信パケット情報の表示
			//ここではIPv4に限って送信元と送信先、TCPかUDPの場合はポート番号の表示のみを処理している
			{
				IPV4_HDR*	pHdr;

				if(dwReturned < sizeof(IPV4_HDR))
					continue;

				pHdr = (IPV4_HDR*)pBuff;

				//IPv4、かつ、IPヘッダーサイズが5×32bit以外のときは処理しない
				if(pHdr->ip_verlen != 0x45)
					continue;

				ATLTRACE("送信元:%d.%d.%d.%d\n",(pHdr->ip_srcaddr >> 0) & 0xFF,(pHdr->ip_srcaddr >> 8) & 0xFF,(pHdr->ip_srcaddr >> 16) & 0xFF,(pHdr->ip_srcaddr >> 24) & 0xFF);
				ATLTRACE("送信先:%d.%d.%d.%d\n",(pHdr->ip_destaddr >> 0) & 0xFF,(pHdr->ip_destaddr >> 8) & 0xFF,(pHdr->ip_destaddr >> 16) & 0xFF,(pHdr->ip_destaddr >> 24) & 0xFF);

				//プロトコルに応じた処理(プロトコルの一覧は「ws2def.h」内で定義されている)
				switch(pHdr->ip_protocol)
				{
				case	IPPROTO_TCP:
					if(dwReturned >= sizeof(TCP_IP_HDR) - 1)
					{
						USHORT		nSrcPort;
						USHORT		nDstPort;
						TCP_IP_HDR*	pTcp;

						pTcp = (TCP_IP_HDR*)pHdr;

						//ポート番号などはエンディアンが異なるので注意
						nSrcPort = pTcp->sHdrTcp.src_portno;
						nDstPort = pTcp->sHdrTcp.dest_portno;
						SWAP_BYTE_ALIGNMENT(nSrcPort);
						SWAP_BYTE_ALIGNMENT(nDstPort);
						ATLTRACE("送信元ポート番号:%d\n",nSrcPort);
						ATLTRACE("送信先ポート番号:%d\n",nDstPort);
					}
					break;

				case	IPPROTO_UDP:
					if(dwReturned >= sizeof(UDP_IP_HDR) - 1)
					{
						USHORT		nSrcPort;
						USHORT		nDstPort;
						UDP_IP_HDR*	pUdp;

						pUdp = (UDP_IP_HDR*)pHdr;

						//ポート番号などはエンディアンが異なるので注意
						nSrcPort = pUdp->sHdrUdp.src_portno;
						nDstPort = pUdp->sHdrUdp.dest_portno;
						SWAP_BYTE_ALIGNMENT(nSrcPort);
						SWAP_BYTE_ALIGNMENT(nDstPort);
						ATLTRACE("送信元ポート番号:%d\n",nSrcPort);
						ATLTRACE("送信先ポート番号:%d\n",nDstPort);
					}
					break;

				//今回はTCP/UDP以外は処理しない
				default:
					break;
				}

				ATLTRACE("\n");
			}
		}
	} 

	::closesocket(sock);

	return	true;
}


bool	Test()
{
	WSAData wsaData;

	//Winsock2.2
	::WSAStartup(MAKEWORD(2,2),&wsaData);

	PacketCapture();

	::WSACleanup();

	return	true;
}


プロジェクトファイルをダウンロード

2007年02月26日

2進数表記の文字列をlong型の数値に変換する

「01001001」のように0と1だけの2進数で書かれた文字列を数値に変換するにはstrtol系の関数が利用できる。従来通りに1文字ずつ判別しながら数値に変換してもいいが、この関数を利用すればかなり楽ができる。覚えておいて損はないだろう。

bool	Test()
{
	////////////////////////////////
	//	2進数表記の文字列をlong型の数値に変換
	//
	{
		//char型文字列から変換
		long	nValue;
		char	pszText[] = "01001001";
		char*	pszEnd;

		nValue = ::strtol(pszText,&pszEnd,2);
	}
	{
		//wchar_t型文字列から変換
		long		nValue;
		wchar_t		pszText[] = L"01001001";
		wchar_t*	pszEnd;

		nValue = ::wcstol(pszText,&pszEnd,2);
	}
	{
		//TCHAR型文字列から変換
		long	nValue;
		TCHAR	pszText[] = _T("01001001");
		TCHAR*	pszEnd;

		nValue = ::_tcstol(pszText,&pszEnd,2);
	}
	{
		//CString型文字列から変換
		long	nValue;
		CString	strText = _T("01001001");
		TCHAR*	pszEnd;

		nValue = ::_tcstol(strText,&pszEnd,2);
	}

	return	true;
}

プロジェクトファイルをダウンロード

16進数表記の文字列をlong型の数値に変換する

「0x0123」や「0FFF」のように16進数で書かれた文字列を数値に変換するにはstrtol系の関数が利用できる。従来通りに1文字ずつ判別しながら数値に変換してもいいが、この関数を利用すればかなり楽ができる。覚えておいて損はないだろう。

bool	Test()
{
	////////////////////////////////
	//	16進数表記の文字列をlong型の数値に変換
	//
	{
		//char型文字列から変換
		long	nValue;
		char	pszText[] = "0ab3";
		char*	pszEnd;

		nValue = ::strtol(pszText,&pszEnd,16);
	}
	{
		//wchar_t型文字列から変換
		long		nValue;
		wchar_t		pszText[] = L"0ab3";
		wchar_t*	pszEnd;

		nValue = ::wcstol(pszText,&pszEnd,16);
	}
	{
		//TCHAR型文字列から変換
		long	nValue;
		TCHAR	pszText[] = _T("0ab3");
		TCHAR*	pszEnd;

		nValue = ::_tcstol(pszText,&pszEnd,16);
	}
	{
		//CString型文字列から変換
		long	nValue;
		CString	strText = _T("0ab3");
		TCHAR*	pszEnd;

		nValue = ::_tcstol(strText,&pszEnd,16);
	}

	return	true;
}

プロジェクトファイルをダウンロード

10進数表記の文字列をint型の数値に変換する

文字列をint型の数値に変換するには従来通りatoi系の関数が利用できる。変換元の文字列の型に応じてatoi、_wtoi、_ttoiを使い分けるといい。

bool	Test()
{
	////////////////////////////////
	//	10進数表記の文字列をint型数値に変換
	//
	{
		//char型文字列から変換
		int		nValue;
		char	pszText[] = "123";

		nValue = ::atoi(pszText);
	}
	{
		//wchar_t型文字列から変換
		int		nValue;
		wchar_t	pszText[] = L"123";

		nValue = ::_wtoi(pszText);
	}
	{
		//TCHAR型文字列から変換
		int		nValue;
		TCHAR	pszText[] = _T("123");

		nValue = ::_ttoi(pszText);
	}
	{
		//CString型文字列から変換
		int		nValue;
		CString	strText = _T("123");

		nValue = ::_ttoi(strText);
	}

	return	true;
}

プロジェクトファイルをダウンロード

int型の数値を16進数表記の文字列に変換する

test121.gif
int型の数値を16進数の文字列に変換するには、従来からのC/C++ではitoa関数もしくはsprintf関数を利用した。しかしWindowsプログラミングではitoa関数などは使わない方がいい。

もちろんitoa関数も使えるが文字列バッファーサイズを指定できるためセキュリティが若干高い_itoa_s系の関数を利用した方がいい。さらに言うと実際のプログラミングでは速度的には劣るがバッファーのことを考える必要がなくコーディングが楽なCString型の文字列を利用する変換がいいだろう。

_itoa_s系の関数の場合は第4引数に「16」を指定することで16進数の文字列を取得できる。



bool	Test()
{
	////////////////////////////////
	//	int型数値を16進数文字列に変換
	//
	{
		//char型文字列に変換
		int		nValue = 123;
		char	pszText[256];

		//itoaや_itoaは利用しない方がいい!代わりに_itoa_sを使う
		::_itoa_s(nValue,pszText,256 * sizeof(char),16);

		::MessageBoxA(NULL,pszText,"",MB_OK);
	}
	{
		//wchar_t型文字列に変換
		int		nValue = 123;
		wchar_t	pszText[256];

		//_itowは利用しない方がいい!代わりに_itow_sを使う
		::_itow_s(nValue,pszText,256 * sizeof(wchar_t),16);

		::MessageBoxW(NULL,pszText,L"",MB_OK);
	}
	{
		//TCHAR型文字列に変換
		int		nValue = 123;
		TCHAR	pszText[256];

		//_itotは利用しない方がいい!代わりに_itot_sを使う
		::_itot_s(nValue,pszText,256 * sizeof(TCHAR),16);

		::MessageBox(NULL,pszText,_T(""),MB_OK);
	}
	{
		//CString型文字列に変換
		int		nValue = 123;
		CString	strText;

		//小文字の16進数
		strText.Format(_T("%x"),nValue);
		::MessageBox(NULL,strText,_T(""),MB_OK);

		//大文字の16進数
		strText.Format(_T("%X"),nValue);
		::MessageBox(NULL,strText,_T(""),MB_OK);
	}

	return	true;
}


プロジェクトファイルをダウンロード

前の10件 5  6  7  8  9  10  11  12  13  14  15