diff --git a/addons/native_dialog/win_dialog.c b/addons/native_dialog/win_dialog.c
index 47410edfc..a391a7a11 100644
--- a/addons/native_dialog/win_dialog.c
+++ b/addons/native_dialog/win_dialog.c
@@ -280,31 +280,28 @@ bool _al_show_native_file_dialog(ALLEGRO_DISPLAY *display,
    return true;
 }
 
+// DialogProc callback for customized windows dialog boxes
+INT_PTR CALLBACK _al_winAPI_DlgProc(HWND hwnd, UINT wm, WPARAM wParam, LPARAM lParam)
+{
+   (void)lParam; // unused
+   if (wm == WM_INITDIALOG)
+      return TRUE;
+   if (wm == WM_COMMAND)
+   {
+      int control_id = LOWORD(wParam);
+
+      if (control_id > 1000)
+      {
+         EndDialog(hwnd, control_id-1000);
+         return TRUE;
+      }
+   }
+   return FALSE;
+}
+
 int _al_show_native_message_box(ALLEGRO_DISPLAY *display,
    ALLEGRO_NATIVE_DIALOG *fd)
 {
-   UINT type = MB_SETFOREGROUND;
-   int result;
-
-   uint16_t *wide_text, *wide_title;
-   size_t text_len, title_len;
-
-   /* Note: the message box code cannot assume that Allegro is installed. */
-
-   if (fd->flags & ALLEGRO_MESSAGEBOX_QUESTION)
-      type |= MB_ICONQUESTION;
-   else if (fd->flags & ALLEGRO_MESSAGEBOX_WARN)
-      type |= MB_ICONWARNING;
-   else if (fd->flags & ALLEGRO_MESSAGEBOX_ERROR) 
-      type |= MB_ICONERROR;
-   else 
-      type |= MB_ICONINFORMATION;
-
-   if (fd->flags & ALLEGRO_MESSAGEBOX_YES_NO)
-      type |= MB_YESNO;
-   else if (fd->flags & ALLEGRO_MESSAGEBOX_OK_CANCEL)
-      type |= MB_OKCANCEL;
-
    /* heading + text are combined together */
 
    if (al_ustr_size(fd->mb_heading)) 
@@ -312,32 +309,251 @@ int _al_show_native_message_box(ALLEGRO_DISPLAY *display,
 
    al_ustr_append(fd->mb_heading, fd->mb_text);
 
-   text_len = al_ustr_size_utf16(fd->mb_heading);
-   title_len = al_ustr_size_utf16(fd->title);
-
-   wide_text = al_malloc(text_len + 1);
-   if (!wide_text)
-      return 0;
-
-   wide_title = al_malloc(title_len + 1);
-   if (!wide_title) {
-      al_free(wide_text);
-      return 0;
+   // If there are no custom button names, then we can use a standard Windows message box.
+   if (!fd->mb_buttons || (fd->flags & (ALLEGRO_MESSAGEBOX_OK_CANCEL | ALLEGRO_MESSAGEBOX_YES_NO)))
+   {
+       UINT type = MB_SETFOREGROUND;
+       int result;
+
+       uint16_t *wide_text, *wide_title;
+       size_t text_len, title_len;
+
+       /* Note: the message box code cannot assume that Allegro is installed. */
+
+       if (fd->flags & ALLEGRO_MESSAGEBOX_QUESTION)
+          type |= MB_ICONQUESTION;
+       else if (fd->flags & ALLEGRO_MESSAGEBOX_WARN)
+          type |= MB_ICONWARNING;
+       else if (fd->flags & ALLEGRO_MESSAGEBOX_ERROR)
+          type |= MB_ICONERROR;
+       else
+          type |= MB_ICONINFORMATION;
+
+       if (fd->flags & ALLEGRO_MESSAGEBOX_YES_NO)
+          type |= MB_YESNO;
+       else if (fd->flags & ALLEGRO_MESSAGEBOX_OK_CANCEL)
+          type |= MB_OKCANCEL;
+
+       text_len = al_ustr_size_utf16(fd->mb_heading);
+       title_len = al_ustr_size_utf16(fd->title);
+
+       wide_text = al_malloc(text_len + 1);
+       if (!wide_text)
+          return 0;
+
+       wide_title = al_malloc(title_len + 1);
+       if (!wide_title) {
+          al_free(wide_text);
+          return 0;
+       }
+
+       al_ustr_encode_utf16(fd->mb_heading, wide_text, text_len);
+       al_ustr_encode_utf16(fd->title, wide_title, title_len);
+
+       result = MessageBoxW(al_get_win_window_handle(display),
+          (LPCWSTR) wide_text, (LPCWSTR) wide_title, type);
+
+       al_free(wide_text);
+       al_free(wide_title);
+
+       if (result == IDYES || result == IDOK)
+          return 1;
+       else
+          return 0;
    }
+   // else there are custom buttons - so things are more complex
+   else
+   {
+      size_t text_size = al_ustr_size_utf16(fd->mb_heading);
+      size_t title_size = al_ustr_size_utf16(fd->title);
+
+      size_t num_buttons;
+      int* button_caption_start;
+      int* button_caption_end;
+
+      // count the number of custom buttons
+      {
+         int offset = 0;
+         num_buttons = 0;
+         do
+         {
+            offset = al_ustr_find_chr(fd->mb_buttons, offset, '|');
+            al_ustr_next(fd->mb_buttons, &offset);
+            num_buttons++;
+         } while (offset > 0);
+      }
+      ASSERT(num_buttons > 0);
+
+      // record the start and end of each button name
+      button_caption_start = al_malloc(sizeof(*button_caption_start)*num_buttons);
+      button_caption_end = al_malloc(sizeof(*button_caption_end)*num_buttons);
+      if (!button_caption_start || !button_caption_end)
+      {
+          // failed to allocate memory for at least one of the arrays
+          al_free(button_caption_start);
+          al_free(button_caption_end);
+          return 0;
+      }
 
-   al_ustr_encode_utf16(fd->mb_heading, wide_text, text_len);
-   al_ustr_encode_utf16(fd->title, wide_title, title_len);
-
-   result = MessageBoxW(al_get_win_window_handle(display),
-      (LPCWSTR) wide_text, (LPCWSTR) wide_title, type);
+      // record the start and end of each button name
+      {
+         int offset = 0;
+         num_buttons = 0;
+         do
+         {
+            button_caption_start[num_buttons] = offset;
+            offset = al_ustr_find_chr(fd->mb_buttons, offset, '|');
+            button_caption_end[num_buttons] = offset;
+            al_ustr_next(fd->mb_buttons, &offset);
+            num_buttons++;
+         } while (offset > 0);
+         button_caption_end[num_buttons-1] = al_ustr_size(fd->mb_buttons);
+      }
 
-   al_free(wide_text);
-   al_free(wide_title);
+      {
+         // Note: the sizes of the buttons, and text box, and the dialog itself are currently mostly just arbitrary numbers.
+         //       Ideally these sizes should be calculated to best suit the content of the dialog.
+         const short button_width = 50;
+         const short button_height = 14;
+         short dlg_width = 200 > 20+num_buttons*(button_width+20) ? 200 : 20+num_buttons*(button_width+20);
+         short dlg_height = 80;
+
+		 // Get windows settings for native fonts, sizes, etc.
+		 NONCLIENTMETRICS windows_metrics;
+		 windows_metrics.cbSize = sizeof(NONCLIENTMETRICS);
+		 SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &windows_metrics, 0);
+		 LOGFONT lf = windows_metrics.lfMessageFont;
+		 HFONT hFont = CreateFont(lf.lfHeight, lf.lfWidth,
+								  lf.lfEscapement, lf.lfOrientation, lf.lfWeight,
+								  lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet,
+								  lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,
+								  lf.lfPitchAndFamily, lf.lfFaceName);
+		 DeleteObject(hFont);
+         //
+         int result;
+         size_t i; // generic iterator
+         DLGTEMPLATE* dlg_template;
+         BYTE* buffer;
+         size_t dlg_buffer_size = sizeof(DLGTEMPLATE);
+         dlg_buffer_size += 2*sizeof(WORD); // menu and class (unused)
+         dlg_buffer_size += title_size; // dialog title
+
+         // text (heading and text combined)
+         dlg_buffer_size += (sizeof(DWORD) - dlg_buffer_size % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD
+
+         // item data
+         dlg_buffer_size += sizeof(DLGITEMTEMPLATE);
+         dlg_buffer_size += sizeof(DWORD); // class
+         dlg_buffer_size += text_size; // the text itself
+         dlg_buffer_size += sizeof(WORD); // creation data
+
+         // similarly for each of the buttons.
+         for (i = 0; i < num_buttons; i++)
+         {
+            dlg_buffer_size += (sizeof(DWORD) - dlg_buffer_size % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD
+
+            // add the data size of this button
+            dlg_buffer_size += sizeof(DLGITEMTEMPLATE);
+            dlg_buffer_size += sizeof(DWORD); // class
+            // caption
+            {
+                ALLEGRO_USTR_INFO str_info;
+                const ALLEGRO_USTR* sub_string = al_ref_ustr(&str_info, fd->mb_buttons, button_caption_start[i], button_caption_end[i]);
+                size_t caption_size = al_ustr_size_utf16(sub_string);
+                dlg_buffer_size += caption_size;
+            }
+            dlg_buffer_size += sizeof(WORD); // creation data
+         }
 
-   if (result == IDYES || result == IDOK)
-      return 1;
-   else
-      return 2;
+         dlg_template = al_malloc(dlg_buffer_size);
+         if (!dlg_template)
+         {
+            // Not enough memory. :(
+            al_free(button_caption_start);
+            al_free(button_caption_end);
+            return 0;
+         }
+         dlg_template->x     = 0;
+         dlg_template->y     = 0;
+         dlg_template->cx    = dlg_width;
+         dlg_template->cy    = dlg_height;
+         dlg_template->cdit  = 1 + num_buttons; // components = text + number of buttons (todo + icons for warning / error etc)
+         dlg_template->style = DS_CENTER | DS_MODALFRAME | WS_CAPTION | WS_POPUP | WS_BORDER;
+         dlg_template->dwExtendedStyle = 0;
+
+         buffer = (BYTE*)dlg_template;
+         buffer += sizeof(DLGTEMPLATE);
+         *(WORD*)buffer = 0; // menu (none)
+         buffer += sizeof(WORD);
+         *(WORD*)buffer = 0; // class (none)
+         buffer += sizeof(WORD);
+         al_ustr_encode_utf16(fd->title, (uint16_t*)buffer, title_size); // title
+         buffer += title_size;
+
+         // dialog header complete.  Now for the dialog items.
+         // First, the text.
+         buffer += (sizeof(DWORD) - (buffer - (BYTE*)dlg_template) % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD
+         {
+            DLGITEMTEMPLATE* item = (DLGITEMTEMPLATE*)(buffer);
+            item->dwExtendedStyle = 0;
+            item->style = WS_CHILD | WS_VISIBLE;
+            item->x     = 7;
+            item->y     = 7;
+            item->cx    = dlg_width - 14;
+            item->cy    = dlg_height-7-button_height-7;
+            item->id    = -1;
+         }
+         buffer += sizeof(DLGITEMTEMPLATE);
+
+         *(WORD*)buffer = 0xFFFF; // pre-defined type to follow
+         *(WORD*)(buffer+sizeof(WORD)) = 0x0082; // 'static'
+         buffer += 2*sizeof(WORD);
+
+         al_ustr_encode_utf16(fd->mb_heading, (uint16_t*)buffer, text_size); // text body
+         buffer += text_size;
+         *(WORD*)buffer = 0; // creation data (whatever that means - we don't need it)
+         buffer += sizeof(WORD);
+
+         // Next, each of the buttons.
+         for (i = 0; i < num_buttons; i++)
+         {
+            buffer += (sizeof(DWORD) - (buffer - (BYTE*)dlg_template) % sizeof(DWORD)) % sizeof(DWORD); // align to DWORD
+            {
+                DLGITEMTEMPLATE* item = (DLGITEMTEMPLATE*)buffer;
+                item->dwExtendedStyle = 0;
+                item->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON | (i == 0 ? WS_GROUP : 0);
+                item->x     = dlg_width - (num_buttons - i) * (button_width+20);
+                item->y     = dlg_height - button_height - 7;
+                item->cx    = button_width;
+                item->cy    = button_height;
+                item->id    = 1000 + i + 1;
+                // Note: item id will determine dialog return value. See _al_winAPI_DlgProc.
+            }
+            buffer += sizeof(DLGITEMTEMPLATE);
+
+            *(WORD*)buffer = 0xFFFF; // pre-defined type to follow
+            *(WORD*)(buffer+sizeof(WORD)) = 0x0080; // 'button'
+            buffer += 2*sizeof(WORD);
+
+            // caption
+            {
+                ALLEGRO_USTR_INFO str_info;
+                const ALLEGRO_USTR* sub_string = al_ref_ustr(&str_info, fd->mb_buttons, button_caption_start[i], button_caption_end[i]);
+                size_t caption_size = al_ustr_size_utf16(sub_string);
+                al_ustr_encode_utf16(sub_string, (uint16_t*)buffer, caption_size);
+                buffer += caption_size;
+            }
+            *(WORD*)buffer = 0;
+            buffer += sizeof(WORD);
+         }
+         ASSERT(buffer - (BYTE*)dlg_template == (int)dlg_buffer_size);
+         result = DialogBoxIndirect(0, dlg_template, al_get_win_window_handle(display), _al_winAPI_DlgProc);
+         al_free(dlg_template);
+         al_free(button_caption_start);
+         al_free(button_caption_end);
+         return result == -1 ? 0 : result;
+      }
+   }
 }
 
 
