<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
  <channel><title>Simon Mourier's blog</title>
<description>Simon Mourier's blog about Microsoft technologies C#, C/C++, .NET, WinRT, COM, DirectX, Direct2D, Direct Composition, Windows Shell, Windows Imaging (Wic), P/Invoke, Web, Desktop, Winforms, WPF, WinUI3, MAUI, SQLite, etc.</description>
<generator>SmoBlog</generator>
<link>https://www.simonmourier.com/</link><atom:link rel="self" type="application/rss+xml" href="https://www.simonmourier.com/feed/rss" xmlns:atom="http://www.w3.org/2005/Atom" />
<item>
  <title>IWebBrowser2::Quit Do not work on some explorer tabs</title>
  <link>https://www.simonmourier.com/blog/79919134/</link>
  <description>&lt;p&gt;Well, I can reproduce &amp;quot;sometimes&amp;quot;... It's strange but here is a workaround that should work, it uses &lt;a href="https://learn.microsoft.com/en-us/previous-versions/mt725310(v=vs.85)" rel="nofollow noreferrer"&gt;IWebBrowser2::HWND&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagew" rel="nofollow noreferrer"&gt;PostMessage&lt;/a&gt; asking the owner window to close:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var staThread = new Thread(() =&amp;gt;
{
    var shellType = Type.GetTypeFromCLSID(new Guid(&amp;quot;9BA05972-F6A8-11CF-A442-00A0C90A8F39&amp;quot;))!;
    dynamic shellWindows = Activator.CreateInstance(shellType!)!;

    // we need to loop here because some windows may not close immediately
    do
    {
        var toClose = new List&amp;lt;dynamic&amp;gt;();
        foreach (var window in shellWindows)
        {
            // add your logic here...
            toClose.Add(window);
        }
        if (toClose.Count == 0)
            break;

        foreach (var window in toClose)
        {
            nint hwnd;
            try
            {
                hwnd = (nint)window.HWND;
            }
            catch
            {
                continue;
            }

            const uint WM_CLOSE = 0x0010;
            PostMessage(hwnd, WM_CLOSE, 0, 0);
        }
    } while (true);
});

staThread.SetApartmentState(ApartmentState.STA);
staThread.Start();
staThread.Join();

[DllImport(&amp;quot;user32&amp;quot;)]
public static extern nint PostMessage(nint hWnd, uint Msg, nint wParam, nint lParam);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PS: your definition of &lt;code&gt;IShellWindows&lt;/code&gt; is useless since you use &lt;code&gt;dynamic&lt;/code&gt; (&lt;code&gt;IDispatch&lt;/code&gt; undercovers).&lt;/p&gt;
</description>
  <category>c#</category>
  <category>winapi</category>
  <category>com</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79919134/</guid>
  <pubDate>Thu, 02 Apr 2026 17:06:06 GMT</pubDate>
</item>
<item>
  <title>What/How is `D3DKMT_BRIGHTNESS_INFO` used for?</title>
  <link>https://www.simonmourier.com/blog/79910661/</link>
  <description>&lt;p&gt;These functions are for kernel mode. In user mode you can use &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/highlevelmonitorconfigurationapi/nf-highlevelmonitorconfigurationapi-getmonitorbrightness" rel="nofollow noreferrer"&gt;GetMonitorBrightness&lt;/a&gt; but it will only work if your monitor supports it.&lt;/p&gt;
</description>
  <category>c/c++</category>
  <category>winapi</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79910661/</guid>
  <pubDate>Thu, 19 Mar 2026 14:37:20 GMT</pubDate>
</item>
<item>
  <title>Providing stubs to a static library</title>
  <link>https://www.simonmourier.com/blog/79910129/</link>
  <description>&lt;p&gt;In the end, you should always be able to provide ABI-compatible .obj files that you compiled yourself (not necessarily in assembler, C or &amp;quot;extern C&amp;quot; C++ for example)&lt;/p&gt;
</description>
  <category>system</category>
  <category>winapi</category>
  <category>c/c++</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79910129/</guid>
  <pubDate>Wed, 18 Mar 2026 17:29:51 GMT</pubDate>
</item>
<item>
  <title>How to check if Windows TPM is available and enabled using NCrypt API?</title>
  <link>https://www.simonmourier.com/blog/79909778/</link>
  <description>&lt;p&gt;AI probably told you that crap :-) Here's the official way &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/tbs/nf-tbs-tbsi_getdeviceinfo" rel="nofollow noreferrer"&gt;Tbsi_GetDeviceInfo&lt;/a&gt; There's also the simple function &lt;code&gt;BOOL WINAPI Tbsi_Is_Tpm_Present()&lt;/code&gt; in the same tbs.h file, it's not visible in the doc but works.&lt;/p&gt;
</description>
  <category>c/c++</category>
  <category>winapi</category>
  <category>cryptography</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79909778/</guid>
  <pubDate>Wed, 18 Mar 2026 08:18:31 GMT</pubDate>
</item>
<item>
  <title>Windows application packaging project for native AOT</title>
  <link>https://www.simonmourier.com/blog/76359820/</link>
  <description>&lt;p&gt;Here's one solution (works for any .exe in fact), not sure it's officially sanctified though:&lt;/p&gt;
&lt;p&gt;Once you have created your packaging project, instead of adding a reference, just change the  &lt;code&gt;.wapproj&lt;/code&gt;, add these properties (adapt to your context):&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;  &amp;lt;PropertyGroup&amp;gt;
    ...
    &amp;lt;AppxBundlePlatforms&amp;gt;x64&amp;lt;/AppxBundlePlatforms&amp;gt;
    &amp;lt;EntryPointExe&amp;gt;MyApp.exe&amp;lt;/EntryPointExe&amp;gt;
    ...
  &amp;lt;/PropertyGroup&amp;gt;

  &amp;lt;ItemGroup&amp;gt;
    ...
    &amp;lt;Content Include=&amp;quot;..\MyApp\bin\x64\Release\net10.0-windows10.0.22000.0\publish\MyApp.exe&amp;quot;&amp;gt;
      &amp;lt;Link&amp;gt;MyApp.exe&amp;lt;/Link&amp;gt;
      &amp;lt;CopyToOutputDirectory&amp;gt;PreserveNewest&amp;lt;/CopyToOutputDirectory&amp;gt;
    &amp;lt;/Content&amp;gt;
    ...
  &amp;lt;/ItemGroup&amp;gt;

&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>c#</category>
  <category>console</category>
  <category>windows-shell</category>
  <category>aot</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/76359820/</guid>
  <pubDate>Thu, 05 Mar 2026 09:06:25 GMT</pubDate>
</item>
<item>
  <title>Integrating Windows Explorer as a control in Windows 11</title>
  <link>https://www.simonmourier.com/blog/79900361/</link>
  <description>&lt;p&gt;Yes, you can use &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nn-shobjidl_core-inamespacetreecontrol" rel="nofollow noreferrer"&gt;INameSpaceTreeControl&lt;/a&gt; as shown here &lt;a href="https://stackoverflow.com/a/48591421/403671"&gt;https://stackoverflow.com/a/48591421/403671&lt;/a&gt;&lt;/p&gt;
</description>
  <category>winapi</category>
  <category>com</category>
  <category>windows-shell</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79900361/</guid>
  <pubDate>Tue, 03 Mar 2026 23:11:22 GMT</pubDate>
</item>
<item>
  <title>How to ask Windows if a printer spools or not (in either .NET or Win32)?</title>
  <link>https://www.simonmourier.com/blog/79897185/</link>
  <description>&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/win32/printdocs/getprinter" rel="nofollow noreferrer"&gt;GetPrinter(...,2,...)&lt;/a&gt; should get you a &lt;a href="https://learn.microsoft.com/en-us/windows/win32/printdocs/printer-info-2" rel="nofollow noreferrer"&gt;PRINTER_INFO_2&lt;/a&gt; that has an &lt;code&gt;Attribute&lt;/code&gt; field that can contain &lt;code&gt;PRINTER_ATTRIBUTE_DIRECT&lt;/code&gt; (=2) if job is sent directly to the printer, not spooled.&lt;/p&gt;
</description>
  <category>.net</category>
  <category>winapi</category>
  <category>printing</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79897185/</guid>
  <pubDate>Thu, 26 Feb 2026 16:38:33 GMT</pubDate>
</item>
<item>
  <title>How do I get SCons to work on the Windows 10 command line?</title>
  <link>https://www.simonmourier.com/blog/59097128/</link>
  <description>&lt;p&gt;FWIW, in case this can be useful to anyone, I had &lt;strong&gt;no&lt;/strong&gt; &lt;code&gt;scons.bat&lt;/code&gt; anywhere in the tons of my python directories (much could be said also about python install mess...) after having done all the usual python pip trials.&lt;/p&gt;
&lt;p&gt;So what I did is create a &lt;code&gt;scons.bat&lt;/code&gt; file manually in a folder that I know is in my &lt;em&gt;PATH&lt;/em&gt; with simply this inside:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;python -m SCons %*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note SCons is here &lt;strong&gt;case-sensitive&lt;/strong&gt;&lt;/p&gt;
</description>
  <category>python</category>
  <category>winapi</category>
  <category>console</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/59097128/</guid>
  <pubDate>Sat, 31 Jan 2026 12:08:19 GMT</pubDate>
</item>
<item>
  <title>How to prevent "appstarting" cursor when calling IContextMenu::QueryContextMenu?</title>
  <link>https://www.simonmourier.com/blog/79851038/</link>
  <description>&lt;p&gt;This is not officially documented but you can implement the &lt;code&gt;IWaitCursorManager&lt;/code&gt; interface, here is its definition (in C#):&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;[Guid(&amp;quot;fc992f1f-debb-4596-b355-50c7a6dd1222&amp;quot;)]
public interface IWaitCursorManager
{
    HRESULT Start(CURSORID id);
    HRESULT Restore();
    HRESULT Stop(CURSORID id);
}

public enum CURSORID
{
    CID_WAIT = 0,
    CID_APPSTARTING = 1,
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To implement it you must pass a &amp;quot;site&amp;quot; to the &lt;code&gt;IContextMenu&lt;/code&gt; implementation. For that just &lt;code&gt;QueryInterface&lt;/code&gt; it for the &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/ocidl/nn-ocidl-iobjectwithsite" rel="nofollow noreferrer"&gt;IObjectWithSite&lt;/a&gt; interface, something like this (again in C#)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;var ows = (IObjectWithSite)contextMenu;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and then pass a COM object of yours that must implement &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/servprov/nn-servprov-iserviceprovider" rel="nofollow noreferrer"&gt;IServiceProvider&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;var mySite = new MySite();
ows.SetSite(mySite);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Your COM object will be called on &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/servprov/nf-servprov-iserviceprovider-queryservice(refguid_refiid_void)" rel="nofollow noreferrer"&gt;QueryService&lt;/a&gt; for a &lt;code&gt;IWaitCursorManager&lt;/code&gt;'s IID (among other IIDs). Pass an implementation of it that does nothing, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;public class MySite : IServiceProvider, IWaitCursorManager
{
    HRESULT QueryService(Guid siid,  Guid iid,  out object obj)
    {
       if (iid == typeof(IWaitCursorManager).Guid)
       {
          obj = this;
          return 0;
       }
          
       ...
    }

    HRESULT Start(CURSORID id)
    {
        return 0;
    }   
    
    HRESULT Restore()
    {
        return 0;
    }   

    HRESULT Stop(CURSORID id)
    {
        return 0;
    }   
}

&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>winapi</category>
  <category>rust</category>
  <category>windows-shell</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79851038/</guid>
  <pubDate>Mon, 22 Dec 2025 16:17:29 GMT</pubDate>
</item>
<item>
  <title>Is it possible to connect the New Outlook for Windows to our own server?</title>
  <link>https://www.simonmourier.com/blog/79847805/</link>
  <description>&lt;p&gt;Outlook for Windows is a pure web app, so you can't use COM addins, see here &lt;a href="https://learn.microsoft.com/en-us/microsoft-365-apps/outlook/get-started/state-of-com-add-ins" rel="nofollow noreferrer"&gt;https://learn.microsoft.com/en-us/microsoft-365-apps/outlook/get-started/state-of-com-add-ins&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The new Outlook for Windows doesn't support COM add-ins, but it does support Office web add-ins. To learn more about Office Add-ins, see Office Add-ins platform overview.&lt;/p&gt;
&lt;p&gt;For guidance to migrate COM add-ins to web add-ins, see Migrate from COM to web add-ins.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
  <category>com</category>
  <category>outlook</category>
  <category>winapi</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79847805/</guid>
  <pubDate>Mon, 15 Dec 2025 17:11:04 GMT</pubDate>
</item>
<item>
  <title>What are the strengths and weaknesses of using VTable/COM-centric APIs?</title>
  <link>https://www.simonmourier.com/blog/79842064/</link>
  <description>&lt;p&gt;This is why Microsoft has taken the COM approach more than 30 years ago and sticks with it (WinRT &lt;em&gt;is&lt;/em&gt; COM) almost everywhere in Windows. At its root, COM is &amp;quot;just&amp;quot; a &lt;em&gt;binary&lt;/em&gt; (not source code, this is fundamental) reusable contract, the vtable, plus the &lt;code&gt;IUnknown&lt;/code&gt; interface. Then, on top of this you have many optional other services (factory, registration, automation, threading, etc.). Just using vtable + IUnknown is often called &amp;quot;Nano COM&amp;quot; today. Typically the DirectX devs (Direct3D, Direct2D, DirectWrite, DirectComposition, etc.) have chosen this path, they provide an initial factory (no &lt;code&gt;CoCreateInstance&lt;/code&gt;/registry needed) and you can program the whole API using binary interfaces. If you can get a copy of &amp;quot;Essential COM&amp;quot; by Don Box, that may be of interest (especially the first pages that explain history, links with C++, etc.).&lt;/p&gt;
</description>
  <category>c/c++</category>
  <category>com</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79842064/</guid>
  <pubDate>Tue, 09 Dec 2025 15:16:36 GMT</pubDate>
</item>
<item>
  <title>How to force the Application.Run to wait for all async void handlers?</title>
  <link>https://www.simonmourier.com/blog/79821798/</link>
  <description>&lt;p&gt;Since what causes Application.Run to return is the main form closing, the most simple not hacky way to do it for me seems to prevent the form from closing (duh).&lt;/p&gt;
&lt;p&gt;For example with something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;internal static class Program
{
    [STAThread]
    static void Main()
    {
        ApplicationConfiguration.Initialize();
        var completedOperations = 0;
        var form = new Form();

        form.FormClosing += EarlyClosingBlocker.HandleFormClosing;
        form.Load += (s, e) =&amp;gt; _ = EarlyClosingBlocker.Run(async () =&amp;gt;
        {
            await Task.Delay(1000);
            completedOperations++;
        });

        form.Shown += (s, e) =&amp;gt; _ = EarlyClosingBlocker.Run(async () =&amp;gt;
        {
            await Task.Delay(2000);
            completedOperations++;
        });

        Application.Run(form);
        MessageBox.Show($&amp;quot;Completed operations: {completedOperations}&amp;quot;);
    }
}

public static class EarlyClosingBlocker
{
    private static readonly ConcurrentDictionary&amp;lt;AutoResetEvent, object?&amp;gt; _waitEvents = new();

    public static async Task Run(Func&amp;lt;Task&amp;gt; action)
    {
        var are = new AutoResetEvent(false);
        _waitEvents[are] = null;
        try
        {
            await action();
        }
        finally
        {
            are.Set();
            _waitEvents.TryRemove(are, out _);
        }
    }

    public static void HandleFormClosing(object? sender, FormClosingEventArgs e)
    {
        var events = _waitEvents.ToArray();
        if (events.Length == 0)
            return;

        // note we could use e.CloseReason to decide not to wait whatsoever
        e.Cancel = true;
        var form = (Form)sender!;
        form.Hide(); // or put a message, etc.
        _ = Task.Run(() =&amp;gt;
        {
            // wait for all events to finish, note this cannot run in an STA thread
            WaitHandle.WaitAll([.. events.Select(e =&amp;gt; e.Key)]);
            form.BeginInvoke(form.Close);
        });
    }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>c#</category>
  <category>winforms</category>
  <category>algorithms</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79821798/</guid>
  <pubDate>Wed, 19 Nov 2025 07:29:10 GMT</pubDate>
</item>
<item>
  <title>Flickering when drawing to IDCompositionSurface</title>
  <link>https://www.simonmourier.com/blog/79822270/</link>
  <description>&lt;p&gt;If you want to use Direct2D with Direct Composition, you want to use &lt;code&gt;IDCompositionSurface::BeginDraw&lt;/code&gt; asking for a D2D device context. Note for that you must create the Direct Composition device using a D2D device.&lt;/p&gt;
&lt;p&gt;This code should work better:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;int main()
{
    constexpr const wchar_t* WINDOW_CLASS_NAME = _T(&amp;quot;a&amp;quot;);
    constexpr const wchar_t* WINDOW_TITLE_NAME = _T(&amp;quot;a&amp;quot;);

    HRESULT hr = S_OK;

    CComPtr&amp;lt;ID3D11Device&amp;gt; d3dDevice;
    hr = D3D11CreateDevice(
        nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr,
        D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_DEBUG,
        nullptr, 0, D3D11_SDK_VERSION, &amp;amp;d3dDevice, nullptr, nullptr
    );
    CComPtr&amp;lt;IDXGIDevice&amp;gt; dxgiDevice;
    hr = d3dDevice-&amp;gt;QueryInterface(IID_PPV_ARGS(&amp;amp;dxgiDevice));

    CComPtr&amp;lt;ID2D1Device&amp;gt; d2dDevice;
    D2D1_CREATION_PROPERTIES creationProc = {};
    creationProc.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
    hr = D2D1CreateDevice(dxgiDevice, creationProc, &amp;amp;d2dDevice);

    // *use D2D device to create DComp device*
    CComPtr&amp;lt;IDCompositionDesktopDevice&amp;gt; dcDesktopDevice;
    hr = DCompositionCreateDevice3(d2dDevice, IID_PPV_ARGS(&amp;amp;dcDesktopDevice));

    WNDCLASSEX wndClass = { 0 };
    wndClass.cbSize = sizeof(WNDCLASSEX);
    wndClass.style = CS_NOCLOSE | CS_VREDRAW | CS_HREDRAW;
    wndClass.lpfnWndProc = DefWindowProc;
    wndClass.hInstance = GetModuleHandle(NULL);
    wndClass.lpszClassName = WINDOW_CLASS_NAME;
    hr = RegisterClassEx(&amp;amp;wndClass) ? S_OK : E_FAIL;

    HWND hwnd;
    hwnd = CreateWindowEx(
        WS_EX_OVERLAPPEDWINDOW | WS_EX_TRANSPARENT | WS_EX_NOREDIRECTIONBITMAP,
        WINDOW_CLASS_NAME, WINDOW_TITLE_NAME,
        WS_POPUP | WS_VISIBLE,
        1100, 980, 800, 80,
        nullptr, nullptr, GetModuleHandle(nullptr), nullptr
    ); hr = hwnd ? S_OK : E_FAIL;
    SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);

    CComPtr&amp;lt;IDCompositionTarget&amp;gt; dcTarget;
    hr = dcDesktopDevice-&amp;gt;CreateTargetForHwnd(hwnd, FALSE, &amp;amp;dcTarget);

    CComPtr&amp;lt;IDCompositionVisual2&amp;gt; dcVisual;
    hr = dcDesktopDevice-&amp;gt;CreateVisual(&amp;amp;dcVisual);
    dcTarget-&amp;gt;SetRoot(dcVisual);

    CComPtr&amp;lt;IDCompositionSurface&amp;gt; surface;
    hr = dcDesktopDevice-&amp;gt;CreateSurface(
        800, 80,
        DXGI_FORMAT_B8G8R8A8_UNORM,
        DXGI_ALPHA_MODE_PREMULTIPLIED,
        &amp;amp;surface
    );
    hr = dcVisual-&amp;gt;SetContent(surface);

    while (true)
    {
        POINT drawOffset = { 0,0 };
        CComPtr&amp;lt;ID2D1DeviceContext&amp;gt; d2dContext;
        hr = surface-&amp;gt;BeginDraw(nullptr, IID_PPV_ARGS(&amp;amp;d2dContext), &amp;amp;drawOffset);

        d2dContext-&amp;gt;Clear();

        // you *MUST* use the drawoffset you get back 
        d2dContext-&amp;gt;SetTransform(D2D1::Matrix3x2F::Translation((FLOAT)drawOffset.x, (FLOAT)drawOffset.y));

        CComPtr&amp;lt;ID2D1SolidColorBrush&amp;gt; brush;
        hr = d2dContext-&amp;gt;CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Red), &amp;amp;brush);
        d2dContext-&amp;gt;FillRoundedRectangle(D2D1_ROUNDED_RECT{ D2D1::RectF(10,10,200,50), 10, 10 }, brush);

        hr = surface-&amp;gt;EndDraw();
        hr = dcDesktopDevice-&amp;gt;Commit();
        hr = dcDesktopDevice-&amp;gt;WaitForCommitCompletion(); // optional
        Sleep(50); // this and the loop is optional see remarks
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note you don't need to call Sleep and do it again as with Direct Composition, you &lt;em&gt;compose&lt;/em&gt; your scene usually once (unless you want to animate it of course), you don't need to refresh it again at each vblank.&lt;/p&gt;
&lt;p&gt;Also, it's much better to use &lt;a href="https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/ui/visual-layer-in-desktop-apps" rel="nofollow noreferrer"&gt;Windows Visual Layer&lt;/a&gt; wich is simply the evolution of Direct Composition. It's not at all restricted to UWP, but it does require using WinRT API. There's an official example here &lt;a href="https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/HelloComposition" rel="nofollow noreferrer"&gt;https://github.com/microsoft/Windows.UI.Composition-Win32-Samples/tree/master/cpp/HelloComposition&lt;/a&gt; and a less bloated one here &lt;a href="https://gist.github.com/kennykerr/62923cdacaba28fedc4f3dab6e0c12ec" rel="nofollow noreferrer"&gt;https://gist.github.com/kennykerr/62923cdacaba28fedc4f3dab6e0c12ec&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With the Visual Layer the idea is roughly the same you can use a &lt;a href="https://learn.microsoft.com/en-us/uwp/api/windows.ui.composition.spritevisual" rel="nofollow noreferrer"&gt;SpriteVisual&lt;/a&gt; and from that SpriteVisual you can get a D2D context to write &lt;a href="https://learn.microsoft.com/en-us/windows/uwp/composition/composition-native-interop" rel="nofollow noreferrer"&gt;https://learn.microsoft.com/en-us/windows/uwp/composition/composition-native-interop&lt;/a&gt;&lt;/p&gt;
</description>
  <category>winapi</category>
  <category>directx</category>
  <category>direct2D</category>
  <category>direct-composition</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79822270/</guid>
  <pubDate>Tue, 18 Nov 2025 07:37:32 GMT</pubDate>
</item>
<item>
  <title>CsWin32 how to create an instance of PWSTR for e.g. GetWindowText</title>
  <link>https://www.simonmourier.com/blog/78300830/</link>
  <description>&lt;p&gt;With the latest version of &lt;a href="https://microsoft.github.io/CsWin32/docs/getting-started.html" rel="nofollow noreferrer"&gt;CsWin32&lt;/a&gt;, currently 0.3.242, this is much more simple and natural than it used to be:&lt;/p&gt;
&lt;p&gt;NativeMethods.txt:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;GetWindowTextW
GetWindowTextLengthW

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Program.cs:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;...
var len = PInvoke.GetWindowTextLength(hwnd);
Span&amp;lt;char&amp;gt; chars = new char[len + 1];
PInvoke.GetWindowText(hwnd, chars);
var text = chars.ToString();
...
&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>c#</category>
  <category>winapi</category>
  <category>interop</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/78300830/</guid>
  <pubDate>Tue, 04 Nov 2025 21:58:28 GMT</pubDate>
</item>
<item>
  <title>COM CCertRequest via CSWin32</title>
  <link>https://www.simonmourier.com/blog/79808656/</link>
  <description>&lt;p&gt;It should work, but it depends on your configuration. Also note CsWin32 has been &lt;a href="https://microsoft.github.io/CsWin32/docs/getting-started.html" rel="nofollow noreferrer"&gt;greatly improved lately&lt;/a&gt; and you want to use the &lt;code&gt;CsWin32RunAsBuildTask &lt;/code&gt;directive, so here's an example that works:&lt;/p&gt;
&lt;p&gt;csproj:
`&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;&amp;lt;PropertyGroup&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net9.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
    &amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;
    &amp;lt;CsWin32RunAsBuildTask&amp;gt;true&amp;lt;/CsWin32RunAsBuildTask&amp;gt;
    &amp;lt;DisableRuntimeMarshalling&amp;gt;true&amp;lt;/DisableRuntimeMarshalling&amp;gt;
    &amp;lt;InvariantGlobalization&amp;gt;true&amp;lt;/InvariantGlobalization&amp;gt;
&amp;lt;/PropertyGroup&amp;gt;

&amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;Microsoft.Windows.CsWin32&amp;quot; Version=&amp;quot;0.3.242&amp;quot;&amp;gt;
        &amp;lt;PrivateAssets&amp;gt;all&amp;lt;/PrivateAssets&amp;gt;
        &amp;lt;IncludeAssets&amp;gt;runtime; build; native; contentfiles; analyzers; buildtransitive&amp;lt;/IncludeAssets&amp;gt;
    &amp;lt;/PackageReference&amp;gt;
&amp;lt;/ItemGroup&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

`
&lt;p&gt;NativeMethods.txt:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;ICertAdmin
CCertRequest
ICertRequest
ICertRequest2
ICertRequest3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Program.cs:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-cpp"&gt;using System;
using System.Runtime.Versioning;
using Windows.Win32.Security.Cryptography.Certificates;

[assembly: SupportedOSPlatform(&amp;quot;windows5.1.2600&amp;quot;)]

namespace Test;

internal class Program
{
    static void Main()
    {
        var instance = CCertRequest.CreateInstance&amp;lt;ICertRequest&amp;gt;();
        Console.WriteLine(instance);
    }
}

&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>winapi</category>
  <category>com</category>
  <category>interop</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79808656/</guid>
  <pubDate>Tue, 04 Nov 2025 08:56:14 GMT</pubDate>
</item>
<item>
  <title>How to get the HWND handle of elements in .Net MAUI</title>
  <link>https://www.simonmourier.com/blog/79772961/</link>
  <description>&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/apps/winui/winui3/" rel="nofollow noreferrer"&gt;WinUI3&lt;/a&gt; is the technology used by MAUI on Windows, and you can refer to &lt;a href="https://learn.microsoft.com/en-us/windows/apps/develop/ui-input/windowing-overview" rel="nofollow noreferrer"&gt;Windowing overview for WinUI and Windows App SDK&lt;/a&gt; to understand the link between a WinUI3 Window to a Win32 HWND.&lt;/p&gt;
&lt;p&gt;Then, you can use &lt;a href="https://learn.microsoft.com/en-us/dotnet/maui/fundamentals/app-lifecycle#windows" rel="nofollow noreferrer"&gt;Platform lifecycle events&lt;/a&gt; to get it, something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using Microsoft.Maui.LifecycleEvents;

namespace MauiApp1
{
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp&amp;lt;App&amp;gt;()
                .ConfigureLifecycleEvents(events =&amp;gt;
                {
#if WINDOWS
                    events.AddWindows(bld =&amp;gt;
                    {
                        // window here is of Microsoft.UI.Xaml.Window type (WinUI3)
                        bld.OnWindowCreated(window =&amp;gt;
                        {
                            var hwnd = Microsoft.UI.Win32Interop.GetWindowFromWindowId(window.AppWindow.Id);
                        });
                    });
#endif
                });

            return builder.Build();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>c#</category>
  <category>wpf</category>
  <category>maui</category>
  <category>winui</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79772961/</guid>
  <pubDate>Tue, 23 Sep 2025 20:00:57 GMT</pubDate>
</item>
<item>
  <title>WinUI 3 DataTemplateSelector always crashed when using AOT</title>
  <link>https://www.simonmourier.com/blog/79767074/</link>
  <description>&lt;p&gt;Your project doesn't even work w/o AOT publishing, it raises an &lt;code&gt;InvalidCastException &lt;/code&gt;error:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://i.sstatic.net/jtBY1ghF.png" rel="nofollow noreferrer"&gt;&lt;img src="https://i.sstatic.net/jtBY1ghF.png" alt="InvalidCastException" referrerpolicy="no-referrer" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The problem comes from the fact you want to expose a &amp;quot;pure&amp;quot; managed object (of type &lt;code&gt;ADataTemplateSelector&lt;/code&gt;) to the underlying WinUI3 (native) layers but there's not enough information for C#/WinRT to project this type back. Invalid cast here means: this type doesn't implement what's needed to participate to the XAML/WinUI3 party.&lt;/p&gt;
&lt;p&gt;If you work with Visual Studio and take a closer look over this managed class, you will see this:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://i.sstatic.net/cwwGcdMg.png" rel="nofollow noreferrer"&gt;&lt;img src="https://i.sstatic.net/cwwGcdMg.png" alt="partial warning" referrerpolicy="no-referrer" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Class ... implements WinRT interface but it ... isn't marked partial.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The warning tells you to add a &lt;code&gt;partial&lt;/code&gt; keyword to the class to allow C#/WinRT to generate necessary information for this type to be recognised as a valid WinUI3 framework type:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public partial class ADataTemplateSelector : DataTemplateSelector
{
    ...
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you've done that the warning should go and the project should run and work when published as AOT.&lt;/p&gt;
&lt;p&gt;PS: there are other warnings in this code. In general and especially with AOT publishing you really want to make sure there are none left, even when it seems to work:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://i.sstatic.net/mLhU2dFD.png" rel="nofollow noreferrer"&gt;&lt;img src="https://i.sstatic.net/mLhU2dFD.png" alt="AOT warnings" referrerpolicy="no-referrer" /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
  <category>c#</category>
  <category>winui</category>
  <category>aot</category>
  <category>winapi</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79767074/</guid>
  <pubDate>Wed, 17 Sep 2025 16:41:39 GMT</pubDate>
</item>
<item>
  <title>How to get the value of a runtime property back into a design time property?</title>
  <link>https://www.simonmourier.com/blog/79761084/</link>
  <description>&lt;p&gt;Assuming you know how to dynamically add a TypeConverter to a property, as seen in &lt;a href="https://stackoverflow.com/a/79754918/403671"&gt;How to get a runtime list into a PropertyGrid?&lt;/a&gt;, you need to build a a converter with properties that remember the initial instance that's being worked on.&lt;/p&gt;
&lt;p&gt;Here is some sample code you can test like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-clike"&gt;public class A
{
    [TypeConverter(typeof(FullNameConverter))] // for quick demo purpose
    public string FullName { get; set; } = &amp;quot;John Doe&amp;quot;;
}

// a class that knows how to parse and format a full name
public class FullName
{
    public string Forename { get; set; }
    public string LastName { get; set; }

    public override string ToString() =&amp;gt; $&amp;quot;{Forename} {LastName}&amp;quot;;

    public static FullName Parse(string? fullName)
    {
        if (string.IsNullOrWhiteSpace(fullName))
            return new FullName();

        var split = fullName.Split(' ');
        return new FullName { Forename = split[0], LastName = split[1] };
    }
}

// A &amp;lt;=&amp;gt; FullName property descriptors (they remember the A instance)
public class FullNamePropertyDescriptor : PropertyDescriptor
{
    // we use initial value to be able to reset (feature sometimes used by property grids)
    public FullNamePropertyDescriptor(A instance, object initialValue, string name, Type propertyType, Attribute[]? attrs)
        : base(name, attrs)
    {
        Instance = instance;
        PropertyType = propertyType;
        InitialValue = GetValue(initialValue);
    }

    public A Instance { get; }
    public object? InitialValue { get; }

    public override Type ComponentType =&amp;gt; Instance.GetType();
    // honor ReadOnly attributes on FullName properties
    public override bool IsReadOnly =&amp;gt; Attributes.OfType&amp;lt;ReadOnlyAttribute&amp;gt;().FirstOrDefault()?.IsReadOnly ?? false;
    public override Type PropertyType { get; }
    public override bool CanResetValue(object component) =&amp;gt; true;
    public override bool ShouldSerializeValue(object component) =&amp;gt; false;
    public override void ResetValue(object component) =&amp;gt; SetValue(component, InitialValue);

    // component is a string here
    public override object? GetValue(object? component)
    {
        var fn = FullName.Parse(component as string);

        // defer to FullName properties
        return TypeDescriptor.GetProperties(typeof(FullName))[Name].GetValue(fn);
    }

    // component is a string here
    public override void SetValue(object? component, object? value)
    {
        var fn = FullName.Parse(component as string);

        // defer to FullName properties
        TypeDescriptor.GetProperties(typeof(FullName))[Name].SetValue(fn, value);

        // update original object
        Instance.FullName = fn.ToString();
    }
}

// the A &amp;lt;=&amp;gt; FullName type converter
public class FullNameConverter : TypeConverter
{
    public override bool GetPropertiesSupported(ITypeDescriptorContext? context) =&amp;gt; true;
    public override PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
        =&amp;gt; new([.. TypeDescriptor.GetProperties(typeof(FullName))
            .OfType&amp;lt;PropertyDescriptor&amp;gt;()
            .Select(pd =&amp;gt; new FullNamePropertyDescriptor(
                (A)context.Instance,
                value,
                pd.Name,
                pd.PropertyType,
                pd.Attributes.Cast&amp;lt;Attribute&amp;gt;().ToArray()))]
            );

    // we can convert to and from string (the underlying type)
    public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType) =&amp;gt; typeof(string) == destinationType || base.CanConvertTo(context, destinationType);
    public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) =&amp;gt; typeof(string) == sourceType || base.CanConvertFrom(context, sourceType);
    public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) =&amp;gt; value;
    public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) =&amp;gt; value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here's the result:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://i.sstatic.net/Ol4mASe1.png" rel="nofollow noreferrer"&gt;&lt;img src="https://i.sstatic.net/Ol4mASe1.png" alt="enter image description here" referrerpolicy="no-referrer" /&gt;&lt;/a&gt;&lt;/p&gt;
</description>
  <category>propertygrid</category>
  <category>system</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79761084/</guid>
  <pubDate>Wed, 10 Sep 2025 18:55:12 GMT</pubDate>
</item>
<item>
  <title>Managed exceptions in MAUI Windows app logged as CoreMessagingXP.dll crashes</title>
  <link>https://www.simonmourier.com/blog/79759069/</link>
  <description>&lt;p&gt;MAUI on Windows is implemented using &lt;a href="https://learn.microsoft.com/en-us/windows/apps/winui/winui3/" rel="nofollow noreferrer"&gt;WinUI3&lt;/a&gt;. WinUI3 itself is a native framework that has bindings/projections for .NET (but also C++), etc.&lt;/p&gt;
&lt;p&gt;Anyway in Windows, unless something catches it, a crash will end up in the Event Log. This is the case by default with WinUI3 and MAUI over it, and that's what you see (note that with proper setup and symbols, a developer can investigate that kind of raw reports).&lt;/p&gt;
&lt;p&gt;Here is how you can catch it (I've use the &lt;a href="https://www.nuget.org/packages/system.diagnostics.eventlog" rel="nofollow noreferrer"&gt;System.Diagnostics.EventLog&lt;/a&gt; package but you can use any logging facility):&lt;/p&gt;
&lt;p&gt;Change &lt;code&gt;Platforms\Windows\App.xaml.cs&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public partial class App : MauiWinUIApplication
{
    public App()
    {
        // we handle only our specific exception type in case of
        // unobserved exceptions, best effort for bad coding practises :-)
        AppDomain.CurrentDomain.FirstChanceException +=
            (sender, e) =&amp;gt; HandleException(e.Exception as MyException);

        UnhandledException += (sender, e) =&amp;gt;
        {
            // prevent the app from crashing &amp;amp; exit gracefully
            e.Handled = true;
            HandleException(e.Exception);
        };

        InitializeComponent();
    }

    private void HandleException(Exception? ex)
    {
        if (ex == null) return;

        // pick a source that exists like &amp;quot;Application&amp;quot; or create a new one (requires admin rights)
        // here we use &amp;quot;.NET Runtime&amp;quot;
        // see why here https://www.jitbit.com/alexblog/266-writing-to-an-event-log-from-net-without-the-description-for-event-id-nonsense/
        System.Diagnostics.EventLog.WriteEntry(
            &amp;quot;.NET Runtime&amp;quot;,
            $&amp;quot;Unhandled exception: {ex.Message}&amp;quot;,
            System.Diagnostics.EventLogEntryType.Error,
            1000);

        Exit();
    }

    protected override MauiApp CreateMauiApp() =&amp;gt; MauiProgram.CreateMauiApp();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the result in Event Viewer:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://i.sstatic.net/2qurGdM6.png" rel="nofollow noreferrer"&gt;&lt;img src="https://i.sstatic.net/2qurGdM6.png" alt="Event Viewer" referrerpolicy="no-referrer" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, why the first line? Because your reproducing code does this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private async void OnLanguageClicked(object? sender, EventArgs e)
{
    throw new Exception(&amp;quot;Simulated exception in OnLanguageClicked&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note with Visual Studio the green swiggles:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://i.sstatic.net/3tOuEtlD.png" rel="nofollow noreferrer"&gt;&lt;img src="https://i.sstatic.net/3tOuEtlD.png" alt="swiggles" referrerpolicy="no-referrer" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;What it means is the implicit task you're starting with the &lt;code&gt;async&lt;/code&gt; keyword is &lt;em&gt;unobserved&lt;/em&gt;. It's generally bad practise, and in this case it crashes the native layers underneath (which could handle this in a better way we agree on that...) so you get a real native handled error that you can see in the event viewer.&lt;/p&gt;
&lt;p&gt;In this case there are two ways of dealing with this kind of errors:&lt;/p&gt;
&lt;p&gt;A: use a custom exception type for all your errors and you can catch it in FirstChanceException handler (there are many other 1st chance exceptions that we don't care about that's why you need a custom one), so for example this would be caught by our handlers:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private async void OnCounterClicked(object? sender, EventArgs e)
{
    throw new MyException(&amp;quot;hello from main page&amp;quot;);
}

public class MyException : Exception
{
    public MyException(string message) : base(message) { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;B: (preferred as it works for types of exceptions), just make sure you don't use this type of &lt;code&gt;void async&lt;/code&gt; &lt;a href="https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming" rel="nofollow noreferrer"&gt;bad practise code&lt;/a&gt; and use this pattern instead:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private void OnCounterClicked(object? sender, EventArgs e)
{
    _ = DoSomethingThatThrows();
}

Task DoSomethingThatThrows()
{
    throw new Exception(&amp;quot;hello from main page&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>c#</category>
  <category>.net</category>
  <category>maui</category>
  <category>winui</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79759069/</guid>
  <pubDate>Tue, 09 Sep 2025 08:13:45 GMT</pubDate>
</item>
<item>
  <title>How to get a runtime list into a PropertyGrid?</title>
  <link>https://www.simonmourier.com/blog/79754850/</link>
  <description>&lt;p&gt;You can override type descriptions before using types with the &lt;a href="https://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.typedescriptor" rel="nofollow noreferrer"&gt;TypeDescriptor&lt;/a&gt; class,  passing it a custom type descriptor.&lt;/p&gt;
&lt;p&gt;It's a bit convoluted and you want to make sure you scan types and properties before you configure type descriptor or you may run into stack overflow. (&lt;code&gt;TypeDescriptor.GetProperties&lt;/code&gt; has a &lt;code&gt;noCustomTypeDesc&lt;/code&gt; parameter but it seems ineffective with my .NET 9 tests...)&lt;/p&gt;
&lt;p&gt;Here is a sample &lt;a href="http://learn.microsoft.com/en-us/dotnet/api/system.componentmodel.typedescriptionprovider" rel="nofollow noreferrer"&gt;TypeDescriptionProvider&lt;/a&gt; that should do what you need from assembly B (.NET Core code):&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-dotnet"&gt;Data obj = new Data();

// configure a custom type descriptor for the Data type
var td = new DataDescriptor();
TypeDescriptor.AddProvider(new DescriptionProvider(td), typeof(Data));

propertyGrid1.SelectedObject = obj;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sample classes:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-dotnet"&gt;public class DescriptionProvider(ICustomTypeDescriptor descriptor) : TypeDescriptionProvider
{
    public override ICustomTypeDescriptor? GetTypeDescriptor([DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] Type objectType, object? instance)
        =&amp;gt; descriptor;
}

public class DataDescriptor : CustomTypeDescriptor
{
    public DataDescriptor()
    {
        var properties = TypeDescriptor.GetProperties(typeof(Data));
        var list = new List&amp;lt;PropertyDescriptor&amp;gt;();
        foreach (PropertyDescriptor property in properties)
        {
            if (property.Name == &amp;quot;Text&amp;quot;)
            {
                list.Add(TypeDescriptor.CreateProperty(typeof(Data), property, new TypeConverterAttribute(typeof(CustomConverter))));
            }
            else
            {
                list.Add(property);
            }
        }
        _properties = new PropertyDescriptorCollection([.. list]);
    }

    private PropertyDescriptorCollection _properties;
    public override PropertyDescriptorCollection GetProperties(Attribute[]? attributes) =&amp;gt; _properties;
}

public class CustomConverter : TypeConverter
{
    public override bool GetStandardValuesSupported(ITypeDescriptorContext? context) 
        =&amp;gt; true;
    public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
        =&amp;gt; new(new List&amp;lt;string&amp;gt; { &amp;quot;hello&amp;quot;, &amp;quot;world&amp;quot; });
}
&lt;/code&gt;&lt;/pre&gt;
</description>
  <category>winforms</category>
  <category>propertygrid</category>
  <category>system</category>
  <guid isPermaLink="false">https://www.simonmourier.com/blog/79754850/</guid>
  <pubDate>Wed, 03 Sep 2025 17:18:33 GMT</pubDate>
</item></channel>
</rss>