2013-02-19 12 views
5

これは大した問題ではありませんが、これらの特定の問題に問題がある人を助けるのはもっと残念です。私が取り組んでいる問題は、シリアルI/Oの使用が必要ですが、主にWindows CE 6.0で実行されています。しかし、私は最近、アプリケーションもWindowsでも動作させることができるかどうか尋ねられたので、この問題を解決することにしました。私は誰かが私が探していた答えを持っていたかどうかを見て周りを見回すかなりの時間を費やした、それはすべての誤った情報といくつかのインスタンスでは基本的に間違っていたものの多くとして出くわした。この問題を解決したので、私は私の所見を誰とも分かち合いたいと思ったので、このような困難に遭遇した人は誰でも答えを得ました。Windows/Windows CEでシリアルI/Oが重複/重複していない

Windows CEでは、OVERLAPPED I/Oはではなく、に対応しています。これは、シリアルポートを介した双方向通信が非常に面倒である可能性があることを意味します。主な問題は、シリアルポートからのデータを待っているときにデータを送信できないということです。リード操作が完了するかタイムアウトになるまでメインスレッドがブロックされます(タイムアウトを設定したかどうかによって異なります)

ほとんどの人がシリアルI/Oをやっているように、シリアルデータを待つためにEV_RXCHARマスクを使ってWaitCommEvent()を使ったシリアルポートを読み取るためのリーダーシリアルスレッドを用意しました。これは、WindowsとWindows CEで問題が発生する場所です。

私は例として、このような単純な読者のスレッドがある場合: -

UINT SimpleReaderThread(LPVOID thParam) 
{ 
    DWORD eMask; 
    WaitCommEvent(thParam, &eMask, NULL); 
    MessageBox(NULL, TEXT("Thread Exited"), TEXT("Hello"), MB_OK); 
} 

もちろん、上記の例では、私はシリアルポートか何かからデータを読んでいないことだし、私が想定していますthParamにはcommポートなどにオープンされたハンドルが含まれています。これは、スレッドが実行されてWaitCommEvent()にヒットしたときにWindows上で問題が発生するため、リーダースレッドはシリアルポートデータを待ってスリープ状態になります。それはうまくいくはずですが、どうしてこのスレッドを終了してMessageBox()を表示させるのですか?さて、明らかになったように、実際には簡単ではなく、Windows CEとWindowsのシリアルI/Oのやり方の基本的な違いです。

Windows CEの場合、SetCommMask(COMMPORT_HANDLE、0)やCloseHandle(COMMPORT_HANDLE)など、WaitCommEvent()のフォールスルーを行うためにいくつかのことができます。これにより、スレッドを正しく終了させることができ、シリアルポートを解放してデータの再送信を開始することができます。しかし、これらのものはどちらもWindowsでは動作しません。両方ともWaitCommEvent()の完了を待って、それらを呼び出すスレッドがスリープする原因になります。では、WindowsではWaitCommEvent()をどのように終了させますか?通常はOVERLAPPED I/Oを使用し、スレッドブロッキングは問題にはなりませんが、解決策はWindows CEと互換性がなければならないため、OVERLAPPED I/Oはオプションではありません。 WaitCommEvent()を終了するためにWindowsの下で行うことができ、CancelSynchronousIo()関数を呼び出すことができます。これはWaitCommEvent()を終了しますが、これはデバイスに依存する可能性があることに注意してください。 CancelSynchronousIo()の主な問題は、Windows CEでもサポートされていないことです。この問題を回避するには不運です!

だからあなたはどのようにしていますか?事実、この問題を解決するには、Windows CEでサポートされているWindows上でこの関数を終了する方法がないため、WaitCommEvent()を使用できません。そうするとReadFile()が残っています。これは、NON OVERLAPPED I/Oを読み取っている間に再度ブロックされ、WILLはCommタイムアウトで動作します。

ReadFile()とCOMMTIMEOUTS構造体を使用することは、シリアルデータを待っているループを長くしなければならないことを意味しますが、大量のシリアルデータを受信して​​いない場合は問題ありません。また、小さなタイムアウトでループを終了させるイベントでも、リソースがシステムに戻され、プロセッサを100%負荷で叩くことはありません。以下は私が思いついた解決策であり、あなたが改善できると思うなら、いくつかのフィードバックを感謝します。だから、あなただけのevent_handleにイベントを通知し、それはあなたが適切にスレッドを終了し、リソースをクリーンアップすることを可能にし、WindowsおよびWindows CE上で正常に動作するスレッドを終了する

typedef struct 
{ 
    UINT8 sync; 
    UINT8 op 
    UINT8 dev; 
    UINT8 node; 
    UINT8 data; 
    UINT8 csum; 
} COMMDAT; 

COMSTAT cs = {0}; 
DWORD byte_count; 
COMMDAT cd; 

ZeroMemory(&cd, sizeof(COMMDAT)); 
bool recv = false; 
do 
{ 
    ClearCommError(comm_handle, 0, &cs); 
    if (cs.cbInQue == sizeof(COMMDAT)) 
    { 
     ReadFile(comm_handle, &cd, sizeof(COMMDAT), &byte_count, NULL); 
     recv = true; 
    } 
} while ((WaitForSingleObject(event_handle, 2) != WAIT_OBJECT_0) && !recv); 
ThreadExit(recv ? cd.data : 0xFF); 

私が見たことのある人に役立つことを希望しています。

+0

実行時に動的にCancelSynchronousIo()をロードしようと、あなたはCEの下でならちょうど空のスタブを呼び出すないのはなぜ?デスクトップの場合は重複しているが、Windows CEでは重複していない場合はライブラリの使用を重複させないでください。この問題を回避するにはよりスムーズな方法があるようです。 – ctacke

+0

CancelymotionIo()を動的にロードすることはできません.Windows CEの一部ではないWindows API関数です。この点は、Windows CEとWindowsの両方で修正なしに動作するソリューションを持つことです。 #ifdef/#endifだけでなく、ターゲットプラットフォームのコードを条件付きでコンパイルするだけで、Windows用のオーバーラップされたバージョンとWindows CE用のオーバーラップされていないバージョンを別々に持つことには意味がありません。 –

+0

'SetCommMask'は' WaitCommEvent'を早期に完了させるためにいつも働いていました。しかし、私のコードでは、常に進行中の重複した 'WaitCommEvent'の完了をトリガーしています。重複した 'WaitCommEvent'は' SetCommMask'で完了するのですが、同期していないのですか? –

答えて

5

上記のコメントに誤解があったと思いますので、ここではきついループを使用しない2つの可能な解決策について詳しく説明します。これらは実行時の決定を使用しているため、どちらのOSでもコンパイルする必要があることに注意してください。#ifdefを使用してもどちらのコンパイラでもコンパイルが終了する可能性は低いです。

まず、CancelSynchonousIoを動的にロードし、OSに存在する場合に使用することができます。場合によっては、CEのキャンセル(ハンドルを閉じるなど)の代わりに何かをやっています。

typedef BOOL (WINAPI *CancelIo)(HANDLE hThread); 

HANDLE hPort; 

BOOL CancelStub(HANDLE h) 
{ 
    // stub for WinCE 
    CloseHandle(hPort); 
} 

void IoWithCancel() 
{ 
    CancelIo cancelFcn; 

    cancelFcn = (CancelIo)GetProcAddress(
     GetModuleHandle(_T("kernel32.dll")), 
     _T("CancelSynchronousIo")); 

    // if for some reason you want something to happen in CE 
    if(cancelFcn == NULL) 
    { 
     cancelFcn = (CancelIo)CancelStub; 
    } 

    hPort = CreateFile(/* blah, blah */); 

    // do my I/O 

    if(cancelFcn != NULL) 
    { 
     cancelFcn(hPort); 
    } 
} 

あなたがC++を使用している場合、それはとにかくプラットフォームに基づいて別々のクラスのための優れたケースだろうけれどもそう(異なるスレッドモデルを持っているつもりだと、もう少し手間がかかります他のオプション、 )プラットフォームと使用がデスクトップ上に重なっ決定するために、次のようになります。

HANDLE hPort; 

void IoWithOverlapped() 
{ 
    DWORD overlapped = 0; 
    OSVERSIONINFO version; 

    GetVersionEx(&version); 
    version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); 
    if((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) 
     || (version.dwPlatformId == VER_PLATFORM_WIN32_NT)) 
    { 
     overlapped = FILE_FLAG_OVERLAPPED; 
    } 
    else 
    { 
     // create a receive thread 
    } 

    hPort = CreateFile(
     _T("COM1:"), 
     GENERIC_READ | GENERIC_WRITE, 
     FILE_SHARE_READ | FILE_SHARE_WRITE, 
     NULL, 
     OPEN_EXISTING, 
     overlapped, 
     NULL); 
} 
+0

さて、CancelSynchronousIo()が存在するかどうかを判断し、それが利用可能であれば使用することについて、あなたの意見に同意します!うまくやって、ちょうど私の心を横切ったことはありません...私はちょうど両方のプラットフォームで同じコードに行きました。まあ、私のoldschoolバイトの節約方法! –

+0

'cancelFcn(hPort);'が間違っています。その関数は同期I/Oを取り消すためにスレッドのハンドルを取ります。 (例えば 'CreateThread'によって返された)ハンドルを供給するか、ポートハンドルを取る' CancelIoEx'を使います。 –

関連する問題