I am trying to send a Windows notification through Lua, I found a few methods to do it, but in Python, I couldn't find anything for Lua. Is there any method I can do this? And if it is, could someone tell me how? Thanks!
I tried searching for answers, but I couldn't find anything about it.
Lua does not have such standard function, you need to use some external library.
For example, you can use FFI library to invoke Win32API functions directly.
The following script uses LuaJIT FFI:
local ffi = require'ffi'
local shell32 = ffi.load'shell32'
ffi.cdef[[
typedef int BOOL;
typedef unsigned int DWORD;
typedef unsigned int UINT;
typedef intptr_t HANDLE;
typedef intptr_t HWND;
typedef intptr_t HICON;
typedef intptr_t HINSTANCE;
typedef struct {int Data[4];} GUID;
typedef struct {
DWORD cbSize;
HWND hWnd;
UINT uID;
UINT uFlags;
UINT uCallbackMessage;
HICON hIcon;
char szTip[128];
DWORD dwState;
DWORD dwStateMask;
char szInfo[256];
union {
UINT uTimeout;
UINT uVersion;
};
char szInfoTitle[64];
DWORD dwInfoFlags;
GUID guidItem;
HICON hBalloonIcon;
} NOTIFYICONDATAA;
BOOL Shell_NotifyIconA(
int dwMessage,
NOTIFYICONDATAA * lpData
);
HICON LoadIconA(
HINSTANCE hInstance,
intptr_t IconCode
);
BOOL DestroyIcon(
HICON hIcon
);
HWND GetConsoleWindow();
]]
-- icon codes:
-- 32512 Your application's icon
-- 32513 Error ("X" inside red circle)
-- 32514 Question ("?" inside blue circle)
-- 32515 Warning ("!" inside yellow triangle)
-- 32516 Information ("i" inside blue circle)
-- 32518 Security Shield
local tray_icon_code = 32516 -- Information
local balloon_icon_code = 32518 -- Shield
-- create tray icon
local tray_icon_handle = ffi.C.LoadIconA(0, tray_icon_code)
local balloon_icon_handle = ffi.C.LoadIconA(0, balloon_icon_code)
local notify_icon_data = ffi.new"NOTIFYICONDATAA"
notify_icon_data.cbSize = ffi.sizeof(notify_icon_data)
notify_icon_data.hWnd = ffi.C.GetConsoleWindow() -- HWND of your application window
notify_icon_data.uFlags = 1 + 2 -- NIF_MESSAGE | NIF_ICON
notify_icon_data.hIcon = tray_icon_handle
notify_icon_data.uVersion = 4
notify_icon_data.hBalloonIcon = balloon_icon_handle
shell32.Shell_NotifyIconA(0, notify_icon_data) -- NIM_ADD
shell32.Shell_NotifyIconA(4, notify_icon_data) -- NIM_SETVERSION
print("Tray icon added. Press Enter to continue..."); io.read()
-- show notifications
local function copy_string(dest_array_ptr, str)
ffi.copy(dest_array_ptr, (str or ""):sub(1, ffi.sizeof(dest_array_ptr) - 1))
end
local function show_notification(text, title)
notify_icon_data.uFlags = 1 + 2 + 16 -- NIF_MESSAGE | NIF_ICON | NIF_INFO
notify_icon_data.dwInfoFlags = 4 + 32 -- NIIF_USER | NIIF_LARGE_ICON
copy_string(notify_icon_data.szInfoTitle, title)
copy_string(notify_icon_data.szInfo, text)
shell32.Shell_NotifyIconA(1, notify_icon_data) -- NIM_MODIFY
end
show_notification("some text", "some title")
print("Notification displayed. Press Enter to continue..."); io.read()
show_notification("first line\nsecond line", "another title")
print("Another notification displayed. Press Enter to continue..."); io.read()
-- notification requests generated by show_notification() are queued,
-- each notification is kept on the screen for several seconds before next one is displayed
-- to immediately remove the notification - remove the tray icon
-- remove tray icon
shell32.Shell_NotifyIconA(2, notify_icon_data) -- NIM_DELETE
ffi.C.DestroyIcon(balloon_icon_handle)
ffi.C.DestroyIcon(tray_icon_handle)
print("Tray icon removed. Press Enter to exit..."); io.read()
Related
I'm trying to detour ::DrawText() (and other draw text functions) for an application that has a lot of DLLs, some of which also use those functions.
I would have thought that if I detoured the main function using DetourFindFunction(), this would capture all cases from all functions in all DLLs, such that I could solve this question as there is text being displayed on the window.
Unfortunately, either CDHtmalDialog is somehow getting around the detour, or it is using some other function to draw the text on the window.
Could someone confirm that if I were to do this:
int (WINAPI *pDrawTextExW)(
_In_ HDC hdc,
_Inout_ LPWSTR lpchText,
_In_ int cchText,
_Inout_ LPRECT lprc,
_In_ UINT dwDTFormat,
_In_ LPDRAWTEXTPARAMS lpDTParams
) = 0;
int WINAPI MyDrawTextExW(
_In_ HDC hdc,
_Inout_ LPWSTR lpchText,
_In_ int cchText,
_Inout_ LPRECT lprc,
_In_ UINT dwDTFormat,
_In_ LPDRAWTEXTPARAMS lpDTParams
)
{
return (*pDrawTextExW)(hdc, lpchText, cchText, lprc, dwDTFormat, lpDTParams);
}
CCalcDrillDownDlg::CCalcDrillDownDlg(/* bunch of parameters */, CWnd *pParent)
: CDHtmlDialog(CCalcDrillDownDlg::IDD, 0, pParent)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
pDrawTextExW = decltype(pDrawTextExW)(DetourFindFunction("user32.dll", "DrawTextExW"));
DetourAttach(&(PVOID&)pDrawTextExW, MyDrawTextExW);
if (DetourTransactionCommit() == NO_ERROR)
OutputDebugString("DrawTextExW() detoured successfully\n");
for each of DrawTextA, DrawTextW, DrawTextExA, DrawTextExW that if CDHtmlDialog were to use one of these functions to draw the text with, it would get detoured? And perhaps someone would know how that class outputs text on the window's DC?
One funny thing that I did notice is that DrawTextExW() is getting detoured, trying to print the OK text on a button that is actually not displayed on the window. Not sure what that's about.
> cv32.dll!MyDrawTextExW(HDC__ * hdc=0x940111a2, wchar_t * lpchText=0x04925128, int cchText=2, tagRECT * lprc=0x012eb314, unsigned int dwDTFormat=1048613, tagDRAWTEXTPARAMS * lpDTParams=0x00000000) Line 4186 C++
uxtheme.dll!CTextDraw::DrawTextW() Unknown
uxtheme.dll!DrawThemeText() Unknown
comctl32.dll!Button_DrawThemed() Unknown
comctl32.dll!Button_DrawPush() Unknown
comctl32.dll!Button_PaintImpl() Unknown
comctl32.dll!Button_WndProc() Unknown
user32.dll!__InternalCallWinProc#20() Unknown
user32.dll!UserCallWinProcCheckWow() Unknown
user32.dll!DispatchClientMessage() Unknown
user32.dll!___fnDWORD#4() Unknown
ntdll.dll!_KiUserCallbackDispatcher#12() Unknown
user32.dll!_DispatchMessageA#4() Unknown
mfc120d.dll!AfxInternalPumpMessage() Line 181 C++
mfc120d.dll!CWinThread::PumpMessage() Line 900 C++
mfc120d.dll!AfxPumpMessage() Line 190 C++
mfc120d.dll!CWnd::RunModalLoop(unsigned long dwFlags=4) Line 4644 C++
mfc120d.dll!CWnd::CreateRunDlgIndirect(const DLGTEMPLATE * lpDialogTemplate=0x7216cc28, CWnd * pParentWnd=0x012ecd60, HINSTANCE__ * hInst=0x6f730000) Line 470 C++
mfc120d.dll!CDialog::DoModal() Line 633 C++
Using this code the resulting dialog box is drawn without ability to be able to be resized by mouse:
#include <windows.h>
static UINT_PTR CALLBACK OFNHookProc (HWND hdlg, UINT uiMsg, WPARAM wParam, LPARAM lParam) {
return 0;
}
int main() {
OPENFILENAMEW ofn;
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(OPENFILENAMEW);
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_ENABLEHOOK;
ofn.lpfnHook = OFNHookProc;
GetOpenFileNameW(&ofn);
return 0;
}
Removing OFN_ENABLEHOOK shows correct dialog window with resize indicator at bottom right corner. How to make dialog that is user-resizeable and with hook procedure ?
(of course that hook is mock here, only to illustrate the error, no matter what I put inside, of course if it is correct on other matters, result is the same)
You need to include the OFN_ENABLESIZING flag when using OFN_ENABLEHOOK. This is documented behavior:
OPENFILENAME structure
OFN_ENABLESIZING
0x00800000
Enables the Explorer-style dialog box to be resized using either the mouse or the keyboard. By default, the Explorer-style Open and Save As dialog boxes allow the dialog box to be resized regardless of whether this flag is set. This flag is necessary only if you provide a hook procedure or custom template. The old-style dialog box does not permit resizing.
The windows messaging system assigns the post time as a DWORD to every message.
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time; // <---
POINT pt;
} MSG, *PMSG, *LPMSG;
But I can find no documentation indicating what that DWORD represents. Is it seconds, milliseconds, nanoseconds? From what point in history is it counting?
Found it.
GetMessageTime defines it as the number of milliseconds since the system was started.
My example:
PowerPoint start Excel when insert embedded workbook.
I continue to work with PowerPoint. Excel stays as hidden process.
When I close PowerPoint it says to Excel to close itself (Excel closed later then PowerPoint).
When I kill PowerPoint, Excel becomes zombie.
My add-in is loaded into Excel process.
What message(I read that COM STA objects communicate through windows messaging) I should intercept to be notified about such event? What hook should I use? Is there any mean to be notified about count of reference to COM object changes?
UPDATE
My question seems was ambiguous. I am not trying to handle case when I kill PowerPoint. It just for evidence that PowerPoint send some message to Excel when closed normally. I am trying to intercept this message for a good reason.
UPDATE2
I cannot use API - because API fails. I am trying to solve Creating class in new thread in new app domain and providing it with Office COM object in embedded mode leads to zombie process
In case if you kill 'PowerPoint' there will be no reference count changes in Excel. That is why it is not being unloaded.
I believe you may simply post WM_QUIT message to the main window of the Excel process. You may find it using "Spy++.exe" which is a tool bundled with Visual Studio. Also, it is possible, that you may acquire a class factory for some object in Excel and invoke the 'LockServer' method with fLock = FALSE. This will decrease the reference count of Excel server.
But take this as a hack, since an add-in should not influence the behavior of the host application.
None. COM on a local server does use windows messages but it uses private messages which are an implementation detail. All it really does is post messages to a hidden window meaning "you have new COM events in your apartment". The hidden window passes this to the COM library which uses (I believe) shared memory to communicate with other processes, and does the rest of the work such as finding out what the event is (call, call return etc).
In other words, there is no specific message to say "excel go away", and even if there was you couldn't rely on it becaues it is an implementation detail which can change.
When you close the PowerPoint document containing the embedded spreadsheet, PowerPoint will call IUnknown::Release() on all the objects it holds. Excel keeps an internal track of outstanding objects, and will close itself when they all go away.
If you have an add-in in the excel process, and want to know when excel closes, use the addin api to find out.
Depending on the type of Add-In there are different APIs for this. The COM add-ins have events you can register for. But that is a different question: "how can my Excel add-in get notification that an invisible Excel instance is closing".
Added Solution to actual problem/question not asked
#asd.and.Rizzo, this is occurring because you hare holding on to the objects created by Excel. This means that Excel thinks it cannot exit because someone is using the objects. Excel is correct because that somone is your add-in.
This sort of lifetime issue is typical when creating any Office or Excel add-in and is not unique to .Net add-ins.
You are wanting to solve the problem by cheating Excel's reference count. But it is in your hands: The reference count is only up because you are holding objects! If you release them the count will go down to zero with no cheating.
Solution
The best solution is to avoid sinking events and avoid holding on to any object. Depending on what your add-in is doing this may not be possible.
If you cannot, you must sink the document close events. In each, check how many documents are open. When the count reaches zero you should remove your event handlers, call Marshall.ReleaseComObject on every object you have ever held, and unload your add-in. Excel will then exit.
But please note: You must call Marshall.ReleaseComObject on every object you ever hold. If you don't, Excel will never exit. This should occur if you unload the AppDomain.
I found next (from interpreting COM interfaces as pure C structs + function and Direct X hacks). Works but does not fix problem for some reason. My be should do the same for Workbooks, Worksheets...
Declare:
typedef HRESULT STDMETHODCALLTYPE QueryInterfacePtr( REFIID, void **);
typedef ULONG STDMETHODCALLTYPE AddRefPtr( );
typedef ULONG STDMETHODCALLTYPE ReleasePtr( );
typedef ULONG STDMETHODCALLTYPE GetTypeInfoCountPtr( UINT *) ;
typedef ULONG STDMETHODCALLTYPE GetTypeInfoPtr ( UINT, LCID, ITypeInfo **) ;
typedef ULONG STDMETHODCALLTYPE GetIDsOfNamesPtr ( REFIID, LPOLESTR *,
UINT, LCID, DISPID *) ;
typedef ULONG STDMETHODCALLTYPE InvokePtr ( DISPID, REFIID,
LCID, WORD, DISPPARAMS *,
VARIANT *, EXCEPINFO *, UINT *) ;
typedef struct {
// IUnknown functions
QueryInterfacePtr * QueryInterface;
AddRefPtr * AddRef;
ReleasePtr * Release;
// IDispatch functions
GetTypeInfoCountPtr* GetTypeInfoCount;
GetTypeInfoPtr* GetTypeInfo;
GetIDsOfNamesPtr* GetIDsOfNames;
InvokePtr* Invoke;
} IDispatchInterceptor;
typedef ULONG __stdcall releasePTR(IDispatch *self);
typedef ULONG __stdcall addrefPTR(IDispatch *self);
Next I have done for Excel:
static IDispatch * application = NULL;
static releasePTR* realRelease = NULL;
static addrefPTR* realAddRef = NULL;
static ULONG wasAdd = 0;
static ULONG oldCount = 0;
HRESULT STDMETHODCALLTYPE QueryInterfaceNativeOutOfProcSrv(REFIID riid, void **ppv){
return application->QueryInterface(riid,ppv);
}
ULONG STDMETHODCALLTYPE AddRefNativeOutOfProcSrv(){
return application->AddRef();
}
ULONG STDMETHODCALLTYPE ReleaseNativeOutOfProcSrv(){
return application->Release();
}
ULONG STDMETHODCALLTYPE GetTypeInfoCountSrv( UINT * count) {
return application->GetTypeInfoCount(count);
}
ULONG STDMETHODCALLTYPE GetTypeInfoSrv ( UINT n, LCID id, ITypeInfo ** inf) {
return application->GetTypeInfo(n,id,inf);
}
ULONG STDMETHODCALLTYPE GetIDsOfNamesSrv ( REFIID a, LPOLESTR * b, UINT c, LCID d, DISPID * e) {
return application->GetIDsOfNames(a,b,c,d,e);
}
ULONG STDMETHODCALLTYPE InvokeSrv ( DISPID a, REFIID b, LCID c, WORD d, DISPPARAMS * e, VARIANT * i, EXCEPINFO * j, UINT *k) {
return application->Invoke(a,b,c,d,e,i,j,k);
}
static IDispatchInterceptor interceptor =
{QueryInterfaceNativeOutOfProcSrv,AddRefNativeOutOfProcSrv,ReleaseNativeOutOfProcSrv,
GetTypeInfoCountSrv,GetTypeInfoSrv,GetIDsOfNamesSrv,InvokeSrv
};
ULONG __stdcall release(IDispatch *self)
{
ULONG c = realRelease(self);
Log->logWrite("release %d",c);
if ( c == 1)
{
if (instance != NULL)
{
instance->OnBeginShutdown(NULL);
Log->logWrite("OnBeginShutdown %d",c);
instance->OnEmbeddedDisconnection();
Log->logWrite("OnEmbeddedDisconnection %d",c);
instance = NULL;
}
}
//if (c == 2) {
// c = realRelease(self);
// c = realRelease(self);
//}
return c;
}
ULONG __stdcall addref(IDispatch *self)
{
ULONG c = oldCount;
if (wasAdd == 0)
{
c = realAddRef(self);
oldCount = c;
wasAdd++;
}
else if (wasAdd == 1)
{
Log->logWrite("ADDREF FAKE %d",c);
wasAdd++;
}
else if (wasAdd == 2)
{
Log->logWrite("ADDREF FAKE %d",c);
wasAdd++;
}
else
{
c = realAddRef(self);
}
Log->logWrite("ADDREF %d",c);
return c;
}
void InterceptRelease( IDispatch obj){
void iunknown_vtable= (void*)((unsigned int)obj);
void* idispatch_vtable = (void*)(((unsigned int)iunknown_vtable)+8);
unsigned int* v1 = (unsigned int*)idispatch_vtable;
realRelease = (releasePTR*)*v1;
DWORD old;
VirtualProtect(v1,4,PAGE_EXECUTE_READWRITE,&old);
*v1 = (unsigned int) release;
//while(obj->Release() > 0){};
}
void InterceptAddRef( IDispatch obj){
void iunknown_vtable= (void*)((unsigned int)obj);
void* idispatch_vtable = (void*)(((unsigned int)iunknown_vtable)+4);
unsigned int* v1 = (unsigned int*)idispatch_vtable;
realAddRef = (addrefPTR*)*v1;
DWORD old;
VirtualProtect(v1,4,PAGE_EXECUTE_READWRITE,&old);
*v1 = (unsigned int) addref;
}
Apply:
IDispatch * app = Application;
InterceptRelease(app);
InterceptAddRef(app);
I am writing a dll which is a COM wrapper for another dll (the inner dll) without COM support. The inner dll performs a lengthy computation and lets the outer dll know how progress is going via a callback function. The outer dll just makes the functions visible over COM.
However, I need the outer dll to pop up a progress bar dialog (the COM client I'm serving can't do this itself for various reasons). So how do I go about doing that? All examples I have seen so far revolve around Win32 apps which have a WinMain entry point; what can be done if we're already in a dll call when the dialog is needed?
I'm new to windows GUI programming, so quite out of my depth here. The existing code is included below - specific suggestions on what to call where would be appreciated. I'm guessing I may need to fire off a second thread to refresh the progress dialog.
Inner dll .h file (for implicit linking):
#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);
Then in the outer dll, the com wrapper, 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;
}
I'm posting a new answer which is more relevant to your updated question (and in order to be eligible for the bounty). Consider first this minimal source for a regular executable which contains a progress bar:
#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;
}
If you like, I can post the relevant .rc resource file for this program although the code is mostly for you to gain the correct conceptual understanding. So to quickly summarise, this program:
Contains a single dialog containing a single progress bar
Sets a timer to fire each second
Each time the timer fires, the timer message triggers the progress bar to be updated
Graphically, it looks like this:
Your question is how to increment this bar from a DLL instead. What you need to do is to allow some way for the DLL to communicate with the window containing the progress bar. I'm not quite sure how you are loading the DLL, but this is the approach I would take assuming it is done through DLL injection:
Load/inject the DLL into the target
Have the DLL export some initialisation routine allowing it to receive information about the injector/client process.
Use GetProcAddress and CreateRemoteThread from the client to invoke this initialisation routine.
In the DLL, use the received information to get the HWND of the client.
Concretely, the intialisation routine would look like this:
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);
}
The client code might be something like this:
/*
* 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);
}
}
So now you have the HWND of the client in the DLL and hence a way for you to do the communication. You can then specify a custom message in the client to change the progress bar:
/*
* The new progress position can be passed in wParam.
*/
#define WM_UPDATE_PROGRESS_BAR (WM_APP + 1)
Also add the corresponding case in the DialogFunc (we can remove the WM_TIMER code now because that was only there to demonstrate how to interact with progress bars):
case WM_UPDATE_PROGRESS_BAR:
SendMessage(GetDlgItem(hwndDlg, IDC_PROGRESS1), PBM_SETPOS, wParam, 0);
break;
And now to trigger changes in the progress bar of the client, the DLL simply has to do:
SendMessage(hwndClient, WM_UPDATE_PROGRESS_BAR, ..., 0);
Note that WM_UPDATE_PROGRESS_BAR needs to be redefined in the DLL as well.
To fit this all in with your current code:
/*
* 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;
}
Since you state that the DLL has no GUI and the client handles all the user interaction, why don't you send the progress information to the client instead and have it displayed there?
If you want to display the dialog in the DLL, you do so in exactly the same way as you would within a regular executable. There is absolutely no difference. If you want the DLL to continue working while it updates the progress bar, you can just kick off a new thread with CreateThread.
If you show some code, we'll be able to help you more directly.