From: Pat Thoyts Date: Fri, 4 Oct 2024 16:06:58 +0000 (+0100) Subject: C version of the UI Accessibility custom control demo X-Git-Url: https://privyetmir.co.uk/gitweb.cgi?a=commitdiff_plain;ds=sidebyside;p=accessibility%2Fuiademo_c.git C version of the UI Accessibility custom control demo --- cbb8c4e67d769284ffe8c604ceb7913af621e278 diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..05fb160 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# 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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcc9ffb --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode/ +build/ +*.aps \ No newline at end of file diff --git a/ButtonProvider.c b/ButtonProvider.c new file mode 100644 index 0000000..c2cfaf7 --- /dev/null +++ b/ButtonProvider.c @@ -0,0 +1,384 @@ +#include "ButtonProvider.h" +#include +#include + +#include "UIAutomationTypedefs.h" +#include +#include +#include + +#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 diff --git a/ButtonProvider.h b/ButtonProvider.h new file mode 100644 index 0000000..633cc38 --- /dev/null +++ b/ButtonProvider.h @@ -0,0 +1,6 @@ +#include +#include + +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); diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5b1af0d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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}) diff --git a/PolyButton.c b/PolyButton.c new file mode 100644 index 0000000..738d46b --- /dev/null +++ b/PolyButton.c @@ -0,0 +1,205 @@ +#include "PolyButton.h" +#include "ButtonProvider.h" +#include +#define _USE_MATH_DEFINES +#include + +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); +} diff --git a/PolyButton.h b/PolyButton.h new file mode 100644 index 0000000..35d3633 --- /dev/null +++ b/PolyButton.h @@ -0,0 +1,8 @@ +#ifndef _PolyButton_h_INCLUDE +#define _PolyButton_h_INCLUDE + +#include + +void PolyButton_RegisterControl(_In_ HINSTANCE hInstance); + +#endif // _PolyButton_h_INCLUDE diff --git a/UIAutomationTypedefs.h b/UIAutomationTypedefs.h new file mode 100644 index 0000000..3652b15 --- /dev/null +++ b/UIAutomationTypedefs.h @@ -0,0 +1,43 @@ +// 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; diff --git a/main.c b/main.c new file mode 100644 index 0000000..cb35f29 --- /dev/null +++ b/main.c @@ -0,0 +1,58 @@ +#include +#include +#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 diff --git a/main.rc b/main.rc new file mode 100644 index 0000000..bf59131 --- /dev/null +++ b/main.rc @@ -0,0 +1,165 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +///////////////////////////////////////////////////////////////////////////// +#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 \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 + diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..17eda44 --- /dev/null +++ b/resource.h @@ -0,0 +1,24 @@ +//{{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