#include "main.h" #include "./deviceActivity.h" #include typedef struct DeviceActivityThreadParam { DeviceActivity *activity; unsigned int duration; unsigned int interval; HANDLE readyEvent; }DeviceActivityThreadParam; DeviceActivity::DeviceActivity(DeviceActivityFlags flags, DeviceActivityCallback startCb, DeviceActivityCallback finishCb, DeviceActivityProgressCallback progressCb, void *user) : ref(1), displayName(NULL), status(NULL), activityThread(FALSE), cancelEvent(NULL), progress(0) { InitializeCriticalSection(&lock); this->flags = flags; callbackStart = startCb; callbackFinish = finishCb; callbackProgress = progressCb; this->user = user; } DeviceActivity::~DeviceActivity() { Stop(); String_Free(displayName); String_Free(status); DeleteCriticalSection(&lock); } HRESULT DeviceActivity::CreateInstance(DeviceActivityFlags flags, DeviceActivityCallback startCb, DeviceActivityCallback finishCb, DeviceActivityProgressCallback progressCb, void *user, DeviceActivity **instance) { DeviceActivity *self; if (NULL == instance) return E_POINTER; *instance = NULL; self = new DeviceActivity(flags, startCb, finishCb, progressCb, user); if (NULL == self) return E_OUTOFMEMORY; *instance = self; return S_OK; } size_t DeviceActivity::AddRef() { return InterlockedIncrement((LONG*)&ref); } size_t DeviceActivity::Release() { if (0 == ref) return ref; LONG r = InterlockedDecrement((LONG*)&ref); if (0 == r) delete(this); return r; } int DeviceActivity::QueryInterface(GUID interface_guid, void **object) { if (NULL == object) return E_POINTER; if (IsEqualIID(interface_guid, IFC_DeviceActivity)) *object = static_cast(this); else { *object = NULL; return E_NOINTERFACE; } if (NULL == *object) return E_UNEXPECTED; AddRef(); return S_OK; } void DeviceActivity::Lock() { EnterCriticalSection(&lock); } void DeviceActivity::Unlock() { LeaveCriticalSection(&lock); } BOOL DeviceActivity::GetActive() { BOOL running; Lock(); running = (NULL != activityThread); Unlock(); return running; } BOOL DeviceActivity::GetCancelable() { BOOL cancelable; Lock(); cancelable = (0 != (DeviceActivityFlag_Cancelable & flags)); Unlock(); return cancelable; } HRESULT DeviceActivity::GetProgress(unsigned int *percentCompleted) { if (NULL == percentCompleted) return E_POINTER; Lock(); *percentCompleted = progress; Unlock(); return S_OK; } HRESULT DeviceActivity::GetDisplayName(wchar_t *buffer, size_t bufferMax) { HRESULT hr; if (NULL == buffer) return E_POINTER; Lock(); if (0 == String_CopyTo(buffer, displayName, bufferMax) && FALSE == IS_STRING_EMPTY(displayName)) { hr = E_FAIL; } else hr = S_OK; Unlock(); return hr; } HRESULT DeviceActivity::GetStatus(wchar_t *buffer, size_t bufferMax) { HRESULT hr; if (NULL == buffer) return E_POINTER; Lock(); if (0 == String_CopyTo(buffer, status, bufferMax) && FALSE == IS_STRING_EMPTY(status)) { hr = E_FAIL; } else hr = S_OK; Unlock(); return hr; } HRESULT DeviceActivity::Cancel(HWND hostWindow) { HRESULT hr; Lock(); if (0 == (DeviceActivityFlag_Cancelable & flags)) hr = E_NOTIMPL; else hr = E_FAIL; Unlock(); return hr; } HRESULT DeviceActivity::Start(unsigned int duration, unsigned int interval) { HRESULT hr; Lock(); if (NULL != activityThread) hr = E_PENDING; else { hr = S_OK; if (NULL == cancelEvent) { cancelEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == cancelEvent) hr = E_FAIL; } if (SUCCEEDED(hr)) { DeviceActivityThreadParam param; param.activity = this; param.duration = duration; param.interval = interval; param.readyEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (NULL == param.readyEvent) hr = E_FAIL; else { DWORD threadId; activityThread = CreateThread(NULL, 0, DeviceActivity_ActivityThreadStarter, ¶m, 0, &threadId); if (NULL == activityThread) hr = E_FAIL; else WaitForSingleObject(param.readyEvent, INFINITE); CloseHandle(param.readyEvent); } } if (FAILED(hr)) Stop(); } Unlock(); return hr; } HRESULT DeviceActivity::Stop() { HRESULT hr; HANDLE threadHandle, eventHandle; Lock(); threadHandle = activityThread; eventHandle = cancelEvent; activityThread = NULL; cancelEvent = NULL; Unlock(); if (NULL != threadHandle) { if (NULL != eventHandle) SetEvent(eventHandle); WaitForSingleObject(threadHandle, INFINITE); CloseHandle(threadHandle); hr = S_OK; } else hr = S_FALSE; if (NULL != eventHandle) CloseHandle(eventHandle); return hr; } HRESULT DeviceActivity::SetDisplayName(const wchar_t *name) { HRESULT hr; Lock(); if (NULL == name && NULL == displayName) hr = S_FALSE; else { if (NULL != displayName && CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, name, -1, displayName, -1)) { hr = S_FALSE; } else { wchar_t *string; string = String_Duplicate(name); if (NULL == string && NULL != name) hr = E_FAIL; else { String_Free(displayName); displayName = string; hr = S_OK; } } } Unlock(); return hr; } HRESULT DeviceActivity::SetStatus(const wchar_t *newStatus) { HRESULT hr; if (NULL == newStatus && NULL == status) return S_FALSE; Lock(); if (NULL != status && CSTR_EQUAL == CompareString(LOCALE_USER_DEFAULT, 0, newStatus, -1, status, -1)) { hr = S_FALSE; } else { wchar_t *string; string = String_Duplicate(newStatus); if (NULL == string && NULL != newStatus) hr = E_FAIL; else { String_Free(status); status = string; hr = S_OK; } } Unlock(); return hr; } HRESULT DeviceActivity::SetUser(void *data) { Lock(); user = data; Unlock(); return S_OK; } HRESULT DeviceActivity::GetUser(void **data) { if (NULL == data) return E_POINTER; Lock(); *data = user; Unlock(); return S_OK; } DWORD DeviceActivity::ActivityThread(unsigned int duration, unsigned int interval) { DWORD waitResult, waitTime; HANDLE cancelEventCopy; unsigned int position; if (interval > duration) interval = duration; position = 0; Lock(); progress = 0; if (NULL == cancelEvent || 0 == DuplicateHandle(GetCurrentProcess(), cancelEvent, GetCurrentProcess(), &cancelEventCopy, 0, FALSE, DUPLICATE_SAME_ACCESS)) { cancelEventCopy = NULL; } Unlock(); if(NULL == cancelEventCopy) return -3; Lock(); if (NULL != callbackStart) callbackStart(this); if (NULL != callbackProgress) callbackProgress(this, position, duration); Unlock(); for(;;) { waitTime = interval; if ((position + waitTime) > duration) waitTime = duration - position; waitResult = WaitForSingleObject(cancelEventCopy, waitTime); if (WAIT_TIMEOUT == waitResult) { position += waitTime; Lock(); if (duration != 0) { progress = 100 * position / duration; if (progress > 100) progress = 100; } else progress = 100; if (NULL != callbackProgress) callbackProgress(this, position, duration); Unlock(); if (position >= duration) break; } else break; } AddRef(); Lock(); if (NULL != activityThread) { CloseHandle(activityThread); activityThread = NULL; } if (NULL != cancelEvent) { CloseHandle(cancelEvent); cancelEvent = NULL; } if (NULL != callbackFinish) callbackFinish(this); Unlock(); Release(); return 0; } static DWORD CALLBACK DeviceActivity_ActivityThreadStarter(void *user) { DeviceActivityThreadParam *param; DeviceActivity *activity; unsigned int duration, interval; DWORD result; param = (DeviceActivityThreadParam*)user; activity = param->activity; duration = param->duration; interval = param->interval; if (NULL != param->readyEvent) SetEvent(param->readyEvent); if (NULL != activity) result = activity->ActivityThread(duration, interval); else result = -2; return result; } #define CBCLASS DeviceActivity START_DISPATCH; CB(ADDREF, AddRef) CB(RELEASE, Release) CB(QUERYINTERFACE, QueryInterface) CB(API_GETACTIVE, GetActive) CB(API_GETCANCELABLE, GetCancelable) CB(API_GETPROGRESS, GetProgress) CB(API_GETDISPLAYNAME, GetDisplayName) CB(API_GETSTATUS, GetStatus) CB(API_CANCEL, Cancel) END_DISPATCH; #undef CBCLASS