ATLをサポートするコマンドアプリの作り方

VisualStudio 2005 以降のプロジェクト・ウィザードでは, ATLプロジェクトで生成するアプリケーションの形式として, DLL, サービスの他に「実行可能なアプリケーション」が選択できるようになりました。

この時生成されるプロジェクトはATLで記述したCOMをサービスするものですが, これを改造して通常のコンソールアプリとするための修正内容をメモします。

ウィザードで生成されるメインコードは, 次のようなCAtlExeModuleTテンプレート派生クラスと メイン関数のみでできています。

class CMyAppModule : public CAtlExeModuleT< CMyAppModule >
{
public :
	DECLARE_LIBID(LIBID_MyAppLib)
	DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYAPP, "{E115D6B3-7016-4740-AF95-D2B76569B56F}")
}

CMyAppModule _AtlModule;

extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, 
                                LPTSTR /*lpCmdLine*/, int nShowCmd)
{
    return _AtlModule.WinMain(nShowCmd);
}

この _tWinMain で行っている処理を別スレッドにして,メインスレッドで 必要な処理をするように変更できれば,目的が達成できそうです。

方法はいろいろありますが,次のような形式で実装してみました。

1. _AtlModule::WinMain から CMyAppModule::Run メソッドがコールされるので これをオーバーロードします。
HRESULT
CMyAppModule::Run(int nShowCmd) throw() {
    this->showcmd_ = nShowCmd;
    unsigned int threadID;
    HANDLE hThread;
    startThread(hThread, threadID);
    doMyOperation();
    stopThread(hThread, threadID);
    return S_OK;
}

startThread / stopThread はスレッドの起動/停止メソッドで, ここで起動するスレッドで元々の Run メソッドを実行してやります。
void
CMyAppModule::startThread(HANDLE& hThread, unsigned int& threadID) {
    hThread = (HANDLE)::_beginthreadex(NULL, 0, threadFunc, (LPVOID)this, 0, &threadID);
}

void
CMyAppModule::stopThread(HANDLE hThread, DWORD threadID) {
    do {
        ::PostThreadMessage(threadID, WM_QUIT, NULL, NULL);
    } while(::WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT);
}

stopThread では, スレッドに WM_QUIT を投げた後,停止の待ち合わせをしています。

スレッド関数 threadFunc はこんな感じ:
static unsigned int __stdcall threadFunc(LPVOID param) {
    ::CoInitialize(NULL);
    CMyAppModule* this_ = (CMyAppModule*)param;
    return this_->CAtlExeModuleT::Run(this_->showcmd_);
}

インスタンス変数 showcmd_ は Run メソッドに渡された引数をスレッド関数に 引き渡すためのものです。

doMyOperation( ) は実際に行いたい処理を実装する場所です。

コード全体としては,おおよそ以下のような形式です。 エラー処理などは必要に応じて実装してください(^^;)。
class CMyAppModule : public CAtlExeModuleT< CMyAppModule >
{
public :
    DECLARE_LIBID(LIBID_MyAppLib)
    DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYAPP, "{E115D6B3-7016-4740-AF95-D2B76569B56F}")

    HRESULT
    CMyAppModule::Run(int nShowCmd) throw() {
        showcmd_ = nShowCmd;
        unsigned int threadID;
        HANDLE hThread;
        startThread(hThread, threadID);
	doMyOperation( );
        stopThread(hThread, threadID);
        return S_OK;
    }

private:
    int showcmd_;

    void startThread(HANDLE& hThread, unsigned int& threadID) {
        hThread = (HANDLE)::_beginthreadex(NULL, 0, threadFunc, (LPVOID)this, 0, &threadID);
    }

    void stopThread(HANDLE hThread, DWORD threadID) {
        do {
            ::PostThreadMessage(threadID, WM_QUIT, NULL, NULL);
        } while(::WaitForSingleObject(hThread, 500) == WAIT_TIMEOUT);
    }

    static unsigned int __stdcall threadFunc(LPVOID param) {
        ::CoInitialize(NULL);
        CMyAppModule* this_ = (CMyAppModule*)param;
        return this_->CAtlExeModuleT::Run(this_->showcmd_);
    }

    void doMyOperation( ) {
        // ここに必要な処理を実装します。
	// このメソッドが終了すると,アプリケーションも停止します。
    }
};

CMyAppModule _AtlModule;


extern "C" int WINAPI _tWinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, 
                                LPTSTR /*lpCmdLine*/, int nShowCmd)
{
    return _AtlModule.WinMain(nShowCmd);
}


タグ:ATL C++
この記事へのコメント
コメントを書く
お名前: [必須入力]

メールアドレス: [必須入力]

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
※ブログオーナーが承認したコメントのみ表示されます。

この記事へのトラックバック
rank← ランキングはこちらをクリック!

×

この広告は180日以上新しい記事の投稿がないブログに表示されております。