0

I am totally failing to understand the reason for this. When I compile it and run it, program continually opens printer selection dialogs. I know I can be mistaking something but I though that I can at least prevent this with mutex, why aren't mutexes preventing the multiple threads from executing? PrintDlgEx is a blocking procedure and I confirmed that there are multiple threads (????) running with debugging output. The good code would open print dialog only after previous one was closed.

The typical output of the following code running for 5 or so seconds is:

168
called pdx
called pdx
168
pdx returned
HRES 0
pdx returned
HRES 0

What definitely means that there are multiple threads running BUT I did not see those threads in ProcessExplorer. How can one thread run multiple instances of WndProc?

So after it was pointed out that mutexes are thread-owned the real question now probably is why there are multiple instances of WndProc running?

#include <windows.h>
#include <stdio.h>

HANDLE ghMutex=NULL;

enum hotkey {
    bESC
};


static LRESULT CALLBACK WndProc(
    HWND hwnd,        // handle to window
    UINT uMsg,        // message identifier
    WPARAM wParam,    // first message parameter
    LPARAM lParam)    // second message parameter
{

    static enum class mode {init,signal,noise,search} Mode;
    static HRESULT hResult;
    static PRINTDLGEX pdx;
    static LPPRINTPAGERANGE pPageRanges = NULL;
    static enum class status {printing, stopped} Status;
    static int jobId;
    static HANDLE printerHd;
    JOB_INFO_1A *JobInfo1;

    static bool inited;

    if(ghMutex==NULL){
        ghMutex = CreateMutex( 
            NULL,              // default security attributes
            FALSE,             // initially not owned
            NULL);             // unnamed mutex
        printf("%d\n",(int)ghMutex);
        if(ghMutex==NULL) return 0;
    }

    if(WaitForSingleObject( 
        ghMutex,
        INFINITE)!=WAIT_OBJECT_0) return 0;

    switch (uMsg) 
    {
        case WM_TIMER:
            {
                if(!inited) {
                    pdx = PRINTDLGEX{0};
                    pPageRanges = NULL;

                    // Allocate an array of PRINTPAGERANGE structures.
                    pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));
                    if (!pPageRanges)
                        break;

                    //  Initialize the PRINTDLGEX structure.
                    pdx.lStructSize = sizeof(PRINTDLGEX);
                    pdx.hwndOwner = GetDesktopWindow();
                    pdx.hDevMode = NULL;
                    pdx.hDevNames = NULL;
                    pdx.hDC = NULL;
                    pdx.Flags = PD_RETURNDC | PD_COLLATE | PD_NOPAGENUMS;
                    pdx.Flags2 = 0;
                    pdx.ExclusionFlags = 0;
                    pdx.nPageRanges = 0;
                    pdx.nMaxPageRanges = 10;
                    pdx.lpPageRanges = pPageRanges;
                    pdx.nMinPage = 1;
                    pdx.nMaxPage = 1000;
                    pdx.nCopies = 1;
                    pdx.hInstance = 0;
                    pdx.lpPrintTemplateName = NULL;
                    pdx.lpCallback = NULL;
                    pdx.nPropertyPages = 0;
                    pdx.lphPropertyPages = NULL;
                    pdx.nStartPage = START_PAGE_GENERAL;
                    pdx.dwResultAction = 0;

                    //  Invoke the Print property sheet.

                    printf("called pdx\n");
                    hResult = PrintDlgEx(&pdx);
                    printf("pdx returned\n");
                    inited=1;
                }
                printf("HRES %d\n",(int)hResult);
                if ((hResult == S_OK) && pdx.dwResultAction == PD_RESULT_PRINT) 
                {
                    // User clicked the Print button, so use the DC and other information returned in the 
                    // PRINTDLGEX structure to print the document.

                    int ph=GetDeviceCaps(pdx.hDC,PHYSICALHEIGHT);
                    int pw=GetDeviceCaps(pdx.hDC,PHYSICALWIDTH);
                    int offX=GetDeviceCaps(pdx.hDC,PHYSICALOFFSETX);
                    int offY=GetDeviceCaps(pdx.hDC,PHYSICALOFFSETY);

                    int dimX=pw-2*offX;
                    int dimY=ph-2*offY;


                    DOCINFOA nao{sizeof(DOCINFOA),"test",NULL,NULL,0};

                    jobId=StartDoc(pdx.hDC,&nao);
                    StartPage(pdx.hDC);
        ////////////nothing
                    EndPage(pdx.hDC);
                    EndDoc(pdx.hDC);
                }

            } break;
        case WM_CREATE: 
            // Initialize the window. 
            {
                inited=false;
                SetTimer(hwnd,1,2500,NULL);
            }
            break;

        case WM_DESTROY: 
            // Clean up window-specific data objects.
            {
                CloseHandle(ghMutex);
                ghMutex=NULL;
            }
            SendMessage(hwnd,WM_USER+1,0,0);
            break;

        case WM_HOTKEY:
            {
                DestroyWindow(hwnd);
            }
            break;
        // 
        // Process other messages. 
        // 
        default: 
            ReleaseMutex(ghMutex); return DefWindowProc(hwnd, uMsg, wParam, lParam); 
    } 
    ReleaseMutex(ghMutex);return 0; 
}


void testPrinter()
{

    HWND hwnd;
    {
        WNDCLASSA wc;
        wc.style = CS_HREDRAW | CS_VREDRAW; 
        wc.lpfnWndProc = (WNDPROC) WndProc; 
        wc.cbClsExtra = 0; 
        wc.cbWndExtra = 0; 
        wc.hInstance = GetModuleHandle(NULL); 
        wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); 
        wc.hCursor = LoadCursor(NULL, IDC_ARROW); 
        wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); 
        wc.lpszMenuName =  ""; 
        wc.lpszClassName = "PinTest"; 

        if (!RegisterClass(&wc)) 
           return; //cannot register window class

        hwnd=CreateWindowEx(0,"PinTest","topmost print testing window without borders and title bar",WS_VISIBLE|WS_BORDER,0,0,300,300,0,0,0,0);

        RegisterHotKey(hwnd,bESC,0,VK_ESCAPE);
    }


    MSG recent;
    BOOL result;
    while((result=GetMessage(&recent,hwnd,0,0))&&result!=-1) { //bool can be -1 in MS world
        if(recent.message==WM_USER+1) break;
        TranslateMessage(&recent);
        DispatchMessage(&recent);
    }


    return;
}



int main(){
    testPrinter();
}
Euri Pinhollow
  • 332
  • 2
  • 17
  • Tagged as C because lambda does not make it related to C++. – Euri Pinhollow Jun 07 '17 at 23:45
  • WTB some `break` statements in that `switch (uMsg)` – WhozCraig Jun 07 '17 at 23:45
  • @WhozCraig there are returns in the end of each case. Does not matter. – Euri Pinhollow Jun 07 '17 at 23:46
  • 1
    @EuriPinhollow There are many people here with the opposite view. – user253751 Jun 07 '17 at 23:49
  • @immibis this is not production code. – Euri Pinhollow Jun 07 '17 at 23:51
  • 1
    Just for my own sanity, you *intended* to specify `hwnd` as the second parameter to `GetMessage` ? Normally on the application message pump that isn't done. So.. you *wanted* that? – WhozCraig Jun 07 '17 at 23:52
  • 2
    @EuriPinhollow I mean that this should be tagged C++ and not C because it is C++ and not C, even though you *could* rewrite it in C (which you haven't). – user253751 Jun 07 '17 at 23:53
  • @WhozCraig yes, I was going to write a standalone window which won't interfere with the rest of program. – Euri Pinhollow Jun 07 '17 at 23:59
  • @immibis if questions would be tagged with all technologies which are mentioned or used in them it would be total mess and I can find a busload of examples of how tags do not mention whole lot of technologies used or mentioned in question. – Euri Pinhollow Jun 08 '17 at 00:08
  • 5
    It is very hard to see what the mutex is supposed to accomplish. All code runs on a single thread and mutex is re-entrant on the thread that owns it. So sure, nothing useful will happen. – Hans Passant Jun 08 '17 at 00:09
  • First, as Hans said in the comment, you don't need a mutex. Your code is single threaded. You just need a boolean variable to gate whatever code you are trying to prevent from being reentrant. – selbie Jun 08 '17 at 02:17
  • 2
    The technical reason why your mutex isn't blocking anything is because you are reacquiring it each time in the same thread. Each call to WndProc is guaranteed to be on the same thread. So each corresponding call to WaitForSingleObject only increments the ownership count for that mutex. – selbie Jun 08 '17 at 02:18
  • 1
    There is no cout in C. Please don't tag C++ questions C, especially not when you are obviously using a C++ compiler. – Lundin Jun 08 '17 at 06:44
  • 1
    "why aren't mutexes preventing the multiple threads from executing?" What multiple threads? There are no threads anywhere in the question. None of this makes any sense. – Lundin Jun 08 '17 at 06:49
  • Thanks for good points everyone, I tried to address them in question. – Euri Pinhollow Jun 08 '17 at 10:25
  • @selbie that totally was my clue until I say multiple windows of print dialog opened while the procedure call which does it is blocking until the dialog is closed. Try running the code above. – Euri Pinhollow Jun 08 '17 at 10:26
  • 1
    No, there isn't more than one thread involved. What you're overlooking is that window procedures are re-entrant: the call to PrintDlgEx is calling your WndProc, which, unfortunately, is calling PrintDlgEx again, and so on. – Harry Johnston Jun 08 '17 at 10:55
  • @HarryJohnston that kinda explains it for me, thanks for the tip. What should I do with the question? Edit it and answer it? – Euri Pinhollow Jun 08 '17 at 11:09
  • @EuriPinhollow - the print dialog that you think is blocking is actually pumping windows messages. That why you are getting re-entrant calls on the same thread. – selbie Jun 08 '17 at 19:29
  • regarding: `(result=GetMessage(&recent,hwnd,0,0))` an assignment always results in a true condition. Suggest something similar to: `(result=GetMessage(&recent,hwnd,0,0)) != -1` – user3629249 Jun 08 '17 at 21:35
  • @user3629249 assignment does not return true, assignment returns the value of assignment. `GetMessage` is not inherently `true`. – Euri Pinhollow Jun 09 '17 at 11:58
  • @EuriPinhollow, the complier will output a warning if the assignment in the `if` statement does not have the parens. And the OP wanted to make the comparison to `-1`. I may have misspoken about an assignment always having a condition of `true` – user3629249 Jun 10 '17 at 03:11
  • @user3629249 aaaaaah I see. Check out the comment on that line: `BOOL` from MS is retarded and probably is just `char`. No precision/range loss in result of assignment, return type is `BOOL` too. – Euri Pinhollow Jun 10 '17 at 04:25

0 Answers0