Search code examples
winapibuttonpropertysheet

How to respond to a PropertySheet's OK or Apply button after the pages have processed it?


The PropertySheet API lets you define a PropSheetProc that can (on Windows XP and above) receive messages when the OK or Apply button is pressed. This lets you do processing when one of these buttons is clicked.

However, this handler is called before the individual property pages receive the PSN_APPLY notification through their respective dialog procedures. I want to do my processing after these notifications have been processed, preferably without ugly hacks. How do I do this?

Background: I'm storing my configuration in a single struct, and the individual pages each modify parts of this struct when they are applied. Then, after these values have been written, I want to apply the settings from the struct all at once, instead of re-applying all of them from each property page.


Solution

  • I ended up using the PropSheetProc callback to capture the property sheet's window handle, then hook up a subclass window procedure, like this:

    // This is not officially defined, but the whole world uses it.
    #define ID_APPLY_NOW 0x3021
    
    WNDPROC origWinProc;
    
    LRESULT CALLBACK MyWinProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
      // Call the original window procedure.
      LRESULT retVal = CallWindowProc(origWinProc, hwndDlg, msg, wParam, lParam);
      // Now, and only now, do our own stuff.
      switch (msg) {
        case WM_COMMAND:
          switch (LOWORD(wParam)) {
            case IDOK:
            case ID_APPLY_NOW:
              applyConfig();
              saveConfig();
              break;
          }
          break;
      }
      // Return the original winproc's result.
      return retVal;
    }
    
    int CALLBACK myPropSheetProc(HWND hwndDlg, UINT msg, LPARAM lParam) {
      switch (msg) {
        case PSCB_INITIALIZED:
          // Override the property sheet's window procedure with our own.
          origWinProc = (WNDPROC)SetWindowLongPtr(hwndDlg, GWLP_WNDPROC, (LONG_PTR)&MyWinProc);
          break;
      }
      return 0;
    }
    

    (Yes, I use switch statements even if there is only one case to consider. I'm strange like that.)