From 38d9a9aa3784709cd1c9f96ad6bd0a5a06512040 Mon Sep 17 00:00:00 2001 From: eccles Date: Sun, 1 Feb 2004 15:50:00 +0000 Subject: [PATCH] - Fixed potential crash caused by WinSock being shutdown while the connection is still open. - Cleaned up dialog creation a little (some details, such as font, were being changed after the dialog was already visible). - Restores focus to its previous state when exiting. - Fixed another one of those "holding down Cancel at the wrong moment can cause the installer to suddenly exit" problems. git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@3443 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/NSISdl/Script1.rc | 12 ++-- Contrib/NSISdl/nsisdl.cpp | 130 +++++++++++++++++++------------------- Plugins/nsisdl.dll | Bin 13312 -> 13824 bytes 3 files changed, 72 insertions(+), 70 deletions(-) diff --git a/Contrib/NSISdl/Script1.rc b/Contrib/NSISdl/Script1.rc index 9c10dbe1..568dfb7c 100644 --- a/Contrib/NSISdl/Script1.rc +++ b/Contrib/NSISdl/Script1.rc @@ -27,14 +27,14 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // IDD_DIALOG1 DIALOGEX 0, 0, 265, 104 -STYLE DS_CONTROL | WS_CHILD +STYLE WS_CHILD EXSTYLE WS_EX_NOPARENTNOTIFY FONT 8, "MS Sans Serif" BEGIN - CONTROL "Progress1",IDC_PROGRESS1,"msctls_progress32",NOT + CONTROL "Progress1",IDC_PROGRESS1,"msctls_progress32",NOT WS_VISIBLE | WS_BORDER,0,36,265,11 CTEXT "",IDC_STATIC2,0,25,263,8 - CONTROL "Progress1",IDC_PROGRESS2,"msctls_progress32",PBS_SMOOTH | + CONTROL "Progress1",IDC_PROGRESS2,"msctls_progress32",PBS_SMOOTH | NOT WS_VISIBLE | WS_BORDER,0,37,265,11 END @@ -45,18 +45,18 @@ END // TEXTINCLUDE // -1 TEXTINCLUDE DISCARDABLE +1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END -2 TEXTINCLUDE DISCARDABLE +2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END -3 TEXTINCLUDE DISCARDABLE +3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" diff --git a/Contrib/NSISdl/nsisdl.cpp b/Contrib/NSISdl/nsisdl.cpp index 78468b98..757860a6 100644 --- a/Contrib/NSISdl/nsisdl.cpp +++ b/Contrib/NSISdl/nsisdl.cpp @@ -51,8 +51,8 @@ ULONG idThreadOwner; ULONG ulRefCount; static void *lpWndProcOld; -BOOL CALLBACK DownloadDialogProc(HWND hwndDlg, - UINT uMsg, +BOOL CALLBACK DownloadDialogProc(HWND hwndDlg, + UINT uMsg, WPARAM wParam, LPARAM lParam) { @@ -83,7 +83,7 @@ BOOL TryEnterCS() bRet = FALSE; } - ::InterlockedExchange(plBusy, 0); + ::InterlockedExchange(plBusy, 0); return bRet; } @@ -111,18 +111,15 @@ static LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LP LRESULT Res = 0; while ( !TryEnterCS() ) Sleep(0); if (message == WM_COMMAND && LOWORD(wParam) == IDCANCEL) - { - SendMessage(GetDlgItem(hwnd, IDCANCEL), BM_SETSTATE, FALSE, 0); g_cancelled = 1; - } else Res = CallWindowProc((long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))lpWndProcOld,hwnd,message,wParam,lParam); LeaveCS(); return Res; } -BOOL APIENTRY DllMain( HANDLE _hModule, - DWORD ul_reason_for_call, +BOOL APIENTRY DllMain( HANDLE _hModule, + DWORD ul_reason_for_call, LPVOID lpReserved ) { @@ -153,18 +150,17 @@ void progress_callback(HWND dlg, char *msg, int read_bytes) extern char *_strstr(char *i, char *s); #define strstr _strstr -extern "C" +extern "C" { __declspec(dllexport) void download (HWND parent, - int string_size, - char *variables, + int string_size, + char *variables, stack_t **stacktop) { char buf[1024]; char url[1024]; char filename[1024]; - int wasen=0; HWND hwndAux; HWND hwndL=0; HWND hwndB=0; @@ -174,8 +170,6 @@ __declspec(dllexport) void download (HWND parent, RECT r, cr, orig_childRc; int timeout_ms=30000; - JNL_HTTPGet *get = 0; - char *error=NULL; static char szDownloading[1024];//= "Downloading %s"; @@ -220,10 +214,16 @@ __declspec(dllexport) void download (HWND parent, HANDLE hFile = CreateFile(filename,GENERIC_WRITE,FILE_SHARE_READ,NULL,CREATE_ALWAYS,0,NULL); - if (hFile == INVALID_HANDLE_VALUE) { - wsprintf (buf, "Unable to open %s", filename); - error=buf; - } else { + if (hFile == INVALID_HANDLE_VALUE) + { + wsprintf(buf, "Unable to open %s", filename); + error = buf; + } + else + { + HWND hwndPrevFocus; + BOOL fCancelDisabled; + if (parent) { childwnd=FindWindowEx(parent,NULL,"#32770",NULL); @@ -236,7 +236,7 @@ __declspec(dllexport) void download (HWND parent, lpWndProcOld = (void *)SetWindowLong(parent,GWL_WNDPROC,(long)ParentWndProc); - dlg = CreateDialog((HINSTANCE)hModule, + dlg = CreateDialog((HINSTANCE)hModule, MAKEINTRESOURCE(IDD_DIALOG1), parent, DownloadDialogProc); @@ -251,10 +251,10 @@ __declspec(dllexport) void download (HWND parent, HWND pb = g_hwndProgressBar = GetDlgItem(dlg, pbid); - long c; - if (hwPb) { + long c; + c = SendMessage(hwPb, PBM_SETBARCOLOR, 0, 0); SendMessage(hwPb, PBM_SETBARCOLOR, 0, c); SendMessage(pb, PBM_SETBARCOLOR, 0, c); @@ -264,23 +264,23 @@ __declspec(dllexport) void download (HWND parent, SendMessage(pb, PBM_SETBKCOLOR, 0, c); } - ShowWindow(pb, SW_SHOW); + ShowWindow(pb, SW_SHOWNA); GetWindowRect(childwnd,&orig_childRc); GetWindowRect(dlg,&cr); ScreenToClient(dlg,(LPPOINT)&cr); ScreenToClient(dlg,((LPPOINT)&cr)+1); - + hwndAux = GetDlgItem(childwnd,1016); GetWindowRect(hwndAux,&r); ScreenToClient(childwnd,(LPPOINT)&r); ScreenToClient(childwnd,((LPPOINT)&r)+1); SetWindowPos(childwnd,0,0,0,r.right-r.left,r.top,SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE); - + GetWindowRect(hwndAux,&r); ScreenToClient(parent,(LPPOINT)&r); ScreenToClient(parent,((LPPOINT)&r)+1); - SetWindowPos(dlg,0,r.left,r.top,r.right-r.left,cr.bottom-cr.top,SWP_NOACTIVATE|SWP_NOZORDER|SWP_SHOWWINDOW); + SetWindowPos(dlg,0,r.left,r.top,r.right-r.left,cr.bottom-cr.top,SWP_NOACTIVATE|SWP_NOZORDER); hwndAux = GetDlgItem(dlg,IDC_STATIC2); GetWindowRect(hwndAux,&cr); @@ -305,20 +305,25 @@ __declspec(dllexport) void download (HWND parent, long hFont = SendMessage(parent, WM_GETFONT, 0, 0); SendDlgItemMessage(dlg, pbid, WM_SETFONT, hFont, 0); SendDlgItemMessage(dlg, IDC_STATIC2, WM_SETFONT, hFont, 0); + + ShowWindow(dlg, SW_SHOWNA); } // enable the cancel button - wasen=EnableWindow(GetDlgItem(parent,IDCANCEL),TRUE); - SendMessage(parent, WM_NEXTDLGCTL, (WPARAM) GetDlgItem(parent, IDCANCEL), TRUE); + hwndPrevFocus = GetFocus(); + fCancelDisabled = EnableWindow(GetDlgItem(parent, IDCANCEL), TRUE); + SendMessage(parent, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(parent, IDCANCEL), TRUE); } { WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); + JNL_HTTPGet *get = 0; + static char main_buf[8192]; char *buf=main_buf; char *p=NULL; - + HKEY hKey; if (RegOpenKeyEx(HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",0,KEY_READ,&hKey) == ERROR_SUCCESS) { @@ -373,16 +378,22 @@ __declspec(dllexport) void download (HWND parent, } } while (!TryEnterCS()); // Process messages - if ((g_cancelled || error) && dlg) - DestroyWindow(dlg); + if (g_cancelled || error) + { + if (dlg) + DestroyWindow(dlg); + dlg = NULL; + if (!error) + error = "cancel"; + } LeaveCS(); - if ( g_cancelled || error ) + if (error) { if (parent) { SetWindowLong(parent,GWL_WNDPROC,(long)lpWndProcOld); - + if (hwndB) ShowWindow(hwndB,SW_SHOWNA); if (hwndL) ShowWindow(hwndL,SW_SHOWNA); @@ -393,21 +404,24 @@ __declspec(dllexport) void download (HWND parent, SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE ); - if (wasen) + // Prevent wierd stuff happening if the cancel button happens to be + // pressed at the moment we are finishing + SendMessage(GetDlgItem(parent, IDCANCEL), BM_SETSTATE, FALSE, 0); + // Restore the previous focus and cancel button states + SendMessage(parent, WM_NEXTDLGCTL, (WPARAM)hwndPrevFocus, TRUE); + if (fCancelDisabled) EnableWindow(GetDlgItem(parent, IDCANCEL), FALSE); } - if ( !error ) - error = "cancel"; break; } - + Sleep(25); st = get->run (); if (st == -1) { - error=get->geterrorstr(); - //break; + lstrcpyn(url, get->geterrorstr(), sizeof(url)); + error = url; } else if (st == 1) { if (sofar < cl) error="download incomplete"; @@ -416,25 +430,18 @@ __declspec(dllexport) void download (HWND parent, bSuccess=TRUE; error = "success"; } - //break; } else { if (get->get_status () == 0) { // progressFunc ("Connecting ...", 0); if (last_recv_time+timeout_ms < GetTickCount()) - { error = "Timed out on connecting."; - //break; - } } else if (get->get_status () == 1) { progress_callback(dlg, "Reading headers", 0); if (last_recv_time+timeout_ms < GetTickCount()) - { error = "Timed out on getting headers."; - //break; - } } else if (get->get_status () == 2) { @@ -443,12 +450,11 @@ __declspec(dllexport) void download (HWND parent, last_recv_time=GetTickCount(); cl = get->content_length (); - if (cl == 0) { + if (cl == 0) error = "Server did not specify content length."; - //break; - } else if (dlg) { - SendMessage(g_hwndProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0,30000)); - g_file_size=cl; + else if (dlg) { + SendMessage(g_hwndProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0,30000)); + g_file_size=cl; } } @@ -465,7 +471,7 @@ __declspec(dllexport) void download (HWND parent, int bps=sofar/(time_sofar?time_sofar:1); int remain=MulDiv(time_sofar,cl,sofar) - time_sofar; char *rtext=szSecond; - if (remain >= 60) + if (remain >= 60) { remain/=60; rtext=szMinute; @@ -475,7 +481,7 @@ __declspec(dllexport) void download (HWND parent, rtext=szHour; } } - wsprintf (buf, + wsprintf (buf, szProgress, sofar/1024, MulDiv(100,sofar,cl), @@ -491,45 +497,41 @@ __declspec(dllexport) void download (HWND parent, } else { if (sofar < cl) error = "Server aborted."; - //break; } } if (GetTickCount() > last_recv_time+timeout_ms) - { error = "Downloading timed out."; - //break; - } } else { error = "Bad response status."; } } - + } + // Clean up the connection then release winsock + if (get) delete get; WSACleanup(); } - + CloseHandle(hFile); } - + if (g_cancelled || !bSuccess) { DeleteFile(filename); } pushstring(error); - - if (get) delete get; } __declspec(dllexport) void download_quiet(HWND parent, - int stringsize, - char *variables, + int stringsize, + char *variables, stack_t **stacktop) { g_hwndProgressBar=0; download(NULL,stringsize,variables,stacktop); } -} //extern "C" \ No newline at end of file +} //extern "C" diff --git a/Plugins/nsisdl.dll b/Plugins/nsisdl.dll index 7142f325382efadd21d692ef524da644c87a85a3..360fd8f07eb7037ccc2cadd3bbca4d4fc8d604d4 100644 GIT binary patch delta 5543 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