3737 Allow to leave a dropfield (combo box) with TAB key
3838 Allow navigating to previous controls with Shift+TAB key
3939 Path length limitations
40+ Propagation of R errors from window procedures
4041 */
4142
4243#include "internal.h"
@@ -240,12 +241,12 @@ static int showMDIToolbar = 1;
240241void toolbar_show (void )
241242{
242243 showMDIToolbar = 1 ;
243- SendMessage (hwndFrame ,WM_PAINT , (WPARAM ) 0 , (LPARAM ) 0 );
244+ sendmessage (hwndFrame ,WM_PAINT , (WPARAM ) 0 , (LPARAM ) 0 );
244245}
245246void toolbar_hide (void )
246247{
247248 showMDIToolbar = 0 ;
248- SendMessage (hwndFrame ,WM_PAINT , (WPARAM ) 0 , (LPARAM ) 0 );
249+ sendmessage (hwndFrame ,WM_PAINT , (WPARAM ) 0 , (LPARAM ) 0 );
249250}
250251
251252static void handle_mdiframesize (void )
@@ -274,7 +275,7 @@ static void handle_mdiframesize(void)
274275 if (status ) {
275276 MoveWindow (status ,1 ,fh - sh ,fw - 2 ,sh ,TRUE);
276277 }
277- SetFocus ((HWND )SendMessage (hwndClient ,
278+ SetFocus ((HWND )sendmessage (hwndClient ,
278279 WM_MDIGETACTIVE ,(WPARAM )0 ,(LPARAM ) 0 ));
279280}
280281
@@ -843,20 +844,121 @@ static long handle_message(HWND hwnd, UINT message,
843844 * for a window from just knowing the hwnd (which may or may not
844845 * belong to us).
845846 */
847+
848+ /* R modification: propagation of R errors from window procedures.
849+ R errors are implemented using long jumps, but Windows API and
850+ specifically window procedures don't work with them (e.g. causing
851+ a crash). This uses R_UnwindProtect to catch errors still inside
852+ a window procedure and propagate it via global variables to the
853+ call sites of Windows API calls that may end up calling a window
854+ procedure. */
855+
856+ #include <setjmp.h>
857+
858+ typedef void * SEXP ;
859+ typedef bool Rboolean ;
860+
861+ SEXP R_UnwindProtect (SEXP (* fun )(void * data ), void * data ,
862+ void (* clean )(void * data , Rboolean jump ), void * cdata ,
863+ SEXP cont );
864+ SEXP R_MakeUnwindCont (void );
865+ SEXP Rf_protect (SEXP );
866+ void Rf_unprotect (int );
867+ void R_ContinueUnwind (SEXP cont );
868+ extern LibImport SEXP R_NilValue ;
869+
870+ static SEXP wndproc_cont_token ;
871+
872+ static void wndproc_rethrow_error (void )
873+ {
874+ if (wndproc_cont_token ) {
875+ SEXP token = wndproc_cont_token ;
876+ wndproc_cont_token = NULL ;
877+ R_ContinueUnwind (token );
878+ }
879+ }
880+
881+ typedef struct {
882+ WNDPROC proc_real ;
883+ HWND hwnd ;
884+ UINT message ;
885+ WPARAM wParam ;
886+ LPARAM lParam ;
887+ LRESULT res ;
888+ } wndproc_call ;
889+
890+ SEXP wndproc_unwind_fun (void * data )
891+ {
892+ wndproc_call * w = data ;
893+ w -> res = w -> proc_real (w -> hwnd , w -> message , w -> wParam , w -> lParam );
894+ return NULL ;
895+ }
896+
897+ void wndproc_unwind_clean (void * data , Rboolean jump )
898+ {
899+ if (jump )
900+ longjmp (* (jmp_buf * )data , 1 );
901+ }
902+
903+ LRESULT WINAPI wndproc_unwind (WNDPROC proc_real , HWND hwnd , UINT message ,
904+ WPARAM wParam , LPARAM lParam )
905+ {
906+ jmp_buf jmpbuf ;
907+ wndproc_call w ;
908+ volatile SEXP token ;
909+
910+ if (R_NilValue == NULL ) {
911+ /* when R heap hasn't been initialized yet */
912+ wndproc_cont_token = NULL ;
913+ return proc_real (hwnd , message , wParam , lParam );
914+ }
915+ if (setjmp (jmpbuf ) == 1 ) {
916+ /* long jump */
917+ wndproc_cont_token = token ;
918+ return 0 ;
919+ }
920+ w .hwnd = hwnd ;
921+ w .message = message ;
922+ w .wParam = wParam ;
923+ w .lParam = lParam ;
924+ w .proc_real = proc_real ;
925+ SEXP saved_token = wndproc_cont_token ;
926+ token = Rf_protect (R_MakeUnwindCont ());
927+ R_UnwindProtect (wndproc_unwind_fun , & w , wndproc_unwind_clean , jmpbuf , token );
928+ Rf_unprotect (1 );
929+ wndproc_cont_token = saved_token ;
930+ return w .res ;
931+ }
932+
933+ /* end of R modification */
934+
846935LRESULT WINAPI
847- app_win_proc (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
936+ app_win_proc_real (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
848937{
849938 long result ;
850939 int pass = 0 ;
851940
852941 result = handle_message (hwnd , message , wParam , lParam , & pass );
853- if (pass )
942+ if (pass ) {
854943 result = DefWindowProc (hwnd , message , wParam , lParam );
944+ wndproc_rethrow_error ();
945+ }
855946 return result ;
856947}
857948
949+ /* R modification: propagation of R errors from window procedures. */
950+
858951LRESULT WINAPI
859- app_doc_proc (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
952+ app_win_proc (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
953+ {
954+ return wndproc_unwind (app_win_proc_real , hwnd , message , wParam , lParam );
955+ }
956+
957+ /* end of R modification */
958+
959+
960+ LRESULT WINAPI
961+ app_doc_proc_real (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
860962{
861963 long result ;
862964 int pass = 0 ;
@@ -868,7 +970,7 @@ app_doc_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
868970 handle_mdiframesize ();
869971 if (obj && obj -> menubar ) {
870972 menu mdi = (obj -> menubar )-> menubar ;
871- SendMessage (hwndClient , WM_MDISETMENU ,
973+ sendmessage (hwndClient , WM_MDISETMENU ,
872974 (WPARAM )obj -> menubar -> handle ,
873975 (LPARAM )(mdi ?(mdi -> handle ):0 ));
874976 DrawMenuBar (hwndFrame );
@@ -880,13 +982,25 @@ app_doc_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
880982 return 1 ;
881983 }
882984 result = handle_message (hwnd , message , wParam , lParam , & pass );
883- if (pass )
985+ if (pass ) {
884986 result = DefMDIChildProc (hwnd , message , wParam , lParam );
987+ wndproc_rethrow_error ();
988+ }
885989 return result ;
886990}
887991
992+ /* R modification: propagation of R errors from window procedures. */
993+
888994LRESULT WINAPI
889- app_work_proc (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
995+ app_doc_proc (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
996+ {
997+ return wndproc_unwind (app_doc_proc_real , hwnd , message , wParam , lParam );
998+ }
999+
1000+ /* end of R modification */
1001+
1002+ LRESULT WINAPI
1003+ app_work_proc_real (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
8901004{
8911005 long result ;
8921006 int pass = 0 ;
@@ -896,6 +1010,17 @@ app_work_proc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
8961010 return result ;
8971011}
8981012
1013+ /* R modification: propagation of R errors from window procedures. */
1014+
1015+ LRESULT WINAPI
1016+ app_work_proc (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
1017+ {
1018+ return wndproc_unwind (app_work_proc_real , hwnd , message , wParam , lParam );
1019+ }
1020+
1021+ /* end of R modification */
1022+
1023+
8991024/*
9001025 * To handle controls correctly, we replace each control's event
9011026 * handling procedure with our own when we create it. We handle
@@ -923,8 +1048,8 @@ static void send_char(object obj, int ch)
9231048 keystate = 0 ;
9241049}
9251050
926- long WINAPI
927- app_control_procedure (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
1051+ LRESULT WINAPI
1052+ app_control_procedure_real (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
9281053{
9291054 int prevent_activation = 0 ;
9301055 int key ;
@@ -1034,19 +1159,31 @@ app_control_procedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
10341159 }
10351160
10361161 result = CallWindowProc ((obj -> winproc ), hwnd , message , wParam , lParam );
1162+ wndproc_rethrow_error ();
10371163
10381164 /* Re-activate the control if necessary. */
10391165 if (prevent_activation )
10401166 obj -> state |= GA_Enabled ;
10411167 return result ;
10421168}
10431169
1044- long WINAPI
1045- edit_control_procedure (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
1170+ /* R modification: propagation of R errors from window procedures. */
1171+
1172+ LRESULT WINAPI
1173+ app_control_procedure (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
1174+ {
1175+ return wndproc_unwind (app_control_procedure_real , hwnd , message , wParam , lParam );
1176+ }
1177+
1178+ /* end of R modification */
1179+
1180+ LRESULT WINAPI
1181+ edit_control_procedure_real (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
10461182{
10471183 int key ;
10481184 object obj , next , prev ;
10491185 HANDLE hwndCombo ;
1186+ long result ;
10501187
10511188 /* Find the library (dropfield/combo box) object associated
10521189 with the hwnd. */
@@ -1088,9 +1225,22 @@ edit_control_procedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
10881225 break ;
10891226 }
10901227
1091- return CallWindowProc ((obj -> edit_winproc ), hwnd , message , wParam , lParam );
1228+ result = CallWindowProc ((obj -> edit_winproc ), hwnd , message , wParam , lParam );
1229+ wndproc_rethrow_error ();
1230+ return result ;
1231+ }
1232+
1233+ /* R modification: propagation of R errors from window procedures. */
1234+
1235+ LRESULT WINAPI
1236+ edit_control_procedure (HWND hwnd , UINT message , WPARAM wParam , LPARAM lParam )
1237+ {
1238+ return wndproc_unwind (edit_control_procedure_real , hwnd , message , wParam , lParam );
10921239}
10931240
1241+ /* end of R modification */
1242+
1243+
10941244/*
10951245 * Timer functions use a timer procedure not associated with a window.
10961246 * We use this one procedure to handle both timer events and mouse-down
@@ -1277,6 +1427,7 @@ int doevent(void)
12771427 return result ;
12781428 TranslateMessage (& msg );
12791429 DispatchMessage (& msg );
1430+ wndproc_rethrow_error ();
12801431 }
12811432 deletion_traversal ();
12821433 if ((active_windows <= 0 ) || (msg .message == WM_QUIT ))
@@ -1331,3 +1482,15 @@ void finish_events(void)
13311482 settimer (0 );
13321483 setmousetimer (0 );
13331484}
1485+
1486+ /* R modification: propagation of R errors from window procedures. */
1487+
1488+ PROTECTED LRESULT
1489+ sendmessage_unwind (HWND hWnd , UINT Msg , WPARAM wParam , LPARAM lParam )
1490+ {
1491+ LRESULT result = SendMessage (hWnd , Msg , wParam , lParam );
1492+ wndproc_rethrow_error ();
1493+ return result ;
1494+ }
1495+
1496+ /* end of R modification */
0 commit comments