📄 sumatrapdf.cpp.svn-base
字号:
/* Reverse of FileTimeToStr: convert string <s> to <ft>. */void StrToFileTime(char *s, FILETIME* ft){ hexstr_to_mem(s, (unsigned char*)ft, sizeof(*ft));}/* Get current UTS cystem time as string. Caller needs to free() the result. */char *GetSystemTimeAsStr(){ FILETIME ft; GetSystemTimeAsFileTime(&ft); return FileTimeToStr(&ft);}static void FileTimeToLargeInteger(FILETIME *ft, LARGE_INTEGER *lt){ lt->LowPart = ft->dwLowDateTime; lt->HighPart = ft->dwHighDateTime;}/* Return <ft1> - <ft2> in seconds */DWORD FileTimeDiffInSecs(FILETIME *ft1, FILETIME *ft2){ LARGE_INTEGER t1; LARGE_INTEGER t2; FileTimeToLargeInteger(ft1, &t1); FileTimeToLargeInteger(ft2, &t2); // diff is in 100 nanoseconds LONGLONG diff = t1.QuadPart - t2.QuadPart; diff = diff / (LONGLONG)10000000L; return (DWORD)diff;}class MemSegment {private: class MemSegment *next;public: MemSegment(const void *buf, DWORD size) { next = NULL; data = NULL; add(buf, size); }; MemSegment() { next = NULL; data = NULL; } bool add(const void *buf, DWORD size) { assert(size > 0); if (!data) { dataSize = size; data = malloc(size); if (!data) return false; memcpy(data, buf, size); } else { MemSegment *ms = new MemSegment(buf, size); if (!ms) return false; if (!ms->data) { delete ms; return false; } ms->next = next; next = ms; } return true; } void freeAll() { free(data); data = NULL; // clever trick: each segment will delete the next segment if (next) { delete next; next = NULL; } } ~MemSegment() { freeAll(); } void *getData(DWORD *sizeOut); void *data; DWORD dataSize;};void *MemSegment::getData(DWORD *sizeOut){ DWORD totalSize = dataSize; MemSegment *curr = next; while (curr) { totalSize += curr->dataSize; curr = curr->next; } if (0 == dataSize) return NULL; char *buf = (char*)malloc(totalSize + 1); // +1 for 0 termination if (!buf) return NULL; buf[totalSize] = 0; // the chunks are linked in reverse order, so we must reassemble them properly char *end = buf + totalSize; curr = next; while (curr) { end -= curr->dataSize; memcpy(end, curr->data, curr->dataSize); curr = curr->next; } end -= dataSize; memcpy(end, data, dataSize); assert(end == buf); *sizeOut = totalSize; return (void*)buf;}#ifdef DEBUGvoid u_hexstr(){ unsigned char buf[6] = {1, 2, 33, 255, 0, 18}; unsigned char buf2[6] = {0}; char *s = mem_to_hexstr(buf, sizeof(buf)); BOOL ok = hexstr_to_mem(s, buf2, sizeof(buf2)); assert(ok); for (int i=0; i<sizeof(buf); i++) { assert(buf[i] == buf2[i]); } free(s); FILETIME ft1, ft2; GetSystemTimeAsFileTime(&ft1); s = FileTimeToStr(&ft1); StrToFileTime(s, &ft2); DWORD diff = FileTimeDiffInSecs(&ft1, &ft2); assert(0 == diff); assert(ft1.dwLowDateTime == ft2.dwLowDateTime); assert(ft1.dwHighDateTime == ft2.dwHighDateTime); free(s);}void u_testMemSegment(){ MemSegment *ms; DWORD size; char *data; char buf[2] = {'a', '\0'}; ms = new MemSegment(); for (int i=0; i<7; i++) { ms->add(buf, 1); buf[0] = buf[0] + 1; } data = (char*)ms->getData(&size); delete ms; assert(str_eq("abcdefg", data)); assert(7 == size); free(data); ms = new MemSegment("a", 1); data = (char*)ms->getData(&size); ms->freeAll(); delete ms; assert(str_eq("a", data)); assert(1 == size); free(data);}#endif// based on information in http://www.codeproject.com/KB/IP/asyncwininet.aspxclass HttpReqCtx {public: // the window to which we'll send notification about completed download HWND hwndToNotify; // message to send when download is complete UINT msg; // handle for connection during request processing HINTERNET httpFile; char * url; MemSegment data; /* true for automated check, false for check triggered from menu */ bool autoCheck; HttpReqCtx(char *_url, HWND _hwnd, UINT _msg) { assert(_url); hwndToNotify = _hwnd; url = strdup(_url); msg = _msg; autoCheck = false; httpFile = 0; } ~HttpReqCtx() { free(url); data.freeAll(); }};void __stdcall InternetCallbackProc(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID statusInfo, DWORD statusLen){ char buf[256]; INTERNET_ASYNC_RESULT* res; HttpReqCtx *ctx = (HttpReqCtx*)dwContext; switch (dwInternetStatus) { case INTERNET_STATUS_HANDLE_CREATED: res = (INTERNET_ASYNC_RESULT*)statusInfo; ctx->httpFile = (HINTERNET)(res->dwResult); _snprintf(buf, 256, "HANDLE_CREATED (%d)", statusLen ); break; case INTERNET_STATUS_REQUEST_COMPLETE: { // Check for errors. if (LPINTERNET_ASYNC_RESULT(statusInfo)->dwError != 0) { _snprintf(buf, 256, "REQUEST_COMPLETE (%d) Error (%d) encountered", statusLen, GetLastError()); break; } // Set the resource handle to the HINTERNET handle returned in the callback. HINTERNET hInt = HINTERNET(LPINTERNET_ASYNC_RESULT(statusInfo)->dwResult); assert(hInt == ctx->httpFile); _snprintf(buf, 256, "REQUEST_COMPLETE (%d)", statusLen); INTERNET_BUFFERSA ib = {0}; ib.dwStructSize = sizeof(ib); ib.lpvBuffer = malloc(1024); // This is not exactly async, but we're assuming it'll complete quickly // because the update file is small and we now that connection is working // since we already got headers back BOOL ok; while (TRUE) { ib.dwBufferLength = 1024; ok = InternetReadFileExA(ctx->httpFile, &ib, IRF_ASYNC, (LPARAM)ctx); if (ok || (!ok && GetLastError()==ERROR_IO_PENDING)) { DWORD readSize = ib.dwBufferLength; if (readSize > 0) { ctx->data.add(ib.lpvBuffer, readSize); } } if (ok || GetLastError()!=ERROR_IO_PENDING) break; // read the whole file or error } free(ib.lpvBuffer); InternetCloseHandle(ctx->httpFile); ctx->httpFile = 0; if (ok) { // read the whole file PostMessage(ctx->hwndToNotify, ctx->msg, (WPARAM) ctx, 0); } else { delete ctx; } } break;#ifdef DEBUG case INTERNET_STATUS_CLOSING_CONNECTION: _snprintf(buf, 256, "CLOSING_CONNECTION (%d)", statusLen); break; case INTERNET_STATUS_CONNECTED_TO_SERVER: _snprintf(buf, 256, "CONNECTED_TO_SERVER (%d)", statusLen); break; case INTERNET_STATUS_CONNECTING_TO_SERVER: _snprintf(buf, 256, "CONNECTING_TO_SERVER (%d)", statusLen); break; case INTERNET_STATUS_CONNECTION_CLOSED: _snprintf(buf, 256, "CONNECTION_CLOSED (%d)", statusLen); break; case INTERNET_STATUS_HANDLE_CLOSING: _snprintf(buf, 256, "HANDLE_CLOSING (%d)", statusLen); break; case INTERNET_STATUS_INTERMEDIATE_RESPONSE: _snprintf(buf, 256, "INTERMEDIATE_RESPONSE (%d)", statusLen ); break; case INTERNET_STATUS_NAME_RESOLVED: _snprintf(buf, 256, "NAME_RESOLVED (%d)", statusLen); break; case INTERNET_STATUS_RECEIVING_RESPONSE: _snprintf(buf, 256, "RECEIVING_RESPONSE (%d)",statusLen); break; case INTERNET_STATUS_RESPONSE_RECEIVED: _snprintf(buf, 256, "RESPONSE_RECEIVED (%d)", statusLen); break; case INTERNET_STATUS_REDIRECT: _snprintf(buf, 256, "REDIRECT (%d)", statusLen); break; case INTERNET_STATUS_REQUEST_SENT: _snprintf(buf, 256, "REQUEST_SENT (%d)", statusLen); break; case INTERNET_STATUS_RESOLVING_NAME: _snprintf(buf, 256, "RESOLVING_NAME (%d)", statusLen); break; case INTERNET_STATUS_SENDING_REQUEST: _snprintf(buf, 256, "SENDING_REQUEST (%d)", statusLen); break; case INTERNET_STATUS_STATE_CHANGE: _snprintf(buf, 256, "STATE_CHANGE (%d)", statusLen); break; default: _snprintf(buf, 256, "Unknown: Status %d Given", dwInternetStatus); break;#endif } DBG_OUT(buf); DBG_OUT("\n");}static HINTERNET g_hOpen = NULL;#ifndef SUMATRA_UPDATE_INFO_URL#ifdef SVN_PRE_RELEASE_VER#define SUMATRA_UPDATE_INFO_URL "http://kjkpub.s3.amazonaws.com/sumatrapdf/sumpdf-prerelease-latest.txt"#else#define SUMATRA_UPDATE_INFO_URL "http://blog.kowalczyk.info/software/sumatrapdf/sumpdf-latest.txt"#endif#endif#ifndef SVN_UPDATE_LINK#ifdef SVN_PRE_RELEASE_VERi#define SVN_UPDATE_LINK "http://blog.kowalczyk.info/software/sumatrapdf/prerelase.html"#else#define SVN_UPDATE_LINK "http://blog.kowalczyk.info/software/sumatrapdf"#endif#endifbool WininetInit(){ if (!g_hOpen) g_hOpen = InternetOpenA("SumatraPDF", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC); if (NULL == g_hOpen) { DBG_OUT("InternetOpenA() failed\n"); return false; } return true;}void WininetDeinit(){ if (g_hOpen) InternetCloseHandle(g_hOpen);}#define SECS_IN_DAY 60*60*24void DownloadSumatraUpdateInfo(WindowInfo *win, bool autoCheck){ if (!WininetInit()) return; assert(win); HWND hwndToNotify = win->hwndFrame; /* For auto-check, only check if at least a day passed since last check */ if (autoCheck && gGlobalPrefs.m_lastUpdateTime) { FILETIME lastUpdateTimeFt; StrToFileTime(gGlobalPrefs.m_lastUpdateTime, &lastUpdateTimeFt); FILETIME currentTimeFt; GetSystemTimeAsFileTime(¤tTimeFt); int secs = FileTimeDiffInSecs(¤tTimeFt, &lastUpdateTimeFt); assert(secs >= 0); // if secs < 0 => somethings wrong, so ignore that case if ((secs > 0) && (secs < SECS_IN_DAY)) return; } char *url = SUMATRA_UPDATE_INFO_URL "?v=" UPDATE_CHECK_VER; HttpReqCtx *ctx = new HttpReqCtx(url, hwndToNotify, WM_APP_URL_DOWNLOADED); ctx->autoCheck = autoCheck; InternetSetStatusCallback(g_hOpen, (INTERNET_STATUS_CALLBACK)InternetCallbackProc); HINTERNET urlHandle; urlHandle = InternetOpenUrlA(g_hOpen, url, NULL, 0, INTERNET_FLAG_RELOAD | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_CACHE_WRITE, (LPARAM)ctx); /* MSDN says NULL result from InternetOpenUrlA() means an error, but in my testing in async mode InternetOpenUrl() returns NULL and error is ERROR_IO_PENDING */ if (!urlHandle && (GetLastError() != ERROR_IO_PENDING)) { DBG_OUT("InternetOpenUrlA() failed\n"); delete ctx; } free(gGlobalPrefs.m_lastUpdateTime); gGlobalPrefs.m_lastUpdateTime = GetSystemTimeAsStr();}static void SerializableGlobalPrefs_Init() { gGlobalPrefs.m_inverseSearchCmdLine = strdup(DEFAULT_INVERSE_SEARCH_COMMANDLINE);}static void SerializableGlobalPrefs_Deinit(){ free(gGlobalPrefs.m_versionToSkip); free(gGlobalPrefs.m_inverseSearchCmdLine); free(gGlobalPrefs.m_lastUpdateTime);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -