aboutsummaryrefslogtreecommitdiff
path: root/Src/Winamp/dispatchCallback.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Winamp/dispatchCallback.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Winamp/dispatchCallback.cpp')
-rw-r--r--Src/Winamp/dispatchCallback.cpp525
1 files changed, 525 insertions, 0 deletions
diff --git a/Src/Winamp/dispatchCallback.cpp b/Src/Winamp/dispatchCallback.cpp
new file mode 100644
index 00000000..d14c5938
--- /dev/null
+++ b/Src/Winamp/dispatchCallback.cpp
@@ -0,0 +1,525 @@
+//#include "main.h"
+#include "./dispatchCallback.h"
+#include <new.h>
+
+DispatchCallback::DispatchCallback()
+ : ref(1), dispatch(NULL), threadId(0), threadHandle(NULL)
+{
+}
+
+DispatchCallback::~DispatchCallback()
+{
+ if (NULL != dispatch)
+ dispatch->Release();
+
+ if (NULL != threadHandle)
+ CloseHandle(threadHandle);
+}
+
+HRESULT DispatchCallback::CreateInstance(IDispatch *dispatch, DispatchCallback **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+ *instance = NULL;
+
+ if (NULL == dispatch)
+ return E_INVALIDARG;
+
+ DispatchCallback *self = new DispatchCallback();
+ if (NULL == self)
+ return E_OUTOFMEMORY;
+
+ self->dispatch = dispatch;
+ self->dispatch->AddRef();
+ self->threadId = GetCurrentThreadId();
+
+ HANDLE processHandle = GetCurrentProcess();
+
+ if (FALSE == DuplicateHandle(processHandle,
+ GetCurrentThread(),
+ processHandle,
+ &self->threadHandle,
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ self->threadHandle = NULL;
+ delete(self);
+
+ return E_FAIL;
+ }
+
+ *instance = self;
+ return S_OK;
+}
+
+unsigned long DispatchCallback::AddRef()
+{
+ return InterlockedIncrement((long*)&ref);
+}
+
+unsigned long DispatchCallback::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((long*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+IDispatch *DispatchCallback::GetDispatch()
+{
+ return dispatch;
+}
+
+unsigned long DispatchCallback::GetThreadId()
+{
+ return threadId;
+}
+
+HANDLE DispatchCallback::GetThreadHandle()
+{
+ return threadHandle;
+}
+
+
+DispatchCallbackEnum::DispatchCallbackEnum()
+ : ref(1), buffer(NULL), size(0), cursor(0)
+{
+}
+
+DispatchCallbackEnum::~DispatchCallbackEnum()
+{
+ if (NULL != buffer)
+ {
+ while(size--)
+ {
+ buffer[size]->Release();
+ }
+ }
+}
+
+HRESULT DispatchCallbackEnum::CreateInstance(DispatchCallback **objects, size_t count, DispatchCallbackEnum **instance)
+{
+ DispatchCallback *callback = NULL;
+ DispatchCallbackEnum *enumerator = NULL;
+
+ if (NULL == instance)
+ return E_POINTER;
+
+ *instance = NULL;
+
+ size_t size = sizeof(DispatchCallbackEnum) + (sizeof(DispatchCallback**) * count);
+ void *storage = calloc(size, 1);
+ if (NULL == storage)
+ return E_OUTOFMEMORY;
+
+ enumerator = new(storage) DispatchCallbackEnum();
+ if (NULL == enumerator)
+ {
+ free(storage);
+ return E_FAIL;
+ }
+
+ enumerator->buffer = (DispatchCallback**)(((BYTE*)enumerator) + sizeof(DispatchCallback));
+
+ for (size_t index = 0; index < count; index++)
+ {
+ callback = objects[index];
+ if (NULL != callback)
+ {
+ enumerator->buffer[enumerator->size] = callback;
+ callback->AddRef();
+ enumerator->size++;
+ }
+ }
+
+ *instance = enumerator;
+ return S_OK;
+}
+
+unsigned long DispatchCallbackEnum::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+unsigned long DispatchCallbackEnum::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+HRESULT DispatchCallbackEnum::Next(DispatchCallback **objects, size_t bufferMax, size_t *fetched)
+{
+ if (NULL == objects)
+ return E_POINTER;
+
+ if (0 == bufferMax)
+ return E_INVALIDARG;
+
+ if (cursor >= size)
+ {
+ if (NULL != fetched)
+ *fetched = 0;
+
+ return S_FALSE;
+ }
+
+ size_t available = size - cursor;
+ size_t copied = ((available > bufferMax) ? bufferMax : available);
+
+ DispatchCallback **source = buffer + cursor;
+ CopyMemory(objects, source, copied * sizeof(DispatchCallback*));
+
+ for(size_t index = 0; index < copied; index++)
+ objects[index]->AddRef();
+
+ cursor += copied;
+
+ if (NULL != fetched)
+ *fetched = copied;
+
+ return (bufferMax == copied) ? S_OK : S_FALSE;
+}
+
+HRESULT DispatchCallbackEnum::Reset(void)
+{
+ cursor = 0;
+ return S_OK;
+}
+
+HRESULT DispatchCallbackEnum::Skip(size_t count)
+{
+ cursor += count;
+ if (cursor > size)
+ cursor = size;
+
+ return (cursor < size) ? S_OK : S_FALSE;
+}
+
+HRESULT DispatchCallbackEnum::GetCount(size_t *count)
+{
+ if (NULL == count)
+ return E_POINTER;
+
+ *count = size;
+
+ return S_OK;
+}
+
+HRESULT DispatchCallbackEnum::Notify(DispatchCallbackNotifyFunc notifyCb, DispatchCallbackFreeFunc freeCb, void *param)
+{
+ DispatchCallbackApc *apc = NULL;
+ unsigned long threadId = GetCurrentThreadId();
+
+ if (NULL == buffer)
+ return E_UNEXPECTED;
+
+ HRESULT hr = DispatchCallbackApc::CreateInstance(notifyCb, freeCb, param, &apc);
+ if (FAILED(hr) || apc == NULL)
+ return hr;
+
+ for (size_t index = 0; index < size; index++)
+ {
+ DispatchCallback *callback = buffer[index];
+ if (callback)
+ {
+ if (callback->GetThreadId() == threadId)
+ apc->Call(callback->GetDispatch());
+ else
+ apc->Queue(callback->GetThreadHandle(), callback->GetDispatch());
+ }
+ }
+
+ apc->Release();
+ return hr;
+}
+
+DispatchCallbackStore::DispatchCallbackStore()
+{
+ InitializeCriticalSection(&lock);
+}
+
+DispatchCallbackStore::~DispatchCallbackStore()
+{
+ UnregisterAll();
+ DeleteCriticalSection(&lock);
+}
+
+void DispatchCallbackStore::Lock()
+{
+ EnterCriticalSection(&lock);
+}
+
+void DispatchCallbackStore::Unlock()
+{
+ LeaveCriticalSection(&lock);
+}
+
+CRITICAL_SECTION *DispatchCallbackStore::GetLock()
+{
+ return &lock;
+}
+
+HRESULT DispatchCallbackStore::Register(IDispatch *dispatch)
+{
+ DispatchCallback *callback = NULL;
+
+ if (NULL == dispatch)
+ return E_INVALIDARG;
+
+ Lock();
+
+ HRESULT hr = S_OK;
+ size_t index = list.size();
+ while(index--)
+ {
+ callback = list[index];
+ if (callback->GetDispatch() == dispatch)
+ {
+ hr = S_FALSE;
+ break;
+ }
+ }
+
+ if (S_OK == hr)
+ {
+ hr = DispatchCallback::CreateInstance(dispatch, &callback);
+ if (SUCCEEDED(hr))
+ list.push_back(callback);
+ }
+
+ Unlock();
+
+ return hr;
+}
+
+HRESULT DispatchCallbackStore::Unregister(IDispatch *dispatch)
+{
+ if (NULL == dispatch)
+ return E_INVALIDARG;
+
+ Lock();
+
+ HRESULT hr = S_FALSE;
+ size_t index = list.size();
+ while(index--)
+ {
+ DispatchCallback *callback = list[index];
+ if (callback->GetDispatch() == dispatch)
+ {
+ list.erase(list.begin() + index);
+ callback->Release();
+ hr = S_OK;
+ break;
+ }
+ }
+
+ Unlock();
+
+ return hr;
+}
+
+void DispatchCallbackStore::UnregisterAll()
+{
+ Lock();
+
+ size_t index = list.size();
+ while(index--)
+ {
+ DispatchCallback *callback = list[index];
+ callback->Release();
+ }
+
+ list.clear();
+
+ Unlock();
+}
+
+HRESULT DispatchCallbackStore::Enumerate(DispatchCallbackEnum **enumerator)
+{
+ if (NULL == enumerator || !(list.size() > 0))
+ return E_POINTER;
+
+ Lock();
+ HRESULT hr = DispatchCallbackEnum::CreateInstance(&list[0], list.size(), enumerator);
+ Unlock();
+
+ return hr;
+}
+
+HRESULT DispatchCallbackStore::RegisterFromDispParam(DISPPARAMS *pdispparams, unsigned int position,
+ unsigned int *puArgErr)
+{
+ VARIANTARG varg;
+ VariantInit(&varg);
+ HRESULT hr = DispGetParam(pdispparams, position, VT_DISPATCH, &varg, puArgErr);
+ if (SUCCEEDED(hr))
+ {
+ hr = Register(V_DISPATCH(&varg));
+ VariantClear(&varg);
+ }
+
+ return hr;
+}
+
+HRESULT DispatchCallbackStore::UnregisterFromDispParam(DISPPARAMS *pdispparams, unsigned int position,
+ unsigned int *puArgErr)
+{
+ VARIANTARG varg;
+ VariantInit(&varg);
+ HRESULT hr = DispGetParam(pdispparams, position, VT_DISPATCH, &varg, puArgErr);
+ if (SUCCEEDED(hr))
+ {
+ hr = Unregister(V_DISPATCH(&varg));
+ VariantClear(&varg);
+ }
+
+ return hr;
+}
+
+HRESULT DispatchCallbackStore::Notify(DispatchCallbackNotifyFunc notifyCb, DispatchCallbackFreeFunc freeCb, void *param)
+{
+ DispatchCallbackEnum *enumerator = NULL;
+ HRESULT hr = Enumerate(&enumerator);
+ if (SUCCEEDED(hr))
+ {
+ hr = enumerator->Notify(notifyCb, freeCb, param);
+ enumerator->Release();
+ }
+
+ return hr;
+}
+
+DispatchCallbackApc::DispatchCallbackApc()
+ : ref(1), notifyCb(NULL), freeCb(NULL), param(NULL)
+{
+}
+
+DispatchCallbackApc::~DispatchCallbackApc()
+{
+ if (NULL != freeCb)
+ freeCb(param);
+}
+
+HRESULT DispatchCallbackApc::CreateInstance(DispatchCallbackNotifyFunc notifyCb, DispatchCallbackFreeFunc freeCb,
+ void *param, DispatchCallbackApc **instance)
+{
+ if (NULL == instance)
+ return E_POINTER;
+
+ *instance = NULL;
+
+ if (NULL == notifyCb)
+ return E_INVALIDARG;
+
+ DispatchCallbackApc *self = new DispatchCallbackApc();
+ if (NULL == self)
+ return E_OUTOFMEMORY;
+
+ self->notifyCb = notifyCb;
+ self->freeCb = freeCb;
+ self->param = param;
+
+ *instance = self;
+
+ return S_OK;
+}
+
+unsigned long DispatchCallbackApc::AddRef()
+{
+ return InterlockedIncrement((LONG*)&ref);
+}
+
+unsigned long DispatchCallbackApc::Release()
+{
+ if (0 == ref)
+ return ref;
+
+ LONG r = InterlockedDecrement((LONG*)&ref);
+ if (0 == r)
+ delete(this);
+
+ return r;
+}
+
+HRESULT DispatchCallbackApc::Call(IDispatch *dispatch)
+{
+ if (NULL == notifyCb)
+ return E_UNEXPECTED;
+
+ notifyCb(dispatch, param);
+ return S_OK;
+}
+
+HRESULT DispatchCallbackApc::Queue(HANDLE threadHandle, IDispatch *dispatch)
+{
+ if (NULL == threadHandle || ((unsigned int)dispatch) < 65536)
+ return E_INVALIDARG;
+
+ DispatchCallbackApcParam *apcParam = new DispatchCallbackApcParam(dispatch, this);
+ if (NULL == apcParam || ((unsigned int)apcParam) < 65536)
+ return E_OUTOFMEMORY;
+
+ if (0 == QueueUserAPC(QueueApcCallback, threadHandle, (ULONG_PTR)apcParam))
+ {
+ unsigned long errorCode = GetLastError();
+ delete(apcParam);
+
+ return HRESULT_FROM_WIN32(errorCode);
+ }
+
+ return S_OK;
+}
+
+void CALLBACK DispatchCallbackApc::QueueApcCallback(ULONG_PTR user)
+{
+ DispatchCallbackApcParam *apcParam = (DispatchCallbackApcParam*)user;
+ if (NULL == apcParam)
+ return;
+
+ DispatchCallbackApc *apc = apcParam->GetApc();
+ if (NULL != apc)
+ apc->Call(apcParam->GetDispatch()),
+
+ delete(apcParam);
+}
+
+DispatchCallbackApcParam::DispatchCallbackApcParam(IDispatch *_dispatch, DispatchCallbackApc *_apc)
+ : dispatch(_dispatch), apc(_apc)
+{
+ if (NULL != dispatch && ((unsigned long)dispatch >= 65536))
+ dispatch->AddRef();
+
+ if (NULL != apc && ((unsigned long)apc >= 65536))
+ apc->AddRef();
+}
+
+DispatchCallbackApcParam::~DispatchCallbackApcParam()
+{
+ if (NULL != dispatch)
+ dispatch->Release();
+
+ if (NULL != apc)
+ apc->Release();
+}
+
+IDispatch *DispatchCallbackApcParam::GetDispatch()
+{
+ return dispatch;
+}
+
+DispatchCallbackApc *DispatchCallbackApcParam::GetApc()
+{
+ return apc;
+} \ No newline at end of file