Hi All!
I discovered that calling a function that take long time is an effective way to raise exceptions :biggrin: :biggrin: Just is needed to make several clicks with mouse meanwhile!
I think that I have to create a Thread with that function, but I don't know if capture mouse and keyboard events, lock others processes initiation, ...
Is there and standard way to deal with this?
Thanks in advance, HSE
A thread, indeed - and I don't see a problem with threads. When it reaches the end, just send a message to yourself. Actually, I see lots of "professional" software that hangs while it's doing important things :cool:
No need to stop windows procedure, so thread for process data is solution - maybe is need intercep SEH vs C++ exceptions (http://www.codeproject.com/Tips/721807/SEH-vs-Cplusplus-exceptions)
Thanks JJ and Adamanteus.
Creating a thread for functions apparently solved exceptions.
Now I have to see how to limit other access to same data while still are in process.
Quote from: HSE on May 10, 2021, 08:39:53 AMhow to limit other access to same data while still are in process.
Case WM_COMMAND
movzx eax, word ptr wParam
.if eax==StartThreadID && ThreadStarted==0
inc ThreadStarted
invoke CreateThread, ...
.endif
CASE WM_MYSTUFFISDONE
and ThreadStarted, 0
invoke MessageBox, 0, Chr$("The thread says it's done"), Chr$("Hello:"), MB_OK
:thumbsup: Probably some variation of that could be. I'm also trying to intercept mouse an keyboard :biggrin:
Hi HSE,
another interesting example, MsgWaitForMultipleObjects - https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjects
The
MsgWaitForMultipleObjects function can specify handles of any of the following object types in the pHandles array:
QuoteChange notification
Console input
Event
Memory resource notification
Mutex
Process
Semaphore
Thread
Waitable timer
Multithreaded programming with Win32 - https://archive.org/details/multithreadedpro0000pham
https://github.com/jimbeveridge/multithreading/blob/master/src/PrntWait.03/PrntWait.c
/*
* PrntWait.c
*
* Sample code for "Multithreading Applications in Win32"
* This is from Chapter 3, Listing 3-4
*
* Demonstrate background printing and
* using MsgWaitForMultipleObjects to
* wait for threads to exit.
*/
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include "resource.h"
#include "../MtVerify.h"
//
// Macro definitions
//
#define WM_SHOWBITMAP WM_APP
#define WM_THREADCOUNT WM_APP+1
#define MAX_PRINT_JOBS 64
//
// Structures
//
typedef struct
{ // Information passed to background thread for printing
HWND hDlg;
HWND hWndParent;
HDC hDc;
BOOL bPrint; // TRUE if printing;
char szText[256];
} ThreadPrintInfo;
//
// Global variables
//
HANDLE hInst;
HBITMAP gbmpDisplay;
RECT gDisplayRect;
int gNumPrinting = 0;
// Handle to each created thread
HANDLE gPrintJobs[64];
// Height of bitmap returned by DrawText
int iHeight;
// HWND of the dialog so other threads can find it.
HWND hDlgMain;
//
// Function declarations
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam);
void PrintDlg_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
void PrintDlg_OnPaint(HWND hwnd);
void PrintText(HWND hwndParent, char *pszText);
void PrintToDisplay(HWND hwndParent, char *pszText);
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid);
///////////////////////////////////////////////////////////
//
// WinMain
//
// Main entry point of application. This will be a
// dialog based app, not a normal window, so this
// routine acts a little differently than "normal".
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
HWND hWnd;
WNDCLASS wc;
BOOL quit;
int exitCode;
hInst = hInstance;
if (!hPrevInstance)
{
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = MainWndProc;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, "GenIco");
wc.hCursor = LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground= GetSysColorBrush(COLOR_BACKGROUND);
wc.lpszMenuName = "PRINTING_MENU";
wc.lpszClassName= "PrintDlgClass";
if (!RegisterClass(&wc))
return FALSE;
}
hWnd = CreateWindow(
"PrintDlgClass",
"Printing Hands-On",
WS_OVERLAPPED|WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU,
CW_USEDEFAULT, // At this point we do not want to
0, // show the window until we know
0, // how big the Dialog Box is so
0, // that we can fit the main window
NULL, // around it.
NULL,
hInstance,
NULL);
hDlgMain = CreateDialog(hInst,
MAKEINTRESOURCE(IDD_PRINT),
hWnd, PrintDlgProc);
ShowWindow(hWnd, nCmdShow);
ShowWindow(hDlgMain, SW_SHOW);
quit = FALSE;
exitCode = 0;
while (!quit || gNumPrinting > 0)
{ // Wait for next message or object being signaled
DWORD dwWake;
dwWake = MsgWaitForMultipleObjects(
gNumPrinting,
gPrintJobs,
FALSE,
INFINITE,
QS_ALLEVENTS);
if (dwWake >= WAIT_OBJECT_0 && dwWake < WAIT_OBJECT_0 + gNumPrinting)
{ // Object has been signaled
// Reorder the handle array so we do not leave
// empty slots. Take the handle at the end of
// the array and move it into the now-empty slot.
int index = dwWake - WAIT_OBJECT_0;
gPrintJobs[index] = gPrintJobs[gNumPrinting-1];
gPrintJobs[gNumPrinting-1] = 0;
gNumPrinting--;
SendMessage(hDlgMain, WM_THREADCOUNT, gNumPrinting, 0L);
} // end if
else if (dwWake == WAIT_OBJECT_0 + gNumPrinting)
{
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{ // Get Next message in queue
if(hDlgMain == NULL || !IsDialogMessage(hDlgMain,&msg))
{
if (msg.message == WM_QUIT)
{
quit = TRUE;
exitCode = msg.wParam;
break;
} // end if
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} // end while
}
} // end while
return (exitCode); /* Returns the value from PostQuitMessage */
}
LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
break;
case WM_COMMAND:
switch (wParam)
{
case IDM_ABOUT:
DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wParam, lParam));
}
case WM_SETFOCUS:
// ensure that the Dialog Box has the focus
SetFocus(hDlgMain);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
DestroyWindow(hDlg);
hDlgMain = NULL;
break;
case WM_DESTROY:
return TRUE;
break;
case WM_SHOWBITMAP:
if (gbmpDisplay)
DeleteObject(gbmpDisplay);
gDisplayRect = *(RECT*)wParam;
gbmpDisplay = (HBITMAP) lParam;
InvalidateRect(hDlgMain, NULL, TRUE);
break;
case WM_THREADCOUNT:
{
HMENU hMenu;
// Show number of threads
SetDlgItemInt(hDlg, IDC_EDIT_THREADS, wParam, FALSE);
// Enable/Disable File.Exit menu
hMenu = GetMenu(GetParent(hDlg));
EnableMenuItem(hMenu, IDM_EXIT, wParam != 0);
break;
} // end case
HANDLE_MSG(hDlg, WM_INITDIALOG, PrintDlg_OnInitDialog);
HANDLE_MSG(hDlg, WM_COMMAND, PrintDlg_OnCommand);
HANDLE_MSG(hDlg, WM_PAINT, PrintDlg_OnPaint);
default:
return (FALSE);
}
return 0;
}
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam)
{
RECT rect;
// Size parent to fit this dialog
GetWindowRect(hwndDlg, &rect);
SetWindowPos(GetParent(hwndDlg),NULL,
0,0,
rect.right-rect.left,
rect.bottom-rect.top+GetSystemMetrics(SM_CYMENU)
+GetSystemMetrics(SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER);
SetDlgItemInt(hwndDlg, IDC_EDIT_THREADS, 0, FALSE);
return TRUE;
}
void PrintDlg_OnCommand(HWND hDlg, int id,HWND hwndCtl, UINT codeNotify)
{
char szText[256];
switch (id)
{
case IDC_PRINT:
GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
PrintText(hDlg, szText);
break;
case IDC_DISPLAY:
GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
PrintToDisplay(hDlg, szText);
break;
case IDCANCEL:
case IDM_EXIT:
PostMessage(GetParent(hDlg),WM_DESTROY,
(WPARAM)0, (LPARAM)0);
DestroyWindow(hDlgMain);
hDlgMain = NULL;
break;
default:
break;
}
}
void PrintDlg_OnPaint( HWND hwnd )
{
PAINTSTRUCT paint;
HWND hwndCtrl;
HDC hdc;
HDC hDcMem;
HBITMAP bmpOld;
RECT rect;
POINT point;
if (!gbmpDisplay)
return;
hwndCtrl = GetDlgItem(hwnd, IDC_OUTPUT);
hdc = BeginPaint(hwnd, &paint);
GetWindowRect(hwndCtrl, &rect);
point = *((POINT *)&rect);
ScreenToClient(hwnd, &point);
hDcMem = CreateCompatibleDC(NULL);
bmpOld = SelectObject(hDcMem, gbmpDisplay);
// Copy bitmap to screen
MTVERIFY( BitBlt(hdc, point.x+10, point.y+40,
gDisplayRect.right-gDisplayRect.left, gDisplayRect.bottom-gDisplayRect.top,
hDcMem, iHeight, 0, SRCCOPY) );
SelectObject(hDcMem, bmpOld);
DeleteDC(hDcMem);
EndPaint(hwnd, &paint);
}
//
// Asks user which printer to use, then creates
// background printing thread.
//
void PrintText(HWND hwndParent, char *pszText)
{
ThreadPrintInfo *pInfo;
HANDLE hThread;
DWORD dwThreadId;
int result;
DOCINFO docInfo;
PRINTDLG dlgPrint;
// Put up Common Dialog for Printing and get hDC.
memset(&dlgPrint, 0, sizeof(PRINTDLG));
dlgPrint.lStructSize = sizeof(PRINTDLG);
dlgPrint.hwndOwner = hwndParent;
dlgPrint.Flags = PD_ALLPAGES | PD_USEDEVMODECOPIES
| PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
dlgPrint.hInstance = hInst;
if (!PrintDlg(&dlgPrint))
return;
// Initialize Printer device
docInfo.cbSize = sizeof(DOCINFO);
docInfo.lpszDocName = "Background Printing Example";
docInfo.lpszOutput = NULL;
docInfo.lpszDatatype = NULL;
docInfo.fwType = 0;
result = StartDoc(dlgPrint.hDC, &docInfo);
result = StartPage(dlgPrint.hDC);
pInfo = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(ThreadPrintInfo));
pInfo->hDlg = hwndParent;
pInfo->hWndParent = hwndParent;
pInfo->hDc = dlgPrint.hDC;
pInfo->bPrint = TRUE;
strcpy(pInfo->szText, pszText);
MTVERIFY( hThread = CreateThread(NULL, 0,
BackgroundPrintThread, (LPVOID)pInfo,
0, &dwThreadId ));
// keep track of all background printing threads
gPrintJobs[gNumPrinting++] = hThread;
SendMessage(hwndParent, WM_THREADCOUNT, gNumPrinting, 0L);
}
//
// Shows output on the dialog box.
//
void PrintToDisplay(HWND hwndParent, char *pszText)
{
ThreadPrintInfo *pInfo;
DWORD dwThreadId;
HANDLE hThread;
pInfo = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(ThreadPrintInfo));
pInfo->hDlg = hwndParent;
pInfo->hWndParent = GetDlgItem(hwndParent, IDC_OUTPUT);
pInfo->hDc = GetDC(pInfo->hWndParent);
pInfo->bPrint = FALSE;
strcpy(pInfo->szText, pszText);
MTVERIFY( hThread = CreateThread(NULL, 0,
BackgroundPrintThread,
(LPVOID)pInfo,
0, &dwThreadId ));
// keep track of all background printing threads
gPrintJobs[gNumPrinting++] = hThread;
SendMessage(hwndParent, WM_THREADCOUNT, gNumPrinting, 0L);
}
//---------------------------------------------------------
// About Box Handling
//---------------------------------------------------------
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_COMMAND:
if (LOWORD(wParam) == IDOK
|| LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, TRUE);
return (TRUE);
}
break;
default:
return (DefWindowProc(hDlg, message, wParam, lParam));
}
return FALSE;
}
//---------------------------------------------------------
// Background Printing Code
//---------------------------------------------------------
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid)
{
ThreadPrintInfo *pInfo = (ThreadPrintInfo*) pVoid;
RECT rect;
RECT rectMem;
HDC hDcMem;
HBITMAP bmpMem;
HBITMAP bmpOld;
int x, y;
int counter = 0;
int nHeight;
HFONT hFont;
HFONT hFontOld;
// Get dimensions of paper into rect
rect.left = 0;
rect.top = 0;
rect.right = GetDeviceCaps(pInfo->hDc, HORZRES);
rect.bottom = GetDeviceCaps(pInfo->hDc, VERTRES);
nHeight = -MulDiv(36, GetDeviceCaps(pInfo->hDc, LOGPIXELSY), 72);
// Create Font
hFont = CreateFont(nHeight, 0,
0, 0, FW_DONTCARE,
FALSE, FALSE, FALSE,
ANSI_CHARSET,
OUT_TT_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
VARIABLE_PITCH,
"Arial");
MTASSERT( hFont != 0);
// Draw into memory device context
hDcMem = CreateCompatibleDC(pInfo->hDc);
hFontOld = SelectObject(hDcMem, hFont);
iHeight = DrawText(hDcMem, pInfo->szText, -1, &rect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
rectMem = rect;
rectMem.left = rect.left + iHeight;
rectMem.right = rect.right + (iHeight*2);
bmpMem = CreateCompatibleBitmap(hDcMem,
rectMem.right, rect.bottom);
bmpOld = SelectObject(hDcMem, bmpMem);
OffsetRect(&rect, iHeight, 0);
DrawText(hDcMem, pInfo->szText, -1, &rect,
DT_LEFT | DT_NOPREFIX | DT_WORDBREAK);
// Italicize bitmap. We use GetPixel and
// SetPixel because they are horribly inefficient,
// thereby causing the thread to run for awhile.
for (y = 0; y < iHeight; y++)
{ // Italicize line y
for (x = rectMem.right; x > iHeight; x--)
{ // Move specified pixel to the right.
COLORREF color;
int offset;
offset = y - iHeight;
color = GetPixel(hDcMem, x + offset, y);
if (color != 0)
counter++;
SetPixel(hDcMem, x, y, color);
} // end for x
} // end for y
MTASSERT( counter > 0);
// Copy bitmap of italicized text from memory to device
if (pInfo->bPrint)
{
BitBlt(pInfo->hDc, 50, 50, rectMem.right-rect.left, rectMem.bottom-rect.top,
hDcMem, iHeight, 0, SRCCOPY);
}
SelectObject(hDcMem, hFontOld);
SelectObject(hDcMem, bmpOld);
DeleteDC(hDcMem);
if (!pInfo->bPrint)
{
// We can't just write to the global variable where the
// bitmap is kept or we might overwrite the work of
// another thread, thereby "losing" a bitmap
// Also, if we used PostMessage instead of SendMessage, then
// the rectangle could have been deleted (it's on the stack)
// by the time the main message loop is reached.
SendMessage(pInfo->hDlg, WM_SHOWBITMAP, (WPARAM)&rectMem, (LPARAM) bmpMem);
}
if (pInfo->bPrint)
{ // Finish printing
int result;
result = EndPage(pInfo->hDc);
MTASSERT (result != SP_ERROR);
result = EndDoc(pInfo->hDc);
MTASSERT (result != SP_ERROR);
DeleteDC(pInfo->hDc);
// If we are printing, we are done with the bitmap.
DeleteObject(bmpMem);
}
else
{
ReleaseDC(pInfo->hWndParent, pInfo->hDc);
}
// free data structure passed in.
HeapFree(GetProcessHeap(), 0, pInfo);
return 0;
}
Quote from: HSE on May 10, 2021, 11:58:39 PM
:thumbsup: Probably some variation of that could be. I'm also trying to intercept mouse an keyboard :biggrin:
A test case - full project attached, 85 lines of pure Masm32 (no MasmBasic):
MySlowAlgo proc uses esi edi arg
push 9
mov edi, arg
.Repeat
lea esi, [edi+12]
invoke lstrcpy, esi, chr$("Running thread #")
mov ecx, dword ptr [esp]
invoke lstrcat, esi, str$(ecx)
invoke SetWindowText, dword ptr [edi+4], esi
xor ecx, ecx
.Repeat
inc ecx
.Until Sign? ; that takes almost a second
dec dword ptr [esp]
.Until Sign?
pop edx
invoke SendMessage, [edi], WM_USER+999, 0, 0 ; tell main that you are ready
ret
MySlowAlgo endp
Thanks LiaoMi! I have to study that :biggrin:
Nice JJ :thumbsup:
Example from the old forum :arrow_down:
Quote from: LiaoMi on May 12, 2021, 06:04:18 AM
Example from the old forum :arrow_down:
What's the purpose of offset
flat:ButtonTxt_Stopped? The code generated is identical with or without the flat: :cool:
Look like I have a solution with a lock and PeekMessage.
Function in new Thread:Method BibTexApp.WriteT1, uses xsi, xtype:XWORD
SetObject xsi
OCall [xsi].BibBigMaster::BibTexBigMaster.WriteT, xtype
invoke CloseHandle, [xsi].lockwin ; <--
mov [xsi].BibBigMaster.mlock, 0 ;<--
MethodEnd
Creating the Thread in main WndProc: mov [xsi].BibBigMaster.mlock, 1 <--
invoke CreateThread, NULL, 0, $MethodAddr(BibTexApp.WriteT1), xsi, 1, NULL
mov [xsi].lockwin, xax
fDone:
invoke PeekMessage, addr msg, [xsi].hWnd, 0, 0, PM_REMOVE
cmp [xsi].BibBigMaster.mlock, 0
jnz fDone
Only a minimum problem because apparently some redraw messages from system also are removed.
A very elemental test show the overload of messages interception is around 5%:
mov [xsi].BibBigMaster.mlock, 1
OCall [xsi].BibBigMaster::BibTexBigMaster.WriteT, 1
invoke GetTickCount
mov ticks1, eax
OCall [xsi].BibBigMaster::BibTexBigMaster.WriteT, 1
invoke GetTickCount
mov ticks3, eax
sub eax, ticks1
mov ticks2, eax
invoke CreateThread, NULL, 0, $MethodAddr(BibTexApp.WriteT1), xsi, 0, NULL
mov [xsi].lockwin, xax
fDone:
invoke PeekMessage, addr msg, [xsi].hWnd, 0, 0, PM_REMOVE
.if [xsi].BibBigMaster.mlock
jnz fDone
.endif
invoke GetTickCount
sub eax, ticks3
mov ticks1, eax
fild ticks2
fidiv ticks1
fstp ticksr
DbgFloat ticksr
Thanks, HSE
simple methods can be used to prevent exceptions:
*Verify preservation of the stack and the register
* Create local SDWORD and put -1 in them (FFFFFFFFFFFF)
From time to time,verify that they are always the same value.
Simple but it works.
Quote from: TouEnMasm on May 13, 2021, 01:52:09 AM
*Verify preservation of the stack and the register
No doubt, some corruption happen after "no response". :thumbsup:
Hector, what about starting the thread using the suspended flag and in the thread proc, run the code then suspend it again. Also isolates between caller and thread so you can track down where a problem is located.
Hi Hutch!
There is no problem if function run in a thread.
Removing application messages prevent to run other function using same data, no problem neither.
Some messages from system apparently are also intercepted, then some window drawings only can be updated after thread finish (here I have a random popup that say one disk is full :biggrin: that sometimes is drawed over the application). If that become a problem I think is posible to filter what messages are removed, but perhaps don't worth the effort right now.
Quote from: jj2007 on May 12, 2021, 06:44:22 AM
Quote from: LiaoMi on May 12, 2021, 06:04:18 AM
Example from the old forum :arrow_down:
What's the purpose of offset flat:ButtonTxt_Stopped? The code generated is identical with or without the flat: :cool:
Hi jj2007,
Microsoft Corporation - Microsoft Macro Assembler 6.11 Reference Manual_Macro Assembler Reference-Microsoft Corporation (1992)
@dataThe name of the default data group. Evaluates to DGROUP for all models
except FLAT. Evaluates to FLAT under the FLAT memory model (text
macro).
FLAT as a group override ("mov eax, offset FLAT:<symbol>") <-"OFFSET FLAT:" to reference memory locations in the .data section.
OFFSET FLAT is used when the memory model is flat. OFFSET would have translated to a segment selector (DS) and OFFSET, whereas OFFSET FLAT translates to an absolute address of the label xxx relative to 0 (seeing as that's the base of the DS segment in flat mode), which the linker resolves after it is assembled.
Thanks, LiaoMi. That's what I imagined, but it doesn't make much sense in that code, because offset ButtonTxt works fine without the "flat". Where are segment selectors in a flat model anyway? Looks like a relic from the 16-bit world :rolleyes:
Sometimes I declare read-only strings in the code section, and it makes no difference if the string is there or in .data - offset works:
.code
title db "Hello", 0
Sayhi proc arg
invoke MessageBox, 0, arg, offset title, MB_OK
ret
Sayhi endp
@jj2007QuoteLooks like a relic from the 16-bit world :rolleyes:
I think so :azn: