[RAPPS] snapshot url support (#2925)

* [RAPPS] add snapshot url support
* [RAPPS] fix snapshot prev window won't update if size not changed
* [RAPPS] fix crash after cancel download
* [RAPPS] show broken-img if failed to create file / make network request
* [RAPPS] create a snapshots folder if it does not exist
* [RAPPS] add a helper function to perform callback
* [RAPPS] check bIsCancelled when handling ERROR_INVALID_HANDLE
* [RAPPS] remove W suffix for AsyncInetDownload
* [RAPPS] add assert for unknown event
* [RAPPS] Improve AsyncInetDownload error handling
* [RAPPS] move DisplayFailed to private, fix crash when cancelling
* [RAPPS] check for ERROR_INTERNET_OPERATION_CANCELLED
* [RAPPS] improve error logging
* [RAPPS] remove \r in error logging
* [RAPPS] rewrite AsyncInet
This commit is contained in:
He Yang 2020-06-21 18:26:41 +08:00 committed by Mark Jansen
parent fbf119fde1
commit e506581454
No known key found for this signature in database
GPG key ID: B39240EE84BEAE8B
6 changed files with 735 additions and 40 deletions

View file

@ -7,6 +7,7 @@ include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/cryptlib)
include_directories(include) include_directories(include)
list(APPEND SOURCE list(APPEND SOURCE
asyncinet.cpp
available.cpp available.cpp
cabinet.cpp cabinet.cpp
gui.cpp gui.cpp
@ -18,6 +19,7 @@ list(APPEND SOURCE
winmain.cpp winmain.cpp
unattended.cpp unattended.cpp
include/rapps.h include/rapps.h
include/asyncinet.h
include/available.h include/available.h
include/dialogs.h include/dialogs.h
include/installed.h include/installed.h

View file

@ -0,0 +1,453 @@
/*
* PROJECT: ReactOS Applications Manager
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: Async Internet operation using WinINet
* COPYRIGHT: Copyright 2020 He Yang (1160386205@qq.com)
*/
#include "rapps.h"
#include <wininet.h>
#include <atlbase.h>
#include "asyncinet.h"
BOOL AsyncInetIsCanceled(pASYNCINET AsyncInet);
BOOL AsyncInetAcquire(pASYNCINET AsyncInet);
VOID AsyncInetRelease(pASYNCINET AsyncInet);
int AsyncInetPerformCallback(pASYNCINET AsyncInet,
ASYNC_EVENT Event,
WPARAM wParam,
LPARAM lParam
);
VOID CALLBACK AsyncInetStatusCallback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
);
VOID AsyncInetReadFileLoop(pASYNCINET AsyncInet);
BOOL AsyncInetCleanUp(pASYNCINET AsyncInet);
VOID AsyncInetFree(pASYNCINET AsyncInet);
pASYNCINET AsyncInetDownload(LPCWSTR lpszAgent,
DWORD dwAccessType,
LPCWSTR lpszProxy,
LPCWSTR lpszProxyBypass,
LPCWSTR lpszUrl,
BOOL bAllowCache,
ASYNCINET_CALLBACK Callback,
VOID* Extension
) // allocate memory for AsyncInet and start a download task
{
pASYNCINET AsyncInet = NULL;
BOOL bSuccess = FALSE;
DWORD InetOpenUrlFlag = 0;
INTERNET_STATUS_CALLBACK OldCallbackFunc;
AsyncInet = (pASYNCINET)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(ASYNCINET));
if (!AsyncInet)
{
OutputDebugStringA("At File: " __FILE__ " HeapAlloc returned 0\n");
return 0;
}
AsyncInet->bCancelled = FALSE;
AsyncInet->hInternet = NULL;
AsyncInet->hInetFile = NULL;
AsyncInet->Callback = Callback;
AsyncInet->Extension = Extension;
AsyncInet->hEventHandleCreated = CreateEvent(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&(AsyncInet->CriticalSection));
AsyncInet->ReferenceCnt = 1; // 1 for callee itself
AsyncInet->hEventHandleClose = CreateEvent(NULL, FALSE, FALSE, NULL);
if (AsyncInet->hEventHandleCreated && AsyncInet->hEventHandleClose)
{
AsyncInet->hInternet = InternetOpenW(lpszAgent, dwAccessType, lpszProxy, lpszProxyBypass, INTERNET_FLAG_ASYNC);
if (AsyncInet->hInternet)
{
OldCallbackFunc = InternetSetStatusCallbackW(AsyncInet->hInternet, AsyncInetStatusCallback);
if (OldCallbackFunc != INTERNET_INVALID_STATUS_CALLBACK)
{
InetOpenUrlFlag |= INTERNET_FLAG_PASSIVE | INTERNET_FLAG_RESYNCHRONIZE;
if (!bAllowCache)
{
InetOpenUrlFlag |= INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD;
}
AsyncInet->hInetFile = InternetOpenUrlW(AsyncInet->hInternet, lpszUrl, 0, 0, InetOpenUrlFlag, (DWORD_PTR)AsyncInet);
if (AsyncInet->hInetFile)
{
// operate complete synchronously
bSuccess = TRUE;
AsyncInetReadFileLoop(AsyncInet);
}
else
{
if (GetLastError() == ERROR_IO_PENDING)
{
// everything fine. waiting for handle created
switch (WaitForSingleObject(AsyncInet->hEventHandleCreated, INFINITE))
{
case WAIT_OBJECT_0:
if (AsyncInet->hInetFile)
{
bSuccess = TRUE;
}
}
}
}
}
}
}
if (!bSuccess)
{
AsyncInetFree(AsyncInet);
AsyncInet = NULL;
}
if (AsyncInet)
{
// add reference count for caller.
// the caller is responsible for call AsyncInetRelease when no longer using it.
AsyncInetAcquire(AsyncInet);
}
return AsyncInet;
}
BOOL AsyncInetCancel(pASYNCINET AsyncInet) // mark as cancelled (this will send a cancel notificaion at last) and do graceful cleanup.
{
if (AsyncInet)
{
HINTERNET hInetFile;
EnterCriticalSection(&(AsyncInet->CriticalSection));
AsyncInet->bCancelled = TRUE;
hInetFile = AsyncInet->hInetFile;
AsyncInet->hInetFile = NULL;
LeaveCriticalSection(&(AsyncInet->CriticalSection));
if (hInetFile)
{
InternetCloseHandle(hInetFile);
return TRUE;
}
}
return FALSE;
}
BOOL AsyncInetIsCanceled(pASYNCINET AsyncInet) // if returned TRUE, no operation should be exectued further
{
if (AsyncInet)
{
EnterCriticalSection(&(AsyncInet->CriticalSection));
if (AsyncInet->bCancelled)
{
LeaveCriticalSection(&(AsyncInet->CriticalSection));
return TRUE;
}
LeaveCriticalSection(&(AsyncInet->CriticalSection));
}
return FALSE;
}
BOOL AsyncInetAcquire(pASYNCINET AsyncInet) // try to increase refcnt by 1. if returned FALSE, AsyncInet should not be used anymore
{
BOOL bResult = FALSE;
if (AsyncInet)
{
EnterCriticalSection(&(AsyncInet->CriticalSection));
ATLASSERT(AsyncInet->ReferenceCnt > 0);
if (!AsyncInetIsCanceled(AsyncInet))
{
AsyncInet->ReferenceCnt++;
bResult = TRUE;
}
// otherwise (AsyncInet->bCleanUp == TRUE)
// AsyncInetAcquire will return FALSE.
// In this case, any thread should no longer use this AsyncInet
LeaveCriticalSection(&(AsyncInet->CriticalSection));
}
return bResult;
}
VOID AsyncInetRelease(pASYNCINET AsyncInet) // try to decrease refcnt by 1
{
BOOL bCleanUp = FALSE;
if (AsyncInet)
{
EnterCriticalSection(&(AsyncInet->CriticalSection));
ATLASSERT(AsyncInet->ReferenceCnt);
AsyncInet->ReferenceCnt--;
if (AsyncInet->ReferenceCnt == 0)
{
bCleanUp = TRUE;
}
LeaveCriticalSection(&(AsyncInet->CriticalSection));
if (bCleanUp)
{
AsyncInetCleanUp(AsyncInet);
}
}
}
int AsyncInetPerformCallback(pASYNCINET AsyncInet,
ASYNC_EVENT Event,
WPARAM wParam,
LPARAM lParam
)
{
if (AsyncInet && AsyncInet->Callback)
{
return AsyncInet->Callback(AsyncInet, Event, wParam, lParam, AsyncInet->Extension);
}
return 0;
}
VOID CALLBACK AsyncInetStatusCallback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
)
{
pASYNCINET AsyncInet = (pASYNCINET)dwContext;
switch (dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CREATED:
{
// retrieve handle created by InternetOpenUrlW
AsyncInet->hInetFile = *((LPHINTERNET)lpvStatusInformation);
SetEvent(AsyncInet->hEventHandleCreated);
break;
}
case INTERNET_STATUS_HANDLE_CLOSING:
{
if (AsyncInetIsCanceled(AsyncInet))
{
AsyncInetPerformCallback(AsyncInet, ASYNCINET_CANCELLED, 0, 0);
}
SetEvent(AsyncInet->hEventHandleClose);
break;
}
case INTERNET_STATUS_REQUEST_COMPLETE:
{
INTERNET_ASYNC_RESULT* AsyncResult = (INTERNET_ASYNC_RESULT*)lpvStatusInformation;
if (!AsyncInet->hInetFile)
{
if (!AsyncInetIsCanceled(AsyncInet))
{
// some error occurs during InternetOpenUrl
// and INTERNET_STATUS_HANDLE_CREATED is skipped
SetEvent(AsyncInet->hEventHandleCreated);
break;
}
}
switch (AsyncResult->dwError)
{
case ERROR_SUCCESS:
{
// there are two possibilities:
// 1: InternetOpenUrlW (async mode) complete. (this should be only for the first time)
// 2: InternetReadFile (async mode) complete.
// so I use bIsOpenUrlComplete field in ASYNCINET to indicate this.
if (!(AsyncInet->bIsOpenUrlComplete)) // InternetOpenUrlW completed
{
AsyncInet->bIsOpenUrlComplete = TRUE;
}
else // asynchronous InternetReadFile complete
{
AsyncInetPerformCallback(AsyncInet, ASYNCINET_DATA, (WPARAM)(AsyncInet->ReadBuffer), (LPARAM)(AsyncInet->BytesRead));
}
AsyncInetReadFileLoop(AsyncInet);
}
break;
case ERROR_INVALID_HANDLE:
case ERROR_INTERNET_OPERATION_CANCELLED:
case ERROR_CANCELLED:
if (AsyncInetIsCanceled(AsyncInet))
{
AsyncInetRelease(AsyncInet);
break;
}
// fall down
default:
// something went wrong
if (AsyncInetIsCanceled(AsyncInet))
{
// sending both ASYNCINET_ERROR and ASYNCINET_CANCELLED may lead to unpredictable behavior
// TODO: log the error
AsyncInetRelease(AsyncInet);
}
else
{
AsyncInetPerformCallback(AsyncInet, ASYNCINET_ERROR, 0, (LPARAM)(AsyncResult->dwError));
AsyncInetRelease(AsyncInet);
}
break;
}
break;
}
}
return;
}
VOID AsyncInetReadFileLoop(pASYNCINET AsyncInet)
{
if ((!AsyncInet) || (!AsyncInet->hInetFile))
{
return;
}
while (1)
{
if (AsyncInetIsCanceled(AsyncInet))
{
// abort now.
AsyncInetRelease(AsyncInet);
break;
}
BOOL bRet = InternetReadFile(AsyncInet->hInetFile,
AsyncInet->ReadBuffer,
_countof(AsyncInet->ReadBuffer),
&(AsyncInet->BytesRead));
if (bRet)
{
if (AsyncInet->BytesRead == 0)
{
// everything read. now complete.
AsyncInetPerformCallback(AsyncInet, ASYNCINET_COMPLETE, 0, 0);
AsyncInetRelease(AsyncInet);
break;
}
else
{
// read completed immediately.
AsyncInetPerformCallback(AsyncInet, ASYNCINET_DATA, (WPARAM)(AsyncInet->ReadBuffer), (LPARAM)(AsyncInet->BytesRead));
}
}
else
{
DWORD dwError;
if ((dwError = GetLastError()) == ERROR_IO_PENDING)
{
// performing asynchronous IO, everything OK.
break;
}
else
{
if (dwError == ERROR_INVALID_HANDLE ||
dwError == ERROR_INTERNET_OPERATION_CANCELLED ||
dwError == ERROR_CANCELLED)
{
if (AsyncInetIsCanceled(AsyncInet))
{
// not an error. just normally cancelling
AsyncInetRelease(AsyncInet);
break;
}
}
if (!AsyncInetIsCanceled(AsyncInet)) // can not send both ASYNCINET_ERROR and ASYNCINET_CANCELLED
{
AsyncInetPerformCallback(AsyncInet, ASYNCINET_ERROR, 0, dwError);
}
else
{
// TODO: log the error
}
AsyncInetRelease(AsyncInet);
break;
}
}
}
return;
}
BOOL AsyncInetCleanUp(pASYNCINET AsyncInet) // close all handle and clean up
{
if (AsyncInet)
{
ATLASSERT(AsyncInet->ReferenceCnt == 0);
// close the handle, waiting for all pending request cancelled.
if (AsyncInet->bCancelled) // already closed
{
AsyncInet->hInetFile = NULL;
}
else if (AsyncInet->hInetFile)
{
InternetCloseHandle(AsyncInet->hInetFile);
AsyncInet->hInetFile = NULL;
}
// only cleanup when handle closed notification received
switch (WaitForSingleObject(AsyncInet->hEventHandleClose, INFINITE))
{
case WAIT_OBJECT_0:
{
AsyncInetFree(AsyncInet); // now safe to free the structure
return TRUE;
}
default:
ATLASSERT(FALSE);
AsyncInetFree(AsyncInet);
return FALSE;
}
}
else
{
return FALSE;
}
}
VOID AsyncInetFree(pASYNCINET AsyncInet) // close all handles, free the memory occupied by AsyncInet
{
if (AsyncInet)
{
DeleteCriticalSection(&(AsyncInet->CriticalSection));
if (AsyncInet->hEventHandleCreated)
{
CloseHandle(AsyncInet->hEventHandleCreated);
AsyncInet->hEventHandleCreated = NULL;
}
if (AsyncInet->hEventHandleClose)
{
CloseHandle(AsyncInet->hEventHandleClose);
AsyncInet->hEventHandleClose = NULL;
}
if (AsyncInet->hInternet)
{
InternetCloseHandle(AsyncInet->hInternet);
AsyncInet->hInternet = NULL;
}
if (AsyncInet->hInetFile)
{
InternetCloseHandle(AsyncInet->hInetFile);
AsyncInet->hInetFile = NULL;
}
HeapFree(GetProcessHeap(), 0, AsyncInet);
}
}

View file

@ -59,22 +59,28 @@ VOID CAvailableApplicationInfo::RetrieveGeneralInfo(AvailableStrings& AvlbString
{ {
WCHAR SnapshotField[sizeof("Snapshot") + 4]; WCHAR SnapshotField[sizeof("Snapshot") + 4];
wsprintfW(SnapshotField, L"Snapshot%d", i + 1); wsprintfW(SnapshotField, L"Snapshot%d", i + 1);
ATL::CStringW SnapshotFileName; ATL::CStringW SnapshotLocation;
if (!GetString(SnapshotField, SnapshotFileName)) if (!GetString(SnapshotField, SnapshotLocation))
{ {
continue; continue;
} }
// TODO: Add URL Support
// TODO: Does the filename contain anything stuff like "\\" ".." ":" "<" ">" ? if (PathIsURLW(SnapshotLocation.GetString()))
// these stuff may lead to security issues {
m_szSnapshotLocation.Add(SnapshotLocation);
}
else
{
// TODO: Does the filename contain anything stuff like "\\" ".." ":" "<" ">" ?
// these stuff may lead to security issues
ATL::CStringW SnapshotName = AvlbStrings.szAppsPath; ATL::CStringW SnapshotName = AvlbStrings.szAppsPath;
PathAppendW(SnapshotName.GetBuffer(MAX_PATH), L"snapshots"); PathAppendW(SnapshotName.GetBuffer(MAX_PATH), L"snapshots");
PathAppendW(SnapshotName.GetBuffer(), SnapshotFileName.GetString()); PathAppendW(SnapshotName.GetBuffer(), SnapshotLocation.GetString());
SnapshotName.ReleaseBuffer(); SnapshotName.ReleaseBuffer();
m_szSnapshotFilename.Add(SnapshotName); m_szSnapshotLocation.Add(SnapshotName);
}
} }
RetrieveSize(); RetrieveSize();
@ -232,11 +238,11 @@ BOOL CAvailableApplicationInfo::HasUpdate() const
BOOL CAvailableApplicationInfo::RetrieveSnapshot(UINT Index,ATL::CStringW& SnapshotFileName) const BOOL CAvailableApplicationInfo::RetrieveSnapshot(UINT Index,ATL::CStringW& SnapshotFileName) const
{ {
if (Index >= (UINT)m_szSnapshotFilename.GetSize()) if (Index >= (UINT)m_szSnapshotLocation.GetSize())
{ {
return FALSE; return FALSE;
} }
SnapshotFileName = m_szSnapshotFilename[Index]; SnapshotFileName = m_szSnapshotLocation[Index];
return TRUE; return TRUE;
} }

View file

@ -11,6 +11,7 @@
#include "rapps.h" #include "rapps.h"
#include "rosui.h" #include "rosui.h"
#include "crichedit.h" #include "crichedit.h"
#include "asyncinet.h"
#include <shlobj_undoc.h> #include <shlobj_undoc.h>
#include <shlguid_undoc.h> #include <shlguid_undoc.h>
@ -43,11 +44,16 @@ using namespace Gdiplus;
// minimum width of richedit // minimum width of richedit
#define RICHEDIT_MIN_WIDTH 160 #define RICHEDIT_MIN_WIDTH 160
// user-defined window message
#define WM_RAPPS_DOWNLOAD_COMPLETE (WM_USER + 1) // notify download complete. wParam is error code, and lParam is a pointer to SnapshotDownloadParam
#define WM_RAPPS_RESIZE_CHILDREN (WM_USER + 2) // ask parent window to resize children.
enum SNPSHT_STATUS enum SNPSHT_STATUS
{ {
SNPSHTPREV_EMPTY, // show nothing SNPSHTPREV_EMPTY, // show nothing
SNPSHTPREV_LOADING, // image is loading (most likely downloading) SNPSHTPREV_LOADING, // image is loading (most likely downloading)
SNPSHTPREV_FILE, // display image from a file SNPSHTPREV_IMAGE, // display image from a file
SNPSHTPREV_FAILED // image can not be shown (download failure or wrong image) SNPSHTPREV_FAILED // image can not be shown (download failure or wrong image)
}; };
@ -59,6 +65,14 @@ enum SNPSHT_STATUS
#define PI 3.1415927 #define PI 3.1415927
typedef struct __SnapshotDownloadParam
{
LONGLONG ID;
HANDLE hFile;
HWND hwndNotify;
ATL::CStringW DownloadFileName;
} SnapshotDownloadParam;
INT GetSystemColorDepth() INT GetSystemColorDepth()
{ {
DEVMODEW pDevMode; DEVMODEW pDevMode;
@ -290,6 +304,40 @@ public:
} }
}; };
int SnapshotDownloadCallback(
pASYNCINET AsyncInet,
ASYNC_EVENT Event,
WPARAM wParam,
LPARAM lParam,
VOID* Extension
)
{
SnapshotDownloadParam* DownloadParam = (SnapshotDownloadParam*)Extension;
switch (Event)
{
case ASYNCINET_DATA:
DWORD BytesWritten;
WriteFile(DownloadParam->hFile, (LPCVOID)wParam, (DWORD)lParam, &BytesWritten, NULL);
break;
case ASYNCINET_COMPLETE:
CloseHandle(DownloadParam->hFile);
SendMessage(DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, (WPARAM)ERROR_SUCCESS, (LPARAM)DownloadParam);
break;
case ASYNCINET_CANCELLED:
CloseHandle(DownloadParam->hFile);
SendMessage(DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, (WPARAM)ERROR_CANCELLED, (LPARAM)DownloadParam);
break;
case ASYNCINET_ERROR:
CloseHandle(DownloadParam->hFile);
SendMessage(DownloadParam->hwndNotify, WM_RAPPS_DOWNLOAD_COMPLETE, wParam, (LPARAM)DownloadParam);
break;
default:
ATLASSERT(FALSE);
break;
}
return 0;
}
class CAppSnapshotPreview : class CAppSnapshotPreview :
public CWindowImpl<CAppSnapshotPreview> public CWindowImpl<CAppSnapshotPreview>
{ {
@ -301,6 +349,9 @@ private:
BOOL bLoadingTimerOn = FALSE; BOOL bLoadingTimerOn = FALSE;
int LoadingAnimationFrame = 0; int LoadingAnimationFrame = 0;
int BrokenImgSize = BROKENIMG_ICON_SIZE; int BrokenImgSize = BROKENIMG_ICON_SIZE;
pASYNCINET AsyncInet = NULL;
LONGLONG ContentID = 0; // used to determine whether image has been switched when download complete. Increase by 1 each time the content of this window changed
ATL::CStringW TempImagePath; // currently displayed temp file
BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId) BOOL ProcessWindowMessage(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT& theResult, DWORD dwMapId)
{ {
@ -324,6 +375,42 @@ private:
} }
break; break;
} }
case WM_RAPPS_DOWNLOAD_COMPLETE:
{
SnapshotDownloadParam* DownloadParam = (SnapshotDownloadParam*)lParam;
AsyncInetRelease(AsyncInet);
AsyncInet = NULL;
switch (wParam)
{
case ERROR_SUCCESS:
if (ContentID == DownloadParam->ID)
{
DisplayFile(DownloadParam->DownloadFileName);
// send a message to trigger resizing
::SendMessageW(::GetParent(m_hWnd), WM_RAPPS_RESIZE_CHILDREN, 0, 0);
InvalidateRect(0, 0);
TempImagePath = DownloadParam->DownloadFileName; // record tmp file path in order to delete it when cleanup
}
else
{
// the picture downloaded is already outdated. delete it.
DeleteFileW(DownloadParam->DownloadFileName);
}
break;
case ERROR_CANCELLED:
DeleteFileW(DownloadParam->DownloadFileName);
break;
default:
DisplayFailed();
// send a message to trigger resizing
::SendMessageW(::GetParent(m_hWnd), WM_RAPPS_RESIZE_CHILDREN, 0, 0);
InvalidateRect(0, 0);
DeleteFileW(DownloadParam->DownloadFileName);
break;
}
delete DownloadParam;
break;
}
case WM_PAINT: case WM_PAINT:
{ {
PAINTSTRUCT ps; PAINTSTRUCT ps;
@ -388,6 +475,38 @@ private:
return FALSE; return FALSE;
} }
VOID DisplayLoading()
{
SetStatus(SNPSHTPREV_LOADING);
if (bLoadingTimerOn)
{
KillTimer(TIMER_LOADING_ANIMATION);
}
LoadingAnimationFrame = 0;
bLoadingTimerOn = TRUE;
SetTimer(TIMER_LOADING_ANIMATION, 1000 / LOADING_ANIMATION_FPS, 0);
}
VOID DisplayFailed()
{
InterlockedIncrement64(&ContentID);
SetStatus(SNPSHTPREV_FAILED);
PreviousDisplayCleanup();
}
BOOL DisplayFile(LPCWSTR lpszFileName)
{
PreviousDisplayCleanup();
SetStatus(SNPSHTPREV_IMAGE);
pImage = Bitmap::FromFile(lpszFileName, 0);
if (pImage->GetLastStatus() != Ok)
{
DisplayFailed();
return FALSE;
}
return TRUE;
}
VOID SetStatus(SNPSHT_STATUS Status) VOID SetStatus(SNPSHT_STATUS Status)
{ {
SnpshtPrevStauts = Status; SnpshtPrevStauts = Status;
@ -445,7 +564,7 @@ private:
} }
break; break;
case SNPSHTPREV_FILE: case SNPSHTPREV_IMAGE:
{ {
if (pImage) if (pImage)
{ {
@ -541,39 +660,82 @@ public:
delete pImage; delete pImage;
pImage = NULL; pImage = NULL;
} }
if (AsyncInet)
{
AsyncInetCancel(AsyncInet);
}
if (!TempImagePath.IsEmpty())
{
DeleteFileW(TempImagePath.GetString());
TempImagePath.Empty();
}
} }
VOID DisplayEmpty() VOID DisplayEmpty()
{ {
InterlockedIncrement64(&ContentID);
SetStatus(SNPSHTPREV_EMPTY); SetStatus(SNPSHTPREV_EMPTY);
PreviousDisplayCleanup(); PreviousDisplayCleanup();
} }
VOID DisplayLoading() BOOL DisplayImage(LPCWSTR lpszLocation)
{ {
SetStatus(SNPSHTPREV_LOADING); LONGLONG ID = InterlockedIncrement64(&ContentID);
PreviousDisplayCleanup(); PreviousDisplayCleanup();
bLoadingTimerOn = TRUE;
SetTimer(TIMER_LOADING_ANIMATION, 1000 / LOADING_ANIMATION_FPS, 0);
}
BOOL DisplayFile(LPCWSTR lpszFileName) if (PathIsURLW(lpszLocation))
{
SetStatus(SNPSHTPREV_FILE);
PreviousDisplayCleanup();
pImage = Bitmap::FromFile(lpszFileName, 0);
if (pImage->GetLastStatus() != Ok)
{ {
DisplayFailed(); DisplayLoading();
return FALSE;
}
return TRUE;
}
VOID DisplayFailed() SnapshotDownloadParam* DownloadParam = new SnapshotDownloadParam;
{ if (!DownloadParam) return FALSE;
SetStatus(SNPSHTPREV_FAILED);
PreviousDisplayCleanup(); DownloadParam->hwndNotify = m_hWnd;
DownloadParam->ID = ID;
// generate a filename
ATL::CStringW SnapshotFolder = CAvailableApps::m_Strings.szAppsPath;
PathAppendW(SnapshotFolder.GetBuffer(MAX_PATH), L"snapshots");
SnapshotFolder.ReleaseBuffer();
if (!PathIsDirectoryW(SnapshotFolder.GetString()))
{
CreateDirectoryW(SnapshotFolder.GetString(), NULL);
}
if (!GetTempFileNameW(SnapshotFolder.GetString(), L"img",
0, DownloadParam->DownloadFileName.GetBuffer(MAX_PATH)))
{
DownloadParam->DownloadFileName.ReleaseBuffer();
delete DownloadParam;
DisplayFailed();
return FALSE;
}
DownloadParam->DownloadFileName.ReleaseBuffer();
DownloadParam->hFile = CreateFileW(DownloadParam->DownloadFileName.GetString(),
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (DownloadParam->hFile == INVALID_HANDLE_VALUE)
{
delete DownloadParam;
DisplayFailed();
return FALSE;
}
AsyncInet = AsyncInetDownload(0, INTERNET_OPEN_TYPE_PRECONFIG, 0, 0, lpszLocation, TRUE, SnapshotDownloadCallback, DownloadParam);
if (!AsyncInet)
{
CloseHandle(DownloadParam->hFile);
DeleteFileW(DownloadParam->DownloadFileName.GetBuffer());
delete DownloadParam;
DisplayFailed();
return FALSE;
}
return TRUE;
}
else
{
return DisplayFile(lpszLocation);
}
} }
int GetRequestedWidth(int Height) // calculate requested window width by given height int GetRequestedWidth(int Height) // calculate requested window width by given height
@ -584,7 +746,7 @@ public:
return 0; return 0;
case SNPSHTPREV_LOADING: case SNPSHTPREV_LOADING:
return 200; return 200;
case SNPSHTPREV_FILE: case SNPSHTPREV_IMAGE:
if (pImage) if (pImage)
{ {
// return the width needed to display image inside the window. // return the width needed to display image inside the window.
@ -631,6 +793,11 @@ private:
ResizeChildren(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); ResizeChildren(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break; break;
} }
case WM_RAPPS_RESIZE_CHILDREN:
{
ResizeChildren();
break;
}
case WM_COMMAND: case WM_COMMAND:
{ {
OnCommand(wParam, lParam); OnCommand(wParam, lParam);
@ -777,10 +944,10 @@ public:
BOOL ShowAvailableAppInfo(CAvailableApplicationInfo* Info) BOOL ShowAvailableAppInfo(CAvailableApplicationInfo* Info)
{ {
ATL::CStringW SnapshotFilename; ATL::CStringW SnapshotLocation;
if (Info->RetrieveSnapshot(0, SnapshotFilename)) if (Info->RetrieveSnapshot(0, SnapshotLocation))
{ {
SnpshtPrev->DisplayFile(SnapshotFilename); SnpshtPrev->DisplayImage(SnapshotLocation);
} }
else else
{ {

View file

@ -0,0 +1,66 @@
#ifndef ASYNC_INET
#define ASYNC_INET
enum ASYNC_EVENT
{
ASYNCINET_DATA, // wParam is the Data retrieved from the internet, lParam is the length of Data
ASYNCINET_COMPLETE, // wParam and lParam are not used.
// when receiving this, AsyncInet will be free soon and should not used anymore
ASYNCINET_CANCELLED, // wParam and lParam are not used.
// when receiving this, AsyncInet will be free soon and should not used anymore
ASYNCINET_ERROR // wParam is not used. lParam specify the error code (if there is one).
// when receiving this, AsyncInet will be free soon and should not used anymore
};
typedef struct __AsyncInet ASYNCINET, * pASYNCINET;
typedef int
(*ASYNCINET_CALLBACK)(
pASYNCINET AsyncInet,
ASYNC_EVENT Event,
WPARAM wParam,
LPARAM lParam,
VOID* Extension
);
typedef struct __AsyncInet
{
HINTERNET hInternet;
HINTERNET hInetFile;
HANDLE hEventHandleCreated;
UINT ReferenceCnt;
CRITICAL_SECTION CriticalSection;
HANDLE hEventHandleClose;
BOOL bIsOpenUrlComplete;
BOOL bCancelled;
BYTE ReadBuffer[4096];
DWORD BytesRead;
ASYNCINET_CALLBACK Callback;
VOID* Extension;
} ASYNCINET, * pASYNCINET;
pASYNCINET AsyncInetDownload(LPCWSTR lpszAgent,
DWORD dwAccessType,
LPCWSTR lpszProxy,
LPCWSTR lpszProxyBypass,
LPCWSTR lpszUrl,
BOOL bAllowCache,
ASYNCINET_CALLBACK Callback,
VOID* Extension
);
BOOL AsyncInetCancel(pASYNCINET AsyncInet);
VOID AsyncInetRelease(pASYNCINET AsyncInet);
#endif

View file

@ -51,7 +51,7 @@ struct CAvailableApplicationInfo
ATL::CStringW m_szUrlSite; ATL::CStringW m_szUrlSite;
ATL::CStringW m_szUrlDownload; ATL::CStringW m_szUrlDownload;
ATL::CSimpleArray<LCID> m_LanguageLCIDs; ATL::CSimpleArray<LCID> m_LanguageLCIDs;
ATL::CSimpleArray<ATL::CStringW> m_szSnapshotFilename; ATL::CSimpleArray<ATL::CStringW> m_szSnapshotLocation;
ULONG m_SizeBytes; ULONG m_SizeBytes;
@ -100,10 +100,11 @@ typedef BOOL(CALLBACK *AVAILENUMPROC)(CAvailableApplicationInfo *Info, LPCWSTR s
class CAvailableApps class CAvailableApps
{ {
static AvailableStrings m_Strings;
ATL::CAtlList<CAvailableApplicationInfo*> m_InfoList; ATL::CAtlList<CAvailableApplicationInfo*> m_InfoList;
public: public:
static AvailableStrings m_Strings;
CAvailableApps(); CAvailableApps();
static BOOL UpdateAppsDB(); static BOOL UpdateAppsDB();