// Job.cpp: implementation of the Job class.
//
//////////////////////////////////////////////////////////////////////
#include "Job.h"
#include "resource.h"
#include <tchar.h>
#include <shlwapi.h>
#include "Helpers.h"
//#include <cmath>
#define NORM_RECT_X(rc) if (rc.left>rc.right) rc.right=rc.left
#define NORM_RECT_Y(rc) if (rc.top>rc.bottom) rc.bottom=rc.top
LONG WINAPI MainExceptionFilter(PEXCEPTION_POINTERS info);
void GradientRect(HDC hdc, RECT &rc, bool LeftTop_RightBottom, COLORREF LeftTop,COLORREF RightTop,COLORREF LeftBottom ,COLORREF RightBottom);
//#include <oleauto.h>
//INT SystemTimeToVariantTime(LPSYSTEMTIME lpSystemTime, double *pvtime);
//WINGDIAPI
typedef BOOL (WINAPI *GRADIENTFILL)(HDC,PTRIVERTEX,ULONG,PVOID,ULONG,ULONG);
GRADIENTFILL ptrGradientFill=((GRADIENTFILL)-1);
#define IOBUFFERLEN 0x10000
#define JOB_FIELDS_INIT hwndNotify(hwnd), err(NULL)
DWORD MAX_RETRIES = 10;
extern HINSTANCE hInstance;
DWORD BAR = 5;
DWORD HISTORY_LEN = 15;
DWORD MAX_SPEED = 0x100000; //
bool AUTO_SEEK_GOOD = true;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
typedef struct PROP_BAG {
struct FIXED {
DWORD dwSize;
// BYTE IsUnicode; // in order to determine invalid headers
DWORD dwLenSrc;
DWORD dwLenDst;
Job::JobStates state;
ULARGE_INTEGER size;
ULARGE_INTEGER written;
Job::RANGE range;
// bool CheckIdentity;
} fixed;
WCHAR names[MAX_PATH*2];
} PROP_BAG;
COLORREF LightenColor(COLORREF color, BYTE LIGHTEN = 64) {
BYTE Red = GetRValue(color);
BYTE Green = GetGValue(color);
BYTE Blue = GetBValue(color);
COLORREF newColor = RGB(
(Red <= 255-LIGHTEN)?(Red + LIGHTEN):(255),
(Green <= 255-LIGHTEN)?(Green + LIGHTEN):(255),
(Blue <= 255-LIGHTEN)?(Blue + LIGHTEN):(255));
return newColor;
}
COLORREF DarkenColor(COLORREF color, BYTE DARKEN = 32) {
BYTE Red = GetRValue(color);
BYTE Green = GetGValue(color);
BYTE Blue = GetBValue(color);
COLORREF newColor = RGB(
(Red >= DARKEN)?(Red - DARKEN):(DARKEN),
(Green >= DARKEN)?(Green - DARKEN):(DARKEN),
(Blue >= DARKEN)?(Blue - DARKEN):(DARKEN));
return newColor;
}
Job::Job(LPCTSTR src, LPCTSTR dst, HWND hwnd):JOB_FIELDS_INIT
{
dwSrcLen=StrLen(src);
dwDstLen=StrLen(dst);
Initialize(src, dst);
}
Job::Job(HANDLE file, HWND hwnd):JOB_FIELDS_INIT
{
PROP_BAG pb;
DWORD need, read;
lpSrcFull=lpDstFull=lpSrcName=lpDstName= NULL;
dwSrcLen = dwDstLen = 0;
current = JS_STOPPED;
ThreadID=0;
hThread=NULL;
if (ReadFile(file, &pb.fixed, sizeof(pb.fixed), &read, NULL) && read==sizeof(pb.fixed)) {
if (pb.fixed.dwSize != read) {
SetFilePointer(file, pb.fixed.dwSize-read, NULL, FILE_CURRENT);
}
need = (pb.fixed.dwLenSrc + pb.fixed.dwLenDst+2)*sizeof(WCHAR);
if (ReadFile(file, pb.names, need , &read , NULL) && read==need) { // || pb.fixed.IsUnicode>TRUE)) {
dwSrcLen = pb.fixed.dwLenSrc;
dwDstLen = pb.fixed.dwLenDst;
#ifndef UNICODE
LPTSTR lpSrcTmp=new TCHAR[dwSrcLen+1],
lpDstTmp=new TCHAR[dwDstLen+1];
//DWORD ln;
if (lpSrcTmp && lpDstTmp) {
if (UnicodeToChar(lpSrcTmp,pb.names, dwSrcLen) &&
UnicodeToChar(lpDstTmp,pb.names+dwSrcLen+1, dwDstLen)){
Initialize(lpSrcTmp,lpDstTmp);
} else {
err.Refresh(0);
}
}
if (lpSrcTmp) delete lpSrcTmp;
if (lpDstTmp) delete lpDstTmp;
#else
Initialize(pb.names, pb.names+pb.fixed.dwLenSrc+1);
#endif
// CheckIdentity = pb.fixed.CheckIdentity;
range = pb.fixed.range;
size = pb.fixed.size;
written = pb.fixed.written;
sp.lastWritten=written.QuadPart;
if (size.QuadPart) {
percent = (DWORD)((written.QuadPart*100)/size.QuadPart);
} else {
percent=0;
}
switch (pb.fixed.state) {
case JS_RUN:
case JS_START_PENDING:
Start();
break;
case JS_STOP_PENDING:
current=JS_STOPPED;
break;
default:
//SetState(pb.fixed.state);
current=pb.fixed.state;
}
}
}
}
void Job::Initialize(LPCTSTR src, LPCTSTR dst)
{
DWORD lnSrc, lnDst;
lpSrcFull=lpDstFull=lpSrcName=lpDstName= NULL;
current = JS_STOPPED;
ZeroMemory(&sp,sizeof(sp));
sp.lastTime = GetCurrentTime();
size.QuadPart = 0;
written.QuadPart = 0;
range.start = range.end = 0;
percent=0;
retries=MAX_RETRIES;
ThreadID=0;
hThread=NULL;
// lnSrc = GetFullPathNameW(src, 0, temp, &lpSrcName);
// lnDst = GetFullPathNameW(dst, 0, temp, &lpDstName);
if (dwSrcLen==0 || dwDstLen==0) return;
lpSrcFull = new TCHAR[dwSrcLen+1];
lpDstFull = new TCHAR[dwDstLen+MAX_PATH];
if (lpSrcFull && lpDstFull) {
lnSrc = GetFullPathName(src, dwSrcLen+1, lpSrcFull, &lpSrcName);
lnDst = GetFullPathName(dst, dwDstLen+MAX_PATH, lpDstFull, &lpDstName);
if (lpSrcName==NULL) {
lpSrcName = StrRChar(lpSrcFull, '\\');
if (lpSrcName) lpSrcName++;
}
err.SetIdentifier(lpSrcName);
// only if DESTINATION ends by '\' append file name
if (lnSrc>0 && lnDst>0 && lnSrc<=dwSrcLen && lnDst<=dwDstLen) {
if (lpDstName==NULL) {
lpDstName = lpDstFull+dwDstLen;
dwDstLen += (StrNCopy(lpDstName, lpSrcName, MAX_PATH) - lpDstName - 1);
} else {
/*
hFind = FindFirstFile(lpDstFull, wfd);
if (hFind != INVALID_HANDLE_VALUE) {
if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
ret = PathAppend(lpDstName, lpSrcName);
}
FindClose(hFind);
}
*/
}
} else {
err.Refresh(0);
StrNCopy(lpSrcFull, src, dwSrcLen);
StrNCopy(lpDstFull, dst, dwDstLen);
SetState(JS_ERROR);
}
}
for (DWORD idx=JS_STOPPED; idx <_JS_TOTAL_STATES_; idx++)
img[idx]= (HICON) LoadImage(hInstance, MAKEINTRESOURCE(IDI_Y_STOP+idx), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR |LR_SHARED);
}
Job::~Job()
{
Stop(true);
if (lpSrcFull) delete lpSrcFull;
if (lpDstFull) delete lpDstFull;
// if (err) delete err;
}
bool Job::Start()
{
if (current==JS_STOPPED || current==JS_ERROR) {
err.Clear();
SetState(JS_START_PENDING);
if (hThread) {
CloseHandle(hThread);
hThread=NULL;
}
retries=MAX_RETRIES;
hThread = CreateThread(NULL,0, (LPTHREAD_START_ROUTINE)CopyProc, (LPVOID) this, 0, &ThreadID);
return hThread!=NULL;
}
return false;
}
bool Job::Stop(bool wait)
{
if (current==JS_START_PENDING || current==JS_RUN) {
SetState(JS_STOP_PENDING);
if (hThread) {
DWORD state = WaitForSingleObject(hThread, 50);
if (state != WAIT_FAILED) {
if (state != WAIT_OBJECT_0) {
if (!wait) return true;
if (WaitForSingleObject(hThread, INFINITE)==WAIT_TIMEOUT) {
TerminateThread(hThread, -1);
}
}
CloseHandle(hThread);
}
hThread=NULL;
}
SetState(JS_STOPPED);
return true;
} else if (current==JS_COMPLETED) {
//SetState(JS_STOPPED);
}
return false;
}
void Job::Draw(HDC dc, RECT rc, UINT state, bool longformat)
{
//DrawText(dc, lpSrcFull,20, &rc, DT_SINGLELINE | DT_VCENTER);
if (this==NULL) return;
const int SPACE = 2;
const int GRAPH = BAR*HISTORY_LEN+(SPACE)*2;
const int STATS = 100;
const int ROW = 16;
HDC hdc;
HBITMAP hbmp;
HFONT hFont;
DWORD i;
bool selected = (state & ODS_SELECTED)!=0;
POINT pt={rc.left, rc.top};
COLORREF fr=GetSysColor(state & ODS_SELECTED? COLOR_HIGHLIGHTTEXT: ((state & ODS_DISABLED)?COLOR_GRAYTEXT:COLOR_WINDOWTEXT)),
bk=(selected? COLOR_HIGHLIGHT: COLOR_WINDOW);
COLORREF fr2, bk2;
OffsetRect(&rc, -pt.x, -pt.y);
RECT //rcIcon = {rc.left+SPACE, rc.top+SPACE, rc.left+SPACE+16,
rcSrc = {rc.left+16+SPACE*2,rc.top+SPACE, rc.right-GRAPH-STATS-SPACE*3, rc.top+ROW+SPACE},
rcDst = {rcSrc.left, rcSrc.bottom, rcSrc.right, rcSrc.bottom+ROW},
rcPercent = {rcSrc.left, rcDst.bottom+SPACE, rcSrc.left+STATS*2, rcDst.bottom+ROW+SPACE*2},
rcSize = {rcSrc.right+SPACE, rcSrc.top, rc.right-GRAPH-SPACE*2, rcSrc.bottom},
rcDone = {rcSize.left, rcSize.bottom, rcSize.right, rcSize.bottom+ROW},
//rcSpeed = {rcSize.left, rcDone.bottom, rcSize.right, rcDone.bottom+ROW},
rcErr = {rcPercent.right+SPACE, rcPercent.top, rcSize.right, rcPercent.bottom},
rcGraph = {rc.right-GRAPH-SPACE,rcSrc.top, rc.right-SPACE, rcPercent.bottom},
rcSpeed = {rcGraph.left, rcGraph.top, rcGraph.right, rcGraph.bottom};
TCHAR lpText[MAX_PATH];
LPCTSTR lpStr;
//Prepare intermediate drawing surface
hdc = CreateCompatibleDC(dc);
hbmp = CreateCompatibleBitmap(dc, rc.right, rc.bottom);
SaveDC(hdc);
hFont = (HFONT)GetCurrentObject(dc, OBJ_FONT);
SelectObject(hdc,hbmp);
SelectObject(hdc, hFont);
// Prepare "nice background"
//FillRect(hdc, &rc, GetSysColorBrush(bk));
//GradientFill(hdc, vert, COUNT(vert), pGrad, COUNT(pGrad), GRADIENT_FILL_TRIANGLE);
bk = GetSysColor(bk);
fr2 = LightenColor(bk);
bk2 = DarkenColor(bk);
GradientRect(hdc, rc, false, bk2, bk, bk, fr2);
DrawEdge(hdc, &rc, EDGE_ETCHED, BF_BOTTOM);
SetTextColor(hdc, fr);
SetBkMode(hdc, TRANSPARENT);
// draw state icon
DrawIconEx(hdc,SPACE,SPACE, img[current], 0,0, 0, NULL,DI_NORMAL);
// Files: SOURCE
StrNCopy(lpText,lpSrcFull, dwSrcLen);
if (PathCompactPath(hdc,lpText, rcSrc.right-rcSrc.left)) {
DrawText(hdc, lpText, StrLen(lpText), &rcSrc, DT_SINGLELINE | DT_VCENTER);
} else {
DrawText(hdc, lpSrcName, StrLen(lpSrcName), &rcSrc, DT_SINGLELINE | DT_VCENTER);
}
// DESTINATION
StrNCopy(lpText,lpDstFull, dwDstLen);
if (PathCompactPath(hdc,lpText, rcSrc.right-rcSrc.left)) {
DrawText(hdc, lpText, StrLen(lpText), &rcDst, DT_SINGLELINE | DT_VCENTER);
} else {
DrawText(hdc, lpDstName, StrLen(lpDstName), &rcDst, DT_SINGLELINE | DT_VCENTER);
}
// sizes
if (longformat) wsprintf(lpText, TEXT("%lu b"), size.QuadPart);
else StrFormatByteSize(size.QuadPart, lpText, MAX_PATH);
DrawText(hdc, lpText, StrLen(lpText), &rcSize, DT_SINGLELINE | DT_VCENTER | DT_RIGHT);
if (longformat) wsprintf(lpText, TEXT("%lu b"), written.QuadPart);
else StrFormatByteSize(written.QuadPart, lpText, MAX_PATH);
DrawText(hdc, lpText, StrLen(lpText), &rcDone, DT_SINGLELINE | DT_VCENTER | DT_RIGHT);
// error message
if ((current != JS_RUN && current != JS_COMPLETED) && (lpStr = *err)!=NULL) {
if (!selected && current != JS_STOPPED) SetTextColor(hdc, RGB(255,0,0));
DrawText(hdc, lpStr, StrLen(lpStr), &rcErr, DT_SINGLELINE | DT_VCENTER);
}
// progress bar
if (rcPercent.left<rcPercent.right-4) {
DrawEdge(hdc, &rcPercent, BDR_SUNKENINNER, BF_ADJUST | BF_RECT);
FillRect(hdc, &rcPercent, GetSysColorBrush(COLOR_WINDOW));
SetTextColor(hdc, RGB(220,220,0));//SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
InflateRect(&rcPercent, -1,-1);
rcSrc=rcPercent;
i = rcPercent.right-rcPercent.left;
rcSrc.right=rcPercent.left + (i*percent)/100;
GradientRect(hdc, rcSrc, false, RGB(166,204,240), RGB(0,0,128), RGB(106,154,190), RGB(0,0,88)); //FillRect(hdc, &rcPercent, GetSysColorBrush(COLOR_HIGHLIGHT));
// range
if (range.start < range.end && size.QuadPart>0) {
rcSrc.left = rcPercent.left + (DWORD)((range.start * i)/size.QuadPart);
if (size.QuadPart >= range.end) {
rcSrc.right = rcPercent.left + (DWORD)((range.end * i)/size.QuadPart);
} else {
rcSrc.right = rcPercent.right;
}
HGDIOBJ pen = SelectObject(hdc, GetStockObject(WHITE_PEN)),
brush = SelectObject(hdc, GetStockObject(HOLLOW_BRUSH));
SetROP2(hdc, R2_XORPEN);
Rectangle(hdc, rcSrc.left, rcSrc.top+1, rcSrc.right, rcSrc.bottom-1);
SetROP2(hdc, R2_COPYPEN);
DeleteObject(SelectObject(hdc, pen));
DeleteObject(SelectObject(hdc, brush));
//InvertRect(hdc, &rcSrc);
//FrameRect(hdc, &rcSrc, GetSysColorBrush(COLOR_HIGHLIGHT));
}
wsprintf(lpText, TEXT("%u%%"), percent);
DrawText(hdc, lpText, StrLen(lpText), &rcPercent, DT_SINGLELINE | DT_VCENTER | DT_CENTER);
}
// graph
DrawEdge(hdc, &rcGraph, BDR_SUNKENINNER, BF_ADJUST | BF_RECT);
FillRect(hdc, &rcGraph, GetSysColorBrush(COLOR_APPWORKSPACE));
InflateRect(&rcGraph, -SPACE,-SPACE);
rcGraph.right = rcGraph.left+BAR-1;
//rcGraph.left = rcGraph.right-BAR+1;
fr=RGB(27,222,164);
bk=RGB(43,141,114);
fr2 = DarkenColor(fr);
bk2 = DarkenColor(bk);
rcSrc.top=rcGraph.top;
i=((sp.idx>=HISTORY_LEN)?(sp.idx - HISTORY_LEN):(sp.idx+MAX_HISTORY-HISTORY_LEN));
do {
i=(i+1)%MAX_HISTORY;
rcGraph.top = rcGraph.bottom-((rcGraph.bottom-rcSrc.top)* sp.speed[i])/MAX_SPEED;
if (rcGraph.top<rcSrc.top) rcGraph.top=rcSrc.top;
GradientRect(hdc, rcGraph, true, fr, fr2, bk, bk2);
rcGraph.left += BAR;
rcGraph.right += BAR;
//OffsetRect(&rcGraph, -BAR, 0);
} while (i != sp.idx);
// speed
if (longformat) {
wsprintf(lpText, TEXT("%u/%u"),sp.average, sp.speed[sp.idx]);
} else {
LPTSTR lpTmp = NULL;
StrFormatByteSize(sp.average, lpText, MAX_PATH);
StrCopy(StrChar(lpText,0), TEXT("/"));
lpTmp = StrChar(lpText,0);
StrFormatByteSize(sp.speed[sp.idx], lpTmp, lpTmp - lpText);
}
SetTextColor(hdc, 0xFFFFFF); //white
//DrawText(hdc, lpText, StrLen(lpText), &rcSpeed, DT_SINGLELINE | DT_VCENTER | DT_RIGHT);
DrawText(hdc, lpText, StrLen(lpText), &rcSpeed, DT_SINGLELINE | DT_TOP | DT_RIGHT);
BitBlt(dc, pt.x, pt.y, rc.right, rc.bottom, hdc, 0, 0, SRCCOPY);
RestoreDC(hdc, -1);
DeleteDC(hdc);
DeleteObject(hbmp);
}
bool Job::Ready()
{
return this!=NULL && lpSrcFull!=NULL && lpDstFull!=NULL;
}
Job::JobStates Job::GetState()
{
return current;
}
void Job::SetState(JobStates newstate)
{
current = newstate;
if (newstate==JS_ERROR) err.Refresh(0);
if (hwndNotify) {
InvalidateRect(hwndNotify, NULL, FALSE);
PostMessage(hwndNotify, JNM_STATECHANGED, newstate, (LPARAM) this);
}
}
bool Job::SetRange(const RANGE &rng)
{
if (rng.start < rng.end || (rng.start==0 && rng.start == rng.end)) {
range = rng;
if (current == JS_COMPLETED) {
SetState(JS_STOPPED);
} else {
InvalidateRect(hwndNotify, NULL, FALSE);
}
return true;
}
return false;
}
double Job::GetCurrentTime()
{
double ret=0.0;
SYSTEMTIME st;
GetSystemTime(&st);
//SystemTimeToVariantTime(&st, &ret);
ret = ((double)(st.wHour*3600+st.wMinute*60+st.wSecond)) + ((double) st.wMilliseconds)/1000.0;
return ret;
}
bool Job::Store(HANDLE file)
{
PROP_BAG pb;
DWORD sz,wrt;
pb.fixed.dwSize = sizeof(pb.fixed);
pb.fixed.dwLenSrc = StrLen(lpSrcFull);
pb.fixed.dwLenDst = StrLen(lpDstFull);
pb.fixed.state = current;
pb.fixed.size.QuadPart = size.QuadPart;
pb.fixed.written.QuadPart = written.QuadPart;
pb.fixed.range = range;
// pb.fixed.CheckIdentity = CheckIdentity;
#ifdef UNICODE
StrNCopy(pb.names, lpSrcFull, dwSrcLen+1);
StrNCopy(pb.names+pb.fixed.dwLenSrc+1, lpDstFull,dwDstLen+1);
#else
CharToUnicode(pb.names, lpSrcFull, dwSrcLen);
CharToUnicode(pb.names+pb.fixed.dwLenSrc+1, lpDstFull,dwDstLen);
#endif
/*if (current != JS_COMPLETED) {
LPWSTR tmp = StrRChar(pb.names+pb.fixed.dwLenSrc+1,'\\');
StrCopy(tmp, TEXT("\\~Incomplete\\"));
tmp = StrRChar(tmp,'\\');
StrCopy(tmp+1, lpDstName);
pb.fixed.dwLenDst = StrLen(pb.names+pb.fixed.dwLenSrc+1);
}
*/
sz=pb.fixed.dwSize + (pb.fixed.dwLenSrc + pb.fixed.dwLenDst + 2)*sizeof(WCHAR);
return (WriteFile(file, &pb, sz , &wrt, NULL) && sz==wrt);
}
DWORD Job::CalcSpeed()
{
DWORD i;
ULONGLONG newWrtn = written.QuadPart;
sp.idx=(sp.idx+1)%MAX_HISTORY;
sp.newTime = GetCurrentTime();
i = (DWORD)(newWrtn - sp.lastWritten);
sp.speed[sp.idx]=(DWORD)((double)i/(sp.newTime-sp.lastTime));
sp.lastTime = sp.newTime;
sp.lastWritten = newWrtn;
// testing
//written.QuadPart+=(rand()*30);
//written.QuadPart %= size.QuadPart;
if (size.QuadPart>0) {
percent = (DWORD)((written.QuadPart*100)/size.QuadPart);
}
newWrtn = 0;
i=((sp.idx>=HISTORY_LEN)?(sp.idx - HISTORY_LEN):(sp.idx+MAX_HISTORY-HISTORY_LEN));
do {
i=(i+1)%MAX_HISTORY;
newWrtn += sp.speed[i];
//newWrtn += (sp.speed[i]*sp.speed[i]);
} while ( i != sp.idx);
sp.average = (DWORD)(newWrtn / HISTORY_LEN);
//sp.average = (DWORD)sqrt(((LONGLONG)newWrtn) / MAX_HISTORY);
return sp.speed[sp.idx];
}
int Job::Compare(Job &job)
{
int i;
i = StrCmp(lpSrcFull,job.lpSrcFull);
if (i==0) {
i = StrCmp(lpDstFull,job.lpDstFull);
}
if (i<0) i =-1; else if (i>0) i = 1;
return i;
}
bool Job::ProcessAsDir()
{
WIN32_FIND_DATA wfd;
HANDLE hFind;
TCHAR lpSFull[MAX_PATH],lpDFull[MAX_PATH];
LPTSTR lpSName, lpDName;
Job *pNew=NULL;
bool ret=true;
DWORD res;
// check is source is directory
if (!PathIsDirectory(lpSrcFull)) return false;
StrCopy(lpSFull,lpSrcFull);
StrCopy(lpDFull,lpDstFull);
// if so create sub-structure
CreateDirectory(lpDstFull,NULL);
lpSName=PathAddBackslash(lpSFull);
lpDName=PathAddBackslash(lpDFull);
StrCopy(lpSName, TEXT("*.*"));
hFind = FindFirstFile(lpSFull,&wfd);
if (hFind!=INVALID_HANDLE_VALUE) {
do {
if (StrCmp(wfd.cFileName,TEXT("."))!=0 && StrCmp(wfd.cFileName,TEXT(".."))!=0) {
StrCopy(lpSName, wfd.cFileName);
StrCopy(lpDName, wfd.cFileName);
pNew=new Job(lpSFull,lpDFull,hwndNotify);
if (pNew->Ready()) {
if (!SendMessageTimeout(hwndNotify, JNM_NEWJOB, 0, (LPARAM) pNew, SMTO_NORMAL, 5000, &res)) {
delete pNew;
//ret=false;
//break;
} else if (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
//pNew->Start();
}
} else {
if (pNew) delete pNew;
ret=false;
break;
}
}
} while (FindNextFile(hFind, &wfd));
FindClose(hFind);
}
return ret;
}
DWORD WINAPI Job::CopyProc(Job* pJob)
{
HANDLE hSrc,hDst;
DWORD need, read, wrote, err, SeekCount = 10;
BOOL ret,RangeOnly, CheckIdentity;
const int CmpLength = 20;
BYTE cmpbuf[2][CmpLength];
LPBYTE lpBuffer=NULL;//, lpCheck=NULL;
//LPEXCEPTION_POINTERS info;
//ULONGLONG pos;
try {
lpBuffer= new BYTE[IOBUFFERLEN+2];
if (lpBuffer) {
//SHPathPrepareForWrite(
//lpCheck = (lpBuffer ++);
do {
if (pJob->retries<MAX_RETRIES) {
// wait a little
pJob->SetState(Job::JS_START_PENDING);
Sleep(1000);
}
pJob->retries--;
if (pJob->current==Job::JS_STOP_PENDING) break;
//SetLastError(0);
// Open source and destination files
hSrc=hDst=INVALID_HANDLE_VALUE;
hSrc = CreateFile(pJob->lpSrcFull, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hSrc!=INVALID_HANDLE_VALUE && hSrc!=NULL) {
hDst = CreateFile(pJob->lpDstFull, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
}
if (hSrc!=INVALID_HANDLE_VALUE && hDst!=INVALID_HANDLE_VALUE && hSrc!=NULL && hDst!=NULL) {
// get
pJob->size.LowPart = GetFileSize(hSrc, &pJob->size.HighPart);
RangeOnly=(pJob->range.start < pJob->range.end);
CheckIdentity=false;
if (RangeOnly) {
pJob->written.QuadPart = pJob->range.start;
} else {
pJob->written.LowPart = GetFileSize(hDst, &pJob->written.HighPart);
if (pJob->written.QuadPart>CmpLength) {
pJob->written.QuadPart-=CmpLength;
CheckIdentity=true;
}
}
//pos = pJob->written.QuadPart;
SeekToEnd:
SetLastError(0);
SetFilePointer(hSrc, pJob->written.LowPart, (PLONG)&pJob->written.HighPart, FILE_BEGIN);
SetFilePointer(hDst, pJob->written.LowPart, (PLONG)&pJob->written.HighPart, FILE_BEGIN);
err=GetLastError();
if (err == NO_ERROR) {
pJob->SetState(Job::JS_RUN);
need=IOBUFFERLEN;
if (CheckIdentity) {
ret = ReadFile(hSrc, cmpbuf[0], CmpLength, &read, NULL);
ret = (ret && ReadFile(hDst, cmpbuf[1], CmpLength, &read, NULL));
if (ret) {
pJob->written.QuadPart+=read;
if (memcmp(cmpbuf[0],cmpbuf[1], read)!=0) {
if (AUTO_SEEK_GOOD && (pJob->written.QuadPart > (IOBUFFERLEN*10)) && (SeekCount >0)) {
pJob->err.DebugWriteString(TEXT("Difference at write position -> seeking back"), false);
SeekCount--;
pJob->written.QuadPart-=(IOBUFFERLEN*10);
goto SeekToEnd;
}
pJob->SetState(Job::JS_MISMATCH);
pJob->err.DebugWriteString(TEXT("New file is different than existing!!! Aborting..."),false);
goto CloseFiles;
}
} else {
pJob->SetState(Job::JS_ERROR);
pJob->err.DebugWriteString(TEXT("CheckIdentity failed"));
goto CloseFiles;
}
}
while(pJob->current!=Job::JS_STOP_PENDING) {
if (RangeOnly && ((pJob->written.QuadPart+need)> pJob->range.end)) {
need = (DWORD)(pJob->range.end - pJob->written.QuadPart);
}
// set boundary marks
//*lpCheck = lpCheck[IOBUFFERLEN+1] = 0xAA;
ret = ReadFile(hSrc, lpBuffer, need, &read, NULL);
// check for consistency
//if (*lpCheck != 0xAA || *lpCheck != lpCheck[IOBUFFERLEN+1]) pJob->err.DebugWriteString(TEXT("buffer overflow!"), false);
// check if read operation succeed
if (ret) {
//pos += read;
if (read==0) {
// completed
pJob->SetState(Job::JS_COMPLETED);
break;
} else {
ret = WriteFile(hDst, lpBuffer, read, &wrote, NULL);
if (ret && read==wrote) {
// calc stats
pJob->written.QuadPart += wrote;
pJob->percent = (DWORD)((pJob->written.QuadPart*100)/pJob->size.QuadPart);
if (RangeOnly && pJob->written.QuadPart>=pJob->range.end) {
pJob->SetState(Job::JS_COMPLETED);
break;
}
continue; // skip all error handling stuff and continue normally
}
}
} // here we are only if one of R/W calls failed
if (pJob->current==Job::JS_STOP_PENDING) {
pJob->err.Refresh(0);
} else {
pJob->SetState(Job::JS_ERROR);
}
pJob->err.DebugWriteString(TEXT("read or write failed"));
break;
}
} else {
// seek error
pJob->SetState(Job::JS_ERROR);
pJob->err.DebugWriteString(TEXT("seek error"));
}
} else if (pJob->current != Job::JS_STOP_PENDING) {
// file open error
err=GetLastError();
pJob->SetState(Job::JS_ERROR);
if (err==ERROR_ACCESS_DENIED && hSrc==INVALID_HANDLE_VALUE) { // may be it directory?
//pJob->err.DebugWriteString(TEXT("Access denied -> is it dir?"), false);
if (pJob->ProcessAsDir()) {
pJob->SetState(Job::JS_COMPLETED);
} else {
pJob->err.DebugWriteString(TEXT("access to file denied"), false);
}
} else {
pJob->err.DebugWriteString(TEXT("file open error"));
}
}
CloseFiles:
if (hSrc!=INVALID_HANDLE_VALUE) CloseHandle(hSrc);
if (hDst!=INVALID_HANDLE_VALUE) CloseHandle(hDst);
} while(pJob->current==Job::JS_ERROR && (pJob->retries>0 && pJob->retries<MAX_RETRIES));
if (pJob->current==Job::JS_STOP_PENDING) {
pJob->SetState(Job::JS_STOPPED);
pJob->err.DebugWriteString(TEXT("thread was canceled"), false);
}
} else { // pJob->lpBuffer != NULL
// memory error
pJob->SetState(Job::JS_ERROR);
pJob->err.DebugWriteString(TEXT("memory allocation error"));
}
}
catch(...){
pJob->SetState(Job::JS_ERROR);
pJob->err.DebugWriteString(TEXT("exception in CopyProc"));
}
try {
if (lpBuffer) {
delete lpBuffer;
lpBuffer=NULL;
}
}
catch (...) {
OutputDebugString(TEXT("There was an exception in 'delete lpBuffer'\n"));
}
//CloseHandle(hThread);
//pJob->hThread=NULL;
return 0;
}
void GradientRect(HDC hdc, RECT &rc, bool LeftTop_RightBottom, COLORREF LeftTop,COLORREF RightTop,COLORREF LeftBottom ,COLORREF RightBottom)
{
TRIVERTEX vert[] = {
{rc.left, rc.top, GetRValue(LeftTop)<<8, GetGValue(LeftTop)<<8, GetBValue(LeftTop)<<8, 0},
{rc.right, rc.top, GetRValue(RightTop)<<8, GetGValue(RightTop)<<8, GetBValue(RightTop)<<8, 0},
{rc.left,rc.bottom, GetRValue(LeftBottom)<<8, GetGValue(LeftBottom)<<8, GetBValue(LeftBottom)<<8, 0},
{rc.right,rc.bottom, GetRValue(RightBottom)<<8, GetGValue(RightBottom)<<8, GetBValue(RightBottom)<<8, 0}
};
GRADIENT_TRIANGLE pGrad[] = {
{0,3,1},
{2,0,3}
};
if (ptrGradientFill==((GRADIENTFILL)-1)) {
HMODULE hmod = LoadLibrary(TEXT("msimg32.dll"));
ptrGradientFill = (GRADIENTFILL)GetProcAddress(hmod,"GradientFill");
}
if (ptrGradientFill) {
if (!LeftTop_RightBottom) {
pGrad[0].Vertex1 = 1;
pGrad[0].Vertex2 = 2;
pGrad[0].Vertex3 = 0;
pGrad[1].Vertex1 = 1;
pGrad[1].Vertex2 = 2;
pGrad[1].Vertex3 = 3;
}
GradientFill(hdc,vert,COUNT(vert), pGrad, COUNT(pGrad), GRADIENT_FILL_TRIANGLE);
} else {
FillRect(hdc, &rc, GetSysColorBrush(COLOR_HIGHLIGHT));
}
}
LPCTSTR Job::GetFileName(bool srcFile)
{
if (srcFile) return lpSrcFull;
return lpDstFull;
}
bool Job::UpdateFileNames(LPCTSTR source, LPCTSTR dest)
{
if (!source || !dest) return false;
DWORD lnSrc = StrLen(source),
lnDst = StrLen(dest);
if (lnSrc> dwSrcLen) {
delete lpSrcFull;
lpSrcFull = StrDup(source);
} else {
StrCopy(lpSrcFull, source);
}
if (lnDst> dwDstLen) {
delete lpDstFull;
lpDstFull = StrDup(dest);
} else {
StrCopy(lpDstFull, dest);
}
if (lpSrcFull) {
lpSrcName = StrRChar(lpSrcFull, '\\')+1;
dwSrcLen = lnSrc;
} else {
lpSrcName = NULL;
dwSrcLen = 0;
}
if (lpDstFull) {
lpDstName = StrRChar(lpDstFull, '\\')+1;
dwDstLen = lnDst;
} else {
lpDstName = NULL;
dwDstLen = 0;
}
err.SetIdentifier(lpSrcName);
if (current==JS_MISMATCH) SetState(JS_STOPPED);
return (lpSrcFull && lpDstFull);
}