
はじまして。
GPS(Garmmin GPSmap 60Cx)からUSB通信でデータをインンポートするツールをViualStudio2008 C++/CLIで作っているものです。
DeviceIoControlを実行後、読み取ったバイトサイズが0になるまでReadFileを複数回実行してデータ(ウエイポイント)を取得しています。GPSには、9つのウエイポイントを登録しています。WindowsXPでは、9つすべて問題なく取得できます。しかし、Windows7(32bit,64bit両方とも)では、はじめの3つしか取得できません。デバッグ中にReadFile関数のあとにブレークポイントを置くか、または、Sleep(1)の処理を入れて実行すると9つ全て取得できます。
Sleep(1)の処理を入れる事でなぜうまく動くのかがわからず困っています。Sleep(1)の処理を入れる事で、うまく動く理由、あるいは、別の解決策がわかる方がいらっしゃいましたら、教えていただけないでしょうか。
該当箇所のソースは、以下の通りです。
typedef struct
{
unsigned char mPacketType;
unsigned char mReserved1;
unsigned short mReserved2;
unsigned short mPacketId;
unsigned short mReserved3;
unsigned long mDataSize;
BYTE mData[MAX_BUFFER_SIZE-12];
} GetPacket_t;
static std::vector<GetPacket_t> vecPacket;
#define ASYNC_DATA_SIZE 64
#define IOCTL_ASYNC_IN CTL_CODE (FILE_DEVICE_UNKNOWN, 0x850, METHOD_BUFFERED, FILE_ANY_ACCESS)
GetPacket_t* GarComm::GComm::GetPacket2()
{
GetPacket_t* thePacket = NULL;
GetPacket_t* tempthePacket = NULL;
DWORD theBufferSize = 0;
BYTE* theBuffer = NULL;
for( ; ;
{
// Read async data until the driver returns less than the max async data size, which signifies the end of a packet
BYTE theTempBuffer[ASYNC_DATA_SIZE];
BYTE* theNewBuffer = NULL;
DWORD theBytesReturned = 0;
DeviceIoControl( gHandle,
IOCTL_ASYNC_IN,
0,
0,
theTempBuffer,
sizeof( theTempBuffer ),
&theBytesReturned,
NULL );
theBufferSize += ASYNC_DATA_SIZE;
theNewBuffer = (BYTE*) malloc( theBufferSize );
memcpy( theNewBuffer, theBuffer, theBufferSize - ASYNC_DATA_SIZE );
memcpy( theNewBuffer + theBufferSize - ASYNC_DATA_SIZE,theTempBuffer,ASYNC_DATA_SIZE );
if(theBuffer != NULL ){
free( theBuffer );
}
theBuffer = theNewBuffer;
if( theBytesReturned != ASYNC_DATA_SIZE )
{
thePacket = (GetPacket_t*) theBuffer;
break;
}
}
// If this was a small "signal" packet, read a real packet using ReadFile
if( thePacket->mPacketType == 0 &&
thePacket->mPacketId == 2 )
{
free( thePacket );
int recCnt=0;
int^ ndbg;
BYTE* theNewBuffer =NULL;
DWORD theBytesReturned = 0;
int ret=0;
for( ; ; )
{
if(theNewBuffer!=NULL)free(theNewBuffer);
theNewBuffer = (BYTE*) malloc( MAX_BUFFER_SIZE );
theBytesReturned = 0;
// A full implementation would keep reading (and queueing) packets until the driver returns a 0 size buffer.
ret=ReadFile( gHandle, theNewBuffer, MAX_BUFFER_SIZE, &theBytesReturned, NULL );
recCnt++;
ndbg=recCnt;
tempthePacket=(GetPacket_t*) theNewBuffer;
// ベクターにパケットを保持。
vecPacket.push_back(*tempthePacket);
if (theBytesReturned==0){
free(theNewBuffer);
break;
}
// Win7
Sleep(1);
}
return &(vecPacket[0]);
}
else
{
return thePacket;
}
}
ちなみに、ハンドルをCreateFileでFILE_FLAG_OVERLAPPEDを指定し、DeviceIoControl、ReadFile関数にOverlapped構造体を指定して、非同期で動くかす方法も試したのですが、結果は、同じで、ReadFile関数の処理が完了したあとに、次のReadFile関数を実行する前にSleep(1)を入れないとWindows7では、全てのデータを取得することができませんでした。
また、動作を確認したPCは、
Win7のPCは、Core i5-2500 3.3Ghz 、
WinXPのPCは、Pentium4 3Ghz(くらい)
とマシンスペックには、差があります・・・。
USBは両方とも2.0です。
どうぞよろしくお願いいたします。

No.3ベストアンサー
- 回答日時:
それは、たんに、XP のマシンより、7 のマシンの方が「速い」というだけのことかもしれません。
シリアルポートの読み込みで、「まだデータが届いていない(or そう判断された場合)」読み込みバッファの中身は、「なし」になります。
なので、基本は、
・読み込まれるべきデータが全部そろうまで読み込みを行い、
・途中でデータがなくなる可能性に対応するために、タイムアウトの処理を行う
ということになります。
実際には、USB を使っていたとしても、GPS側の処理スピードで、各データブロックの間には、そこそこの時間が空くことはよくあります。
処理時間が間に合うのなら、
・データが全部そろった → 終了
・読み込みバッファが空 → Sleep(1) で再試行
・Sleep(1) で再試行が、何回も続く → abort
というのも、処理方法としては、間違ってはいないと思います。
ご回答ありがとうございます。
>「まだデータが届いていない(or そう判断された場合)」読み込みバッファの中身は、「なし」になりま>す。
確かに、バッファの中身がなしになっていました。7のマシンが「速い」ためのようです。
そもそも、returnbyteが0で終了条件としていましたが、送信データに終了IDが送信されてくることがわかりました。
終了IDまでデータを読み続け、returnbyte0は、読み飛ばすことでデータをすべて取得することができました。
どうもありがとうございました。
No.2
- 回答日時:
> theDevDetailData->DevicePath
このパスは間違えていないとは思いますが、正しいものが入っていますか?
> SetupComm()、GetCommTimeouts 、SetCommTimeouts のいずれの関数も失敗してしまい(GetLastErrorの戻り値:ファンクションが間違っています。)
これはそれぞれの関数の戻り値が0なのでしょうか?
その上での「GetLastError戻り値:ファクションが間違っています」なのでしょうか?
GerminのGPSを使ったプログラムサンプルなどは、検索したら色々見つかりそうなものですが、
RS232Cのプログラミングは地味に難しいので、1つずつ調べていくしかないです。
No.1
- 回答日時:
初期設定はちゃんと行っておられますか?
以下、MSDNより抜粋
>通信デバイスからデータを読み取っているとき、ReadFile 関数の動作は、現在の通信タイムアウト値の影響を受けます。SetCommTimeouts と GetCommTimeouts の各関数は、この通信タイムアウト値の設定と取得を行います。タイムアウト値の設定を怠ると、予測できない結果が生じることがあります。
初期設定として
・ReadFileのタイムアウト値はどの程度になっているのでしょう。
・SetupComm()関数で送受信バッファーの容量を設定します。十分な値が設定されてますか?ご確認下さい。
ここからは個人的な感想なんですが、Sleep(1)で動くなら、それで良いと思います。
Sleepがないと、for(;;)ではCPUコア1つを100%使ってしまうことになるので。
GPSの通信を受信する程度では、ビジーループにする必要はないでしょう。
この回答への補足
ご回答ありがとうございます。
送受信バッファーの容量設定、タイムアウト値の設定は、しておりませんでした。(ちなみに、元にしたGarmin Device Interface SDK[https://www8.garmin.com/support/commProtocol.html]にない処理でした。)
設定したところ、SetupComm()、GetCommTimeouts 、SetCommTimeouts のいずれの関数も失敗してしまい(GetLastErrorの戻り値:ファンクションが間違っています。)、つたない知識のため、原因がわかりません。
また、ビジーループのお話から、ReadFile繰り返す処理を再帰処理に書き換えてみましたが、現象は、変わりませんでした。しかし、再帰処理の関数内の最後でSleep(1)をすると正常に動作しました。
以下は、バッファー容量、タイムアウト値を設定したソースの抜粋です。
bool GarComm::GComm::Initialize()
{
// Make all the necessary Windows calls to get a handle to our USB device
DWORD theBytesReturned = 0;
PSP_INTERFACE_DEVICE_DETAIL_DATA theDevDetailData = 0;
SP_DEVINFO_DATA theDevInfoData = { sizeof( SP_DEVINFO_DATA ) };
//--------------------------------
//デバイス情報セットを取得する
HDEVINFO theDevInfo = SetupDiGetClassDevs(
(GUID*) &GUID_DEVINTERFACE_GRMNUSB,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE );
//-----------------------------------------------------
//GarmminGPSのデバイスインターフェース情報を取得する。
SP_DEVICE_INTERFACE_DATA theInterfaceData;
theInterfaceData.cbSize = sizeof( theInterfaceData );
if( !SetupDiEnumDeviceInterfaces( theDevInfo,
NULL,
(GUID*) &GUID_DEVINTERFACE_GRMNUSB,
0,
&theInterfaceData ) &&
GetLastError() == ERROR_NO_MORE_ITEMS )
{
gHandle = 0;
return false;
}
//-----------------------------------------------------
//デイバイスインターフェスの詳細情報のサイズを取得する。
SetupDiGetDeviceInterfaceDetail(
theDevInfo,
&theInterfaceData,
NULL,
0,
&theBytesReturned,
NULL );
//-----------------------------------------------------
//デイバイスインターフェスの詳細情報を取得する。
theDevDetailData =
(PSP_INTERFACE_DEVICE_DETAIL_DATA) malloc( theBytesReturned );
theDevDetailData->cbSize = sizeof( SP_INTERFACE_DEVICE_DETAIL_DATA );
SetupDiGetDeviceInterfaceDetail( theDevInfo,
&theInterfaceData,
theDevDetailData,
theBytesReturned,
NULL,
&theDevInfoData );
gHandle = CreateFile(
theDevDetailData->DevicePath,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
free( theDevDetailData );
//送受信バッファーの容量を設定
int a = SetupComm(gHandle,MAX_BUFFER_SIZE+256,MAX_BUFFER_SIZE+256);
DWORD dwGle3 = GetLastError();
//タイムアウトの設定
COMMTIMEOUTS ctOutSet;
ctOutSet.ReadIntervalTimeout =10;
ctOutSet.ReadTotalTimeoutMultiplier=10;
ctOutSet.ReadTotalTimeoutConstant=100;
ctOutSet.WriteTotalTimeoutMultiplier=10;
ctOutSet.WriteTotalTimeoutConstant=100;
int b =SetCommTimeouts(gHandle,&ctOutSet);
DWORD dwGle = GetLastError();
COMMTIMEOUTS ctOut;
int c =GetCommTimeouts(gHandle,&ctOut);
DWORD dwGle2 = GetLastError();
// Get the USB packet size, which we need for sending packets
//パケット送付のために必要なUSBパケット・サイズを得ます。
DeviceIoControl( gHandle,
IOCTL_USB_PACKET_SIZE,
0,
0,
&gUSBPacketSize,
sizeof( gUSBPacketSize ),
&theBytesReturned,
NULL );
//------------------------------------------------------------------
// Tell the device that we are starting a session.
//セッションを始めようとしていると装置に伝える。
//------------------------------------------------------------------
Packet_t theStartSessionPacket = { 0, 0, 0, Pid_Start_Session, 0 , 0 };
Packet_t* thePacket = 0;
SendPacket( theStartSessionPacket );
// Wait until the device is ready to the start the session
// デバイスが開始セッションの準備が整うまで待つ
for( ; ; )
{
thePacket = GetPacket();
if( thePacket->mPacketType == 0 &&
thePacket->mPacketId == 6 )
{
break;
}
free( thePacket );
}
free( thePacket );
return true;
}
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# leetcode 155 minstack 1 2022/05/07 16:43
- C言語・C++・C# 10個の実数に対する降順ソート結果を出力するプログラムを作りたいのですが、以下のプログラムをどう直せ 1 2022/07/09 22:16
- C言語・C++・C# TCP/IP通信時のサーバーからの受信 2 2022/11/23 09:11
- C言語・C++・C# c言語の問題です 課題1 (二分探索木とセット) 大きさ size の配列 array を考える。す 2 2023/01/10 21:08
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- PHP ここでの ②if($su_d<>"")の比較演算子 を使う理由は 1 2022/03/26 02:33
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- PHP if(preg_match("/[^0-9]/",$gu_d)){意味を教えてください。 1 2022/05/06 05:37
- CGI htmlからパラメータで、cgiに渡したい。 1 2023/02/06 16:15
- C言語・C++・C# アセンブラ指令 3 2023/06/17 14:47
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
RS-232Cでバイナリデータを受信...
-
Linuxでのシリアル通信について...
-
write関数でEAGAINのエラー発生...
-
WriteFile()でのデータ送信がで...
-
「スイッチングハブのバッファ...
-
ReadFile(GPSとの通信)Win7で...
-
winsockの動作について。
-
WinsockAPIのrecvfromの受信デ...
-
rs232cでの受信データ(mscomm)...
-
C# シリアル通信でデータ受信...
-
C#で通信処理。応答がない場合...
-
再現性の無いバグ
-
Access Violationについて
-
WSH(VBScript)でアプリケーショ...
-
Macターミナルで実行中のプログ...
-
SetWindowPosについて
-
vba listviewにおけるtextのAli...
-
複数スレッドを動作させるのに...
-
SQLの速度をあげるには・・・
-
オフスクリーンサーフェスへの...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C# シリアル通信でデータ受信...
-
「スイッチングハブのバッファ...
-
winsockでソケット通信の開発を...
-
シリアル通信の出力バッファと...
-
WriteFile()でのデータ送信がで...
-
Linuxでのシリアル通信について...
-
WinsockAPIのrecvfromの受信デ...
-
socket: recvはいつ,どれだけ...
-
SerialPortのDataReceivedイベ...
-
ClearCommError呼び出し時のCE_...
-
TCPでの非同期型select関数につ...
-
シリアルポート通信
-
シリアル通信 大きいサイズの...
-
winsockの動作について。
-
ソケット通信内 read関数について
-
COMポートの同時オープン同時読...
-
SocketのSend関数でのCLOSEの検...
-
waveIn、waveOutでの音声録音・...
-
write関数でEAGAINのエラー発生...
-
rs232cでの受信データ(mscomm)...
おすすめ情報