Initial version, working demo.
authorPat Thoyts <patthoyts@users.sourceforge.net>
Sun, 20 Sep 2020 19:34:17 +0000 (20:34 +0100)
committerPat Thoyts <patthoyts@users.sourceforge.net>
Sun, 20 Sep 2020 19:34:17 +0000 (20:34 +0100)
.gitignore [new file with mode: 0644]
.vscode/settings.json [new file with mode: 0644]
ButtonProvider.cpp [new file with mode: 0644]
ButtonProvider.h [new file with mode: 0644]
CMakeLists.txt [new file with mode: 0644]
PolyButton.cpp [new file with mode: 0644]
PolyButton.h [new file with mode: 0644]
README.md [new file with mode: 0644]
main.cpp [new file with mode: 0644]
main.rc [new file with mode: 0644]
resource.h [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..fcc9ffb
--- /dev/null
@@ -0,0 +1,3 @@
+.vscode/
+build/
+*.aps
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644 (file)
index 0000000..fddb5e4
--- /dev/null
@@ -0,0 +1,20 @@
+{
+    "files.associations": {
+        "memory": "cpp"
+    },
+    "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools",
+    "C_Cpp.default.defines": [
+         "UNICODE", "_UNICODE", "_WIN32_LEAN_AND_MEAN", "STRICT"
+    ],
+    "cmake.configureOnOpen": true,
+    "C_Cpp.default.cppStandard": "c++14",
+    "C_Cpp.default.cStandard": "c11",
+    "c-cpp-flylint.defines": [
+        "UNICODE", "_UNICODE", "_WIN32_LEAN_AND_MEAN", "STRICT"
+    ],
+    "c-cpp-flylint.standard": [
+        "c11",
+        "c++14"
+    ],
+    "c-cpp-flylint.cppcheck.suppressions": []
+}
\ No newline at end of file
diff --git a/ButtonProvider.cpp b/ButtonProvider.cpp
new file mode 100644 (file)
index 0000000..615f0a2
--- /dev/null
@@ -0,0 +1,181 @@
+//#include <initguid.h>
+#include "ButtonProvider.h"
+#include <UIAutomationClient.h>
+#include <UIAutomationCoreApi.h>
+#include <new>
+#include <stdio.h>
+
+HRESULT ButtonProvider::CreateInstance(HWND hwnd, IRawElementProviderSimple **ppProvider)
+{
+    ButtonProvider *provider = new (std::nothrow) ButtonProvider(hwnd);
+       wchar_t wsz[128]; swprintf_s(wsz, L"Created %p\n", provider); OutputDebugStringW(wsz);
+    return provider->QueryInterface(IID_IRawElementProviderSimple, reinterpret_cast<void**>(ppProvider));
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::QueryInterface(REFIID riid, void **ppv)
+{
+    HRESULT hr = S_OK;
+       *ppv = nullptr;
+    if (IsEqualIID(riid, IID_IUnknown)
+        || IsEqualIID(riid, IID_IRawElementProviderSimple))
+    {
+        *ppv = static_cast<IRawElementProviderSimple *>(this);
+        this->AddRef();
+    }
+    else if (IsEqualIID(riid, IID_IInvokeProvider))
+    {
+        *ppv = static_cast<IInvokeProvider *>(this);
+        this->AddRef();
+    }
+       else if (IsEqualIID(riid, IID_IToggleProvider))
+       {
+               *ppv = static_cast<IToggleProvider *>(this);
+               this->AddRef();
+       }
+    else
+        hr = E_NOINTERFACE;
+    return hr;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) ButtonProvider::AddRef()
+{
+       ULONG result = InterlockedIncrement(&_refcount);
+       //wchar_t wsz[128]; swprintf_s(wsz, L"AddRef %lu\n", result); OutputDebugStringW(wsz);
+       return result;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP_(ULONG) ButtonProvider::Release()
+{
+    unsigned long result = InterlockedDecrement(&_refcount);
+       //wchar_t wsz[128]; swprintf_s(wsz, L"Release %lu\n", result); OutputDebugStringW(wsz);
+       if (result == 0)
+    {
+               wchar_t wsz[128]; swprintf_s(wsz, L"Destroy %p\n", this); OutputDebugStringW(wsz);
+               delete this;
+    }
+    return result;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::get_ProviderOptions(ProviderOptions *pVal)
+{
+    HRESULT hr = E_POINTER;
+    if (pVal)
+    {
+               if (!IsWindow(_hwnd))
+                       hr = UIA_E_ELEMENTNOTENABLED;
+               else
+               {
+                       *pVal = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
+                       hr = S_OK;
+               }
+    }
+    return hr;    
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::GetPatternProvider(PATTERNID patternId, IUnknown **ppVal)
+{
+    HRESULT hr = E_POINTER;
+    if (ppVal)
+    {
+        hr = S_OK;
+        *ppVal = nullptr;
+        if (patternId == UIA_InvokePatternId)
+        {
+            hr = QueryInterface(IID_IUnknown, reinterpret_cast<void **>(ppVal));
+        }
+               else if (patternId == UIA_TogglePatternId)
+               {
+                       hr = QueryInterface(IID_IUnknown, reinterpret_cast<void **>(ppVal));
+               }
+    }
+    return hr;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::GetPropertyValue(PROPERTYID propertyId, VARIANT *pVal)
+{
+    if (propertyId == UIA_ControlTypePropertyId)
+    {
+        pVal->vt = VT_I4;
+        pVal->lVal = UIA_ButtonControlTypeId;
+    }
+       else if (propertyId == UIA_IsControlElementPropertyId)
+       {
+               pVal->vt = VT_BOOL;
+               pVal->boolVal = VARIANT_TRUE;
+       }
+       else if (propertyId == UIA_HasKeyboardFocusPropertyId)
+       {
+               pVal->vt = VT_BOOL;
+               pVal->boolVal = VARIANT_FALSE;
+       }
+       else if (propertyId == UIA_ClassNamePropertyId)
+    {
+               wchar_t name[32] = { 0 };
+               GetClassNameW(_hwnd, name, 32);
+        pVal->vt = VT_BSTR;
+        pVal->bstrVal = SysAllocString(name);
+    }
+    else if (propertyId == UIA_NamePropertyId)
+    {
+        int len = GetWindowTextLengthW(_hwnd) + 1;
+        wchar_t *name = new (std::nothrow) wchar_t[len]();
+        GetWindowTextW(_hwnd, name, len);
+        pVal->vt = VT_BSTR;
+        pVal->bstrVal = SysAllocString(name);
+        delete[] name;
+    }
+    else
+    {
+        pVal->vt = VT_EMPTY;
+    }
+    return S_OK;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::get_HostRawElementProvider(IRawElementProviderSimple **ppVal)
+{
+    HRESULT hr = E_POINTER;
+    if (ppVal)
+    {
+        hr = UiaHostProviderFromHwnd(_hwnd, ppVal);
+    }
+    return hr;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::Invoke()
+{
+       HRESULT hr = static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE);
+       if (IsWindow(_hwnd))
+       {
+               PostMessage(_hwnd, BM_CLICK, 0L, 0L);
+               hr = S_OK;
+       }
+       return hr;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::Toggle()
+{
+       HRESULT hr = static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE);
+       if (IsWindow(_hwnd))
+       {
+               PostMessage(_hwnd, BM_CLICK, 0, 0);
+               hr = S_OK;
+       }
+       return hr;
+}
+
+DECLSPEC_NOTHROW STDMETHODIMP ButtonProvider::get_ToggleState(ToggleState *pVal)
+{
+       HRESULT hr = E_POINTER;
+       if (pVal)
+       {
+               if (!IsWindow(_hwnd))
+                       hr = static_cast<HRESULT>(UIA_E_ELEMENTNOTAVAILABLE);
+               else
+               {
+                       auto state = SendMessage(_hwnd, BM_GETCHECK, WPARAM(0), LPARAM(0));
+                       *pVal = (state == BST_CHECKED) ? ToggleState_On : ToggleState_Off;
+                       hr = S_OK;
+               }
+       }
+       return hr;
+}
\ No newline at end of file
diff --git a/ButtonProvider.h b/ButtonProvider.h
new file mode 100644 (file)
index 0000000..ae1d551
--- /dev/null
@@ -0,0 +1,30 @@
+#include <Windows.h>
+#include <Ole2.h>
+#include <UIAutomationCore.h>
+
+class ButtonProvider : public IRawElementProviderSimple, public IInvokeProvider, public IToggleProvider
+{
+public:
+    static HRESULT CreateInstance(HWND hwnd, IRawElementProviderSimple **ppProvider);
+    
+    STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
+    STDMETHOD_(ULONG, AddRef)();
+    STDMETHOD_(ULONG, Release)();
+
+    STDMETHOD(get_ProviderOptions)(ProviderOptions *pVal);
+    STDMETHOD(GetPatternProvider)(PATTERNID patternId, IUnknown **ppVal);
+    STDMETHOD(GetPropertyValue)(PROPERTYID propertyId, VARIANT *pVal);
+    STDMETHOD(get_HostRawElementProvider)(IRawElementProviderSimple **ppVal);
+
+    STDMETHOD(Invoke)();
+
+       STDMETHOD(Toggle)();
+       STDMETHOD(get_ToggleState)(ToggleState *pVal);
+
+private:
+    ButtonProvider() : _refcount(0), _hwnd(HWND_DESKTOP) {}
+    ButtonProvider(HWND hwnd) : _refcount(0), _hwnd(hwnd) {}
+       virtual ~ButtonProvider() {}
+       unsigned long _refcount;
+    HWND _hwnd;
+};
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..a2db266
--- /dev/null
@@ -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.0)
+
+ENABLE_LANGUAGE(RC)
+
+project(UIADemo VERSION 1.0)
+set(SOURCE main.cpp main.rc resource.h
+           PolyButton.cpp PolyButton.h
+           ButtonProvider.cpp ButtonProvider.h)
+set(CMAKE_CXX_STANDARD 14)
+add_definitions(-DUNICODE -D_UNICODE -DSTRICT -D_WIN32_LEAN_AND_MEAN -D_WIN32_WINNT=_WIN32_WINNT_WIN8)
+add_executable(${PROJECT_NAME} WIN32 ${SOURCE})
diff --git a/PolyButton.cpp b/PolyButton.cpp
new file mode 100644 (file)
index 0000000..65d6932
--- /dev/null
@@ -0,0 +1,184 @@
+#include "PolyButton.h"
+#include "ButtonProvider.h"
+#include <gdiplus.h>
+#include <new>
+#include <string>
+
+static LRESULT CALLBACK WndProc(HWND hwnd, UINT messageId, WPARAM wparam, LPARAM lparam);
+
+PolyButton::PolyButton(HWND hwnd) : _hwnd(hwnd), _sides(6), _state(PolyButtonState::Normal), _provider(nullptr)
+{
+}
+
+PolyButton::~PolyButton()
+{
+    if (_provider)
+    {
+        _provider->Release();
+        _provider = nullptr;
+    }
+}
+
+void PolyButton::RegisterControl(HINSTANCE hInstance)
+{
+    WNDCLASS wc = {};
+    wc.style            = CS_HREDRAW | CS_VREDRAW;
+    wc.lpfnWndProc      = WndProc;
+    wc.hInstance        = hInstance;
+    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
+    wc.lpszClassName    = L"POLYBUTTON";
+    RegisterClass(&wc);
+}
+
+IRawElementProviderSimple *PolyButton::GetUIAutomationProvider(HWND hwnd)
+{
+    if (!_provider)
+    {
+        HRESULT hr = ButtonProvider::CreateInstance(hwnd, &_provider);
+    }
+    return _provider;
+}
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT messageId, WPARAM wparam, LPARAM lparam)
+{
+    switch (messageId)
+    {
+        case WM_CREATE:
+            {
+                PolyButton *ctrl = new (std::nothrow) PolyButton(hwnd);
+                if (!ctrl)
+                {
+                    PostQuitMessage(-1);
+                }
+                SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(ctrl));
+            }
+            break;
+        case WM_DESTROY:
+            {
+                PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                               UiaReturnRawElementProvider(hwnd, 0, 0, nullptr);
+                               UiaDisconnectProvider(ctrl->GetUIAutomationProvider(hwnd));
+                delete ctrl;
+            }
+            break;
+        case WM_GETOBJECT:
+            if (static_cast<long>(lparam) == static_cast<long>(UiaRootObjectId))
+            {
+                PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                IRawElementProviderSimple *provider = ctrl->GetUIAutomationProvider(hwnd);
+                return UiaReturnRawElementProvider(hwnd, wparam, lparam, provider);
+            }
+            break;
+        case WM_PAINT:
+            {
+                PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                ctrl->OnPaint();
+                return 1L;
+            }
+            break;
+        case WM_SETFOCUS:
+            {
+                PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                ctrl->OnSetFocus();
+            }
+            break;
+        case WM_KILLFOCUS:
+            {
+                PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                ctrl->OnKillFocus();
+            }
+            break;
+        case WM_LBUTTONDOWN:
+            {
+                PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                ctrl->InvokeButton();
+            }
+            break;
+               case BM_CLICK:
+                       {
+                               PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                               ctrl->InvokeButton();
+                       }
+                       break;
+               case BM_GETCHECK:
+               {
+                       PolyButton *ctrl = reinterpret_cast<PolyButton *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
+                       return ctrl->IsChecked() ? BST_CHECKED : BST_UNCHECKED;
+               }
+    }
+    return DefWindowProc(hwnd, messageId, wparam, lparam);
+}
+
+void PolyButton::OnSetFocus()
+{
+    _state |= PolyButtonState::Focussed;
+    RECT rc = {0};
+    GetClientRect(_hwnd, &rc);
+    InvalidateRect(_hwnd, &rc, TRUE);
+}
+
+void PolyButton::OnKillFocus()
+{
+    _state &= ~PolyButtonState::Focussed;
+    InvalidateRect(_hwnd, nullptr, TRUE);
+}
+
+inline Gdiplus::RectF RectItoF (const Gdiplus::Rect &rc)
+{
+    return Gdiplus::RectF(float(rc.X), float(rc.Y), float(rc.Width), float(rc.Height));
+}
+
+void PolyButton::OnPaint()
+{
+    PAINTSTRUCT ps = {0};
+    RECT rc;
+    GetClientRect(_hwnd, &rc);
+    HDC hdc = BeginPaint(_hwnd, &ps);
+    {
+        Gdiplus::Graphics g(hdc);
+        Gdiplus::Rect clientRect(rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top);
+        Gdiplus::Pen pen(Gdiplus::Color::Black, 1.0F);
+        Gdiplus::SolidBrush redBrush(Gdiplus::Color::Red);
+        Gdiplus::SolidBrush greenBrush(Gdiplus::Color::Green);
+        Gdiplus::Brush *brush = IsChecked() ? &redBrush : &greenBrush;
+        g.FillRectangle(brush, clientRect);
+        g.DrawRectangle(&pen, clientRect);
+        
+        Gdiplus::SolidBrush textBrush(Gdiplus::Color::Black);
+        Gdiplus::Font font(L"Segoe UI", 10);
+        Gdiplus::RectF layoutRect(float(clientRect.X), float(clientRect.Y), float(clientRect.Width), float(clientRect.Height));
+        Gdiplus::StringFormat format(Gdiplus::StringFormat::GenericDefault());
+        format.SetAlignment(Gdiplus::StringAlignmentCenter);
+        format.SetLineAlignment(Gdiplus::StringAlignmentCenter);
+        int len = GetWindowTextLength(_hwnd) + 1;
+        wchar_t *label = new wchar_t[len]();
+        GetWindowTextW(_hwnd, label, len);
+        g.DrawString(label, len, &font, layoutRect, &format, &textBrush);
+        delete [] label;
+    }
+    if ((_state & PolyButtonState::Focussed) == PolyButtonState::Focussed)
+        DrawFocusRect(hdc, &rc);
+    EndPaint(_hwnd, &ps);
+}
+
+void PolyButton::InvokeButton()
+{
+    if ((_state & PolyButtonState::Disabled) != PolyButtonState::Disabled)
+    {
+        if ((_state & PolyButtonState::Focussed) != PolyButtonState::Focussed)
+            SetFocus(_hwnd);
+               _state ^= PolyButtonState::Checked;
+        if (UiaClientsAreListening())
+        {
+            // Raise an event.
+                       IRawElementProviderSimple *p = nullptr;
+                       if (_provider)
+                       {
+                               _provider->QueryInterface(&p);
+                               UiaRaiseAutomationEvent(p, UIA_Invoke_InvokedEventId);
+                               p->Release();
+                       }
+        }
+        InvalidateRect(_hwnd, nullptr, TRUE);
+    }
+}
\ No newline at end of file
diff --git a/PolyButton.h b/PolyButton.h
new file mode 100644 (file)
index 0000000..b262484
--- /dev/null
@@ -0,0 +1,36 @@
+// Provide a polygonal button control.
+// Used to demo UIAutomation support for custom controls.
+
+#include <windows.h>
+#include <ole2.h>
+#include <uiautomation.h>
+
+enum class PolyButtonState {
+       Normal = 0,
+       Active = (1 << 0),
+       Pressed = (1 << 1),
+       Focussed = (1 << 2),
+       Checked = (1 << 3),
+       Disabled = (1 << 4)
+};
+DEFINE_ENUM_FLAG_OPERATORS(PolyButtonState);
+
+class PolyButton
+{
+public:
+    explicit PolyButton(HWND hwnd);
+    virtual ~PolyButton();
+    IRawElementProviderSimple *GetUIAutomationProvider(HWND hwnd);
+    void InvokeButton();
+    static void RegisterControl(HINSTANCE instance);
+       bool IsChecked() const { return ((_state & PolyButtonState::Checked) == PolyButtonState::Checked); }
+    void OnPaint();
+    void OnSetFocus();
+    void OnKillFocus();
+private:
+    PolyButton() {}
+    HWND _hwnd;
+    PolyButtonState _state;
+    int _sides;
+    IRawElementProviderSimple * _provider;
+};
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..eede211
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# Demonstration of UI Accessibility for Custom Control
+
+PolyButton is a custom toggle button with UIA support.
\ No newline at end of file
diff --git a/main.cpp b/main.cpp
new file mode 100644 (file)
index 0000000..6f09f76
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,37 @@
+#include <Windows.h>
+#include <gdiplus.h>
+#include "resource.h"
+#include "PolyButton.h"
+
+#pragma comment(lib, "gdiplus")
+#pragma comment(lib, "uiautomationcore")
+#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
+
+static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM /*lParam*/)
+{
+       switch (message)
+       {
+       case WM_COMMAND:
+               if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+               {
+                       EndDialog(hDlg, LOWORD(wParam));
+                       return TRUE;
+               }
+               break;
+       }
+       return FALSE;
+}
+
+int PASCAL
+WinMain(HINSTANCE hinst, HINSTANCE, LPSTR, int)
+{
+       Gdiplus::GdiplusStartupInput gdiplusStartupInput;
+       ULONG_PTR gdiplusToken(0);
+       Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
+       CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+       PolyButton::RegisterControl(hinst);
+       DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
+       CoUninitialize();
+       Gdiplus::GdiplusShutdown(gdiplusToken);
+       return 0;
+}
diff --git a/main.rc b/main.rc
new file mode 100644 (file)
index 0000000..595a68a
--- /dev/null
+++ b/main.rc
@@ -0,0 +1,162 @@
+// 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,7,7,112,10
+    GROUPBOX        "Static",IDC_STATIC,7,18,105,38
+    CONTROL         "Option 1",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,15,28,91,10
+    CONTROL         "Option 2",IDC_RADIO2,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,15,42,92,10
+    LTEXT           "Label",IDC_STATIC,7,62,18,8
+    EDITTEXT        IDC_EDIT1,33,59,100,14,ES_AUTOHSCROLL
+    CONTROL         "Hello",IDC_CUSTOM1,"POLYBUTTON",WS_TABSTOP,15,82,60,27
+    DEFPUSHBUTTON   "OK",IDOK,198,155,50,14
+    PUSHBUTTON      "Cancel",IDCANCEL,252,155,50,14
+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 (file)
index 0000000..c312e64
--- /dev/null
@@ -0,0 +1,22 @@
+//{{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
+
+// 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         1005
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif