2012-02-15 39 views
1

私はCOMをサポートしていない別のdll(内側のdll)のCOMラッパーであるdllを書いています。内側のdllは長い計算を実行し、コールバック関数を介して進行状況がどのように進行しているかを外側のdllに知らせる。外側のdllはCOM上で関数を見えるようにします。C++のdll(ウィンドウ)からプログレスバーのダイアログをポップアップする最も簡単な方法

しかし、私はプログレスバーのダイアログをポップアップするために外部DLLが必要です(私が提供しているCOMクライアントは、さまざまな理由でこれを行うことができません)。だから私はそれをどうやってやるの?私が今まで見てきたすべての例は、WinMainエントリポイントを持つWin32アプリケーションを中心にしています。ダイアログが必要なときにすでにdll呼び出し中であれば何ができますか?

私はWindows GUIプログラミングには新しく、ここではかなり深いところです。既存のコードは以下に含まれています - どこから呼び出されるかに関する具体的な提案は、どこに評価されますか?私は、進捗ダイアログを更新するために2番目のスレッドを起動する必要があるかもしれないと推測しています。

インナーDLLの.h(暗黙リンク用)ファイル:

#define INNER_API extern "C" __declspec(dllimport) 

//create calculation, passing callbacks for warning messages and progress bar 
INNER_API Calculation* __stdcall calc_create(...blah..., 
    int (__cdecl *set_progressor_callback)(long), 
    int (__cdecl *print_warning_callback)(const char*)); 

INNER_API void __stdcall calc_run(Calculation *c); 

次に外側のdll、COMラッパー、ComWrapperObject.cppで:あなたの状態以来

int my_progressor_callback(long progress) 
    { 
     //set progressor to equal progress, but how? 
     return 0; 
    } 

    STDMETHODIMP ComWrapperObject::do_calculation() 
    { 
     //fire up progress bar and message window here, but how? 

     Calculation *calc = calc_create(...blah..., &my_progressor_callback); 
     calc_run(calc); 

     //wait for user to dismiss message window, but how? 
     return S_OK; 
    } 
+0

あなたはちょうどあなたが実行ファイルと同じようにDLLでダイアログを表示することができますすべてのあなたの現在のコードでこれを合うように

。本当の概念上の違いはありません。しかし、あなたはおそらくそれをしたくないとマイクは言う。 –

+0

大きな問題は、あなたのスレッドがUIスレッドになり(今ではプログレスバーを持つようになりますが)、メッセージをポンピングしないことです(calc_creationがCPUを嫌うためです)。スレッド間で作業を分割する必要があります。 –

答えて

2

DLLにはGUIがなく、クライアントはすべてのユーザー対話を処理しますが、代わりに進捗情報をクライアントに送信してそこに表示させるのはなぜですか?

DLLにダイアログを表示する場合は、通常の実行可能ファイルとまったく同じ方法で表示します。違いは全くありません。プログレスバーを更新している間にDLLを動作させたい場合は、CreateThreadを使って新しいスレッドを開始することができます。

コードを表示すると、より直接的にお手伝いできます。

+0

私はクライアントがプログレスバーを簡単に行うことはできません。クライアントはvisual lispで書かれており、COMよりもその言語へのコールバックを処理する方法に関するドキュメントは見つかりませんでした。 (これを行う方法があれば私を修正してください!)私は少しコードを投稿します。 –

+0

私の編集を参照してください - いくつかのコードを追加しました。あなたができるなら、本当に助けに感謝します! –

4

更新された質問に関連性の高い新しい回答を投稿しています(賞金の対象となるために)。最初のプログレスバーが含まれている定期的な実行のために、この最小限のソースを考えてみましょう:

#include <Windows.h> 
#include <CommCtrl.h> 
#pragma comment(lib, "Comctl32.lib") 
#include "resource.h" 
#pragma comment(linker,"\"/manifestdependency:type='win32' \ 
    name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ 
    processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") 
#define PROGRESSBAR_TIMER_ID 1 

/* 
* This callback is invoked each time the main window receives a message. 
*/ 
INT_PTR CALLBACK DialogFunc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) 
{ 
    switch(uMsg) { 
    case WM_INITDIALOG: { 
    /* 
    * Fire a timer event each second. 
    */ 
    SetTimer(hwndDlg, PROGRESSBAR_TIMER_ID, 1000, NULL); 
    break; 
    } 
    case WM_TIMER: { 
    /* 
    * Catch the timer event that is fired each second. Increment the progress 
    * bar by 10% each time. 
    */ 
    HWND hwndProgressBar = GetDlgItem(hwndDlg, IDC_PROGRESS1); 
    UINT iPos   = SendMessage(hwndProgressBar, PBM_GETPOS, 0, 0); 

    /* 
    * If the position is already full then kill the timer. Else increment the 
    * progress bar. 
    */ 
    if(iPos >= 100) { 
     KillTimer(hwndDlg, PROGRESSBAR_TIMER_ID); 
    } else { 
     SendMessage(hwndProgressBar, PBM_SETPOS, iPos + 10, 0); 
    } 

    break; 
    } 
    case WM_CLOSE: 
    EndDialog(hwndDlg, 0); 
    break; 
    default: 
    return FALSE; 
    } 

    return TRUE; 
} 

BOOL LaunchGUI(HINSTANCE hInstance) 
{ 
    return DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogFunc) == 0; 
} 

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{ 
    /* 
    * Initialise the common controls DLL. 
    */ 
    INITCOMMONCONTROLSEX iccex; 
    iccex.dwSize = sizeof(iccex); 
    iccex.dwICC = ICC_PROGRESS_CLASS | ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES; 

    if(!InitCommonControlsEx(&iccex)) { 
    MessageBox(NULL, L"Problem initialising common controls DLL.", NULL, MB_OK); 
    return -1; 
    } 

    /* 
    * Launches the main GUI window. 
    */ 
    LaunchGUI(hInstance); 
    return ERROR_SUCCESS; 
} 

あなたが好きならば、あなたが正しい概念の理解を得るためのコードはほとんどですが、私はこのプログラムに関連する.rcリソースファイルを投稿することができます。だから、すぐに要約し、このプログラム:

  • は、単一のプログレスバー
  • を含む単一のダイアログは、タイマーが起動するたびに
  • 各秒を発射するためにタイマーを設定します含まれ、タイマーメッセージがにプログレスバーをトリガー

グラフィカルに更新され、それは次のようになります。

Progress Bar

あなたの質問は、代わりにこのバーをDLLからインクリメントする方法です。あなたがする必要があるのは、DLLが進行状況バーを含むウィンドウと通信するための何らかの方法を許可することです。私はあなたがDLLをロードする方法はかなりわからないんだけど、これは私はそれがDLLインジェクションを介して行われていると仮定かかるだろうなアプローチです:

  • ロード/
  • は、DLLのエクスポートを持ってターゲットにDLLを注入それはインジェクタ/クライアントプロセスに関する情報を受け取ることを可能にするいくつかの初期化ルーチンである。
  • クライアントのGetProcAddressCreateRemoteThreadを使用して、この初期化ルーチンを呼び出します。
  • DLLで、受信した情報を使用してクライアントのHWNDを取得します。

具体的には、intialisationルーチンは次のようになります。

HWND hwndClient = NULL; 

BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lParam) 
{ 
    DWORD dwPID; 

    GetWindowThreadProcessId(hwnd, &dwPID); 
    if(dwPID == lParam) { 
    hwndClient = hwnd; 
    } 
} 

/* 
* This code assumes the client has only one window. Given a PID, it populates 
* a global to hold the window handle associated with the PID. 
*/ 
DWORD WINAPI ReceiveClientPID(LPVOID dwPID) 
{ 
    EnumWindows(EnumProc, (LPARAM)dwPID); 
} 

クライアントコードは次のようなものかもしれません:

/* 
* Depending on your method of injection, you should have a handle to the 
* target process as well as a HMODULE of the injected DLL. 
*/ 
void InitDLL(HANDLE hProcess, HMODULE hModule) 
{ 
    FARPROC lpInit = GetProcAddress(hModule, "ReceiveClientPID"); 
    HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, 
     (LPTHREAD_START_ROUTINE)lpInit, (LPVOID)GetCurrentProcessId(), NULL, NULL); 

    if(hThread == NULL) { 
    MessageBox(NULL, L"Problem calling init routine in DLL", NULL, MB_OK); 
    } else { 
    CloseHandle(hThread); 
    } 
} 

だから今、あなたがクライアントのHWNDを持っていますDLLを使用して、通信を行う方法を教えてください。その後、プログレスバーを変更し、クライアントにカスタムメッセージを指定することができます。

/* 
* The new progress position can be passed in wParam. 
*/ 
#define WM_UPDATE_PROGRESS_BAR (WM_APP + 1) 

DialogFuncに対応するケースを追加することが対話する方法を実証するだけであったので(私たちは今WM_TIMERコードを削除することができますプログレスバー):今

case WM_UPDATE_PROGRESS_BAR: 
    SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS1), PBM_SETPOS, wParam, 0); 
    break; 

とクライアントのプログレスバーの変化をトリガするには、DLLは、単純に関係しています:

SendMessage(hwndClient, WM_UPDATE_PROGRESS_BAR, ..., 0); 

WM_UPDATE_PROGRESS_BARもDLLで再定義する必要があることに注意してください。

/* 
* Assumed progress is between 0 and 100. Otherwise it has to be 
* normalised so this is the case (or the range of the progress bar 
* in the client has to be changed). 
*/ 
int my_progressor_callback(long progress) 
{ 
    SendMessage(hwndClient, WM_UPDATE_PROGRESS_BAR, progress, 0); 
    return 0; 
} 
+0

こんにちはマイク、ありがとう。 DLLクライアントは、AutoCAD用の視覚的なリスプです。ああ、インターフェースはいくぶん限定されています。私は、オートキャド・ウインドウのHWNDをそのように取得する方法があるかどうかはまだ分かりません。これはHWNDなしで動作しますか?(プログレスバーには親ウィンドウがありませんが、フリーフローのみです) –

+0

親ウィンドウを表示する必要はありません。 –

+0

恥ですので、HWNDなしでこれを行うことは本当に不可能ですか?それは私にとってこの方法を排除します。どのような場合でも広範な回答を得るための恩恵を授与します。これはこれらのことがどのように機能するかを明らかにしました - ありがとう! –

関連する問題