diff --git a/Contrib/BgImage/BgImage.cpp b/Contrib/BgImage/BgImage.cpp index 03fc9b07..2ff1fd96 100644 --- a/Contrib/BgImage/BgImage.cpp +++ b/Contrib/BgImage/BgImage.cpp @@ -1,6 +1,6 @@ #include #include -#include "exdll.h" +#include "../exdll/exdll.h" int x, y; char temp[MAX_PATH]; diff --git a/Contrib/BgImage/BgImage.dsp b/Contrib/BgImage/BgImage.dsp index be7a129f..ee100eb7 100644 --- a/Contrib/BgImage/BgImage.dsp +++ b/Contrib/BgImage/BgImage.dsp @@ -67,7 +67,7 @@ SOURCE=.\BgImage.cpp # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File -SOURCE=.\exdll.h +SOURCE=..\ExDLL\exdll.h # End Source File # End Group # Begin Group "Resource Files" diff --git a/Contrib/BgImage/exdll.h b/Contrib/BgImage/exdll.h deleted file mode 100644 index 5ecbd013..00000000 --- a/Contrib/BgImage/exdll.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef _EXDLL_H_ -#define _EXDLL_H_ - -// only include this file from one place in your DLL. -// (it is all static, if you use it in two places it will fail) - -#define EXDLL_INIT() { \ - g_hwndParent=hwndParent; \ - g_stringsize=string_size; \ - g_stacktop=stacktop; \ - /*g_variables=variables;*/ } - - -typedef struct _stack_t { - struct _stack_t *next; - char text[1]; // this should be the length of string_size -} stack_t; - - -static int g_stringsize; -static stack_t **g_stacktop; -static HWND g_hwndParent; - -static int popstring(char *str); // 0 on success, 1 on empty stack -static void pushstring(char *str); - -// utility functions (not required but often useful) -static int popstring(char *str) -{ - stack_t *th; - if (!g_stacktop || !*g_stacktop) return 1; - th=(*g_stacktop); - lstrcpy(str,th->text); - *g_stacktop = th->next; - GlobalFree((HGLOBAL)th); - return 0; -} - -static void pushstring(char *str) -{ - stack_t *th; - if (!g_stacktop) return; - th=(stack_t*)GlobalAlloc(GPTR,sizeof(stack_t)+g_stringsize); - lstrcpyn(th->text,str,g_stringsize); - th->next=*g_stacktop; - *g_stacktop=th; -} - -#endif//_EXDLL_H_ \ No newline at end of file diff --git a/Contrib/ExDLL/exdll.h b/Contrib/ExDLL/exdll.h index a64a2ed3..301ec31f 100644 --- a/Contrib/ExDLL/exdll.h +++ b/Contrib/ExDLL/exdll.h @@ -9,6 +9,8 @@ g_stacktop=stacktop; \ g_variables=variables; } +// For page showing plug-ins +#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8) typedef struct _stack_t { struct _stack_t *next; diff --git a/Contrib/InstallOptions/Install Options.html b/Contrib/InstallOptions/Install Options.html index 29ab2282..50c16c54 100644 --- a/Contrib/InstallOptions/Install Options.html +++ b/Contrib/InstallOptions/Install Options.html @@ -282,10 +282,8 @@ Here is a little example: InstallOptions::show Pop $0 StrCmp $0 "success" done - StrCmp $0 "back" 0 +2 - Abort - StrCmp $0 "cancel" 0 error - Quit + StrCmp $0 "back" done + StrCmp $0 "cancel" done error: MessageBox MB_OK|MB_ICONSTOP "IO error: $0" Quit @@ -404,8 +402,8 @@ Here is a little example:
-
  Copyright © 2001 Michael Bishop
-  Portions Copyright © 2001 Nullsoft, Inc.
+
  Copyright © 2001 Michael Bishop
+  Portions Copyright © 2001 Nullsoft, Inc.
 
   This software is provided 'as-is', without any express or implied
   warranty.  In no event will the authors be held liable for any damages
diff --git a/Contrib/InstallOptions/InstallerOptions.cpp b/Contrib/InstallOptions/InstallerOptions.cpp
index 936ae9d2..3da15f66 100644
--- a/Contrib/InstallOptions/InstallerOptions.cpp
+++ b/Contrib/InstallOptions/InstallerOptions.cpp
@@ -165,12 +165,9 @@ HINSTANCE m_hInstance = NULL;
 
 char *pszFilename = NULL;
 char *pszTitle = NULL;
-char *pszCancelQuestion = NULL;
-char *pszCancelQuestionCaption = NULL;
 char *pszCancelButtonText = NULL;
 char *pszNextButtonText = NULL;
 char *pszBackButtonText = NULL;
-unsigned int nCancelConfirmFlags=0;
 BOOL bBackDisabled=FALSE;
 
 BOOL bCancelEnabled=TRUE;  // by ORTIM: 13-August-2002
@@ -455,36 +452,12 @@ char * WINAPI myGetProfileStringDup(LPCTSTR lpAppName, LPCTSTR lpKeyName)
 bool ReadSettings(void) {
   static char szField[25];
   int nIdx;
-  // Messagebox flags
-  static TableEntry MBFlagTable[] = {
-    { "MB_ICONEXCLAMATION", MB_ICONEXCLAMATION },
-//  { "MB_ICONWARNING",     MB_ICONWARNING     }, // same as above
-    { "MB_ICONINFORMATION", MB_ICONINFORMATION },
-//  { "MB_ICONASTERISK",    MB_ICONASTERISK    }, // same as above
-    { "MB_ICONQUESTION",    MB_ICONQUESTION    },
-    { "MB_ICONSTOP",        MB_ICONSTOP        },
-//  { "MB_ICONERROR",       MB_ICONERROR       }, // same as above
-//  { "MB_ICONHAND",        MB_ICONHAND        }, // same as above
-    { "MB_TOPMOST",         MB_TOPMOST         },
-    { "MB_SETFOREGROUND",   MB_SETFOREGROUND   },
-    { "MB_RIGHT",           MB_RIGHT           },
-    { "MB_DEFBUTTON1",      MB_DEFBUTTON1      },
-    { "MB_DEFBUTTON2",      MB_DEFBUTTON2      },
-//  { "MB_DEFBUTTON3",      MB_DEFBUTTON3      }, // useless, as there are only two buttons
-//  { "MB_DEFBUTTON4",      MB_DEFBUTTON4      }, // useless, as there are only two buttons
-    { NULL,                 0                  }
-  };
 
   pszTitle = myGetProfileStringDup("Settings", "Title");
-  pszCancelQuestion = myGetProfileStringDup("Settings", "CancelConfirm");
-  pszCancelQuestionCaption = myGetProfileStringDup("Settings", "CancelConfirmCaption");
   pszCancelButtonText = myGetProfileStringDup("Settings", "CancelButtonText");
   pszNextButtonText = myGetProfileStringDup("Settings", "NextButtonText");
   pszBackButtonText = myGetProfileStringDup("Settings", "BackButtonText");
 
-  myGetProfileString("Settings", "CancelConfirmFlags");
-  nCancelConfirmFlags = LookupTokens(MBFlagTable, szResult);
-
   nNumFields = GetPrivateProfileInt("Settings", "NumFields", 0, pszFilename);
   bBackDisabled = GetPrivateProfileInt("Settings", "BackDisabled", 0, pszFilename);
 
@@ -671,15 +644,9 @@ static void *lpWndProcOld;
 
 static LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-  if (message == WM_CLOSE)
+  if (message == WM_NOTIFY_OUTER_NEXT)
   {
-    message = WM_COMMAND;
-    wParam = IDCANCEL;
-  }
-	if (message == WM_COMMAND && (LOWORD(wParam) == IDCANCEL || LOWORD(wParam) == IDOK || LOWORD(wParam) == 3))
-  {
-		PostMessage(hConfigWindow,WM_USER+666,0,LOWORD(wParam));
-    return 0;
+    PostMessage(hConfigWindow,WM_USER+666,wParam,0);
   }
   return CallWindowProc((long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))lpWndProcOld,hwnd,message,wParam,lParam);
 }
@@ -697,14 +664,11 @@ BOOL CALLBACK cfgDlgProc(HWND   hwndDlg,
     HANDLE_MSG(hwndDlg, WM_COMMAND, WMCommandProc);
     return 0;
     case WM_USER+666:
-      if (lParam != IDCANCEL || !pszCancelQuestion || MessageBox(hwndDlg,pszCancelQuestion,pszCancelQuestionCaption?pszCancelQuestionCaption:"Question",MB_YESNO|nCancelConfirmFlags)==IDYES)
-      {
-        if (lParam == IDCANCEL || lParam == 3 || ValidateFields()) {
-          if (lParam == 3) g_is_back++;
-          if (lParam == IDCANCEL) g_is_cancel++;
-          g_done++;
-          PostMessage(hwndDlg,WM_CLOSE,0,0);
-        }
+      if (wParam == 0xD1E || wParam == -1 || ValidateFields()) {
+        if (wParam == -1) g_is_back++;
+        if (wParam == 0xD1E) g_is_cancel++;
+        g_done++;
+        PostMessage(hwndDlg,WM_CLOSE,0,0);
       }
     break;
     case WM_CTLCOLORSTATIC:
@@ -1036,8 +1000,6 @@ void showCfgDlg()
   if (cw_vis) ShowWindow(childwnd,SW_SHOWNA);
 
   FREE(pszTitle);
-  FREE(pszCancelQuestion);
-  FREE(pszCancelQuestionCaption);
   FREE(pszCancelButtonText);
   FREE(pszNextButtonText);
   FREE(pszBackButtonText);
diff --git a/Contrib/InstallOptions/test.nsi b/Contrib/InstallOptions/test.nsi
index 45ca538a..68aa4598 100644
--- a/Contrib/InstallOptions/test.nsi
+++ b/Contrib/InstallOptions/test.nsi
@@ -61,21 +61,22 @@ FunctionEnd
 
 Function SetCustom
 
- ;Display the Install Options dialog
+  ;Display the Install Options dialog
 
- Push ${TEMP1}
+  Push ${TEMP1}
 
   InstallOptions::dialog "$PLUGINSDIR\test.ini"
   Pop ${TEMP1}
 
-  StrCmp ${TEMP1} "cancel" "" +3
-    Pop ${TEMP1}
-    Quit
+  StrCmp ${TEMP1} "cancel" done
+  StrCmp ${TEMP1} "back" done
+  StrCmp ${TEMP1} "success" 0 error
+    # User clicked Next, all fields validated, read stuff from the INI here or later
+    Goto done
+
+  error:
+    MessageBox MB_OK|MB_ICONSTOP "InstallOptions error:$\r$\n${TEMP1}"
+
+  done: Pop ${TEMP1}
 
-  StrCmp ${TEMP1} "back" "" +3
-    Pop ${TEMP1}
-    Abort
-    
- Pop ${TEMP1}
-  
 FunctionEnd
\ No newline at end of file
diff --git a/Contrib/StartMenu/Example.nsi b/Contrib/StartMenu/Example.nsi
index da8d5916..ba6654dc 100644
--- a/Contrib/StartMenu/Example.nsi
+++ b/Contrib/StartMenu/Example.nsi
@@ -9,7 +9,7 @@ OutFile "StartMenu Test.exe"
 XPStyle on
 
 Page directory
-DirText "This installer will create some shortcuts to MakeNSIS in the start menu.$\nFor this it needs NSIS's folder path." \
+DirText "This installer will create some shortcuts to MakeNSIS in the start menu.$\nFor this it needs NSIS's path." \
   "Please specify the path in which you have installed NSIS:"
 InstallDir "${NSISDIR}"
 Function .onVerifyInstDir
@@ -21,7 +21,7 @@ Page custom StartMenuGroupSelect
 Function StartMenuGroupSelect
 	SendMessage $HWNDPARENT ${WM_SETTEXT} 0 "STR:StartMenu.dll test Setup: Start Menu Folder"
 
-	StartMenu::Select /autoadd "StartMenu.dll test"
+	StartMenu::Select /autoadd /lastused $R0 "StartMenu.dll test"
 	Pop $R1
 
 	StrCpy $R2 $R1 5
@@ -29,11 +29,8 @@ Function StartMenuGroupSelect
 		; error
 		MessageBox MB_OK $R1
 		Return
-	StrCmp $R1 "cancel" 0 +2
-		Quit
-	StrCmp $R1 "back" 0 +2
-		Abort
-	StrCpy $R0 $R1 ; got the dir
+	StrCpy $R0 $R1 ; got the dir, or cancel, but if it's cancel NSIS will exit and
+				   ; then we shouldn't care about the value of $R0
 FunctionEnd
 
 Page instfiles
diff --git a/Contrib/StartMenu/Readme.txt b/Contrib/StartMenu/Readme.txt
index 7ce652bd..e288a529 100644
--- a/Contrib/StartMenu/Readme.txt
+++ b/Contrib/StartMenu/Readme.txt
@@ -5,10 +5,11 @@ To show the dialog use the Select function. This function has one required param
 which is the program group default name, and some more optional parameters:
   /autoadd - automatically adds the program name to the selected folder
   /noicon - doesn't show the icon in the top left corner
-  /text [please select...] - sets the top text to something else than "Select 
-                             the Start Menu folder in which..."
-  /cancelconfirm [text] [caption] [flags] - displays a cancel confirmation 
-              message box when the user click on the cancel button
+  /text [please select...] - sets the top text to something else than
+                             "Select the Start Menu folder in which..."
+  /lastused [folder] - sets the edit box to a specific value folder.
+                       Use this to make this plug-in remember the last
+		       folder selected by the user
 
 The function pushes the folder selection back to the stack. It does not push the 
 full path but only the selected sub-folder. It's up to you to decide if to put 
diff --git a/Contrib/StartMenu/StartMenu.c b/Contrib/StartMenu/StartMenu.c
index c52e8655..bc7af6ae 100644
--- a/Contrib/StartMenu/StartMenu.c
+++ b/Contrib/StartMenu/StartMenu.c
@@ -1,7 +1,9 @@
 #include 
-#include "exdll.h"
+#include "../exdll/exdll.h"
 #include "resource.h"
 
+#define WM_NOTIFY_OUTER_NEXT (WM_USER+0x8)
+
 HINSTANCE g_hInstance;
 
 HWND hwParent;
@@ -15,10 +17,7 @@ HWND hwDirList;
 char buf[MAX_PATH];
 char text[1024];
 char progname[1024];
-char cancelconfirm[1024];
-char cancelconfirmcaption[1024];
-
-unsigned int cancelconfirmflags = 0;
+char lastused[1024];
 
 int autoadd = 0;
 int g_done = 0;
@@ -71,24 +70,9 @@ void __declspec(dllexport) Select(HWND hwndParent, int string_size, char *variab
       {
         autoadd = 1;
       }
-      else if (!lstrcmpi(buf+1, "cancelconfirm"))
+      else if (!lstrcmpi(buf+1, "lastused"))
       {
-        static TableEntry MBFlagTable[] = {
-          { "MB_ICONEXCLAMATION", MB_ICONEXCLAMATION },
-          { "MB_ICONINFORMATION", MB_ICONINFORMATION },
-          { "MB_ICONQUESTION",    MB_ICONQUESTION    },
-          { "MB_ICONSTOP",        MB_ICONSTOP        },
-          { "MB_TOPMOST",         MB_TOPMOST         },
-          { "MB_SETFOREGROUND",   MB_SETFOREGROUND   },
-          { "MB_RIGHT",           MB_RIGHT           },
-          { "MB_DEFBUTTON1",      MB_DEFBUTTON1      },
-          { "MB_DEFBUTTON2",      MB_DEFBUTTON2      },
-          { NULL,                 0                  }
-        };
-        popstring(cancelconfirm);
-        popstring(cancelconfirmcaption);
-        popstring(buf);
-        cancelconfirmflags = LookupTokens(MBFlagTable, buf);
+        popstring(lastused);
       }
       if (popstring(buf))
         *buf = 0;
@@ -131,15 +115,9 @@ void __declspec(dllexport) Select(HWND hwndParent, int string_size, char *variab
 
 static LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
-  if (message == WM_CLOSE)
+  if (message == WM_NOTIFY_OUTER_NEXT)
   {
-    message = WM_COMMAND;
-    wParam = IDCANCEL;
-  }
-	if (message == WM_COMMAND && (LOWORD(wParam) == IDCANCEL || LOWORD(wParam) == IDOK || LOWORD(wParam) == 3))
-  {
-		PostMessage(hwStartMenuSelect,WM_USER+666,0,LOWORD(wParam));
-    return 0;
+    PostMessage(hwStartMenuSelect,WM_USER+666,wParam,0);
   }
   return CallWindowProc((long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))lpWndProcOld,hwnd,message,wParam,lParam);
 }
@@ -238,7 +216,7 @@ BOOL CALLBACK dlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
         temp_r.bottom - temp_r.top
       );
 
-      SendMessage(hwLocation, WM_SETTEXT, 0, (LPARAM) progname);
+      SendMessage(hwLocation, WM_SETTEXT, 0, (LPARAM) (*lastused ? lastused : progname));
 
       ProgressiveSetWindowPos(
         hwDirList,
@@ -268,20 +246,12 @@ BOOL CALLBACK dlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
     break;
     case WM_USER+666:
       g_done = 1;
-      switch (lParam) {
-      	case IDOK:
-          SendMessage(hwLocation, WM_GETTEXT, MAX_PATH, (LPARAM) buf);
-          pushstring(buf);
-          break;
-        case IDCANCEL:
-          if (*cancelconfirm && MessageBox(hwStartMenuSelect, cancelconfirm, cancelconfirmcaption, MB_YESNO|cancelconfirmflags) == IDNO)
-            g_done = 0;
-          else
-            pushstring("cancel");
-          break;
-        case 3:
-          pushstring("back");
-          break;
+      if (wParam == 0xD1E)
+        pushstring("cancel");
+      else
+      {
+        SendMessage(hwLocation, WM_GETTEXT, MAX_PATH, (LPARAM) buf);
+        pushstring(buf);
       }
     break;
   }
diff --git a/Contrib/StartMenu/StartMenu.dsp b/Contrib/StartMenu/StartMenu.dsp
index f197cb10..82f0c009 100644
--- a/Contrib/StartMenu/StartMenu.dsp
+++ b/Contrib/StartMenu/StartMenu.dsp
@@ -101,7 +101,7 @@ SOURCE=.\StartMenu.c
 # PROP Default_Filter "h;hpp;hxx;hm;inl"
 # Begin Source File
 
-SOURCE=.\ExDLL.h
+SOURCE=..\ExDLL\exdll.h
 # End Source File
 # End Group
 # Begin Group "Resource Files"
diff --git a/Contrib/StartMenu/exdll.h b/Contrib/StartMenu/exdll.h
deleted file mode 100644
index a64a2ed3..00000000
--- a/Contrib/StartMenu/exdll.h
+++ /dev/null
@@ -1,93 +0,0 @@
-#ifndef _EXDLL_H_
-#define _EXDLL_H_
-
-// only include this file from one place in your DLL.
-// (it is all static, if you use it in two places it will fail)
-
-#define EXDLL_INIT()           {  \
-        g_stringsize=string_size; \
-        g_stacktop=stacktop;      \
-        g_variables=variables; }
-
-
-typedef struct _stack_t {
-  struct _stack_t *next;
-  char text[1]; // this should be the length of string_size
-} stack_t;
-
-
-static int g_stringsize;
-static stack_t **g_stacktop;
-static char *g_variables;
-
-static int popstring(char *str); // 0 on success, 1 on empty stack
-static void pushstring(char *str);
-
-enum
-{
-INST_0,         // $0
-INST_1,         // $1
-INST_2,         // $2
-INST_3,         // $3
-INST_4,         // $4
-INST_5,         // $5
-INST_6,         // $6
-INST_7,         // $7
-INST_8,         // $8
-INST_9,         // $9
-INST_R0,        // $R0
-INST_R1,        // $R1
-INST_R2,        // $R2
-INST_R3,        // $R3
-INST_R4,        // $R4
-INST_R5,        // $R5
-INST_R6,        // $R6
-INST_R7,        // $R7
-INST_R8,        // $R8
-INST_R9,        // $R9
-INST_CMDLINE,   // $CMDLINE
-INST_INSTDIR,   // $INSTDIR
-INST_OUTDIR,    // $OUTDIR
-INST_EXEDIR,    // $EXEDIR
-INST_LANG,      // $LANGUAGE
-__INST_LAST
-};
-
-
-// utility functions (not required but often useful)
-static int popstring(char *str)
-{
-  stack_t *th;
-  if (!g_stacktop || !*g_stacktop) return 1;
-  th=(*g_stacktop);
-  lstrcpy(str,th->text);
-  *g_stacktop = th->next;
-  GlobalFree((HGLOBAL)th);
-  return 0;
-}
-
-static void pushstring(char *str)
-{
-  stack_t *th;
-  if (!g_stacktop) return;
-  th=(stack_t*)GlobalAlloc(GPTR,sizeof(stack_t)+g_stringsize);
-  lstrcpyn(th->text,str,g_stringsize);
-  th->next=*g_stacktop;
-  *g_stacktop=th;
-}
-
-static char *getuservariable(int varnum)
-{
-  if (varnum < 0 || varnum >= __INST_LAST) return NULL;
-  return g_variables+varnum*g_stringsize;
-}
-
-static void setuservariable(int varnum, char *var)
-{
-	if (var != NULL && varnum >= 0 && varnum < __INST_LAST) 
-		lstrcpy(g_variables + varnum*g_stringsize, var);
-}
-
-
-
-#endif//_EXDLL_H_
\ No newline at end of file
diff --git a/Docs/src/pages.but b/Docs/src/pages.but
index 68cc3f1e..18be93d9 100644
--- a/Docs/src/pages.but
+++ b/Docs/src/pages.but
@@ -25,11 +25,7 @@ Each built-in page has two callback functions. The pre-function and the post-cre
 
 A custom page has only one callback function that creates it but unlike the built-in pages this function is mandatory.
 
-Abort (see \K{abort}) has special usage from pages' callback functions.
-
-\b Use Abort from a built-in pre-function to skip the page
-
-\b Use Abort from a custom page creator function to go to the previous page
+Use Abort (see \K{abort}) from a built-in page pre-function to to skip the page.
 
 Examples:
 
@@ -47,14 +43,12 @@ Examples:
 \c    GetTempFileName $R0
 \c    File /oname=$R0 customPage.ini
 \c    InstallOptions::dialog $R0
-\c    Delete $R0
 \c    Pop $R1
-\c    StrCmp $R1 "cancel" "" nocancel
-\c      Quit
-\c    nocancel:
-\c    StrCmp $R1 "back" "" noback
-\c      Abort
-\c    noback:
+\c    StrCmp $R1 "cancel" done
+\c    StrCmp $R1 "back" done
+\c    StrCmp $R1 "sucess" done
+\c    error: MessageBox MB_OK|MB_ICONSTOP "InstallOptions error:$\r$\n$R1"
+\c    done:
 \c  FunctionEnd
 
 \S{page} Page
diff --git a/Plugins/InstallOptions.dll b/Plugins/InstallOptions.dll
index 1b4631b5..1e9e57e4 100644
Binary files a/Plugins/InstallOptions.dll and b/Plugins/InstallOptions.dll differ
diff --git a/Plugins/StartMenu.dll b/Plugins/StartMenu.dll
index c6fc14b3..e3932d84 100644
Binary files a/Plugins/StartMenu.dll and b/Plugins/StartMenu.dll differ
diff --git a/Source/build.cpp b/Source/build.cpp
index a4dae074..f6c4b389 100644
--- a/Source/build.cpp
+++ b/Source/build.cpp
@@ -1355,7 +1355,7 @@ int CEXEBuild::write_output(void)
 
       page *p=(page *) build_pages.get();
       for (int i=0; iback=2; // 2 - enabled, 1 - disabled, 0 - invisible
+        if (i) p->back=SW_SHOWNA|2; // 2 - enabled, SW_SHOWNA - visible, 0 - invisible (or'ed)
         else p->back=0;
 
         p->next=LANG_BTN_NEXT;
@@ -1367,7 +1367,7 @@ int CEXEBuild::write_output(void)
           p->next=LANG_BTN_LICENSE;
         #endif
         if (p->id==NSIS_PAGE_INSTFILES || p->id==NSIS_PAGE_COMPLETED)
-          p->back=1;
+          p->back&=~2;
       }
       (--p)->next=LANG_BTN_CLOSE;
       if (p->id==NSIS_PAGE_COMPLETED) (--p)->next=LANG_BTN_CLOSE;
diff --git a/Source/exehead/Ui.c b/Source/exehead/Ui.c
index 5d3dfdba..004bbd49 100644
--- a/Source/exehead/Ui.c
+++ b/Source/exehead/Ui.c
@@ -75,8 +75,13 @@ static int num_sections;
 
 #define WM_TREEVIEW_KEYHACK (WM_USER+0x13)
 
-static void NSISCALL outernotify(char num) {
-  SendMessage(g_hwnd,WM_NOTIFY_OUTER_NEXT,(WPARAM)num,0);
+static int m_page,m_abort,m_retcode,m_delta;
+
+static void NSISCALL outernotify(int num) {
+  if (num==0xD1E)
+    g_quit_flag=1;
+  m_delta=num;
+  SendMessage(g_hwnd,WM_NOTIFY_OUTER_NEXT,(WPARAM)num,0); // it sends num again for plugins - DON'T REMOVE!
 }
 
 #ifdef NSIS_CONFIG_VISIBLE_SUPPORT
@@ -102,7 +107,6 @@ page *g_inst_page;
 section *g_inst_section;
 entry *g_inst_entry;
 
-static int m_page=-1,m_abort;
 static HWND m_curwnd, m_bgwnd, m_hwndOK, m_hwndCancel;
 static int m_whichcfg;
 
@@ -442,7 +446,7 @@ BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
 {
   if (uMsg == WM_INITDIALOG || uMsg == WM_NOTIFY_OUTER_NEXT)
   {
-    #define delta wParam
+    page *this_page;
     static struct
     {
       char *id;
@@ -479,10 +483,18 @@ BOOL CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
       ShowWindow(hwndDlg,SW_SHOW);
     }
 
+    // initial m_page is zero, m_pages grows and checked after this line so no memory misread
+    this_page=g_inst_page+m_page;
+
+    // if the last page was a custom page, wait for it to finish by itself.
+    // if it doesn't, it's a bad plugin.
+    // plugins should react to WM_NOTIFY_OUTER_NEXT.
+    if (this_page->id<0) return 0;
+
 nextPage:
 
-    if (m_page<0) delta=1;
-    m_page+=delta;
+    m_page+=m_delta;
+    this_page+=m_delta;
 
 #ifdef NSIS_SUPPORT_CODECALLBACKS
     if (m_page==g_inst_cmnheader->num_pages) ExecuteCodeSegment(g_inst_cmnheader->code_onInstSuccess,NULL);
@@ -491,69 +503,57 @@ nextPage:
     if (g_quit_flag || m_page < 0 || m_page == g_inst_cmnheader->num_pages)
     {
       DestroyWindow(m_curwnd);
-      EndDialog(hwndDlg,0);
+      EndDialog(hwndDlg,m_retcode);
     }
     else
     {
       HWND hwndtmp;
-      int page_id=g_inst_page[m_page].id;
 
-      SetDlgItemTextFromLang(hwndDlg,IDOK,g_inst_page[m_page].next);
+      SetDlgItemTextFromLang(hwndDlg,IDOK,this_page->next);
       
       hwndtmp=GetDlgItem(hwndDlg,IDC_BACK);
-      ShowWindow(hwndtmp,g_inst_page[m_page].back?SW_SHOWNA:SW_HIDE);
-      EnableWindow(hwndtmp, g_inst_page[m_page].back&2);
+      ShowWindow(hwndtmp,this_page->back&SW_SHOWNA);// SW_HIDE = 0
+      EnableWindow(hwndtmp,this_page->back&2);
 
-      if (page_id!=NSIS_PAGE_COMPLETED) DestroyWindow(m_curwnd);
+      if (this_page->id!=NSIS_PAGE_COMPLETED) DestroyWindow(m_curwnd);
       else if (g_autoclose) goto nextPage;
 
-      if (page_id>=0) // NSIS page
-      {
 #ifdef NSIS_SUPPORT_CODECALLBACKS
-        if (ExecuteCodeSegment(g_inst_page[m_page].prefunc,NULL))
-          goto nextPage;
-        else
-#endif //NSIS_SUPPORT_CODECALLBACKS
-        {
-          mystrcpy(g_tmp,g_caption);
-          process_string_fromtab(
-            g_tmp+mystrlen(g_tmp),
-            LANG_SUBCAPTION(page_id-(g_is_uninstaller?NSIS_PAGE_INSTFILES:0))
-          );
-
-          SetWindowText(hwndDlg,g_tmp);
-
-          gDontFookWithFocus = 0;
-          m_curwnd=CreateDialog(g_hInstance,windows[page_id].id,hwndDlg,windows[page_id].proc);
-          if (m_curwnd)
-          {
-            RECT r;
-            GetWindowRect(GetDlgItem(hwndDlg,IDC_CHILDRECT),&r);
-            ScreenToClient(hwndDlg,(LPPOINT)&r);
-            SetWindowPos(m_curwnd,0,r.left,r.top,0,0,SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
-            SendMessage(m_curwnd, WM_NOTIFY_START, 0, 0);
-            ShowWindow(m_curwnd,SW_SHOWNA);
-          }
-
-          //XGE 5th September 2002 - Do *not* move the focus to the OK button if we are
-          //on the license page, instead we want the focus left alone because in
-          //WM_INITDIALOG it is given to the richedit control.
-          if (!gDontFookWithFocus)
-              SetFocus(m_hwndOK);
-          //XGE End
-        }
-      }
-#ifdef NSIS_SUPPORT_CODECALLBACKS
-      else // User custom page
-      {
-        if (ExecuteCodeSegment(g_inst_page[m_page].prefunc,NULL))
-          delta=-1;
-        else
-          delta=1;
+      if (ExecuteCodeSegment(this_page->prefunc,NULL) || this_page->id<0)
         goto nextPage;
+#endif //NSIS_SUPPORT_CODECALLBACKS
+      if (this_page->id>=0) // NSIS page
+      {
+        mystrcpy(g_tmp,g_caption);
+        process_string_fromtab(
+          g_tmp+mystrlen(g_tmp),
+          LANG_SUBCAPTION(this_page->id-(g_is_uninstaller?NSIS_PAGE_INSTFILES:0))
+        );
+
+        SetWindowText(hwndDlg,g_tmp);
+
+        gDontFookWithFocus = 0;
+        m_curwnd=CreateDialog(g_hInstance,windows[this_page->id].id,hwndDlg,windows[this_page->id].proc);
+        if (m_curwnd)
+        {
+          RECT r;
+          GetWindowRect(GetDlgItem(hwndDlg,IDC_CHILDRECT),&r);
+          ScreenToClient(hwndDlg,(LPPOINT)&r);
+          SetWindowPos(m_curwnd,0,r.left,r.top,0,0,SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER);
+          SendMessage(m_curwnd, WM_NOTIFY_START, 0, 0);
+          ShowWindow(m_curwnd,SW_SHOWNA);
+        }
+
+        //XGE 5th September 2002 - Do *not* move the focus to the OK button if we are
+        //on the license page, instead we want the focus left alone because in
+        //WM_INITDIALOG it is given to the richedit control.
+        if (!gDontFookWithFocus)
+            SetFocus(m_hwndOK);
+        //XGE End
       }
 
-      ExecuteCodeSegment(g_inst_page[m_page].postfunc,NULL);
+#ifdef NSIS_SUPPORT_CODECALLBACKS
+      ExecuteCodeSegment(this_page->postfunc,NULL);
 #endif //NSIS_SUPPORT_CODECALLBACKS
     }
   }
@@ -580,7 +580,8 @@ nextPage:
 #ifdef NSIS_SUPPORT_CODECALLBACKS
         ExecuteCodeSegment(g_inst_cmnheader->code_onInstFailed,NULL);
 #endif//NSIS_SUPPORT_CODECALLBACKS
-        EndDialog(hwndDlg,2);
+        m_retcode=2;
+        outernotify(0xD1E);
       }
       else
       {
@@ -588,7 +589,8 @@ nextPage:
         if (!ExecuteCodeSegment(g_inst_cmnheader->code_onUserAbort,NULL))
 #endif//NSIS_SUPPORT_CODECALLBACKS
         {
-          EndDialog(hwndDlg,1);
+          m_retcode=1;
+          outernotify(0xD1E);
         }
       }
     }
@@ -1362,7 +1364,10 @@ static BOOL CALLBACK InstProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa
   if (uMsg == WM_NOTIFY_INSTPROC_DONE)
   {
     if (g_quit_flag)
-        EndDialog(g_hwnd,1);
+    {
+      m_retcode=1;
+      outernotify(0xD1E);
+    }
     else if (!wParam)
     {
       HWND h=m_hwndOK;