From 959b37938133f071876ddd6640e68ed60ac40885 Mon Sep 17 00:00:00 2001 From: ramon18 Date: Tue, 2 Dec 2003 01:13:35 +0000 Subject: [PATCH] Crash fixes, including firewall problems, many Optimizations git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@3243 212acab6-be3b-0410-9dea-997c60f758d6 --- Contrib/NSISdl/Script1.rc | 3 +- Contrib/NSISdl/httpget.cpp | 3 +- Contrib/NSISdl/nsisdl.cpp | 252 ++++++++++++++++++++++++------------- Plugins/nsisdl.dll | Bin 13312 -> 13312 bytes 4 files changed, 167 insertions(+), 91 deletions(-) diff --git a/Contrib/NSISdl/Script1.rc b/Contrib/NSISdl/Script1.rc index 0f6a3283..ad8440cc 100644 --- a/Contrib/NSISdl/Script1.rc +++ b/Contrib/NSISdl/Script1.rc @@ -26,8 +26,9 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // Dialog // -IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 265, 104 +IDD_DIALOG1 DIALOGEX 0, 0, 265, 104 STYLE DS_CONTROL | WS_CHILD +EXSTYLE WS_EX_NOPARENTNOTIFY FONT 8, "MS Sans Serif" BEGIN CONTROL "Progress1",IDC_PROGRESS1,"msctls_progress32",NOT diff --git a/Contrib/NSISdl/httpget.cpp b/Contrib/NSISdl/httpget.cpp index c0163491..15993088 100644 --- a/Contrib/NSISdl/httpget.cpp +++ b/Contrib/NSISdl/httpget.cpp @@ -341,7 +341,8 @@ int JNL_HTTPGet::run() run_again: - static char buf[4096]; + static char main_buf[4096]; + char *buf = main_buf; m_con->run(); if (m_con->get_state()==JNL_Connection::STATE_ERROR) diff --git a/Contrib/NSISdl/nsisdl.cpp b/Contrib/NSISdl/nsisdl.cpp index 011047f3..e32d374d 100644 --- a/Contrib/NSISdl/nsisdl.cpp +++ b/Contrib/NSISdl/nsisdl.cpp @@ -23,8 +23,9 @@ Note: this source code is pretty hacked together right now, improvements and cleanups will come later. - */ + IMPORTANT: The dialog must have the style "No Parent Notify" + */ #include #include #include @@ -35,8 +36,6 @@ #include "httpget.h" #include "../exdll/exdll.h" -int g_timeout_ms=30000; - void *operator new( unsigned int num_bytes ) { return GlobalAlloc(GPTR,num_bytes); @@ -45,9 +44,12 @@ void operator delete( void *p ) { if (p) GlobalFree(p); } HANDLE hModule; -HWND g_dialog; HWND g_hwndProgressBar; -static int g_cancelled; +static int g_cancelled; +long lBusy; +ULONG idThreadOwner; +ULONG ulRefCount; +static void *lpWndProcOld; BOOL CALLBACK DownloadDialogProc(HWND hwndDlg, UINT uMsg, @@ -57,17 +59,65 @@ BOOL CALLBACK DownloadDialogProc(HWND hwndDlg, return 0; } +BOOL TryEnterCS() +{ + DWORD CurThreadID = ::GetCurrentThreadId(); + BOOL bRet = TRUE; + long *plBusy = &lBusy; + while (::InterlockedExchange(plBusy, 1) != 0) + { + Sleep(0); + } -static void *lpWndProcOld; + if (idThreadOwner == 0) + { + idThreadOwner = CurThreadID; + ulRefCount = 1; + } + else if (idThreadOwner == CurThreadID) + { + ulRefCount++; + } + else + { + bRet = FALSE; + } + + ::InterlockedExchange(plBusy, 0); + + return bRet; +} + +void LeaveCS() +{ + long *plBusy = &lBusy; + while (::InterlockedExchange(plBusy, 1) != 0) + { + Sleep(0); + } + if (idThreadOwner == ::GetCurrentThreadId()) + { + if (--ulRefCount == 0) + { + // No owner from now + idThreadOwner = 0; + } + } + ::InterlockedExchange(plBusy, 0); +} static LRESULT CALLBACK ParentWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { + LRESULT Res = 0; + while ( !TryEnterCS() ) Sleep(0); if (message == WM_COMMAND && LOWORD(wParam) == IDCANCEL) { g_cancelled = 1; - return 0; } - return CallWindowProc((long (__stdcall *)(struct HWND__ *,unsigned int,unsigned int,long))lpWndProcOld,hwnd,message,wParam,lParam); + 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, @@ -81,35 +131,27 @@ BOOL APIENTRY DllMain( HANDLE _hModule, static int g_file_size; - -static void progress_callback(char *msg, int read_bytes) +static DWORD g_dwLastTick = 0; +void progress_callback(HWND dlg, char *msg, int read_bytes) { // flicker reduction by A. Schiffler - static DWORD dwLastTick = 0; + DWORD dwLastTick = g_dwLastTick; DWORD dwThisTick = GetTickCount(); - if (g_dialog) + if (dlg) { if (dwThisTick - dwLastTick > 500) { - SetDlgItemText (g_dialog, IDC_STATIC2, msg); + SetDlgItemText (dlg, IDC_STATIC2, msg); dwLastTick = dwThisTick; } if (g_file_size) SendMessage(g_hwndProgressBar, PBM_SETPOS, (WPARAM)MulDiv(read_bytes,30000,g_file_size), 0); + g_dwLastTick = dwLastTick; } } extern char *_strstr(char *i, char *s); #define strstr _strstr -RECT r, cr; -void AdjustSize(int id) -{ - GetWindowRect(GetDlgItem(g_dialog,id),&cr); - ScreenToClient(g_dialog,(LPPOINT)&cr); - ScreenToClient(g_dialog,((LPPOINT)&cr)+1); - SetWindowPos(GetDlgItem(g_dialog,id),0,0,0,r.right-r.left,cr.bottom-cr.top,SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); -} - extern "C" { @@ -118,27 +160,31 @@ __declspec(dllexport) void download (HWND parent, char *variables, stack_t **stacktop) { - static char buf[1024]; - static char url[1024]; - static char filename[1024]; + char buf[1024]; + char url[1024]; + char filename[1024]; int wasen=0; + HWND hwndAux; HWND hwndL=0; HWND hwndB=0; HWND dlg=0; HWND childwnd=0; + BOOL bSuccess=FALSE; + RECT r, cr, orig_childRc; + int timeout_ms=30000; JNL_HTTPGet *get = 0; char *error=NULL; - static char szDownloading[32];//= "Downloading %s"; - static char szConnecting[32];//= "Connecting ..."; - static char szSecond[32];//= "second"; - static char szMinute[32];//= "minute"; - static char szHour[32];//= "hour"; - static char szPlural[32];//= "s"; - static char szProgress[128];//= "%dkB (%d%%) of %dkB @ %d.%01dkB/s"; - static char szRemaining[128];//= " (%d %s%s remaining)"; + char szDownloading[32];//= "Downloading %s"; + char szConnecting[32];//= "Connecting ..."; + char szSecond[32];//= "second"; + char szMinute[32];//= "minute"; + char szHour[32];//= "hour"; + char szPlural[32];//= "s"; + char szProgress[128];//= "%dkB (%d%%) of %dkB @ %d.%01dkB/s"; + char szRemaining[128];//= " (%d %s%s remaining)"; EXDLL_INIT(); @@ -166,7 +212,7 @@ __declspec(dllexport) void download (HWND parent, } lstrcpyn(buf, url, 10); if (!lstrcmpi(buf, "/TIMEOUT=")) { - g_timeout_ms=my_atoi(url+9); + timeout_ms=my_atoi(url+9); popstring(url); } popstring(filename); @@ -182,18 +228,18 @@ __declspec(dllexport) void download (HWND parent, childwnd=FindWindowEx(parent,NULL,"#32770",NULL); hwndL=GetDlgItem(childwnd,1016); hwndB=GetDlgItem(childwnd,1027); - if (hwndL && IsWindowVisible(hwndL)) ShowWindow(hwndL,SW_HIDE); + if ( IsWindowVisible(hwndL) ) ShowWindow(hwndL,SW_HIDE); else hwndL=NULL; - if (hwndB && IsWindowVisible(hwndB)) ShowWindow(hwndB,SW_HIDE); + if (IsWindowVisible(hwndB)) ShowWindow(hwndB,SW_HIDE); else hwndB=NULL; wasen=EnableWindow(GetDlgItem(parent,IDCANCEL),1); - lpWndProcOld = (void *) GetWindowLong(parent,GWL_WNDPROC); - SetWindowLong(parent,GWL_WNDPROC,(long)ParentWndProc); - dlg = g_dialog = CreateDialog((HINSTANCE)hModule, + lpWndProcOld = (void *)SetWindowLong(parent,GWL_WNDPROC,(long)ParentWndProc); + + dlg = CreateDialog((HINSTANCE)hModule, MAKEINTRESOURCE(IDD_DIALOG1), - childwnd, + parent, DownloadDialogProc); if (dlg) { @@ -221,16 +267,34 @@ __declspec(dllexport) void download (HWND parent, ShowWindow(pb, SW_SHOW); + GetWindowRect(childwnd,&orig_childRc); GetWindowRect(dlg,&cr); ScreenToClient(dlg,(LPPOINT)&cr); ScreenToClient(dlg,((LPPOINT)&cr)+1); - GetWindowRect(GetDlgItem(childwnd,1016),&r); + + hwndAux = GetDlgItem(childwnd,1016); + GetWindowRect(hwndAux,&r); ScreenToClient(childwnd,(LPPOINT)&r); ScreenToClient(childwnd,((LPPOINT)&r)+1); - SetWindowPos(dlg,0,r.left,r.top,r.right-r.left,cr.bottom-cr.top,SWP_NOACTIVATE|SWP_NOZORDER); - AdjustSize(IDC_STATIC2); - AdjustSize(pbid); - ShowWindow(dlg,SW_SHOWNA); + 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); + + hwndAux = GetDlgItem(dlg,IDC_STATIC2); + GetWindowRect(hwndAux,&cr); + ScreenToClient(dlg,(LPPOINT)&cr); + ScreenToClient(dlg,((LPPOINT)&cr)+1); + SetWindowPos(hwndAux,0,0,0,r.right-r.left,cr.bottom-cr.top,SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); + + hwndAux = GetDlgItem(dlg,pbid); + GetWindowRect(hwndAux,&cr); + ScreenToClient(dlg,(LPPOINT)&cr); + ScreenToClient(dlg,((LPPOINT)&cr)+1); + SetWindowPos(hwndAux,0,0,0,r.right-r.left,cr.bottom-cr.top,SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER); + char *p=filename; while (*p) p++; while (*p != '\\' && p != filename) p=CharPrev(filename,p); @@ -244,12 +308,12 @@ __declspec(dllexport) void download (HWND parent, SendDlgItemMessage(dlg, IDC_STATIC2, WM_SETFONT, hFont, 0); } } - { WSADATA wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); - static char buf[8192]=""; + static char main_buf[8192]; + char *buf=main_buf; char *p=NULL; HKEY hKey; @@ -293,46 +357,74 @@ __declspec(dllexport) void download (HWND parent, get->connect (url); while (1) { - if (dlg) + do { - MSG msg; - while (PeekMessage(&msg,dlg,0,0,PM_REMOVE)) + if ( dlg ) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + MSG msg; + while (PeekMessage(&msg,dlg,0,0,PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + while (!TryEnterCS()); // Process messages + if ((g_cancelled || error) && dlg) + DestroyWindow(dlg); + LeaveCS(); + + if ( g_cancelled || error ) + { + if (parent) + { + SetWindowLong(parent,GWL_WNDPROC,(long)lpWndProcOld); + + if (hwndB) ShowWindow(hwndB,SW_SHOWNA); + if (hwndL) ShowWindow(hwndL,SW_SHOWNA); + + SetWindowPos(childwnd,0,0,0,orig_childRc.right-orig_childRc.left,orig_childRc.bottom-orig_childRc.top,SWP_NOACTIVATE|SWP_NOZORDER|SWP_NOMOVE); + + if (wasen) EnableWindow(GetDlgItem(parent,IDCANCEL),0); + } + if ( !error ) + error = "cancel"; + break; } Sleep(25); - if (g_cancelled) break; - st = get->run (); if (st == -1) { error=get->geterrorstr(); - break; + //break; } else if (st == 1) { if (sofar < cl) error="download incomplete"; - break; + else + { + bSuccess=TRUE; + error = "success"; + } + //break; } else { if (get->get_status () == 0) { // progressFunc ("Connecting ...", 0); - if (last_recv_time+g_timeout_ms < GetTickCount()) + if (last_recv_time+timeout_ms < GetTickCount()) { error = "Timed out on connecting."; - break; + //break; } } else if (get->get_status () == 1) { - progress_callback("Reading headers", 0); - if (last_recv_time+g_timeout_ms < GetTickCount()) + progress_callback(dlg, "Reading headers", 0); + if (last_recv_time+timeout_ms < GetTickCount()) { error = "Timed out on getting headers."; - break; + //break; } } else if (get->get_status () == 2) { @@ -344,7 +436,7 @@ __declspec(dllexport) void download (HWND parent, cl = get->content_length (); if (cl == 0) { error = "Server did not specify content length."; - break; + //break; } else if (dlg) { SendMessage(g_hwndProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0,30000)); g_file_size=cl; @@ -386,23 +478,21 @@ __declspec(dllexport) void download (HWND parent, rtext, remain==1?"":szPlural ); - progress_callback(buf, sofar); + progress_callback(dlg, buf, sofar); } else { if (sofar < cl) error = "Server aborted."; - - break; + //break; } } - if (GetTickCount() > last_recv_time+g_timeout_ms) + if (GetTickCount() > last_recv_time+timeout_ms) { error = "Downloading timed out."; - break; + //break; } } else { error = "Bad response status."; - break; } } @@ -410,32 +500,16 @@ __declspec(dllexport) void download (HWND parent, WSACleanup(); } - + CloseHandle(hFile); - if (parent) - { - if (dlg) DestroyWindow(dlg); - if (lpWndProcOld) - SetWindowLong(parent,GWL_WNDPROC,(long)lpWndProcOld); - if (childwnd) - { - if (hwndB) ShowWindow(hwndB,SW_SHOWNA); - if (hwndL) ShowWindow(hwndL,SW_SHOWNA); - } - if (wasen) EnableWindow(GetDlgItem(parent,IDCANCEL),0); - } } - if (g_cancelled) { - pushstring("cancel"); + if (g_cancelled || !bSuccess) { DeleteFile(filename); - } else if (error == NULL) { - pushstring("success"); - } else { - DeleteFile(filename); - pushstring(error); } + pushstring(error); + if (get) delete get; } diff --git a/Plugins/nsisdl.dll b/Plugins/nsisdl.dll index 267e8c541a7050311869e78df760176c160d6baf..8c175faa6f5db62407be91ca82dc449fa4e6446d 100644 GIT binary patch delta 5446 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