Skip to content

Commit 6d2efbc

Browse files
author
kalibera
committed
Fix propagation of R errors and interrupts from window procedures on
Windows (includes graphic output to the screen). git-svn-id: https://svn.r-project.org/R/trunk@88464 00db46b3-68df-0310-9c12-caf00c1e9a41
1 parent 0c4353f commit 6d2efbc

File tree

10 files changed

+212
-37
lines changed

10 files changed

+212
-37
lines changed

src/extra/graphapp/R.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ EXPORTS
77
libintl_dgettext
88
libintl_gettext
99
Rprintf
10+
R_UnwindProtect
11+
R_MakeUnwindCont
12+
R_ContinueUnwind
13+
Rf_protect
14+
Rf_unprotect
15+
R_NilValue
16+

src/extra/graphapp/buttons.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,27 +1400,27 @@ progressbar newprogressbar(rect r, int pbmin, int pbmax, int incr, int smooth)
14001400
set_new_winproc(obj); /* set custom winproc */
14011401
settextfont(obj, SystemFont);
14021402
obj->kind = ListboxObject;
1403-
SendMessage(hwnd, PBM_SETRANGE32, (WPARAM) pbmin, (LPARAM) pbmax);
1404-
SendMessage(hwnd, PBM_SETSTEP, (WPARAM) incr, 0);
1403+
sendmessage(hwnd, PBM_SETRANGE32, (WPARAM) pbmin, (LPARAM) pbmax);
1404+
sendmessage(hwnd, PBM_SETSTEP, (WPARAM) incr, 0);
14051405

14061406
return obj;
14071407
}
14081408

14091409
void setprogressbar(progressbar obj, int n)
14101410
{
14111411
if (! obj) return;
1412-
SendMessage(obj->handle, PBM_SETPOS, (WPARAM) n, 0);
1412+
sendmessage(obj->handle, PBM_SETPOS, (WPARAM) n, 0);
14131413
}
14141414

14151415
void stepprogressbar(progressbar obj, int n)
14161416
{
14171417
if (! obj) return;
1418-
SendMessage(obj->handle, PBM_STEPIT, 0, 0);
1418+
sendmessage(obj->handle, PBM_STEPIT, 0, 0);
14191419
}
14201420

14211421
void setprogressbarrange(progressbar obj, int pbmin, int pbmax)
14221422
{
14231423
if (! obj) return;
1424-
SendMessage(obj->handle, PBM_SETRANGE32, (WPARAM) pbmin,
1424+
sendmessage(obj->handle, PBM_SETRANGE32, (WPARAM) pbmin,
14251425
(LPARAM) pbmax);
14261426
}

src/extra/graphapp/events.c

Lines changed: 177 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
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;
240241
void 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
}
245246
void 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

251252
static 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+
846935
LRESULT 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+
858951
LRESULT 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+
888994
LRESULT 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 */

src/extra/graphapp/ga.hide

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
GAI_protect_object
7777
GAI_remove_context
7878
GAI_save_gif
79+
GAI_sendmessage_unwind
7980
GAI_show_window
8081
GAI_simple_window
8182
GAI_string_diff

src/extra/graphapp/gmenus.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ static void mdimenu(menuitem m)
3131
{
3232
switch (getvalue(m)) {
3333
case 1:
34-
SendMessage(hwndClient,WM_MDICASCADE,0,0);
34+
sendmessage(hwndClient,WM_MDICASCADE,0,0);
3535
break;
3636
case 2:
37-
SendMessage(hwndClient,WM_MDITILE,MDITILE_HORIZONTAL,0);
37+
sendmessage(hwndClient,WM_MDITILE,MDITILE_HORIZONTAL,0);
3838
break;
3939
case 3:
40-
SendMessage(hwndClient,WM_MDITILE,MDITILE_VERTICAL,0);
40+
sendmessage(hwndClient,WM_MDITILE,MDITILE_VERTICAL,0);
4141
break;
4242
case 4:
43-
SendMessage(hwndClient,WM_MDIICONARRANGE,0,0);
43+
sendmessage(hwndClient,WM_MDIICONARRANGE,0,0);
4444
break;
4545
}
4646
}
@@ -82,7 +82,7 @@ void gchangemenubar(menubar mb)
8282
SetMenu(w->handle, mb->handle);
8383
if (ismdi()) {
8484
menu mdi = (w->menubar)->menubar;
85-
SendMessage(hwndClient, WM_MDISETMENU,
85+
sendmessage(hwndClient, WM_MDISETMENU,
8686
(WPARAM)w->menubar->handle,
8787
(LPARAM)(mdi ? (mdi->handle) : 0));
8888
DrawMenuBar(hwndFrame);

0 commit comments

Comments
 (0)