winamp/Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.cpp
2024-09-24 14:54:57 +02:00

520 lines
8.2 KiB
C++

#include "main.h"
#include "./deviceActivity.h"
#include <strsafe.h>
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<ifc_deviceactivity*>(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,
&param, 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