--- /dev/null
+# top-most EditorConfig file
+root = true
+
+# Don't use tabs for indentation.
+[*]
+indent_style = space
+trim_trailing_whitespace = true
+# (Please don't specify an indent_size here; that has too many unintended consequences.)
+
+[*.{c,h,cxx,cpp}]
+indent_size = 4
+
+# Powershell files
+[*.{ps1,psm1,psd1}]
+indent_size = 4
+
+# Xml based files
+[*.{proj,xml,nuspec}]
+indent_size = 2
--- /dev/null
+.vscode/
+build/
+*.aps
\ No newline at end of file
--- /dev/null
+#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
--- /dev/null
+#include <Windows.h>
+#include <Ole2.h>
+
+LRESULT ButtonProvider_OnGetObject(_In_ HWND hwnd, _In_ WPARAM wParam, _In_ LPARAM lParam, _Inout_ IUnknown **ppunk);
+LRESULT ButtonProvider_OnDestroy(_In_ HWND hwnd, _In_ IUnknown *punkProvider);
+void ButtonProvider_OnInvoke(_In_opt_ IUnknown *provider);
--- /dev/null
+# CMAKE_BUILD_TYPE has no effect on the Visual Studio generator.
+# cmake -G"Visual Studio 15 2017 Win64" ..
+# cmake --build . --config Release
+#
+# For LLVM / CLang build:
+# cmake -G"Visual Studio 15 2017 Win64" -T LLVM ..
+# cmake --build . --config Release
+
+cmake_minimum_required(VERSION 3.26)
+
+project(UIADemo VERSION 1.0 LANGUAGES C RC)
+
+set(SOURCE main.c main.rc resource.h
+ PolyButton.c PolyButton.h
+ ButtonProvider.c ButtonProvider.h
+ UIAutomationTypedefs.h)
+set(CMAKE_C_STANDARD 11)
+add_definitions(-DUNICODE -D_UNICODE -DSTRICT -DWIN32_LEAN_AND_MEAN -D_WIN32_WINNT=_WIN32_WINNT_WIN7)
+add_executable(${PROJECT_NAME} WIN32 ${SOURCE})
--- /dev/null
+#include "PolyButton.h"
+#include "ButtonProvider.h"
+#include <stdlib.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+typedef enum {
+ PolyButton_Pressed = (1 << 0),
+ PolyButton_Checked = (1 << 1),
+ PolyButton_Disabled = (1 << 2),
+ PolyButton_Hot = (1 << 3),
+ PolyButton_Focussed = (1 << 4),
+ PolyButton_Tracking = (1 << 5)
+} PolyButtonFlags;
+
+typedef struct {
+ UINT sides; // number of sides
+ COLORREF checkedColor;
+ COLORREF normalColor;
+ COLORREF activeColor;
+ PolyButtonFlags flags;
+ TRACKMOUSEEVENT track;
+ POINT* points;
+ IUnknown *provider;
+} PolyButtonData;
+
+static LRESULT CALLBACK WndProc(HWND hwnd, UINT messageId, WPARAM wparam, LPARAM lparam);
+
+void PolyButton_RegisterControl(_In_ HINSTANCE hInstance)
+{
+ WNDCLASS wc = { 0 };
+ wc.style = CS_HREDRAW | CS_VREDRAW;
+ wc.lpfnWndProc = WndProc;
+ wc.hInstance = hInstance;
+ wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wc.lpszClassName = L"POLYBUTTON";
+ RegisterClass(&wc);
+}
+
+static void EnableMouseTracking(HWND hwnd, PolyButtonData *data)
+{
+ data->track.cbSize = sizeof(TRACKMOUSEEVENT);
+ data->track.dwFlags = TME_LEAVE;
+ data->track.dwHoverTime = 0L;
+ data->track.hwndTrack = hwnd;
+ TrackMouseEvent(&data->track);
+ data->flags |= PolyButton_Tracking;
+}
+
+static void CalculatePoints(RECT rc, int sides, POINT *points)
+{
+ POINT center = { (rc.right - rc.left) / 2 + rc.left, (rc.bottom - rc.top) / 2 + rc.top};
+ SIZE radius = { (rc.right - rc.left) / 2, (rc.bottom - rc.top) / 2 };
+ double angle = 3 * M_PI_2;
+ double delta = 2 * M_PI / sides;
+ for (int n = 0; n < sides; ++n, angle += delta)
+ {
+ points[n].x = (int)(radius.cx * cos(angle) + center.x + 0.5);
+ points[n].y = (int)(radius.cy * sin(angle) + center.y + 0.5);
+ }
+}
+
+static void OnSize(HWND hwnd, UINT flag, SIZE size, PolyButtonData* data)
+{
+ if (data->points)
+ free(data->points);
+ data->points = (POINT *)calloc(data->sides, sizeof(POINT));
+ if (data->points != NULL)
+ {
+ RECT rc = { 1, 1, size.cx - 2, size.cy - 2 };
+ CalculatePoints(rc, data->sides, data->points);
+ }
+}
+
+static LRESULT OnPaint(HWND hwnd, PolyButtonData *data)
+{
+ PAINTSTRUCT ps;
+ BeginPaint(hwnd, &ps);
+ HDC hdc = (ps.hdc != NULL) ? ps.hdc : GetWindowDC(hwnd);
+ RECT rc = ps.rcPaint;
+ if (data->points)
+ {
+ COLORREF color = data->normalColor;
+ if (!IsWindowEnabled(hwnd))
+ color = GetSysColor(COLOR_INACTIVEBORDER);
+ else if (data->flags & PolyButton_Checked)
+ color = data->checkedColor;
+ //else if (data->flags & PolyButton_Hot)
+ // color = data->activeColor;
+ COLORREF border = GetSysColor(COLOR_ACTIVEBORDER);
+ if ((data->flags & PolyButton_Hot))
+ border = RGB(0x00, 0xFF, 0xFF);
+ HPEN borderPen = CreatePen(PS_SOLID, 1, border);
+ HBRUSH brush = CreateSolidBrush(color);
+ HGDIOBJ oldPen = SelectObject(hdc, borderPen);
+ HGDIOBJ oldBrush = SelectObject(hdc, (HGDIOBJ)brush);
+ Polygon(hdc, data->points, data->sides);
+ Polyline(hdc, data->points, data->sides);
+ if ((data->flags & PolyButton_Focussed) == PolyButton_Focussed)
+ {
+ HPEN focusPen = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
+ HGDIOBJ prevPen = SelectObject(hdc, (HGDIOBJ)focusPen);
+ POINT *points = (POINT*)calloc(data->sides, sizeof(POINT));
+ if (points != NULL)
+ {
+ CalculatePoints(rc, data->sides, points);
+ Polyline(hdc, points, data->sides);
+ free(points);
+ }
+ SelectObject(hdc, prevPen);
+ DeleteObject(focusPen);
+ }
+ SelectObject(hdc, oldPen);
+ SelectObject(hdc, oldBrush);
+ DeleteObject(brush);
+ DeleteObject(borderPen);
+ }
+ EndPaint(hwnd, &ps);
+ return 1L;
+}
+
+LRESULT OnInvoke(HWND hwnd, PolyButtonData *data)
+{
+ data->flags ^= PolyButton_Checked;
+ ButtonProvider_OnInvoke(data->provider);
+ InvalidateRect(hwnd, NULL, TRUE);
+ return 1L;
+}
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT messageId, WPARAM wParam, LPARAM lParam)
+{
+ PolyButtonData *data = (PolyButtonData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+ switch (messageId)
+ {
+ case WM_CREATE:
+ data = (PolyButtonData *)calloc(1, sizeof(PolyButtonData));
+ if (data == NULL)
+ abort();
+ else
+ {
+ data->activeColor = RGB(0xFF, 0x80, 0x80);
+ data->checkedColor = RGB(0, 0xFF, 0);
+ data->normalColor = RGB(0xFF, 0xFF, 0xFF);
+ data->sides = 6;
+ data->points = NULL;
+ data->provider = NULL;
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)data);
+ }
+ break;
+ case WM_GETOBJECT:
+ return ButtonProvider_OnGetObject(hwnd, wParam, lParam, &data->provider);
+ case WM_DESTROY:
+ if (data->provider)
+ {
+ ButtonProvider_OnDestroy(hwnd, data->provider);
+ data->provider->lpVtbl->Release(data->provider);
+ data->provider = NULL;
+ }
+ free(data);
+ break;
+ case WM_SIZE:
+ {
+ SIZE size = { LOWORD(lParam), HIWORD(lParam) };
+ OnSize(hwnd, (UINT)wParam, size, data);
+ return 1L;
+ }
+ case WM_PAINT:
+ return OnPaint(hwnd, data);
+ case WM_SETFOCUS:
+ data->flags |= PolyButton_Focussed;
+ InvalidateRect(hwnd, NULL, TRUE);
+ break;
+ case WM_KILLFOCUS:
+ data->flags &= ~PolyButton_Focussed;
+ InvalidateRect(hwnd, NULL, TRUE);
+ break;
+ case WM_ENABLE:
+ InvalidateRect(hwnd, NULL, TRUE);
+ break;
+ case WM_MOUSEMOVE:
+ if (!(data->flags & PolyButton_Tracking))
+ {
+ data->flags |= PolyButton_Hot;
+ EnableMouseTracking(hwnd, data);
+ InvalidateRect(hwnd, NULL, TRUE);
+ }
+ break;
+ case WM_MOUSELEAVE:
+ data->flags &= ~(PolyButton_Hot | PolyButton_Tracking);
+ InvalidateRect(hwnd, NULL, TRUE);
+ break;
+ case BM_GETCHECK:
+ return (data->flags & PolyButton_Checked) ? BST_CHECKED : BST_UNCHECKED;
+ case BM_SETCHECK:
+ if (wParam == BST_CHECKED)
+ data->flags |= PolyButton_Checked;
+ else
+ data->flags &= ~PolyButton_Checked;
+ return 0L;
+ case WM_LBUTTONDOWN:
+ case BM_CLICK:
+ return OnInvoke(hwnd, data);
+ }
+ return DefWindowProc(hwnd, messageId, wParam, lParam);
+}
--- /dev/null
+#ifndef _PolyButton_h_INCLUDE
+#define _PolyButton_h_INCLUDE
+
+#include <Windows.h>
+
+void PolyButton_RegisterControl(_In_ HINSTANCE hInstance);
+
+#endif // _PolyButton_h_INCLUDE
--- /dev/null
+// declare missing typedefs for C import of UIA interfaces
+//
+// This file should be included _before_ the UIAutomation headers
+// when using the UIAutomationCode headers in C programs.
+
+#pragma once
+
+typedef enum ConditionType ConditionType;
+typedef enum PropertyConditionFlags PropertyConditionFlags;
+typedef enum AutomationElementMode AutomationElementMode;
+typedef enum TreeScope TreeScope;
+typedef enum NavigateDirection NavigateDirection;
+typedef enum NormalizeState NormalizeState;
+typedef enum TreeTraversalOptions TreeTraversalOptions;
+typedef enum ProviderType ProviderType;
+typedef enum AutomationIdentifierType AutomationIdentifierType;
+typedef enum EventArgsType EventArgsType;
+typedef enum AsyncContentLoadedState AsyncContentLoadedState;
+typedef enum StructureChangeType StructureChangeType;
+typedef enum TextEditChangeType TextEditChangeType;
+typedef enum NotificationKind NotificationKind;
+typedef enum NotificationProcessing NotificationProcessing;
+typedef enum DockPosition DockPosition;
+typedef enum ScrollAmount ScrollAmount;
+typedef enum WindowVisualState WindowVisualState;
+typedef enum SupportedTextSelection SupportedTextSelection;
+typedef enum TextPatternRangeEndpoint TextPatternRangeEndpoint;
+typedef enum TextUnit TextUnit;
+typedef enum TextPatternRangeEndpoint TextPatternRangeEndpoint;
+typedef enum SynchronizedInputType SynchronizedInputType;
+typedef enum ToggleState ToggleState;
+
+typedef struct UiaCondition UiaCondition;
+typedef struct UiaPropertyCondition UiaPropertyCondition;
+typedef struct UiaAndOrCondition UiaAndOrCondition;
+typedef struct UiaNotCondition UiaNotCondition;
+typedef struct UiaCacheRequest UiaCacheRequest;
+typedef struct UiaFindParams UiaFindParams;
+typedef struct UiaEventArgs UiaEventArgs;
+typedef struct UiaPropertyChangedEventArgs UiaPropertyChangedEventArgs;
+typedef struct UiaStructureChangedEventArgs UiaStructureChangedEventArgs;
+typedef struct UiaChangeInfo UiaChangeInfo;
+typedef struct UiaPoint UiaPoint;
--- /dev/null
+#include <Windows.h>
+#include <objbase.h>
+#include "PolyButton.h"
+#include "resource.h"
+
+//#pragma comment(lib, "uiautomationcore")
+#pragma comment(lib, "ole32")
+#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+
+
+LRESULT OnCommand(HWND hwnd, int code, UINT nID, HWND hwndCtrl)
+{
+ switch (nID)
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hwnd, nID);
+ return TRUE;
+ case IDC_CUSTOM_ENABLE:
+ {
+ HWND custom = GetDlgItem(hwnd, IDC_CUSTOM1);
+ EnableWindow(custom, (IsDlgButtonChecked(hwnd, IDC_CUSTOM_ENABLE) == BST_CHECKED));
+ InvalidateRect(custom, NULL, TRUE);
+ return TRUE;
+ }
+ case IDC_CUSTOM_PRESSED:
+ // UINT SendMessage(custom, PBM_GETSTATE, 0L, 0L);
+ // SendMessage(custom, PBM_SETSTATE, (WPARAM)state, 0L);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ CheckDlgButton(hDlg, IDC_CUSTOM_ENABLE,
+ IsWindowEnabled(GetDlgItem(hDlg, IDC_CUSTOM_ENABLE)) ? BST_CHECKED : BST_UNCHECKED);
+ break;
+ case WM_COMMAND:
+ return OnCommand(hDlg, (int)HIWORD(wParam), (UINT)LOWORD(wParam), (HWND)lParam);
+ }
+ return FALSE;
+}
+
+int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nShowCmd)
+{
+ HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ if (SUCCEEDED(hr))
+ {
+ PolyButton_RegisterControl(hInstance);
+ DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
+ CoUninitialize();
+ }
+ return SUCCEEDED(hr) ? ERROR_SUCCESS : 1;
+}
\ No newline at end of file
--- /dev/null
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <winres.h>
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "Comments", "Direct2D sample application"
+ VALUE "CompanyName", "Renishaw plc."
+ VALUE "FileDescription", "Direct2D sample application"
+ VALUE "FileVersion", "1, 0, 0, 0"
+ VALUE "InternalName", "D2DDemo"
+ VALUE "LegalCopyright", "Copyright (c) 2020 Renishaw plc."
+ VALUE "OriginalFilename", "D2DDemo.exe"
+ VALUE "ProductVersion", "1, 0, 0, 0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United Kingdom) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include <winres.h>\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DIALOG1 DIALOGEX 0, 0, 309, 176
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "UIA Custom Control Demo"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ CONTROL "Check1",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,176,7,112,10
+ GROUPBOX "Static",IDC_STATIC,176,18,105,38
+ CONTROL "Option 1",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,184,28,91,10
+ CONTROL "Option 2",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,184,42,92,10
+ LTEXT "Label",IDC_STATIC,176,62,18,8
+ EDITTEXT IDC_EDIT1,202,59,100,14,ES_AUTOHSCROLL
+ CONTROL "Hello",IDC_CUSTOM1,"POLYBUTTON",WS_TABSTOP,29,26,47,37
+ DEFPUSHBUTTON "OK",IDOK,198,155,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,252,155,50,14
+ GROUPBOX "Custom control",IDC_STATIC,7,7,95,120
+ CONTROL "Enabled",IDC_CUSTOM_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,73,82,10
+ CONTROL "Pressed",IDC_CUSTOM_PRESSED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,14,87,82,10
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_DIALOG1, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 302
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 169
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// AFX_DIALOG_LAYOUT
+//
+
+IDD_DIALOG1 AFX_DIALOG_LAYOUT
+BEGIN
+ 0
+END
+
+#endif // English (United Kingdom) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
--- /dev/null
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by main.rc
+//
+#define IDD_DIALOG1 101
+#define IDC_CHECK1 102
+#define IDC_RADIO1 103
+#define IDC_RADIO2 104
+#define IDC_EDIT1 105
+#define IDC_CUSTOM1 106
+#define IDC_CUSTOM_ENABLE 1005
+#define IDC_CUSTOM_ENABLE2 1006
+#define IDC_CUSTOM_PRESSED 1006
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1006
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif