News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

How to prevent funny exceptions?

Started by HSE, May 09, 2021, 02:38:45 AM

Previous topic - Next topic

HSE

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


Equations in Assembly: SmplMath

jj2007

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:

Adamanteus

No need to stop windows procedure, so thread for process data is solution - maybe is need intercep SEH vs C++ exceptions

HSE

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.
Equations in Assembly: SmplMath

jj2007

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

HSE

 :thumbsup: Probably some variation of that could be. I'm also trying to intercept mouse an keyboard :biggrin:
Equations in Assembly: SmplMath

LiaoMi

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;
}

jj2007

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

HSE

Thanks LiaoMi! I have to study that  :biggrin:

Nice JJ  :thumbsup:
Equations in Assembly: SmplMath

LiaoMi


jj2007

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:

HSE

#11
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
Equations in Assembly: SmplMath

TouEnMasm


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.
Fa is a musical note to play with CL

HSE

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:
Equations in Assembly: SmplMath

hutch--

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.