重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

電子書籍の厳選無料作品が豊富!

はじまして。
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です。

どうぞよろしくお願いいたします。

A 回答 (3件)

それは、たんに、XP のマシンより、7 のマシンの方が「速い」というだけのことかもしれません。



シリアルポートの読み込みで、「まだデータが届いていない(or そう判断された場合)」読み込みバッファの中身は、「なし」になります。

なので、基本は、
・読み込まれるべきデータが全部そろうまで読み込みを行い、
・途中でデータがなくなる可能性に対応するために、タイムアウトの処理を行う
ということになります。

実際には、USB を使っていたとしても、GPS側の処理スピードで、各データブロックの間には、そこそこの時間が空くことはよくあります。

処理時間が間に合うのなら、
・データが全部そろった → 終了
・読み込みバッファが空 → Sleep(1) で再試行
・Sleep(1) で再試行が、何回も続く → abort
というのも、処理方法としては、間違ってはいないと思います。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます。

>「まだデータが届いていない(or そう判断された場合)」読み込みバッファの中身は、「なし」になりま>す。

確かに、バッファの中身がなしになっていました。7のマシンが「速い」ためのようです。
そもそも、returnbyteが0で終了条件としていましたが、送信データに終了IDが送信されてくることがわかりました。
終了IDまでデータを読み続け、returnbyte0は、読み飛ばすことでデータをすべて取得することができました。
どうもありがとうございました。

お礼日時:2012/07/05 20:05

> theDevDetailData->DevicePath


このパスは間違えていないとは思いますが、正しいものが入っていますか?

> SetupComm()、GetCommTimeouts 、SetCommTimeouts のいずれの関数も失敗してしまい(GetLastErrorの戻り値:ファンクションが間違っています。)

これはそれぞれの関数の戻り値が0なのでしょうか?
その上での「GetLastError戻り値:ファクションが間違っています」なのでしょうか?
GerminのGPSを使ったプログラムサンプルなどは、検索したら色々見つかりそうなものですが、
RS232Cのプログラミングは地味に難しいので、1つずつ調べていくしかないです。
    • good
    • 0

初期設定はちゃんと行っておられますか?


以下、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;
}

補足日時:2012/06/29 14:37
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!