Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / video / windows / SDL_windowsmessagebox.c
old mode 100755 (executable)
new mode 100644 (file)
index 924b412..3382a2d
@@ -1,6 +1,6 @@
 /*
   Simple DirectMedia Layer
-  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
+  Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
 
 #if SDL_VIDEO_DRIVER_WINDOWS
 
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
+#endif
+
 #include "../../core/windows/SDL_windows.h"
 
-#include "SDL_assert.h"
 #include "SDL_windowsvideo.h"
-
+#include "SDL_windowstaskdialog.h"
 
 #ifndef SS_EDITCONTROL
 #define SS_EDITCONTROL  0x2000
 #endif
 
+#ifndef IDOK
+#define IDOK 1
+#endif
+
+#ifndef IDCANCEL
+#define IDCANCEL 2
+#endif
+
+/* Custom dialog return codes */
+#define IDCLOSED 20
+#define IDINVALPTRINIT 50
+#define IDINVALPTRCOMMAND 51
+#define IDINVALPTRSETFOCUS 52
+#define IDINVALPTRDLGITEM 53
+/* First button ID */
+#define IDBUTTONINDEX0 100
+
+#define DLGITEMTYPEBUTTON 0x0080
+#define DLGITEMTYPESTATIC 0x0082
+
+/* Windows only sends the lower 16 bits of the control ID when a button
+ * gets clicked. There are also some predefined and custom IDs that lower
+ * the available number further. 2^16 - 101 buttons should be enough for
+ * everyone, no need to make the code more complex.
+ */
+#define MAX_BUTTONS (0xffff - 100)
+
+
 /* Display a Windows message box */
 
 #pragma pack(push, 1)
@@ -70,15 +104,79 @@ typedef struct
     Uint8 *data;
     size_t size;
     size_t used;
+    WORD numbuttons;
 } WIN_DialogData;
 
+static SDL_bool GetButtonIndex(const SDL_MessageBoxData *messageboxdata, Uint32 flags, size_t *i)
+{
+    for (*i = 0; *i < (size_t)messageboxdata->numbuttons; ++*i) {
+        if (messageboxdata->buttons[*i].flags & flags) {
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
 
 static INT_PTR MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPARAM lParam)
 {
+    const SDL_MessageBoxData *messageboxdata;
+    size_t buttonindex;
+
     switch ( iMessage ) {
+    case WM_INITDIALOG:
+        if (lParam == 0) {
+            EndDialog(hDlg, IDINVALPTRINIT);
+            return TRUE;
+        }
+        messageboxdata = (const SDL_MessageBoxData *)lParam;
+        SetWindowLongPtr(hDlg, GWLP_USERDATA, lParam);
+
+        if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
+            /* Focus on the first default return-key button */
+            HWND buttonctl = GetDlgItem(hDlg, (int)(IDBUTTONINDEX0 + buttonindex));
+            if (buttonctl == NULL) {
+                EndDialog(hDlg, IDINVALPTRDLGITEM);
+            }
+            PostMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)buttonctl, TRUE);
+        } else {
+            /* Give the focus to the dialog window instead */
+            SetFocus(hDlg);
+        }
+        return FALSE;
+    case WM_SETFOCUS:
+        messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA);
+        if (messageboxdata == NULL) {
+            EndDialog(hDlg, IDINVALPTRSETFOCUS);
+            return TRUE;
+        }
+
+        /* Let the default button be focused if there is one. Otherwise, prevent any initial focus. */
+        if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
+            return FALSE;
+        }
+        return TRUE;
     case WM_COMMAND:
+        messageboxdata = (const SDL_MessageBoxData *)GetWindowLongPtr(hDlg, GWLP_USERDATA);
+        if (messageboxdata == NULL) {
+            EndDialog(hDlg, IDINVALPTRCOMMAND);
+            return TRUE;
+        }
+
         /* Return the ID of the button that was pushed */
-        EndDialog(hDlg, LOWORD(wParam));
+        if (wParam == IDOK) {
+            if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, &buttonindex)) {
+                EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex);
+            }
+        } else if (wParam == IDCANCEL) {
+            if (GetButtonIndex(messageboxdata, SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, &buttonindex)) {
+                EndDialog(hDlg, IDBUTTONINDEX0 + buttonindex);
+            } else {
+                /* Closing of window was requested by user or system. It would be rude not to comply. */
+                EndDialog(hDlg, IDCLOSED);
+            }
+        } else if (wParam >= IDBUTTONINDEX0 && (int)wParam - IDBUTTONINDEX0 < messageboxdata->numbuttons) {
+            EndDialog(hDlg, wParam);
+        }
         return TRUE;
 
     default:
@@ -89,15 +187,30 @@ static INT_PTR MessageBoxDialogProc(HWND hDlg, UINT iMessage, WPARAM wParam, LPA
 
 static SDL_bool ExpandDialogSpace(WIN_DialogData *dialog, size_t space)
 {
+    /* Growing memory in 64 KiB steps. */
+    const size_t sizestep = 0x10000;
     size_t size = dialog->size;
 
     if (size == 0) {
-        size = space;
-    } else {
-        while ((dialog->used + space) > size) {
-            size *= 2;
+        /* Start with 4 KiB or a multiple of 64 KiB to fit the data. */
+        size = 0x1000;
+        if (SIZE_MAX - sizestep < space) {
+            size = space;
+        } else if (space > size) {
+            size = (space + sizestep) & ~(sizestep - 1);
         }
+    } else if (SIZE_MAX - dialog->used < space) {
+        SDL_OutOfMemory();
+        return SDL_FALSE;
+    } else if (SIZE_MAX - (dialog->used + space) < sizestep) {
+        /* Close to the maximum. */
+        size = dialog->used + space;
+    } else if (size < dialog->used + space) {
+        /* Round up to the next 64 KiB block. */
+        size = dialog->used + space;
+        size += sizestep - size % sizestep;
     }
+
     if (size > dialog->size) {
         void *data = SDL_realloc(dialog->data, size);
         if (!data) {
@@ -175,7 +288,7 @@ static void Vec2ToDLU(short *x, short *y)
 }
 
 
-static SDL_bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style, DWORD exStyle, int x, int y, int w, int h, int id, const char *caption)
+static SDL_bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style, DWORD exStyle, int x, int y, int w, int h, int id, const char *caption, WORD ordinal)
 {
     DLGITEMTEMPLATEEX item;
     WORD marker = 0xFFFF;
@@ -205,32 +318,54 @@ static SDL_bool AddDialogControl(WIN_DialogData *dialog, WORD type, DWORD style,
     if (!AddDialogData(dialog, &type, sizeof(type))) {
         return SDL_FALSE;
     }
-    if (!AddDialogString(dialog, caption)) {
-        return SDL_FALSE;
+    if (type == DLGITEMTYPEBUTTON || (type == DLGITEMTYPESTATIC && caption != NULL)) {
+        if (!AddDialogString(dialog, caption)) {
+            return SDL_FALSE;
+        }
+    } else {
+        if (!AddDialogData(dialog, &marker, sizeof(marker))) {
+            return SDL_FALSE;
+        }
+        if (!AddDialogData(dialog, &ordinal, sizeof(ordinal))) {
+            return SDL_FALSE;
+        }
     }
     if (!AddDialogData(dialog, &extraData, sizeof(extraData))) {
         return SDL_FALSE;
     }
+    if (type == DLGITEMTYPEBUTTON) {
+        dialog->numbuttons++;
+    }
     ++dialog->lpDialog->cDlgItems;
 
     return SDL_TRUE;
 }
 
-static SDL_bool AddDialogStatic(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
+static SDL_bool AddDialogStaticText(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text)
+{
+    DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL | WS_GROUP;
+    return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -1, text, 0);
+}
+
+static SDL_bool AddDialogStaticIcon(WIN_DialogData *dialog, int x, int y, int w, int h, Uint16 ordinal)
 {
-    DWORD style = WS_VISIBLE | WS_CHILD | SS_LEFT | SS_NOPREFIX | SS_EDITCONTROL;
-    return AddDialogControl(dialog, 0x0082, style, 0, x, y, w, h, -1, text);
+    DWORD style = WS_VISIBLE | WS_CHILD | SS_ICON | WS_GROUP;
+    return AddDialogControl(dialog, DLGITEMTYPESTATIC, style, 0, x, y, w, h, -2, NULL, ordinal);
 }
 
 static SDL_bool AddDialogButton(WIN_DialogData *dialog, int x, int y, int w, int h, const char *text, int id, SDL_bool isDefault)
 {
-    DWORD style = WS_VISIBLE | WS_CHILD;
+    DWORD style = WS_VISIBLE | WS_CHILD | WS_TABSTOP;
     if (isDefault) {
         style |= BS_DEFPUSHBUTTON;
     } else {
         style |= BS_PUSHBUTTON;
     }
-    return AddDialogControl(dialog, 0x0080, style, 0, x, y, w, h, id, text);
+    /* The first button marks the start of the group. */
+    if (dialog->numbuttons == 0) {
+        style |= WS_GROUP;
+    }
+    return AddDialogControl(dialog, DLGITEMTYPEBUTTON, style, 0, x, y, w, h, id, text, 0);
 }
 
 static void FreeDialogData(WIN_DialogData *dialog)
@@ -341,17 +476,86 @@ static WIN_DialogData *CreateDialogData(int w, int h, const char *caption)
     return dialog;
 }
 
-int
-WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
+/* Escaping ampersands is necessary to disable mnemonics in dialog controls.
+ * The caller provides a char** for dst and a size_t* for dstlen where the
+ * address of the work buffer and its size will be stored. Their values must be
+ * NULL and 0 on the first call. src is the string to be escaped. On error, the
+ * function returns NULL and, on success, returns a pointer to the escaped
+ * sequence as a read-only string that is valid until the next call or until the
+ * work buffer is freed. Once all strings have been processed, it's the caller's
+ * responsibilty to free the work buffer with SDL_free, even on errors.
+ */
+static const char *EscapeAmpersands(char **dst, size_t *dstlen, const char *src)
+{
+    char *newdst;
+    size_t ampcount = 0;
+    size_t srclen = 0;
+
+    if (src == NULL) {
+        return NULL;
+    }
+
+    while (src[srclen]) {
+        if (src[srclen] == '&') {
+            ampcount++;
+        }
+        srclen++;
+    }
+    srclen++;
+
+    if (ampcount == 0) {
+        /* Nothing to do. */
+        return src;
+    }
+    if (SIZE_MAX - srclen < ampcount) {
+        return NULL;
+    }
+    if (*dst == NULL || *dstlen < srclen + ampcount) {
+        /* Allocating extra space in case the next strings are a bit longer. */
+        size_t extraspace = SIZE_MAX - (srclen + ampcount);
+        if (extraspace > 512) {
+            extraspace = 512;
+        }
+        *dstlen = srclen + ampcount + extraspace;
+        SDL_free(*dst);
+        *dst = NULL;
+        newdst = SDL_malloc(*dstlen);
+        if (newdst == NULL) {
+            return NULL;
+        }
+        *dst = newdst;
+    } else {
+        newdst = *dst;
+    }
+
+    /* The escape character is the ampersand itself. */
+    while (srclen--) {
+        if (*src == '&') {
+            *newdst++ = '&';
+        }
+        *newdst++ = *src++;
+    }
+
+    return *dst;
+}
+
+/* This function is called if a Task Dialog is unsupported. */
+static int
+WIN_ShowOldMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
 {
     WIN_DialogData *dialog;
-    int i, x, y;
-    const SDL_MessageBoxButtonData *buttons = messageboxdata->buttons;
+    int i, x, y, retval;
     HFONT DialogFont;
     SIZE Size;
     RECT TextSize;
     wchar_t* wmessage;
     TEXTMETRIC TM;
+    HDC FontDC;
+    INT_PTR result;
+    char *ampescape = NULL;
+    size_t ampescapesize = 0;
+    Uint16 defbuttoncount = 0;
+    Uint16 icon = 0;
 
     HWND ParentWindow = NULL;
 
@@ -359,11 +563,28 @@ WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
     const int ButtonHeight = 26;
     const int TextMargin = 16;
     const int ButtonMargin = 12;
+    const int IconWidth = GetSystemMetrics(SM_CXICON);
+    const int IconHeight = GetSystemMetrics(SM_CYICON);
+    const int IconMargin = 20;
 
+    if (messageboxdata->numbuttons > MAX_BUTTONS) {
+        return SDL_SetError("Number of butons exceeds limit of %d", MAX_BUTTONS);
+    }
+
+    switch (messageboxdata->flags) {
+    case SDL_MESSAGEBOX_ERROR:
+        icon = (Uint16)(size_t)IDI_ERROR;
+        break;
+    case SDL_MESSAGEBOX_WARNING:
+        icon = (Uint16)(size_t)IDI_WARNING;
+        break;
+    case SDL_MESSAGEBOX_INFORMATION:
+        icon = (Uint16)(size_t)IDI_INFORMATION;
+        break;
+    }
 
     /* Jan 25th, 2013 - dant@fleetsa.com
      *
-     *
      * I've tried to make this more reasonable, but I've run in to a lot
      * of nonsense.
      *
@@ -388,12 +609,10 @@ WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
      *
      * Honestly, a long term solution is to use CreateWindow, not CreateDialog.
      *
-
-     *
      * In order to get text dimensions we need to have a DC with the desired font.
      * I'm assuming a dialog box in SDL is rare enough we can to the create.
      */
-    HDC FontDC = CreateCompatibleDC(0);
+    FontDC = CreateCompatibleDC(0);
 
     {
         /* Create a duplicate of the font used in system message boxes. */
@@ -428,11 +647,13 @@ WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
     /* Measure the *pixel* size of the string. */
     wmessage = WIN_UTF8ToString(messageboxdata->message);
     SDL_zero(TextSize);
-    DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT);
+    DrawText(FontDC, wmessage, -1, &TextSize, DT_CALCRECT | DT_LEFT | DT_NOPREFIX | DT_EDITCONTROL);
 
-    /* Add some padding for hangs, etc. */
-    TextSize.right += 2;
-    TextSize.bottom += 2;
+    /* Add margins and some padding for hangs, etc. */
+    TextSize.left += TextMargin;
+    TextSize.right += TextMargin + 2;
+    TextSize.top += TextMargin;
+    TextSize.bottom += TextMargin + 2;
 
     /* Done with the DC, and the string */
     DeleteDC(FontDC);
@@ -444,10 +665,22 @@ WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
     Size.cx += TextMargin * 2;
     Size.cy += TextMargin * 2;
 
+    /* Make dialog wider and shift text over for the icon. */
+    if (icon) {
+        Size.cx += IconMargin + IconWidth;
+        TextSize.left += IconMargin + IconWidth;
+        TextSize.right += IconMargin + IconWidth;
+    }
+
     /* Ensure the size is wide enough for all of the buttons. */
     if (Size.cx < messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin)
         Size.cx = messageboxdata->numbuttons * (ButtonWidth + ButtonMargin) + ButtonMargin;
 
+    /* Reset the height to the icon size if it is actually bigger than the text. */
+    if (icon && Size.cy < IconMargin * 2 + IconHeight) {
+        Size.cy = IconMargin * 2 + IconHeight;
+    }
+
     /* Add vertical space for the buttons and border. */
     Size.cy += ButtonHeight + TextMargin;
 
@@ -456,7 +689,12 @@ WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
         return -1;
     }
 
-    if (!AddDialogStatic(dialog, TextMargin, TextMargin, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
+    if (icon && ! AddDialogStaticIcon(dialog, IconMargin, IconMargin, IconWidth, IconHeight, icon)) {
+        FreeDialogData(dialog);
+        return -1;
+    }
+
+    if (!AddDialogStaticText(dialog, TextSize.left, TextSize.top, TextSize.right - TextSize.left, TextSize.bottom - TextSize.top, messageboxdata->message)) {
         FreeDialogData(dialog);
         return -1;
     }
@@ -464,20 +702,39 @@ WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
     /* Align the buttons to the right/bottom. */
     x = Size.cx - (ButtonWidth + ButtonMargin) * messageboxdata->numbuttons;
     y = Size.cy - ButtonHeight - ButtonMargin;
-    for (i = messageboxdata->numbuttons - 1; i >= 0; --i) {
-        SDL_bool isDefault;
-
-        if (buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
-            isDefault = SDL_TRUE;
+    for (i = 0; i < messageboxdata->numbuttons; i++) {
+        SDL_bool isdefault = SDL_FALSE;
+        const char *buttontext;
+        const SDL_MessageBoxButtonData *sdlButton;
+
+        /* We always have to create the dialog buttons from left to right
+         * so that the tab order is correct.  Select the info to use
+         * depending on which order was requested. */
+        if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT) {
+            sdlButton = &messageboxdata->buttons[i];
         } else {
-            isDefault = SDL_FALSE;
+            sdlButton = &messageboxdata->buttons[messageboxdata->numbuttons - 1 - i];
+        }
+
+        if (sdlButton->flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
+            defbuttoncount++;
+            if (defbuttoncount == 1) {
+                isdefault = SDL_TRUE;
+            }
         }
-        if (!AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttons[i].text, buttons[i].buttonid, isDefault)) {
+
+        buttontext = EscapeAmpersands(&ampescape, &ampescapesize, sdlButton->text);
+        /* Make sure to provide the correct ID to keep buttons indexed in the
+         * same order as how they are in messageboxdata. */
+        if (buttontext == NULL || !AddDialogButton(dialog, x, y, ButtonWidth, ButtonHeight, buttontext, IDBUTTONINDEX0 + (int)(sdlButton - messageboxdata->buttons), isdefault)) {
             FreeDialogData(dialog);
+            SDL_free(ampescape);
             return -1;
         }
+
         x += ButtonWidth + ButtonMargin;
     }
+    SDL_free(ampescape);
 
     /* If we have a parent window, get the Instance and HWND for them
      * so that our little dialog gets exclusive focus at all times. */
@@ -485,10 +742,170 @@ WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
         ParentWindow = ((SDL_WindowData*)messageboxdata->window->driverdata)->hwnd;
     }
 
-    *buttonid = (int)DialogBoxIndirect(NULL, (DLGTEMPLATE*)dialog->lpDialog, ParentWindow, (DLGPROC)MessageBoxDialogProc);
+    result = DialogBoxIndirectParam(NULL, (DLGTEMPLATE*)dialog->lpDialog, ParentWindow, (DLGPROC)MessageBoxDialogProc, (LPARAM)messageboxdata);
+    if (result >= IDBUTTONINDEX0 && result - IDBUTTONINDEX0 < messageboxdata->numbuttons) {
+        *buttonid = messageboxdata->buttons[result - IDBUTTONINDEX0].buttonid;
+        retval = 0;
+    } else if (result == IDCLOSED) {
+        /* Dialog window closed by user or system. */
+        /* This could use a special return code. */
+        retval = 0;
+        *buttonid = -1;
+    } else {
+        if (result == 0) {
+            SDL_SetError("Invalid parent window handle");
+        } else if (result == -1) {
+            SDL_SetError("The message box encountered an error.");
+        } else if (result == IDINVALPTRINIT || result == IDINVALPTRSETFOCUS || result == IDINVALPTRCOMMAND) {
+            SDL_SetError("Invalid message box pointer in dialog procedure");
+        } else if (result == IDINVALPTRDLGITEM) {
+            SDL_SetError("Couldn't find dialog control of the default enter-key button");
+        } else {
+            SDL_SetError("An unknown error occured");
+        }
+        retval = -1;
+    }
 
     FreeDialogData(dialog);
-    return 0;
+    return retval;
+}
+
+/* TaskDialogIndirect procedure
+ * This is because SDL targets Windows XP (0x501), so this is not defined in the platform SDK.
+ */
+typedef HRESULT(FAR WINAPI *TASKDIALOGINDIRECTPROC)(const TASKDIALOGCONFIG *pTaskConfig, int *pnButton, int *pnRadioButton, BOOL *pfVerificationFlagChecked);
+
+int
+WIN_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid)
+{
+    HWND ParentWindow = NULL;
+    wchar_t *wmessage;
+    wchar_t *wtitle;
+    TASKDIALOGCONFIG TaskConfig;
+    TASKDIALOG_BUTTON *pButtons;
+    TASKDIALOG_BUTTON *pButton;
+    HMODULE hComctl32;
+    TASKDIALOGINDIRECTPROC pfnTaskDialogIndirect;
+    HRESULT hr;
+    char *ampescape = NULL;
+    size_t ampescapesize = 0;
+    int nButton;
+    int nCancelButton;
+    int i;
+
+    if (SIZE_MAX / sizeof(TASKDIALOG_BUTTON) < messageboxdata->numbuttons) {
+        return SDL_OutOfMemory();
+    }
+
+    /* If we cannot load comctl32.dll use the old messagebox! */
+    hComctl32 = LoadLibrary(TEXT("Comctl32.dll"));
+    if (hComctl32 == NULL) {
+        return WIN_ShowOldMessageBox(messageboxdata, buttonid);
+    }
+
+    /* If TaskDialogIndirect doesn't exist use the old messagebox!
+       This will fail prior to Windows Vista.
+       The manifest file in the application may require targeting version 6 of comctl32.dll, even
+       when we use LoadLibrary here!
+       If you don't want to bother with manifests, put this #pragma in your app's source code somewhere:
+       pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0'  processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+     */
+    pfnTaskDialogIndirect = (TASKDIALOGINDIRECTPROC) GetProcAddress(hComctl32, "TaskDialogIndirect");
+    if (pfnTaskDialogIndirect == NULL) {
+        FreeLibrary(hComctl32);
+        return WIN_ShowOldMessageBox(messageboxdata, buttonid);
+    }
+
+    /* If we have a parent window, get the Instance and HWND for them
+       so that our little dialog gets exclusive focus at all times. */
+    if (messageboxdata->window) {
+        ParentWindow = ((SDL_WindowData *) messageboxdata->window->driverdata)->hwnd;
+    }
+
+    wmessage = WIN_UTF8ToString(messageboxdata->message);
+    wtitle = WIN_UTF8ToString(messageboxdata->title);
+
+    SDL_zero(TaskConfig);
+    TaskConfig.cbSize = sizeof (TASKDIALOGCONFIG);
+    TaskConfig.hwndParent = ParentWindow;
+    TaskConfig.dwFlags = TDF_SIZE_TO_CONTENT;
+    TaskConfig.pszWindowTitle = wtitle;
+    if (messageboxdata->flags & SDL_MESSAGEBOX_ERROR) {
+        TaskConfig.pszMainIcon = TD_ERROR_ICON;
+    } else if (messageboxdata->flags & SDL_MESSAGEBOX_WARNING) {
+        TaskConfig.pszMainIcon = TD_WARNING_ICON;
+    } else if (messageboxdata->flags & SDL_MESSAGEBOX_INFORMATION) {
+        TaskConfig.pszMainIcon = TD_INFORMATION_ICON;
+    } else {
+        TaskConfig.pszMainIcon = NULL;
+    }
+
+    TaskConfig.pszContent = wmessage;
+    TaskConfig.cButtons = messageboxdata->numbuttons;
+    pButtons = SDL_malloc(sizeof (TASKDIALOG_BUTTON) * messageboxdata->numbuttons);
+    TaskConfig.nDefaultButton = 0;
+    nCancelButton = 0;
+    for (i = 0; i < messageboxdata->numbuttons; i++)
+    {
+        const char *buttontext;
+        if (messageboxdata->flags & SDL_MESSAGEBOX_BUTTONS_LEFT_TO_RIGHT) {
+            pButton = &pButtons[i];
+        } else {
+            pButton = &pButtons[messageboxdata->numbuttons - 1 - i];
+        }
+        if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT) {
+            nCancelButton = messageboxdata->buttons[i].buttonid;
+            pButton->nButtonID = IDCANCEL;
+        } else {
+            pButton->nButtonID = IDBUTTONINDEX0 + i;
+        }
+        buttontext = EscapeAmpersands(&ampescape, &ampescapesize, messageboxdata->buttons[i].text);
+        if (buttontext == NULL) {
+            int j;
+            FreeLibrary(hComctl32);
+            SDL_free(ampescape);
+            SDL_free(wmessage);
+            SDL_free(wtitle);
+            for (j = 0; j < i; j++) {
+                SDL_free((wchar_t *) pButtons[j].pszButtonText);
+            }
+            SDL_free(pButtons);
+            return -1;
+        }
+        pButton->pszButtonText = WIN_UTF8ToString(buttontext);
+        if (messageboxdata->buttons[i].flags & SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT) {
+            TaskConfig.nDefaultButton = pButton->nButtonID;
+        }
+    }
+    TaskConfig.pButtons = pButtons;
+
+    /* Show the Task Dialog */
+    hr = pfnTaskDialogIndirect(&TaskConfig, &nButton, NULL, NULL);
+
+    /* Free everything */
+    FreeLibrary(hComctl32);
+    SDL_free(ampescape);
+    SDL_free(wmessage);
+    SDL_free(wtitle);
+    for (i = 0; i < messageboxdata->numbuttons; i++) {
+        SDL_free((wchar_t *) pButtons[i].pszButtonText);
+    }
+    SDL_free(pButtons);
+
+    /* Check the Task Dialog was successful and give the result */
+    if (SUCCEEDED(hr)) {
+        if (nButton == IDCANCEL) {
+            *buttonid = nCancelButton;
+        } else if (nButton >= IDBUTTONINDEX0 && nButton < IDBUTTONINDEX0 + messageboxdata->numbuttons) {
+            *buttonid = messageboxdata->buttons[nButton - IDBUTTONINDEX0].buttonid;
+        } else {
+            *buttonid = -1;
+        }
+        return 0;
+    }
+
+    /* We failed showing the Task Dialog, use the old message box! */
+    return WIN_ShowOldMessageBox(messageboxdata, buttonid);
 }
 
 #endif /* SDL_VIDEO_DRIVER_WINDOWS */