+#include "ButtonProvider.h"
+#include <stdio.h>
+#include <assert.h>
+
+#include "UIAutomationTypedefs.h"
+#include <UIAutomationCore.h>
+#include <UIAutomationClient.h>
+#include <UIAutomationCoreApi.h>
+
+#pragma comment(lib, "uiautomationcore")
+
+#define BUTTONPROVIDER_MAGIC 0xFA1520BA
+
+static STDMETHODIMP IRawElementProviderSimple_QueryInterface(IRawElementProviderSimple *This, REFIID riid, void *ppvObject);
+static STDMETHODIMP_(ULONG) IRawElementProviderSimple_AddRef(IRawElementProviderSimple *This);
+static STDMETHODIMP_(ULONG) IRawElementProviderSimple_Release(IRawElementProviderSimple *This);
+static STDMETHODIMP IRawElementProviderSimple_get_ProviderOptions(IRawElementProviderSimple *This, enum ProviderOptions *pRetVal);
+static STDMETHODIMP IRawElementProviderSimple_GetPatternProvider(IRawElementProviderSimple *This, PATTERNID patternId, IUnknown **pRetVal);
+static STDMETHODIMP IRawElementProviderSimple_GetPropertyValue(IRawElementProviderSimple *This, PROPERTYID propertyId, VARIANT *pVal);
+static STDMETHODIMP IRawElementProviderSimple_get_HostRawElementProvider(IRawElementProviderSimple *This, IRawElementProviderSimple **pRetVal);
+
+static STDMETHODIMP IInvokeProvider_QueryInterface(IInvokeProvider *This, REFIID riid, void *ppvObject);
+static STDMETHODIMP_(ULONG) IInvokeProvider_AddRef(IInvokeProvider *This);
+static STDMETHODIMP_(ULONG) IInvokeProvider_Release(IInvokeProvider *This);
+static STDMETHODIMP IInvokeProvider_Invoke(IInvokeProvider *This);
+
+static STDMETHODIMP IToggleProvider_QueryInterface(IToggleProvider *This, REFIID riid, void **ppvObject);
+static STDMETHODIMP_(ULONG) IToggleProvider_AddRef(IToggleProvider *This);
+static STDMETHODIMP_(ULONG) IToggleProvider_Release(IToggleProvider *This);
+static STDMETHODIMP IToggleProvider_Toggle(IToggleProvider *This);
+static STDMETHODIMP IToggleProvider_get_ToggleState(IToggleProvider *This, ToggleState *pVal);
+
+static IRawElementProviderSimpleVtbl vtbl = {
+ IRawElementProviderSimple_QueryInterface,
+ IRawElementProviderSimple_AddRef,
+ IRawElementProviderSimple_Release,
+ IRawElementProviderSimple_get_ProviderOptions,
+ IRawElementProviderSimple_GetPatternProvider,
+ IRawElementProviderSimple_GetPropertyValue,
+ IRawElementProviderSimple_get_HostRawElementProvider
+};
+
+static IInvokeProviderVtbl vtbl2 = {
+ IInvokeProvider_QueryInterface,
+ IInvokeProvider_AddRef,
+ IInvokeProvider_Release,
+ IInvokeProvider_Invoke
+};
+
+static IToggleProviderVtbl vtbl3 = {
+ IToggleProvider_QueryInterface,
+ IToggleProvider_AddRef,
+ IToggleProvider_Release,
+ IToggleProvider_Toggle,
+ IToggleProvider_get_ToggleState
+};
+
+typedef struct {
+ IRawElementProviderSimpleVtbl *lpVtbl;
+ IInvokeProviderVtbl *lpVtbl2;
+ IToggleProviderVtbl *lpVtbl3;
+ DWORD magic;
+ ULONG refcount[3];
+ HWND m_hWnd;
+} InstanceData;
+
+static void _cdecl TraceW(LPCWSTR format, ...)
+{
+ WCHAR buffer[512] = { 0 };
+ va_list args;
+ va_start(args, format);
+ vswprintf_s(buffer, sizeof(buffer)/sizeof(WCHAR), format, args);
+ OutputDebugStringW(buffer);
+ va_end(args);
+}
+
+#ifdef _DEBUG
+#define TRACE TraceW
+#else
+#define TRACE 1?((void)0):TraceW
+#endif
+
+// Check the reference count for _all_ interfaces which are managing their own
+// individual counts and only delete the instance if the sum is 0.
+static ULONG CheckInstance(InstanceData* instance)
+{
+ ULONG refcount = instance->refcount[0] + instance->refcount[1] + instance->refcount[2];
+ if (refcount == 0)
+ {
+ TRACE(L"Destroy %p\n", instance);
+ free(instance);
+ }
+ return refcount;
+}
+
+static STDMETHODIMP IRawElementProviderSimple_QueryInterface(IRawElementProviderSimple *This, REFIID riid, void **ppv)
+{
+ InstanceData *instance = (InstanceData *)This;
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ HRESULT hr = E_POINTER;
+ if (ppv)
+ {
+ *ppv = NULL;
+ hr = S_OK;
+ if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IRawElementProviderSimple, riid)) {
+ *ppv = &instance->lpVtbl;
+ instance->lpVtbl->AddRef((IRawElementProviderSimple*)(*ppv));
+ }
+ else if (IsEqualIID(&IID_IInvokeProvider, riid)) {
+ *ppv = &instance->lpVtbl2;
+ instance->lpVtbl2->AddRef((IInvokeProvider*)(*ppv));
+ }
+ else if (IsEqualIID(&IID_IToggleProvider, riid)) {
+ *ppv = &instance->lpVtbl3;
+ instance->lpVtbl3->AddRef((IToggleProvider*)(*ppv));
+ }
+ else
+ hr = E_NOINTERFACE;
+ }
+ return hr;
+}
+
+STDMETHODIMP_(ULONG) IRawElementProviderSimple_AddRef(IRawElementProviderSimple *This)
+{
+ InstanceData * instance = (InstanceData *)This;
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ ULONG result = InterlockedIncrement(&instance->refcount[0]);
+ TRACE(L"IRawElementProviderSimple AddRef %lu\n", result);
+ return result;
+}
+
+STDMETHODIMP_(ULONG) IRawElementProviderSimple_Release(IRawElementProviderSimple *This)
+{
+ InstanceData *instance = (InstanceData *)This;
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ ULONG result = InterlockedDecrement(&instance->refcount[0]);
+ TRACE(L"IRawElementProviderSimple Release %lu\n", result);
+ return CheckInstance(instance);
+}
+
+STDMETHODIMP IRawElementProviderSimple_get_ProviderOptions(IRawElementProviderSimple *This, enum ProviderOptions *pRetVal)
+{
+ InstanceData *instance = (InstanceData *)This;
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ HRESULT hr = E_POINTER;
+ if (pRetVal)
+ {
+ if (!IsWindow(instance->m_hWnd))
+ hr = UIA_E_ELEMENTNOTAVAILABLE;
+ else
+ {
+ *pRetVal = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
+ hr = S_OK;
+ }
+ }
+ return hr;
+}
+
+STDMETHODIMP IRawElementProviderSimple_GetPatternProvider(IRawElementProviderSimple *This, PATTERNID patternId, IUnknown **ppVal)
+{
+ InstanceData *instance = (InstanceData *)This;
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ HRESULT hr = E_POINTER;
+ if (ppVal)
+ {
+ hr = S_OK;
+ *ppVal = NULL;
+ if (patternId == UIA_InvokePatternId || patternId == UIA_TogglePatternId)
+ {
+ hr = instance->lpVtbl->QueryInterface(This, &IID_IUnknown, ppVal);
+ }
+ }
+ return hr;
+}
+
+STDMETHODIMP IRawElementProviderSimple_GetPropertyValue(IRawElementProviderSimple *This, PROPERTYID propertyId, VARIANT *pVal)
+{
+ HRESULT hr = E_POINTER;
+ if (pVal)
+ {
+ hr = S_OK;
+ VariantInit(pVal);
+ pVal->vt = VT_EMPTY;
+
+ InstanceData *instance = (InstanceData *)This;
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ if (propertyId == UIA_ControlTypePropertyId)
+ {
+ pVal->vt = VT_I4;
+ pVal->lVal = UIA_CheckBoxControlTypeId;// UIA_ButtonControlTypeId;
+ }
+ else if (propertyId == UIA_IsControlElementPropertyId)
+ {
+ pVal->vt = VT_BOOL;
+ pVal->boolVal = VARIANT_TRUE;
+ }
+ else if (propertyId == UIA_ClassNamePropertyId)
+ {
+ WCHAR name[32] = { 0 };
+ GetClassNameW(instance->m_hWnd, name, 32);
+ pVal->vt = VT_BSTR;
+ pVal->bstrVal = SysAllocString(name);
+ }
+ else if (propertyId == UIA_NamePropertyId)
+ {
+ int len = GetWindowTextLengthW(instance->m_hWnd) + 1;
+ WCHAR *name = (WCHAR *)malloc(sizeof(WCHAR) * len);
+ if (name != NULL)
+ {
+ GetWindowTextW(instance->m_hWnd, name, len);
+ pVal->vt = VT_BSTR;
+ pVal->bstrVal = SysAllocString(name);
+ free(name);
+ }
+ }
+ }
+ return hr;
+}
+
+STDMETHODIMP IRawElementProviderSimple_get_HostRawElementProvider(IRawElementProviderSimple *This, IRawElementProviderSimple **ppVal)
+{
+ InstanceData *instance = (InstanceData *)This;
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ HRESULT hr = E_POINTER;
+ if (ppVal)
+ {
+ hr = UiaHostProviderFromHwnd(instance->m_hWnd, ppVal);
+ }
+ return hr;
+}
+
+STDMETHODIMP IInvokeProvider_QueryInterface(IInvokeProvider *This, REFIID riid, void **ppvObject)
+{
+ InstanceData *instance = (InstanceData *)(This - 1);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ return instance->lpVtbl->QueryInterface((IRawElementProviderSimple *)instance, riid, ppvObject);
+}
+
+STDMETHODIMP_(ULONG) IInvokeProvider_AddRef(IInvokeProvider *This)
+{
+ InstanceData *instance = (InstanceData *)(This - 1);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ ULONG result = InterlockedIncrement(&instance->refcount[1]);
+ TRACE(L"IInvokeProvider AddRef %lu\n", result);
+ return result;
+}
+
+STDMETHODIMP_(ULONG) IInvokeProvider_Release(IInvokeProvider *This)
+{
+ InstanceData *instance = (InstanceData *)(This - 1);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ ULONG result = InterlockedDecrement(&instance->refcount[1]);
+ TRACE(L"IInvokeProvider Release %lu\n", result);
+ return CheckInstance(instance);
+}
+
+STDMETHODIMP IInvokeProvider_Invoke(IInvokeProvider *This)
+{
+ InstanceData *instance = (InstanceData *)(This - 1);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ PostMessage(instance->m_hWnd, BM_CLICK, 0L, 0L);
+ return S_OK;
+}
+
+STDMETHODIMP IToggleProvider_QueryInterface(IToggleProvider *This, REFIID riid, void **ppvObject)
+{
+ InstanceData *instance = (InstanceData *)(This - 2);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ return instance->lpVtbl->QueryInterface((IRawElementProviderSimple *)instance, riid, ppvObject);
+}
+
+STDMETHODIMP_(ULONG) IToggleProvider_AddRef(IToggleProvider *This)
+{
+ InstanceData *instance = (InstanceData *)(This - 2);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ ULONG result = InterlockedIncrement(&instance->refcount[2]);
+ TRACE(L"IToggleProvider AddRef %lu\n", result);
+ return result;
+}
+
+STDMETHODIMP_(ULONG) IToggleProvider_Release(IToggleProvider *This)
+{
+ InstanceData *instance = (InstanceData *)(This - 2);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ ULONG result = InterlockedDecrement(&instance->refcount[2]);
+ TRACE(L"IToggleProvider Release %lu\n", result);
+ return CheckInstance(instance);
+}
+
+STDMETHODIMP IToggleProvider_Toggle(IToggleProvider *This)
+{
+ InstanceData *instance = (InstanceData *)(This - 2);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ HRESULT hr = UIA_E_ELEMENTNOTAVAILABLE;
+ if (IsWindow(instance->m_hWnd))
+ {
+ PostMessage(instance->m_hWnd, BM_CLICK, 0L, 0L);
+ hr = S_OK;
+ }
+ return hr;
+}
+
+STDMETHODIMP IToggleProvider_get_ToggleState(IToggleProvider *This, ToggleState *pVal)
+{
+ InstanceData *instance = (InstanceData *)(This - 2);
+ assert(instance->magic == BUTTONPROVIDER_MAGIC);
+ HRESULT hr = E_POINTER;
+ if (pVal != NULL)
+ {
+ if (!IsWindow(instance->m_hWnd))
+ hr = UIA_E_ELEMENTNOTAVAILABLE;
+ else
+ {
+ int state = (int)SendMessage(instance->m_hWnd, BM_GETCHECK, 0L, 0L);
+ *pVal = (state == BST_CHECKED) ? ToggleState_On : ToggleState_Off;
+ hr = S_OK;
+ }
+ }
+ return hr;
+}
+
+static HRESULT ButtonProvider_CreateInstance(HWND hwnd, IRawElementProviderSimple **ppProvider)
+{
+ HRESULT hr = E_POINTER;
+ if (ppProvider)
+ {
+ hr = E_OUTOFMEMORY;
+ InstanceData *instance = (InstanceData *)malloc(sizeof(InstanceData));
+ if (instance)
+ {
+ instance->lpVtbl = &vtbl;
+ instance->lpVtbl2 = &vtbl2;
+ instance->lpVtbl3 = &vtbl3;
+ instance->magic = BUTTONPROVIDER_MAGIC;
+ instance->m_hWnd = hwnd;
+ ZeroMemory(instance->refcount, sizeof(instance->refcount));
+ TRACE(L"CreateInstance %p\n", instance);
+ hr = instance->lpVtbl->QueryInterface((IRawElementProviderSimple *)instance, &IID_IRawElementProviderSimple, ppProvider);
+ }
+ }
+ return hr;
+}
+
+LRESULT ButtonProvider_OnGetObject(_In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam, _Inout_ IUnknown **ppunk)
+{
+ LRESULT result = 0L;
+ if (UiaRootObjectId == lParam)
+ {
+ IRawElementProviderSimple *provider = NULL;
+ if (*ppunk == NULL)
+ {
+ ButtonProvider_CreateInstance(hwnd, &provider);
+ provider->lpVtbl->QueryInterface(provider, &IID_IUnknown, ppunk);
+ }
+ else
+ {
+ (*ppunk)->lpVtbl->QueryInterface(*ppunk, &IID_IRawElementProviderSimple, &provider);
+ }
+
+ result = UiaReturnRawElementProvider(hwnd, wParam, lParam, provider);
+ provider->lpVtbl->Release(provider);
+ }
+ return result;
+}
+
+LRESULT ButtonProvider_OnDestroy(_In_ HWND hwnd, _In_ IUnknown *punkProvider)
+{
+ UiaReturnRawElementProvider(hwnd, 0, 0, NULL);
+ UiaDisconnectProvider((IRawElementProviderSimple *)punkProvider);
+ return 0L;
+}
+
+void ButtonProvider_OnInvoke(_In_opt_ IUnknown *punk)
+{
+ if (punk != NULL && UiaClientsAreListening())
+ {
+ IRawElementProviderSimple *provider = NULL;
+ if (SUCCEEDED(punk->lpVtbl->QueryInterface(punk, &IID_IRawElementProviderSimple, &provider)))
+ {
+ UiaRaiseAutomationEvent(provider, UIA_Invoke_InvokedEventId);
+ provider->lpVtbl->Release(provider);
+ }
+ }
+}
\ No newline at end of file