我正在编写一个dll,它是另一个没有COM支持的dll(内部dll)的COM包装器。内部dll执行冗长的计算,并让外部dll通过回调函数知道进度如何。外部dll只是使函数在COM上可见。
但是,我需要外部dll弹出一个进度条对话框(我正在服务的COM客户端因各种原因无法自行完成)。那我该怎么做呢?到目前为止,我看到的所有示例都围绕着具有WinMain
入口点的Win32应用程序;如果我们在需要对话时已经在dll调用中可以做什么?
我是Windows GUI编程的新手,所以我的深度非常深入。现有代码包含在下面 - 具体建议如何调用哪里将受到赞赏。我猜我可能需要启动第二个线程来刷新进度对话框。
内部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 :(得分:5)
我发布的新答案与您更新的问题更相关(并且有资格获得赏金)。首先考虑这个包含进度条的常规可执行文件的最小来源:
#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
资源文件,尽管代码主要是为了您获得正确的概念性理解。所以要快速总结一下,这个程序:
从图形上看,它看起来像这样:
您的问题是如何从DLL增加此栏。您需要做的是允许DLL以某种方式与包含进度条的窗口进行通信。我不太确定你是如何加载DLL的,但这是我假设通过DLL注入完成的方法:
GetProcAddress
和CreateRemoteThread
来调用此初始化例程。HWND
。具体而言,初始化例程如下所示:
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);
}
}
所以现在你在DLL中拥有了HWND
客户端,因此可以进行通信。然后,您可以在客户端中指定自定义消息以更改进度条:
/*
* 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;
}
答案 1 :(得分:2)
由于您声明DLL没有GUI且客户端处理所有用户交互,为什么不将进度信息发送到客户端并将其显示在那里?
如果要在DLL中显示对话框,则执行方式与在常规可执行文件中完全相同。绝对没有区别。如果您希望DLL在更新进度条时继续工作,您可以使用CreateThread启动新线程。
如果您显示一些代码,我们将能够更直接地为您提供帮助。