Rick Strahl's Web Log https://weblog.west-wind.com/ Life, Surf, Code and everything in between West Wind Web Log en-us http://www.west-wind.com/images/WebLogBannerLogo.jpg Rick Strahl's Web Log https://weblog.west-wind.com/ 2026年1月04日 13:27:10 GMT 2025年12月08日 23:15:18 GMT What the heck is a `\\.\nul` path and why is it breaking my Directory Files Lookup? https://weblog.west-wind.com/posts/2025/Dec/08/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup 5133488_202512082315 2025年12月08日 23:15:18 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Dec/08/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup#Comments 4 https://weblog.west-wind.com/commentrss.aspx?id=5133488 dotnet <p><img src="https://weblog.west-wind.com/images/2025/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup/NullDeviceBanner.jpg" alt="Null Device Banner"></p> <p>In the last few months my <a href="https://markdownmonster.west-wind.com">Markdown Monster</a> Application Insights log has been inundated with a hard failure for lookups of a <code>\\.\nul</code> device error. In my logs this shows up as an error like this:</p> <p><img src="https://weblog.west-wind.com/images/2025/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup/NullDeviceErrorInDirectoryListing.png" alt="Null Device Error In Directory Listing"><br> <small><strong>Figure 1</strong> - Null device errors in the Application Insight logs</small></p> <p>This error started creeping up in my logs a few months ago, and since then has gotten more frequent. The error always occurs in the same location in the code and it's related to the File and Folder Browser in MM that displays the files available on the file system.</p> <p><img src="https://weblog.west-wind.com/images/2025/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup/FileAndFolderBrowserInMarkdownMonster.png" alt="File And Folder Browser In Markdown Monster"><br> <small><strong>Figure 2</strong> - The File and Folder Browser in Markdown Monster that is the target of the error</small></p> <p>This is a generic file browser, so it's very susceptible to all sorts of oddball user configurations and mis-configurations and this particular error likely is of the latter kind. However, after doing a bit of research this is not an uncommon error - there are a number of references to this although the cause of it doesn't appear to be very clear and can be related to various different things.</p> <p>In my use case, the error itself is triggered by doing a <code>fileInfo.Attributes</code> flag check on a file name that was returned by the <code>Directory.GetFiles()</code> lookup originally. The file is not a 'regular' file but rather a 'device' and access to <code>.Attributes.HasFlag()</code> throws the exception. And yeah that's <strong>very unexpected</strong>.</p> <p><a href="https://markdownmonster.west-wind.com?ut=weblog" target="_blank" title="Markdown Monster - Easy to use, yet powerfully productive Markdown Editing for Windows"> <img src="/images/sponsors/banner-example.png?v=1.2" class="da-content-image" /> </a></p> <p>Specifically the failure occurs here:</p> <pre><code class="language-csharp">// System.IO Exception: Incorrect Parameter \\.\nul if (fileInfo.Attributes.HasFlag(FileAttributes.Hidden)) item.IsCut = true; </code></pre> <p>This code sits inside of a loop that does a directory lookup then loops through all the files and adds them to the display tree model that eventually displays in the UI shown in <strong>Figure 1</strong>. The key is that the Attribute look up fails on this mystery 'null device' file.</p> <p>This is doubly frustrating in that <code>Attributes</code> is an enumeration that apparently is dynamically evaluated <strong>so the only way to detect the invalid state is to fail with an exception when calling <code>.HasFlag()</code></strong>. Bah 💩!</p> <h2 id="what-the-heck-is--nul">What the heck is <code>\\.\nul</code>?</h2> <p>My first reaction to this error was, yeah some user is doing something very unusual - ignore it. And yes, the error is rare, but there appear to be a number of different users running into this issue and more and more are starting to show up.</p> <p>Thanks to several commentors on this post it appears that this problem is caused by <a href="https://claude.com/product/claude-code">Claude Code</a> running Unix style Bash commands which produce invalid files on Windows. Quoting from <strong>Michael Christensen</strong>'s comment response below:</p> <blockquote> <p>I get this nul file all the time, in my case it's from Claude Code. The LLM has access to run Bash commands, and on Windows it uses Git Bash (presumably that was easier than getting it to use the windows command-line).</p> <p>What happens is it wants to suppress output on a command, but it also knows it's on Windows, so it tries something like <code>command &gt; nul 2&gt;&amp;1</code>. In Git Bash that creates the <code>nul</code> file that Windows has so much trouble with. It should be using <code>/dev/null</code>, which works properly.</p> </blockquote> <p>This explains why this happens - since it's Bash under a Unix shell is running the command it manages to create a <code>nul</code> file, which is an illegal file name in Windows. Windows in turn interprets that file as the <code>nul</code> device which then doesn't behave properly for a local file - specifically missing attributes.</p> <p>Before the comments, I started looking into this using several LLMs.</p> <p>The first response is a standard explanation of a null device:</p> <p><img src="https://weblog.west-wind.com/images/2025/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup/NullDeviceExplanation.png" alt="Null Device Explanation"><br> <small><strong>Figure 3</strong> - Basic LLM definition of a null device in file context</small></p> <p>Turns out <code>nul</code> refers to a the Windows <code>nul</code> device, which as the name suggests is a device sink that doesn't do anything. Apparently, it's meant to be used to pipe or stream STDIN/OUT to oblivion.</p> <p>Under normal circumstances a <code>nul</code> file or folder can't be created in Windows:</p> <p><img src="https://weblog.west-wind.com/images/2025/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup/NulFilesNotAllowedInWindows.png" alt="Nul Files Not Allowed In Windows"><br> <small><strong>Figure 4</strong> - <code>nul</code> files and folders can't be created in Explorer or the Shell</small></p> <p>But if you're doing it from the Bash shell apparently there's a way around that. Once the file exists the directory listing returns is and that's when the problem happens.</p> <p></p> <h2 id="working-around">Working around</h2> <p>So given what we know now, there are workarounds.</p> <ul> <li>Somehow (Claude) a file named <code>nul</code> is created</li> <li>Directory Listing picks up the <code>nul</code> file</li> <li>Windows treats this file as a Device, so some file features are not available (ie. Attributes)</li> </ul> <p>So here are a couple of workarounds I've used for this:</p> <h3 id="bobbing-for-apples---eh-errors">Bobbing for Apples - eh Errors</h3> <p>This falls into the simplest thing possible bucket:</p> <p>Wrap the failure into an exception. This is easy enough but I always hate try/catch wrapping shit willy nilly, because it's one of those one-off fixes that doesn't really address what's going on. But... it's the simplest thing and it works for obvious reasons:</p> <pre><code class="language-csharp">try { if (item.FileInfo.Attributes.HasFlag(FileAttributes.Hidden)) item.IsCut = true; } catch { // if we can't get attributes it's some device or map we shouldn't show item.IsCut = true; } </code></pre> <p>The idea here is that if attributes cannot be retrieved the file cannot be a 'normal' file that should be displayed and we can safely omit rendering it.</p> <h3 id="filtered-directory-listings">Filtered Directory Listings</h3> <p>The second solution handles this from the other end: It tries to ignore any non-file 'resources' or in this case 'devices'. It seems odd to me that <code>Directory.GetFiles()</code> would return a device as part of its default configuration, but apparently it does.</p> <p>It turns out the default <code>Directory.GetFiles()</code> filter mode is rather liberal in what it retrieves using the default <code>EnumerationOptions</code> instance:</p> <p><img src="https://weblog.west-wind.com/images/2025/What-the-heck-is-a-nul-path-and-why-is-it-breaking-my-Directory-Files-Lookup/FileInfoGetAttributesDefaultEnumerationMode.png" alt="File Info Get Attributes Default Enumeration Mode"><br> <small><strong>Figure 5</strong> - Default enumeration mode doesn't skip Devices</small></p> <p>It only skips over hidden and system files, but allows everything else including 'devices'.</p> <p>The <code>\\.\nul</code> error is caused by a <strong>Device</strong> that is the <code>nul</code> file.</p> <p>So, rather than using the default directory listing, we can use explicit <code>EnumerationOptions</code> and skip over devices, like this:</p> <pre><code class="language-csharp">string[] files = []; try { var opts = new EnumerationOptions { AttributesToSkip = FileAttributes.Hidden | FileAttributes.Directory | FileAttributes.Device | FileAttributes.ReparsePoint | FileAttributes.Temporary}; if (config.ShowAllFiles) opts.AttributesToSkip = FileAttributes.Directory | FileAttributes.Device | FileAttributes.ReparsePoint; files = Directory.Files(baseFolder, &quot;*.*&quot;, opts); } catch { /* ignore */ } foreach(var file in files) { ... } </code></pre> <p>This is a bit more verbose obviously, but it makes it real obvious that we're skipping certain files.</p> <p>Keep in mind that this is for a widely used generic implementation so there's a extra safety built in here with the <code>try/catch</code> to protect against invalid user provided folder names etc. from throwing out of the app. In that scenario there's no file list returned.</p> <p>The code above should prevent devices and reparse points - which is the likely the cause of the <code>\\.\nul</code> device errors I'm seeing in the log - to stop occurring as they are not being returned by the directory listing.</p> <p>This is now making me a bit paranoid that I should be using explicit EnumerationOptions whenever I do directory listings, but I suppopse this is a pretty rare error that - unfortunately - will blow up unsuspecting code if it tries to get information about the 'nul' device file returned if it's not filtered out. Ugh.</p> <p>Presumably Claude Code will get this sorted at some point and we'll probably never ever see this error again 😂.</p> <p></p> <h2 id="summary">Summary</h2> <p>Since implementing the second fix (without the explicit try/catch for the Attributes) the logs have been clean of these null file errors. Since I never had a repro scenario, I can only go off my logs though so I can't be 100% certain that the problem is solved yet, but using both the directory skip filter plus the exception handling around the Attributes retrieval most definitely will fix this issue - I'm leaving the latter try/catch out for now to see if the filtered directory list alone will fix the issue and so far it seems that way.</p> <p>However, it's important to remember that there can be funky behaviors with filesystem behavior related to symlinks and remapped folders (like DropBox) and apparently funky files created outside of the Windows direct file system managers (ie. Bash or other Linux shell). For example, I just ran into another issue where <code>FileInfo.Exists</code> reports my Dropbox folder as non-existing where <code>Directory.Exists()</code> does. IOW, special and symlinked files and folders can have odd behaviors and in those scenarios it might be a good idea to <strong>explicitly</strong> specify the file attributes to avoid unexpected failures that can't be easily handled with predictable logic especially when using a generic file client that accesses user provided paths.</p> <div style='margin: 10px 0px'><small>© Rick Strahl, West Wind Technologies, 2005-2026</small></div><div>Posted in <b><a href='/ShowPosts.aspx?Category=dotnet'>dotnet</a>&nbsp;&nbsp;</b></div> </small> Using the new WebView2 AllowHostInputProcessing Keyboard Mapping Feature https://weblog.west-wind.com/posts/2025/Aug/20/Using-the-new-WebView2-AllowHostInputProcessing-Keyboard-Mapping-Feature 4979824_202508210317 2025年8月21日 03:17:34 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Aug/20/Using-the-new-WebView2-AllowHostInputProcessing-Keyboard-Mapping-Feature#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4979824 WPF WebView <p><img src="https://weblog.west-wind.com/images/2025/Using-the-new-WebView2-AllowHostInputProcessing-Keyboard-Mapping-Feature/MenuAccellartorWebViewBanner.png" alt="Menu Accelerator Web View Banner"></p> <p>The WebView2 control is a complex beast and when you're building hybrid Desktop/Web applications that do serious interaction between a WebView2 control and a Desktop interface, you're going to find that there are a lot of areas where the keyboard and focus behavior is not as natural as you want it to be. Probably the most glaring example of this for me in <a href="https://markdownmonster.west-wind.com">Markdown Monster</a> has been key forwarding and specifically in handling the main window menu accelerators.</p> <p>Markdown Monster is a Markdown editor that uses a JavaScript based editor control, running in a WebView. The editor control interacts in a million ways with the WPF desktop interface, primarily from .NET and WPF into the JavaScript editor, but also with some operations in the JS code triggering the WPF UI with content updates from the editor. There are tons of UI operations like popping up context menus, hot keys that trigger operations in the WPF app and so on.</p> <p>For the most part this all works fine with most editor operations handled internally in the editor without ever forwarding keys into the host WPF application. So navigation keys and plain text input is all managed in the JS code.</p> <p>The WebView natively also supports <strong>some</strong> forwarding of keyboard operations from the WebView into the host <strong>if the WebView doesn't handle the keys</strong>. This may or may not be the behavior that you want, but even then it's tricky: some key events don't forward to WPF because the browser overrides them or in some cases forwarded keys are not triggered quite in the same way as 'native' keys trigger in WPF controls.</p> <p>What this means is that there's a lot of custom key handling to override default key behavior and some trial and error to figure whether keys need to be handled in JavaScript or in WPF. 😏</p> <p><a href="https://markdownmonster.west-wind.com?ut=weblog" target="_blank" title="Markdown Monster - Easy to use, yet powerfully productive Markdown Editing for Windows"> <img src="/images/sponsors/banner-example.png?v=1.2" class="da-content-image" /> </a></p> <h2 id="problem-main-window-menu-activation-alt-key-triggering">Problem: Main Window Menu Activation (Alt Key Triggering)</h2> <p>For me, one big issue has been correctly handling the <strong>main window menu accelerator keys</strong> in the WPF Window host. Accelerators are those 'highlighted' keys on buttons or the main application menu, which based on Windows conventions should activate when you press the <code>Alt</code> key. In any 'normal' Windows application pressing <code>Alt</code> anywhere, forces focus into the main menu bar, which in turn shows these accelerators like this:</p> <p><img src="https://weblog.west-wind.com/images/2025/Using-the-new-WebView2-AllowHostInputProcessing-Keyboard-Mapping-Feature/PressingAltActivatesMenuMnemonics.png" alt="Pressing Alt Activates Menu Mnemonics"><br> <small><strong>Figure 1</strong> - Accelerator keys show as underlines in menus and buttons and are typically triggered by pressing <code>alt</code> in the related context. Here <code>alt-t</code> brings up the Tools menu. Note the underlines in the main menu and submenu.</small></p> <p>The issue here is that while some keys pass through to WPF, others like accellerators are not or not recognized as accellerators. <a href="https://github.com/MicrosoftEdge/WebView2Feedback/issues/468">I filed an issue on the WebView2 Feedback site many years ago</a> and this has finally been addressed via a new feature that I describe below.</p> <h2 id="the-old-hack">The old Hack</h2> <p>Prior to the recently introduced <code>AllowInputToHostMapping</code> feature in the WebView, automatic alt key forwarding did not work. Instead, I had to resort to some ugly workarounds that:</p> <ul> <li>intercept alt-key presses</li> <li>use specific timing in code to determine whether it's a key combo or treated like an accelerator key</li> <li>manipulate focus explicitly and forward keys into WPF</li> </ul> <p>It worked, but very badly. Alt-key combos often failed to activate the menu, or the menu would activate but required another hit of <code>alt</code> key to actually activate properly. Good riddance to that code!</p> <h2 id="webview-focus-context-is-internal">WebView Focus Context is Internal</h2> <p>The reason for all of this WebView misbehavior is that by default <strong>the WebView control handles key events internally</strong> and maintains focus internally. Focus in the WebView control stays inside of the control unless you explicitly click outside of it into another control.</p> <p>For a lot of scenarios that is actually the behavior you want - you don't want all events or even keystrokes bleed outside of the WebView. Focusing out adds overhead and can easily cause weird side effects where common browser operations trigger both in the browser and then also in the host.</p> <p>Instead the WebView lets you handle events in JavaScript code, the WebView Shell (ie. things like <code>ctrl-f</code> Find, <code>ctrl-r</code> Refresh etc.) and then the .NET Wrapper exposes some of the events into the WPF host via the control wrapper. This works reasonably well for most things although it does mean if you need specific key sequences and shortcuts to work, you're likely going to have to build handlers both in JavaScript and in WPF.</p> <p>For example, in Markdown Monster I have a ton of keyboard shortcuts that are user customizable, and those are configured through a <code>KeyBindingManager</code> which is configurable and has options to define where a particular key is handled:</p> <ul> <li>In JavaScript Code via Command Name</li> <li>In .NET Via an Command object</li> <li>In some rare cases both</li> </ul> <p>If this sounds extreme - it is due to the nature of this application which is highly customizable and because it's an editor it explicitly deals with many custom key combinations. Figuring out what to handle where is a bit of trial and error.</p> <p>Generally speaking if you control the Html page, handling events in JavaScript is preferable as that's the fastest way to handle events. Calling into .NET is not exactly slow, but it does have more overhead than just running native JavaScript code and in my case calling editor APIs directly, rather than round tripping into .NET to something similar. So raw text operations are handled in the JS code typically while complex or UI heavy or input output related operations typically run in .NET handlers through the WPF interface.</p> <p>For most hybrid applications this isn't a huge issue because you likely just have a few keyboard operations - if any - that need to be explicitly handled.</p> <p>But one thing that almost every hybrid app has to deal with is the Main Menu handling, which is as described above is definitely not well behaved from within a WebView.</p> <h2 id="enter-corewebview2controlleroptionsallowhostinputprocessing">Enter CoreWebView2ControllerOptions.AllowHostInputProcessing</h2> <p>As of WebView .NET SDK <code>1.0.3351</code> and WebView runtime <code>1.0.1901.177</code> you can now have more of keyboard and mouse events bleed into WPF. This specifically fixes the default behavior of the Main Menu accelerator keys in Windows. When this option is enabled more keyboard events using 'special' keys bubble into the host interface where they previously did not.</p> <p>But it doesn't come without caveats, especially if you've been handling keys the way that it has been working previously. Specifically <code>AllowHostInputProcessing</code> intercepts keys <strong>before they hit the WebView Control</strong>, which means that some Windows/WPF default behaviors now end up overriding keys in the WebView.</p> <p>I'll have more on that a little bit later. Suffice it to say there are 'issues' you'll likely have to deal with, especially if you've handled keys based on the old behavior with WebView first key processing as was necessary before.</p> <p>To enable the new <code>AllowHostInputProcessing</code> behavior, requires that you configure the <code>CoreWebView2ControllerOptions.AllowHostInputProcessing</code> which needs to be set up during the WebView environment setup. It's not very obvious as it's part of the WebView Environment setup which is optional - most applications probably don't do this setup by default - so if you want to use this feature a couple of extra steps are required.</p> <p>I'm going to demonstrate this as part of a helper function (part of the <a href="https://github.com/RickStrahl/Westwind.WebView">Westwind.WebView library</a>) as that shows the entire process and is probably a good way in general to instantiate an interactive .NET/JS based WebView control. This helper uses a shared WebView environment, so that multiple controls in the same application all share the same WebView environment without having to explicit configure each one.</p> <p>First here's the relevant bit of code that's needed to use <code>AllowHostInputProcessing</code>:</p> <pre><code class="language-csharp">if (allowHostInputProcessing) { var opts = Environment.CreateCoreWebView2ControllerOptions(); opts.AllowHostInputProcessing = true; await webBrowser.EnsureCoreWebView2Async(environment, opts); } </code></pre> <p>Essentially you need to create an instance of the Controller options and then assign it to an environment that is assigned when the Web view is initially created.</p> <p>What this means is that you can't just simply instantiate a WebView and have this work - you need a bit of machinery to create an environment explicitly, which BTW is a good idea and highly recommended anyway.</p> <p>I use a helper method that handles the entire WebView initialization for me with every WebView I use. This helper does the following important tasks:</p> <ul> <li>Sets up a new WebView Environment in a specified (shared) path<br> <small><em>(<strong>important</strong> because the default deploy folders often don't allow writing which can crash your app or make the WebView not render)</em></small></li> <li>Reuses a previously created environment</li> <li>If <code>allowHostInputProcessing</code> is passed, sets it on the environment</li> <li>Calls and awaits the WebView Initialization (ensure that CoreWebView2 is available)</li> </ul> <p>Here's this that wraps all this busy work into an easy to use function (part of <a href="https://github.com/RickStrahl/Westwind.WebView">Westwind.Webview</a> source <a href="https://github.com/RickStrahl/Westwind.WebView/blob/master/Westwind.WebView/Wpf/CachedWebViewEnvironment.cs">on GitHub</a>)</p> <pre><code class="language-csharp">public async Task InitializeWebViewEnvironment(WebView2 webBrowser, CoreWebView2Environment environment = null, string webViewEnvironmentPath = null, bool allowHostInputProcessing = false) { try { if (environment == null) environment = Environment; if (environment == null) { // lock await _EnvironmentLoadLock.WaitAsync(); if (environment == null) { var envPath = webViewEnvironmentPath ?? Current.EnvironmentFolderName; if (string.IsNullOrEmpty(envPath)) Current.EnvironmentFolderName = Path.Combine(Path.GetTempPath(), Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location) + &quot;_WebView&quot;); // must create a data folder if running out of a secured folder that can't write like Program Files environment = await CoreWebView2Environment.CreateAsync(userDataFolder: EnvironmentFolderName, options: EnvironmentOptions); Environment = environment; } _EnvironmentLoadLock.Release(); } // *** THIS HERE *** if (allowHostInputProcessing) { var opts = Environment.CreateCoreWebView2ControllerOptions(); opts.AllowHostInputProcessing = true; await webBrowser.EnsureCoreWebView2Async(environment, opts); } else await webBrowser.EnsureCoreWebView2Async(environment); } catch (Exception ex) { throw new WebViewInitializationException($&quot;WebView EnsureCoreWebView2AsyncCall failed.\nFolder: {EnvironmentFolderName}&quot;, ex); } } </code></pre> <p>That's a bit of code, but it ensures that you safely create a WebView instance with an attached environment and wait for it to initialize.</p> <blockquote> <p><i class="fas fa-warning" style="font-size: 1.1em"></i> <strong>Important:</strong><br> <code>await webBrowser.EnsureCoreWebView2Async(environment, opts)</code> waits until the WebView has loaded. This method also <strong>will not complete if the WebView is not visible</strong>. By extension the helper method which calls this method also does not complete until those criteria are met.</p> </blockquote> <p>Using this code to initialize the environment, menu accelerators now work as expected. Pressing <code>alt</code> activates the main menu even from within the WebView control!</p> <p>Yay!</p> <p></p> <h2 id="caveats-watch-tab-and-potentially-other-navigation-keys-and-special-keys">Caveats: Watch Tab and Potentially other Navigation Keys and Special Keys</h2> <p>New feature, new challenges! As with all such major changes in how things works there are changes that affect behavior. And it's often not immediately obvious...</p> <h3 id="tab-key-handling">Tab Key Handling</h3> <p>Once you set <code>AllowHostInputProcessing=true</code> keyboard behavior now passes through many things that you might not expect causing actions in the host WPF (or WinForms etc.) container. One of the things that is really noticeable is the <code>Tab</code> and <code>Shift-Tab</code> key which now moves focus to the next control. Again, this may be desirable for some scenarios, but in my editor scenario it most certainly is not: Tab is definitely a character that needs to apply to my text editing and not cause control focus change!</p> <p>Without any intervention the behavior I now see is that the Tab key does fire inside of the editor, but ALSO causes the WebView to lose focus to another control (an editor tab in Markdown Monster's case).</p> <p>The behavior is:</p> <ul> <li>WPF event fires first</li> <li>If not cancelled, event <strong>then</strong> fires in the WebView/Editor</li> </ul> <p>It turns out that this leaves you with some very BAD choices:</p> <ul> <li><p><strong>Handle the key in WPF and cancel</strong><br> no good because the key is not forwarded back into JS code to execute the tab behavior as expected.</p> </li> <li><p><strong>Handle the key in WPF and don't cancel</strong><br> no good because Tab fires and focus changes.</p> </li> </ul> <p>Luckily in my situation I can explicitly call back into the JavaScript code to perform the appropriate tab action. In the case of my editor, the workaround is to handle the tab key event in WPF and cancel the operation, and <strong>then</strong> explicitly call back into the editor to trigger the Indent/Unindent command behavior. Yeah - very roundabout, but it works!</p> <p>Here's what that looks like in code:</p> <ul> <li>Create a custom key Binding for Tab and Shift Tab</li> <li>Create a custom command that handles the operation</li> <li>Basically overrides the .NET WPF event default behavior (WPF <code>KeyBinding</code> does that internally)</li> <li>Fires Tab operation back into JavaScript code <ul> <li>For Tab simulates the Tab key</li> <li>For Shift Tab calls an Editor command directly</li> </ul> </li> </ul> <p>First add the key bindings (these are app custom bindings that wrap WPF KeyBindings but in the end they use the underlying WPF <code>KeyBinding</code> for the key handling)</p> <pre><code class="language-csharp">// *** Special Bindings // Tab Handling for the editor: Bind only the Tab control // Forced to override Tab Handling here due to // WebView2 ApplyHostToInputProcessing setting which handles // Tab before it gets to the JavaScript handler. KeyBindings.AddRange( new AppKeyBinding { Id = &quot;EditorCommand_TabKey&quot;, Key = &quot;Tab&quot;, Command = model.Commands.KeyBoardOperationCommand, CommandParameter = &quot;TabKey&quot;, HasJavaScriptHandler = false, BindingControl = mmApp.Window.TabControl }, new AppKeyBinding { Id = &quot;EditorCommand_ShiftTabKey&quot;, Key = &quot;Shift+Tab&quot;, Command = model.Commands.KeyBoardOperationCommand, CommandParameter = &quot;ShiftTabKey&quot;, HasJavaScriptHandler = false, BindingControl = mmApp.Window.TabControl } ); </code></pre> <p>Then handle that with a custom Command handler:</p> <pre><code class="language-csharp">void Command_KeyBoardOperation() { KeyBoardOperationCommand = new CommandBase(async (parameter, command) =&gt; { var action = parameter as string; if (string.IsNullOrEmpty(action)) return; if (Model.ActiveEditor?.EditorHandler?.DotnetInterop != null) await Model.ActiveEditor.EditorHandler.DotnetInterop.KeyboardCommand(action); }, (p, c) =&gt; true); } // inside of `KeyboardCommand` fire JavaScript handlers that forward to the editor else if (key == &quot;TabKey&quot;) { // override so we don't tab out (AllowHostInput option) await JsInterop.Invoke(&quot;fireTabKey&quot;); // explicit Tab injection so Ghost Text works! } else if (key == &quot;ShiftTabKey&quot;) { // do nothing but keep from tabbing out await JsInterop.ExecCommand(&quot;outdent&quot;); } </code></pre> <p>Finally the js code to simulate a key event in ACE editor that is used for <code>Tab</code> key:</p> <pre><code class="language-javascript">fireTabKey: function (e) { const event = new KeyboardEvent(&quot;keydown&quot;, { key: 'Tab', keyCode: 9, which: 9, code: 'Tab', bubbles: true, cancelable: true, shiftKey: false, ctrlKey: false, altKey: false }); // Dispatch to ACE's hidden textarea te.editor.textInput.getElement().dispatchEvent(event); }, </code></pre> <p><code>shift-tab</code> uses Ace Editor's built-in command via <code>ExecCommand(outdent)</code>. But <code>tab</code> can't use that because there are several special use cases for <code>tab</code> in Markdown Monster. In addition to plain editor Tab behavior, Tab is also used for accepting Ghost Text suggestions. For this reason the explicit and more verbose tab injection is used.</p> <p>For more generic DOM application the <code>.dispatchEvent()</code> approach can always be used to simulate key behavior <strong>after WPF</strong> has had its shot at it. While all of this seems like a circle jerk, it's effective and it works.</p> <p>Here's a more generic version of a function to simulate a key:</p> <pre><code class="language-javascript">fireKey: function (element, key, keyCode, shift = false, ctrl = false, alt = false) { const event = new KeyboardEvent(&quot;keydown&quot;, { key: key, keyCode: keyCode, which: keyCode, code: key, bubbles: true, cancelable: true, shiftKey: shift, ctrlKey: ctrl, altKey: alt }); // Dispatch to ACE's hidden textarea element.dispatchEvent(event); }, </code></pre> <h3 id="esc">ESC</h3> <p>Another key that can cause problems is the <code>ESC</code> key which also bubbles up to the container. If you have the WebView on a form that uses ESC for closing the form for example, it'll do that from within the control now where it didn't before.</p> <p>Again - this may be the very behavior that you want, but perhaps not. For me in MM and other apps that's not been a problem but just be aware.</p> <p>If you do need to override the behavior it's the same scenario as I described with the <code>Tab</code> key - handle in WPF then fire back into the WebView if you need special handling for ESC.</p> <p>This pretty much applies to any key that 'misbehaves' in this way.</p> <p></p> <h2 id="summary">Summary</h2> <p>Keyboard handling for special keys in a WebBrowser control always are and have been a bear to work with. It was a pain in the old IE based WebBrowser control and it's a bear now with the WebView2. Different issues with different behaviors, but similar scenarios. How this is handled is one of those damned if you do, and damned if you don't scenarios, because no matter which route you take there are some things that are not going to work the way you likely want them to work. The <code>alt</code> and <code>tab</code> key behaviors I've described here are perfect examples of that.</p> <p>But where there's a will there's a way - and there are ways around this. Overall while <code>AllowInputHostProcessing</code> causes some new 'behaviors', for complex hybrid UI interactive applications, if you start with <code>AllowHostInputHostProcessing</code> setting on, it provides a better base state for keyboard handling than the default behavior as you get proper forwarding into the host container for Windows specific keyboard combinations and behaviors. Other than the <code>tab</code> key and potentially the <code>ESC</code> key I haven't noticed other abhorrent misbehavior even in Markdown Monster's extreme keyboard interop scenario. Hopefully with the information from this post - you can navigate your way more quickly through the issues you might run into, than I did.</p> <h2 id="resources">Resources</h2> <ul> <li><a href="https://github.com/RickStrahl/Westwind.WebView">West Wind WebView Library (GitHub)</a></li> <li><a href="https://markdownmonster.west-wind.com">See it work in Markdown Monster</a></li> <li><a href="https://github.com/MicrosoftEdge/WebView2Feedback/issues/468">Original WebView2 GitHub Issue Filed (2020)</a></li> </ul> <div style="margin-top: 30px;font-size: 0.8em; border-top: 1px solid #eee;padding-top: 8px;"> <img src="https://markdownmonster.west-wind.com/favicon.png" style="height: 20px;float: left; margin-right: 10px;"> this post created and published with the <a href="https://markdownmonster.west-wind.com" target="top">Markdown Monster Editor</a> </div> <div style='margin: 10px 0px'><small>© Rick Strahl, West Wind Technologies, 2005-2026</small></div><div>Posted in <b><a href='/ShowPosts.aspx?Category=WPF'>WPF</a>&nbsp;&nbsp;<a href='/ShowPosts.aspx?Category=WebView'>WebView</a>&nbsp;&nbsp;</b></div> </small> Fighting through Setting up Microsoft Trusted Signing https://weblog.west-wind.com/posts/2025/Jul/20/Fighting-through-Setting-up-Microsoft-Trusted-Signing 4940804_202507210056 2025年7月21日 00:56:23 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Jul/20/Fighting-through-Setting-up-Microsoft-Trusted-Signing#Comments 24 https://weblog.west-wind.com/commentrss.aspx?id=4940804 Windows Security Azure <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/SigningHandBanner.jpg" alt="Signing Hand Banner"></p> <p>So it's that time of year (actually, the time of several years) to renew my Code Signing certificate. I always dread this because it's a manual process, and invariably, if you're not intimately familiar with the complexities of public key cryptography, the terminology is enough to drive me batty. It's gotten easier since I made some decent notes last few times I went through this...</p> <p>But all that's out of the window this time around because the CodeSigning rules have changed. Drastically! It actually happened a few years ago, but I was lucky and got my local, still exportable certificate just before the rules changed so I was able to free load for nearly 3 years on the old certificate plan.</p> <p>The new rules don't allow for locally stored, exportable certificates. Instead certificates have to be served from one of a few certified online authorities, or the certs must be stored in a FIPS 140-2 Level 2+ compliant hardware security module (HSM). The keys cannot be exportable, so they effectively can't be copied and stored or used elsewhere. So you got the option of server provided keys or hardware keys.</p> <p>The idea behind this is to stop keys getting high jacked and being used by the non-originating organization. So the new keys are one time generated and non-exportable, so that they are much more restricted. Online services issue certificates that are good for only a few days when you can use them to sign with, and then automatically roll over to a new certificate.</p> <p>What all this means the complexity of getting a certificate has gotten exponentially worse, and along with that prices have gone up significantly. Base non-EV certs run in the 350ドル-500 range with fully verified EV certificates starting around 500ドル per year. What used to cost me 180ドル for 3 years, the same provider now wants nearly 1000ドル for. Yikes!</p> <p><a href="https://markdownmonster.west-wind.com?ut=weblog" target="_blank" title="Markdown Monster - Easy to use, yet powerfully productive Markdown Editing for Windows"> <img src="/images/sponsors/banner-example.png?v=1.2" class="da-content-image" /> </a></p> <p>It all seems like a huge grift:</p> <blockquote> <p>As it is the whole CodeSigning thing has turned into another scam of Enshittification of a captured audience.</p> <p>If you're publishing software or even packages on NuGet now, you pretty much have to have a code signing certificate. Certificates that used to be ~100ドル-150 (or less for multi-year certs) a year a few years ago now cost 300ドル-400ドル for basic certs. The EV certs <strong>start at</strong> 500ドル and go up from there.</p> <p>The validation rules for businesses haven't changed and you would think most of the the expense is all in that. But this isn't about security - it's about gate keeping and just one more hurdle for a small business to have to jump over.</p>— <small><a href="https://x.com/RickStrahl/status/1946323611045994548?ref_src=twsrc%5Etfw">@RickStrahl on X</a> <em>July 18, 2025</em></small> </blockquote> <h2 id="microsoft-is-in-the-game-too">Microsoft Is In the Game Too</h2> <p>Microsoft who requires these CodeSigning rules in the first place for Windows SmartScreen validation and also for other things like NuGet packages, is also providing an Azure service called <strong>Trusted Signing</strong> to provide CodeSigning services, so they are on both sides of that transaction (create problem → provide a solution?) To their credit their pricing is much better than what most traditional SSL Cert providers are now charging. Azure Trusted Code Signing is still in <strong>preview</strong> but then again, it's been in preview for well over 2 years but it looks like what you see and can sign up for now is in the final stages before going to a proper release as a service.</p> <h3 id="microsoft-trusted-signing-certificate-pricing">Microsoft Trusted Signing Certificate Pricing</h3> <p>One reason to look at Microsoft's solution - despite the potential pain and suffering - is that the pricing is quite good (as of time of this post):</p> <table> <thead> <tr> <th>Model type</th> <th>Basic</th> <th>Premium</th> </tr> </thead> <tbody> <tr> <td>Base price (monthly)</td> <td>9ドル.99</td> <td>99ドル.99</td> </tr> <tr> <td>Quota (signatures/month)</td> <td>5,000</td> <td>100,000</td> </tr> <tr> <td>Price after quota is reached</td> <td>0ドル.005/signature</td> <td>0ドル.005/signature</td> </tr> </tbody> </table> <p>These are non-EV, base certificates that only do basic vetting. For fully vetted EV certificates you'll need to look elsewhere. This pricing which ends up at ~120ドル/yr for the single cert is cheap compared to most of the SSL vendors most of which start at ~300ドル for certificates with mailed hardware keys. So, you gotta give Microsoft credit here for keeping costs down and providing reasonable pricing.</p> <blockquote> <h5 id="--united-states-and-canada-for-business-only"><i class="fas fa-warning" style="font-size: 1.1em"></i> United States and Canada for Business Only</h5> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/UsAndCanadaOnly.png" alt="Us And Canada Only"><br> Currently Azure Trusted Signing is only available for US and Canadian customers according to Microsoft, although I got several comments that people from other countries have been approved (may have been for earlier previews though). Currently it looks like only US and Canada are supported and the options for business validation in the drop down seems to bear that out.</p> </blockquote> <p>The certificates issued by Microsoft <strong>are very short lived</strong> - with expirations that last only 3 days to aggressively thwart invalid signing attacks in case a certificate is compromised.</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/SigningTimeWindow3days.png" alt="Signing Window is valid only for 3 days"></p> <blockquote> <h5 id="--3-day-certificates-are-not-a-problem"><i class="fas fa-lightbulb" style="font-size: 1.1em"></i> 3 Day Certificates are not a Problem</h5> <p>Azure signing certificates are valid for 3 days only, and <strong>the certificate has to be used for signing a file in the 3 day window</strong>.</p> <p>However, <strong>once a file is signed with a valid and unexpired certificate, it's valid indefinitely!</strong> In other words, end-users are not affected by the short certificate timeout.</p> <p>Since the signing process goes through Azure's servers, Azure controls the lifetime of certificates and automatically renews certificates when they expire so you always get a short lived valid certificate.</p> </blockquote> <p>Doing a bit of research out of all the bad options out there, Microsoft's Trusted Signing seems like the least bad solution that's also cheaper than traditional certs from various SSL vendors. The good news is that it works and pricing is reasonable. The bad news I wasted nearly an entire day trying to get it to work. Hopefully this post will help you reading this not to waste quite so much time 😄.</p> <h3 id="navigating-the-azure-jungle">Navigating the Azure Jungle</h3> <p>If you end up going the Azure Trusted Signing route, plan on having to wade through the Azure dependency jungle of setting up several resources and trying to understand what all the mumbo-jumbo Azure jargon amounts to. If you're doing Azure all day then much of this infrastructure dance will be familar to you, but as someone (me!) who only occasionally jumps in for some very specific services like Trusted Signing, it's incredibly painful to deal with Azure security and the Resource dependencies and the endless nesting of services with badly defined and overlapping naming boundaries.</p> <p>For Trusted Signing finding documentation via search engines was hit or miss - the docs for this are buried behind deeply nested links - perhaps because it's still in or just out of preview (even that's hard to tell since some prompts show preview, none of the headlines do) and, also because previous releases of this technology used a completely different publishing pipeline through the Azure Key Vault.</p> <p>There's official documentation although it took me a bit to discover it here:</p> <ul> <li><a href="https://learn.microsoft.com/en-us/azure/trusted-signing/">Microsoft Trusted Signing Documentation</a></li> </ul> <p>This has everything you need, but the instructions require some... uhm... interpretation. The tools are terrible, and the docs don't make working with them a lot easier by making you figure out where to find files and dependencies and how to install tools.</p> <h3 id="dont-believe-your-lying-ais">Don't believe your lying AIs!</h3> <p>In this day and age of AI assistants and ChatBots you would think that things like Azure configuration instructions for setting up an Azure task would be readily available. Heck there's even an Azure Specific CoPilot Model that you can use from the VS Code CoPilot integration.</p> <p>But that actually yielded surprisingly bad results and did not work well with Trusted Signing either for setup or for the signing part! Part of this might be because Trusted Signing is still in Preview or because the documentation for this is almost non-discoverable and because things have changed so much with the tooling.</p> <p>Long story short: After a very pissed off day of going down many wrong paths I managed to get Trusted Signing to work for my projects, and I'll try my best to provide the details how I have this set up, hopefully sparing a few of you all the pain I ran into.</p> <p></p> <h2 id="setting-up-microsoft-trusted-signing">Setting up Microsoft Trusted Signing</h2> <p>Alright so let's talk about what you need to set up Azure Trusted Signing.</p> <p>There are two parts to this:</p> <ul> <li>Setting up the Certificate and all the Infrastructure for storing the Certificate</li> <li>Signing the Certificate as it require special software</li> </ul> <p>Remember these certificates are not exportable so the only way you can sign is by using the online certificate via a service call by the sign tool.</p> <h3 id="structure">Structure</h3> <p>In their documentation Microsoft provides a high level resource overview of what's involved in the certificate creation process:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/SigningResources.png" alt="Signing Resources"><br> <small>source: <a href="https://learn.microsoft.com/en-us/azure/trusted-signing/concept-trusted-signing-resources-roles">Microsoft</a></small></p> <p>Here's a breakdown of the things needed to create on the Certificate creation side, and the Signing side:</p> <p><strong>For Certificate Creation</strong></p> <p>You will need an Azure account to do any of this. If you don't already have one, you can sign up for a account. Since we're signing up for a paid feature you will need to set up payment information.</p> <p>There are lots of resources available online on how to setup an Azure account, so I won't cover that here. Once you're in the Azure portal you need to:</p> <ul> <li>Create or use an existing Azure Resource Group</li> <li>Create a Trusted Signing Account</li> <li>Create a Trusted Signing Identity (Company Information)</li> <li>Create a Trusted Signing Profile (Certificate)</li> </ul> <p><strong>For Signing</strong></p> <ul> <li>Azure Cli (to log in)</li> <li>Azure CodeSigning SDK</li> <li>SignTool (from Windows SDK)</li> </ul> <h3 id="create-a-trusted-signing-account">Create a Trusted Signing Account</h3> <p>To start to to Azure Home Screen and you should see a Trusted Signing Accounts button at the top:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/TrustedSigningAccountButton.png" alt="Trusted Signing Account Button"></p> <p>Select that then click on Create:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/CreateTrustedSigningAccount.png" alt="Create Trusted Signing Account"></p> <p>You can click through the Next options and click on Create or Review + create to create the account.</p> <p>Next you need to create an Identity. The identity is your business information that identifies your business unit.</p> <p>The first hurdle you're likely to run into is the following error:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/TrustedSigningVerifierError.png" alt="Trusted Signing Verifier Error"></p> <p>Two things that need to happen for that:</p> <ul> <li><p>Add the Trusted Signing Identity Verifier Role<br> <img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/AddTrustedSigningIdentityVerifierRole.png" alt="Add Trusted Signing Identity Verifier Role"></p> </li> <li><p>Add a User to Roles for the Signing Account<br> <img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/AddRoleAssignment.png" alt="Add Role Assignment"></p> </li> </ul> <p>Talk about terrible user experience as the Role selection is totally non-obvious. There are 3 other ways to look at roles, some with selections where you end up in this weird place where you have a role and user selected with no way to submit. The way shown above works with the weird role selection, which enables the <strong>Review + Assign</strong> button.</p> <p>Next you should be able to add an Identity. Go back to the main screen of the Trusted Signing Account and click on Identity Validation again:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/Identity%20Validation.png" alt="Identity Validation"></p> <p>Here you provide your company/organization information that is used on the certificate. Make sure the Url and Organization are the correct entity names. You also need to provide some sort of validation in the form of a Duns Number, Tax Id or Business Number.</p> <p>The email address is validated so make sure that's accurate. In my case the validation with a Duns number took only a few minutes before the validation email showed up so this is surprisingly fast. Unlike previous certificates it also didn't involve a phone number callback which I thought was interesting in that it's actually less strict than previous certs I've signed up for.</p> <p>The final step then is to create a <strong>Certificate Profile</strong> which effectively is the Certificate. Gotta love the bureaucratic naming.</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/CreateCertificateProfile.png" alt="Create Certificate Profile"></p> <p>I didn't fill out here, this form because I couldn't actually create a new certificate since my account is already using the 1 certificate I'm allowed. Here you simply select the identity you set up previously, and the rest auto-fills into the certificate request form.</p> <p>Click <strong>Create</strong> and you're off to the races. It'll take a few minutes for the certificate to be created.</p> <p>Once that's all set and done you end up with a <strong>Certificate Profile</strong> entry which is your signing certificate:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/SignedCertificateInProfile.png" alt="Signed Certificate In Profile"></p> <p>While we're here the three pieces of information that you will need to sign a file are:</p> <ul> <li><strong>Signing Account Uri</strong><br> <small><em>you can find that on the Signing Account Page, something like:<br> <code>https://eus.codesigning.azure.net/</code></em></small></li> <li><strong>The Signing Account Name</strong><br> <small><em>WestWindSigningAccount</em></small></li> <li><strong>The Profile Name</strong><br> <small><em>WestWindCodeSignCertificate</em></small></li> </ul> <p>Congratulations, the certificate should now be ready to go.</p> <p>Half the battle is done - and now we need to sign our binaries and it's not a whole lot easier.</p> <h2 id="signing-an-executable">Signing an Executable</h2> <p>You would think that with all the effort that goes into the Azure CLI that Microsoft would have figured out a way to make it easy to use a certificate from Trusted Signing to sign a file, but - again in perfect counter intuitive fashion - you'd be wrong!</p> <p>Although there's a <code>az trustedsigning</code> addin to the Azure CLI that can be used to list and check for certificates, that same interface <strong>does not support signing using that certificate</strong>.</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/AzTrustedSigning.png" alt="Az Trusted Signing"></p> <p>Note: now way to sign anything, but you can see what certs are installed.</p> <p>Instead the recommendation from the official docs is to use a recent version of the good old <code>Signtool.exe</code> from the Windows SDK with - wait for it... an explicitly referenced DLL from the a separately downloaded and installed SDK.</p> <h3 id="azure-cli">Azure CLI</h3> <p>So, in order to use the signing tools you will need the Azure CLI so you can sign in to your Azure account.</p> <pre><code class="language-powershell">winget install --id Microsoft.AzureCLI -e </code></pre> <h3 id="trusted-signing-client-tools">Trusted Signing Client Tools</h3> <p>Next you'll need to have various tools installed which include:</p> <ul> <li>Trusted Signing SDK (download and install)</li> <li>.NET 8.0 Runtime</li> <li>SignTool.exe</li> </ul> <p>You can find Microsoft's confusing instructions here:</p> <ul> <li><a href="https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-signing-integrations">Installing Client Signing Tools</a></li> </ul> <p>but I'll summarize it for you here.</p> <p>Start with the SDK which you can install these via WinGet which is the easiest:</p> <pre><code class="language-ps">winget install -e --id Microsoft.Azure.TrustedSigningClientTools </code></pre> <blockquote> <h5 id="--path-to-codesigning-dll-required"><i class="fas fa-warning" style="font-size: 1.1em"></i> Path to Codesigning Dll Required!</h5> <p>This SDK installs <code>%localappdata%\Microsoft\MicrosoftTrustedSigningClientTools</code> on Windows. This has everything you need, but I found this doesn't install the latest version of these tools. You'll need to capture the path to the codesiging DLL and use it on the Signtool command line.</p> <p>By default, if you use the installer, the file you need to reference later with <code>SignTool.exe</code> lives here:</p> <pre><code class="language-text">%localappdata%\Microsoft\MicrosoftTrustedSigningClientTools\Azure.CodeSigning.Dlib.dll </code></pre> </blockquote> <p>The docs on this are <strong>very confusing</strong> because the SDK downloads an old version of the SDK, and then later the docs mention downloading what appears to be the very same thing separately via NuGet. Except the NuGet package seems to be up to date with considerably newer versions than the SDK install.</p> <p>The NuGet Package can be found here:</p> <ul> <li><a href="https://www.nuget.org/packages/Microsoft.Trusted.Signing.Client">Microsoft.Trusted.Signing.Client NuGet Package </a></li> </ul> <p>I'm not sure what else the SDK does so to be sure I install both:</p> <ul> <li>Install the TrustedSigning SDK (as per above)</li> <li>Download the NuGet package</li> <li>Update the binaries in the <code>%localappdata%</code> folder with the NuGet binaries</li> </ul> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/TrustedSigningSdkUpdatingToLatestVersino.png" alt="Trusted Signing Sdk Updating To Latest Version"><br> <small>Updating the Trusted Signing SDK to the latest version via downloaded NuGet package</small></p> <h3 id="install-or-find-signtool">Install or Find Signtool</h3> <p>Next you need to install <code>Signtool.exe</code> or - if you have Visual Studio installed you probably already have it locally installed as part of the Windows SDK (10.0.2261.755 or later). Personally I have a recent copy of <code>Signtool.exe</code> in my <code>Utils</code> folder that's on my path, or I copy it directly into my <code>Install</code> folder from which my installer is created and where the signing actually happens.</p> <p>If you want to use the global SDK reference it typically lives in:</p> <pre><code class="language-text">C:\Program Files (x86)\Windows Kits10円\bin10円.0.26100.0\x64\signtool.exe </code></pre> <ul> <li><a href="https://learn.microsoft.com/en-us/azure/trusted-signing/how-to-signing-integrations?source=recommendations#download-and-install-signtool">Signtool and Windows SDK install instructions</a></li> </ul> <h3 id="create-a-metadata-file-with-azure-trusted-signing-information">Create a MetaData File with Azure Trusted Signing Information</h3> <p>The last step is to create a meta data file that references the Azure Trusted Signing account information so that the cert can be found. It's a simple Json file that looks like this:</p> <pre><code class="language-json">{ &quot;Endpoint&quot;: &quot;&lt;Trusted Signing account endpoint&gt;&quot;, &quot;CodeSigningAccountName&quot;: &quot;&lt;Trusted Signing account name&gt;&quot;, &quot;CertificateProfileName&quot;: &quot;&lt;Certificate profile name&gt;&quot; } </code></pre> <p>Filled out it looks something like this (<code>SignfileMetaData.json</code>):</p> <pre><code class="language-json">{ &quot;Endpoint&quot;: &quot;https://eus.codesigning.azure.net/&quot;, &quot;CodeSigningAccountName&quot;: &quot;MySigningAccount&quot;, &quot;CertificateProfileName&quot;: &quot;MyCertificate&quot; } </code></pre> <p>I named mine the same name as the Powershell script I use to sign the file.</p> <h3 id="ready-to-run-signtool-to-sign-your-binary">Ready to run Signtool to sign your Binary</h3> <p>So the actual signing operation is then done in two steps:</p> <ul> <li>Signing into Azure</li> <li>Signing the actual document</li> </ul> <p>To make this a little easier I have a Powershell script that passes in one or more filenames to sign, then logs in if necessary and signs the file.</p> <pre><code class="language-ps"># dotnet tool install --global AzureSignTool #winget install --exact --id Microsoft.AzureCLI param( [string]$file = &quot;&quot;, [string]$file1 = &quot;&quot;, [string]$file2 = &quot;&quot;, [string]$file3 = &quot;&quot;, [string]$file4 = &quot;&quot;, [string]$file5 = &quot;&quot;, [boolean]$login = $false ) if (-not $file) { Write-Host &quot;Usage: SignFile.ps1 -file &lt;path to file to sign&gt;&quot; exit 1 } if ($login) { az config set core.enable_broker_on_windows=false az login az account set --subscription &quot;Pay-As-You-Go&quot; } $args = @( &quot;sign&quot;, &quot;/v&quot;, &quot;/debug&quot;, &quot;/fd&quot;, &quot;SHA256&quot;, &quot;/tr&quot;, &quot;http://timestamp.acs.microsoft.com&quot;, &quot;/td&quot;, &quot;SHA256&quot;, &quot;/dlib&quot;, &quot;$env:LOCALAPPDATA\Microsoft\MicrosoftTrustedSigningClientTools\Azure.CodeSigning.Dlib.dll&quot;, &quot;/dmdf&quot;, &quot;.\SignfileMetadata.json&quot; ) foreach ($f in @($file, $file1, $file2, $file3, $file4, $file5)) { if (![string]::IsNullOrWhiteSpace($f)) { $args += $f } } .\signtool.exe $args </code></pre> <p>The script is pretty straight forward. The only oddity is the Signtool call with it's lengthy reference to the code signing DLL. Note that you need a recent version of <code>SignTool.exe</code> to support the <code>/dlib</code> and <code>/dmdf</code> parameters.</p> <p>When you run this script in a new Terminal instance you will be prompted to login into Azure and if you have multiple subscriptions you have to select the subscription. Once the terminal session is active subsequent requests bypass the login and just go and sign.</p> <p>If all goes well you should see something like this as your output:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/SigningResultInTerminal.png" alt="Signing Result In Terminal"></p> <p>And we have lift off!</p> <p>You're looking for the <strong>Number of files successfully Signed: 1</strong> (or more if you pass multiple files).</p> <h3 id="not-fast">Not Fast!</h3> <p>One thing I am noticing is that the signing process is <strong>very slow</strong>:</p> <p><img src="https://weblog.west-wind.com/images/2025/Fighting-through-Setting-up-Microsoft-Trusted-Signing/SlowSigning.png" alt="Slow Signing"></p> <p>You can see that it takes about 5 seconds for signing each file which seems pretty slow if it's only sending the Digest over the wire as the message is suggesting. The old local cert SignTool processing took less than second at most per file.</p> <p>Oddly it also doesn't get any faster with multiple files sent to SignTool as each request apparently is sent after the previous one completes. No parallelization here.</p> <p>Another issue - and that is likely my ignorance - is that I can't seem to get a hands off Azure login to run. <code>az login</code> prompts for a manual login and then also for selection of a subscription. I know there are options for using a Secret key, but I couldn't figure out how to set this up and get Azure recognize my user and subscription. The secret key is recognized but for some reason it complains that it's not linked to a subscription (which it is). Just more piece of bureaucratic bullshit that I'm going to leave for another day. This is just how it goes for me with Azure - you solve one problem only to run into yet another seemingly unrelated issue and soon you have 20 services you're dealing with to do one simple thing. It's a never ending rabbit hole.</p> <p>I'm hoping that once everything is set up I can just let it be, but knowing Microsoft's propensity to keep moving the cheese around that's also very unlikely 😄</p> <h2 id="alternatives-byo-certificate-and-use-azure-key-vault">Alternatives: BYO Certificate and use Azure Key Vault</h2> <p>Initially I came to Azure mainly in order to save some money because the pricing of providers just seems ridiculous. Given the time I sank into this maybe that wasn't the best use of my money or time 😁.</p> <p>However, it looks like there's another way you can still do this somewhat on the cheap by buying your own certificates and then importing them into Azure Key Vault. Much of the cost of third party CodeSigning certificates is due to the secure token device you have to buy on top of the certificate plus shipping which nearly doubles the cost of the entire affair.</p> <p>Turns out you can however buy a certificate and have it issued directly to a certified provider of which Azure Key Vault is one.</p> <p>I don't have the energy to go through the process of setting up Azure Key Vault which looks to be just as many long steps if not more than what I went through with Trusted Signing.</p> <p>There are many step by steps that take you through this - here's one:</p> <p><a href="https://signmycode.com/resources/how-to-create-private-keys-csr-and-import-code-signing-certificate-in-azure-keyvault-hsm">How to Create Key Vault, CSR, and Import Code Signing Certificate in Azure KeyVault HSM</a></p> <p>As I got feedback for this post several people report using this successfully and one benefit apparently is that performance of Azure Key Vault singing is much better than Trusted Signing.</p> <p>One benefit of Trusted Signing is that once set up, you never need to touch it again (assuming Azure doesn't change and break the process 😢) because certificates have a short lifetime and auto-renew automatically. As long as you pay your monthly bill, in theory you don't need to ever update your certificate - it'll always be up to date.</p> <p></p> <h2 id="summary">Summary</h2> <p>The process to do set up Trusted Signing was way harder than it should have been - in fact the entire process took me the better part of an entire work day. The server process is complicated primarily because the nomenclature is so crazy confusing and the dependency management on Azure is such a pain in the ass. The missing rights from the account to create an identity is particular maddening and how you fix it even more so! But it wouldn't be Azure if you wouldn't be cursing the thing every step of the way.</p> <p>The signing process also is a pain in the ass with 3 different tool chains required. The fact that an <code>az TrustedSigning</code> CLI addin exists, <strong>but doesn't support actually signing</strong> is just ridiculous. With all the resources that are thrown at Azure it seems petty to not support the one feature that everybody is going to need without having to jump through hoops of managing several tool installation instructions.</p> <p>But grudgingly I have to say that at the end of the day the process works, warts and all. Microsoft's pricing for the service maybe makes it worth it than most other services, and frankly the fact that I have my cert running as a service that hopefully doesn't need to be updated unless I quit the service is enticing. Yeah it costs more than it did last time around - I'm now paying almost as much per year what I used to pay for 3 years, but given the circumstances and required Enshittification of all that surrounds the CodeSigning process, this is the best that we can do for now.</p> <p>I'm hoping writing this up is helpful to some, and that these instruction won't be obsolete in a few short months because Microsoft changed designs again as is so often the case.</p> <p>As for Azure one would hope they fix:</p> <ul> <li><strong>Performance</strong><br> 5-8 seconds per file to sign with no parallelism for multiple submissions is bad</li> <li><strong>Self contained Tooling for Signing</strong><br> For heaven's sake provide ONE tool that can handle the signing process in one pass without having to install 50 other things. Or better yet have it built-in to the Azure CLI with the <code>trustedsigning</code> addin that's already there.</li> </ul> <p>One can hope some of this is due to the relative new-ness of Azure Trusted Signing. But... we shall see.</p> <h2 id="resources">Resources</h2> <ul> <li><a href="https://learn.microsoft.com/en-us/azure/trusted-signing/">Microsoft Trusted Signing Documentation</a></li> <li><a href="https://azure.microsoft.com/en-us/pricing/details/trusted-signing/">Microsoft Trusted Signing Pricing Page</a></li> </ul> <div style="margin-top: 30px;font-size: 0.8em; border-top: 1px solid #eee;padding-top: 8px;"> <img src="https://markdownmonster.west-wind.com/favicon.png" style="height: 20px;float: left; margin-right: 10px;"> this post created and published with the <a href="https://markdownmonster.west-wind.com" target="top">Markdown Monster Editor</a> </div> <div style='margin: 10px 0px'><small>© Rick Strahl, West Wind Technologies, 2005-2026</small></div><div>Posted in <b><a href='/ShowPosts.aspx?Category=Windows'>Windows</a>&nbsp;&nbsp;<a href='/ShowPosts.aspx?Category=Security'>Security</a>&nbsp;&nbsp;<a href='/ShowPosts.aspx?Category=Azure'>Azure</a>&nbsp;&nbsp;</b></div> </small> Centering a WPF TreeViewItem in the TreeView ScrollViewer https://weblog.west-wind.com/posts/2025/Jul/15/Centering-a-WPF-TreeViewItem-in-the-TreeView-ScrollViewer 4934685_202507160423 2025年7月16日 04:23:03 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Jul/15/Centering-a-WPF-TreeViewItem-in-the-TreeView-ScrollViewer#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4934685 WPF One of the annoying features of the TreeView in WPF is the inability to easily select and make an item prominently visible. While there is `TreeView.BringIntoView()` it doesn't do a good job dropping the item that the very bottom (or top) of the viewport with no way to center. In this post I show a few helper methods that facilitate this process and a few related tasks with some useful TreeView helpers. Unpacking Zip Folders into Windows Long File Paths https://weblog.west-wind.com/posts/2025/Jun/22/Unpacking-Zip-Folders-into-Windows-Long-File-Paths 4906257_202506221100 2025年6月22日 11:00:48 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Jun/22/Unpacking-Zip-Folders-into-Windows-Long-File-Paths#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4906257 .NET Windows Ah, long file paths bit me again today - this time with `ZipFile.ExtractToDirectory()` not unzipping long path files, even when specifying long path syntax for the output folder. In this post I briefly review Windows long path issues again, and describe what doesn't work and how to work around this particular problem. While specific to `ExtractToDirectory()` a similar approach might apply to other batch file based operations. Adding Runtime NuGet Package Loading to an Application https://weblog.west-wind.com/posts/2025/Jun/09/Adding-Runtime-NuGet-Package-Loading-to-an-Application 4892294_202506100440 2025年6月10日 04:40:37 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Jun/09/Adding-Runtime-NuGet-Package-Loading-to-an-Application#Comments 1 https://weblog.west-wind.com/commentrss.aspx?id=4892294 .NET ASP.NET It's not a common use case, but if you need need to dynamically add external code at runtime, NuGet packages are the most familiar way for developers to add that functionality besides old-school direct assembly loading. In this post I look at my LiveReloadServer local Web Server tool and how I integrated both external assembly loading and NuGet package support to allow extensibility for the Razor (and Markdown) scripting functionality of LiveReloadServer. Distinguished Name on FileZilla Server Self-Generated Certs https://weblog.west-wind.com/posts/2025/Jun/06/Distinguished-Name-on-FileZilla-Server-SelfGenerated-Certs 4888546_202506062044 2025年6月06日 20:44:04 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Jun/06/Distinguished-Name-on-FileZilla-Server-SelfGenerated-Certs#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4888546 Windows FTP Renewing Filezilla certificates I've run into a 'huh?' moment a few times trying to renew the self-signed certificates for the Administration interface. Trying the obvious entries of putting in the DNS names results in an error that's not perplexingly is fixed by providing a full Common Name instead. Configuring Microsoft.AI.Extensions with multiple providers https://weblog.west-wind.com/posts/2025/May/30/Configuring-MicrosoftAIExtension-with-multiple-providers 4880955_202505310856 2025年5月31日 08:56:33 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/May/30/Configuring-MicrosoftAIExtension-with-multiple-providers#Comments 1 https://weblog.west-wind.com/commentrss.aspx?id=4880955 .NET Microsoft.Extensions.AI is the new base library for creating AI clients in an abstracted way. While the library does a great job with the abstraction of the interfaces it works with, the provider interfaces that create the intial abstractions like IChatClient are a lot less user friendly with hard to discover syntax based on extension methods and different syntax for each provider. In this post I review three providers and how to get them instantiated along with a small streaming example to use them. Lazy Loading the Mermaid Diagram Library https://weblog.west-wind.com/posts/2025/May/10/Lazy-Loading-the-Mermaid-Diagram-Library 4856296_202505110926 2025年5月11日 09:26:29 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/May/10/Lazy-Loading-the-Mermaid-Diagram-Library#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4856296 Javascript Web The Mermaid library is a large beast, and if you're using it selectively in your Web content you probably want to make sure you don't load it unless you actually need it, due to it's download footprint and load time. If you're loading Mermaid with server only code it's pretty straight forward, but if you use it on the client there you may want to delay loading the library until you actually need to display a diagram. WebView2: Waiting for Document Loaded https://weblog.west-wind.com/posts/2025/May/06/WebView2-Waiting-for-Document-Loaded 4847859_202505062150 2025年5月06日 21:50:16 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/May/06/WebView2-Waiting-for-Document-Loaded#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4847859 WebView Windows WPF WebBrowser loading is always async and the WebView2 control is no different - in fact it's more complex as there a number of events that need to be handled in order to properly handle loading the control. In this post I describe how you can create a simplified load checking routine, or use the Westwind.WebView library and WebViewHandler to wait on content and process document access using linear `await` syntax. Avoiding WPF Image Control Local File Locking https://weblog.west-wind.com/posts/2025/Apr/28/WPF-Image-Control-Local-File-Locking 4838355_202504282201 2025年4月28日 22:01:46 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Apr/28/WPF-Image-Control-Local-File-Locking#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4838355 WPF .NET Windows WPF locks local images when referenced via a Source attribute. In this post I discuss why this might be a problem and how you can work around it using native XAML and custom binding converter that provides a consolidated approach that includes image caching for better performance of reused images. The Strong ARM of .NET: Wrestling with x64 and Arm64 Desktop App Deployment https://weblog.west-wind.com/posts/2025/Apr/18/The-Strong-ARM-of-NET-Wrestling-with-x64-and-Arm64-Desktop-App-Deployment 4827822_202504190835 2025年4月19日 08:35:43 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Apr/18/The-Strong-ARM-of-NET-Wrestling-with-x64-and-Arm64-Desktop-App-Deployment#Comments 3 https://weblog.west-wind.com/commentrss.aspx?id=4827822 .NET Windows .NET works great for cross-platform development making it easy to build apps that are cross-platform and cross-architecture specifically for x64 and Arm64. However, building a distributable application that can install and run out of box on both of these platforms, that's a bit more effort. In this post I discuss some of the gotchas and how to work around them to distribute apps that run out of the gate on both platforms. Using Windows.Media SpeechRecognition in WPF https://weblog.west-wind.com/posts/2025/Mar/24/Using-WindowsMedia-SpeechRecognition-in-WPF 4800107_202503250433 2025年3月25日 04:33:00 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Mar/24/Using-WindowsMedia-SpeechRecognition-in-WPF#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4800107 WPF Windows .NET Windows has a pretty capable SpeechRecognition engine built-in via Windows Media services. In .NET these features are accessible via the Windows SDK (WinSdk) that expose these Windows features to .NET applications. In this post I discuss how you can integrate these features into a WPF application, and discuss some of the pitfalls along the way that you have to watch out for related to the SDK integration 'features'. Making Html Input Controls Truly ReadOnly https://weblog.west-wind.com/posts/2025/Mar/14/Making-Html-Input-Controls-Truly-ReadOnly 4789670_202503142026 2025年3月14日 20:26:04 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Mar/14/Making-Html-Input-Controls-Truly-ReadOnly#Comments 6 https://weblog.west-wind.com/commentrss.aspx?id=4789670 Html CSS A discussion of how to show readonly controls in user interface and ensuring that they are not UI activated. This post discusses readonly and disabled behavior and how you can make readonly behave better if you choose to use it over disabled. Accessing Windows Settings Dialogs from Code via Shell Commands https://weblog.west-wind.com/posts/2025/Mar/13/Accessing-Windows-Settings-Dialogs-from-Code-via-Shell-Commands 4788723_202503132240 2025年3月13日 22:40:22 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Mar/13/Accessing-Windows-Settings-Dialogs-from-Code-via-Shell-Commands#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4788723 .NET Windows Windows has support for an `ms-setting:` Protocol Handler/Uri Scheme that allows you to directly open most Windows settings dialogs directly via simple Url based shell commands. Resolving Paths To Server Relative Paths in .NET Code https://weblog.west-wind.com/posts/2025/Mar/08/Resolving-Paths-To-Server-Relative-Paths-in-NET-Code 4783718_202503090639 2025年3月09日 06:39:18 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Mar/08/Resolving-Paths-To-Server-Relative-Paths-in-NET-Code#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4783718 ASP.NET ASP.NET Core has support for resolving Urls in Controllers and Razor Pages via embedded `~/` links in Views and via `Url.Content()`. But, these features are tied to Controllers or Razor Pages - what if you need to resolve Urls elsewhere, in middleware or even inside of business logic? In this post I describe how you can build a couple of helpers that are more flexible and also provide some additional functionality of resolving site root and relative paths to full site root paths. Inline Confirmations in JavaScript UI https://weblog.west-wind.com/posts/2025/Feb/26/Inline-Confirmations-in-JavaScript-UI 4772830_202502270902 2025年2月27日 09:02:10 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Feb/26/Inline-Confirmations-in-JavaScript-UI#Comments 2 https://weblog.west-wind.com/commentrss.aspx?id=4772830 Html JavaScript ASP.NET Confirmation dialogs or modal popups can be annoying in HTML applications. What if you could instead use an inline UI to confirm an operation? In this post I describe a simple way you can use an inline UI to confirm an operation that can be easily implemented with a few lines of code and a couple of binding directives. Retrieving Images from the Clipboard Reliably in WPF Revisited https://weblog.west-wind.com/posts/2025/Feb/21/Retrieving-Images-from-the-Clipboard-Reliably-in-WPF-Revisited 4768057_202502220809 2025年2月22日 08:09:36 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Feb/21/Retrieving-Images-from-the-Clipboard-Reliably-in-WPF-Revisited#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4768057 WPF Windows The WPF Clipboard is notoriously bad for retrieving image data, mainly because of the funky behavior of the ImageSource control into which clipboard data is loaded. WPF cuts a lot of corners when retrieving images and there are many scenarios where Clipboard.GetImage() either crashes or doesn't render the supposedly successfully retrieved image. In this post I review some of the challenges and how you can work around the retrieving an ImageSource with an intermediary load of a bitmap in between to provide reliable image retrieval from the clipboard in WPF. Comparing Raw ASP.NET Request Throughput across Versions: 8.0 to 9.0 Edition https://weblog.west-wind.com/posts/2025/Jan/19/Comparing-Raw-ASPNET-Request-Throughput-across-Versions-80-to-90-Edition 4734648_202501192251 2025年1月19日 22:51:02 GMT Rick Strahl https://weblog.west-wind.com/posts/2025/Jan/19/Comparing-Raw-ASPNET-Request-Throughput-across-Versions-80-to-90-Edition#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4734648 .NET ASP.NET Once again I'm taking a look at the newish .NET release and how it compares to the previous release - this time .NET 9.0 from .NET 8.0. I'll run my simple load tests to compare performance and also discuss a number of anecdotes from running .NET 9.0 in production apps for the last couple of months. Back to Basics: Using the Parallel Library to Massively Boost Loop Performance https://weblog.west-wind.com/posts/2024/Dec/27/Back-to-Basics-Using-the-Parallel-Library-to-Massively-Boost-Loop-Performance 4712020_202412272337 2024年12月27日 23:37:19 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Dec/27/Back-to-Basics-Using-the-Parallel-Library-to-Massively-Boost-Loop-Performance#Comments 8 https://weblog.west-wind.com/commentrss.aspx?id=4712020 .NET CSharp I recently had another occasion to add Parallel.ForEachAsync() into an application to improve performance of an Http look up operation drastically. When I tweeted a note on X there was quite a bit of response, so I thought I follow up and discuss Parallel use in this use case and when it makes sense to use Parallel in applications. Getting the Current TabItem when the Tab is not selected in WPF https://weblog.west-wind.com/posts/2024/Nov/08/Getting-the-Current-TabItem-when-the-Tab-is-not-selected-in-WPF 4666224_202411090910 2024年11月09日 09:10:44 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Nov/08/Getting-the-Current-TabItem-when-the-Tab-is-not-selected-in-WPF#Comments 3 https://weblog.west-wind.com/commentrss.aspx?id=4666224 WPF There's no direct support in WPF to find the control that the mouse is hovering over in Items controls like the TabControl, so if you need to bring up context sensitive information like a context menu or tooltip it takes a little extra effort to get the correct item to work with in this case ContextMenuOpening. Using SQL Server on Windows ARM https://weblog.west-wind.com/posts/2024/Oct/24/Using-Sql-Server-on-Windows-ARM 4648676_202410242159 2024年10月24日 21:59:54 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Oct/24/Using-Sql-Server-on-Windows-ARM#Comments 21 https://weblog.west-wind.com/commentrss.aspx?id=4648676 .NET SQL Server Windows I recently got a SnapDragon X Elite ARM device to play with and while I've been impressed with all things that work very well on it, one thing did not install easily: SQL Server. There's no ARM version of SQL Server and the x64 versions don't run on ARM devices. Docker images are also amd64 so that didn't work well either. Turns out you can use LocalDb onin emulation mode, but setting it up is anything but intuitive. In this post I discuss how to get SQL Server to run on a Windows ARM device. Getting the ASP.NET Core Server Hosting Urls at Startup and in Requests https://weblog.west-wind.com/posts/2024/Sep/03/Getting-the-ASPNET-Core-Server-Hosting-Urls-at-Startup-and-in-Requests 4569156_202409040931 2024年9月04日 09:31:35 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Sep/03/Getting-the-ASPNET-Core-Server-Hosting-Urls-at-Startup-and-in-Requests#Comments 4 https://weblog.west-wind.com/commentrss.aspx?id=4569156 asp.net Ever need to retrieve an ASP.NET application's hosting Urls? There are million ways to set these URLs but there's no easy fixed location where you can pick the addresses up from. Instead you have to go through a bit of a dance, and use one of two approaches depending on whether you need the addresses during startup or inside of a request. Nuking Local Nuget Package Sources to show newly Published Packages https://weblog.west-wind.com/posts/2024/Aug/04/Nuking-Local-Nuget-Package-Sources-to-show-newly-Published-Packages 4521831_202408042352 2024年8月04日 23:52:12 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Aug/04/Nuking-Local-Nuget-Package-Sources-to-show-newly-Published-Packages#Comments 1 https://weblog.west-wind.com/commentrss.aspx?id=4521831 .NET NuGet Every once in while when I publish a NuGet package and then try to use the published package in another project I end up with a situation where the new package simply is not recognized. Most of the time it shows up after a few minutes, but on more than a few occasions I've stuck waiting for quite a while waiting for the package cache to refresh. Luckily there's a way to nuke the cache and force Nuget to re-read local packages and while that is the nuclear option that requires downloading a lot of packages it works to make your project compile - right now! Create a .NET PlantUML Markdown Render Extension https://weblog.west-wind.com/posts/2024/Jul/29/Create-a-NET-PlantUML-Markdown-Render-Extension 4512301_202407292216 2024年7月29日 22:16:35 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Jul/29/Create-a-NET-PlantUML-Markdown-Render-Extension#Comments 2 https://weblog.west-wind.com/commentrss.aspx?id=4512301 .NET Markdown Monster PlantUML is a Web based diagramming markup language that can be used to create diagrams using text descriptions that are rendered into images via a PlantUML server. In this post I describe how you can integrate PlantUML image rendering into your .NET application, specifically from a Markdown rendering perspective as Markdown documents are the most common mechanism that PlantUML output is delivered. Back to Basics: Await a Task with a Timeout https://weblog.west-wind.com/posts/2024/Jul/25/Back-to-Basics-Await-a-Task-with-a-Timeout 4505520_202407252148 2024年7月25日 21:48:00 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Jul/25/Back-to-Basics-Await-a-Task-with-a-Timeout#Comments 8 https://weblog.west-wind.com/commentrss.aspx?id=4505520 .NET Sometimes it's useful to call async methods and be able to stop waiting after a given timeout period. Unfortunately there's no native support on Task to provide this. In this short post I show how you can simulate timeouts and provide a couple of helper methods to make the process generically available. Work around the WebView2 NavigateToString() 2mb Size Limit https://weblog.west-wind.com/posts/2024/Jul/22/Work-around-the-WebView2-NavigateToString-2mb-Size-Limit 4500913_202407230753 2024年7月23日 07:53:44 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Jul/22/Work-around-the-WebView2-NavigateToString-2mb-Size-Limit#Comments 4 https://weblog.west-wind.com/commentrss.aspx?id=4500913 WebView The WebView2 control's NavigateToString() method has a limit for the string size of 2mb, which can be a problem especially for HTML pages with embedded content. In this post I'll describe how this problem can show up and show a couple of ways to work around the issue. Dealing with Platform Specific Classes and Methods in CrossPlatform .NET https://weblog.west-wind.com/posts/2024/Jul/18/Dealing-with-Platform-Specific-Classes-and-Methods-in-CrossPlatform-NET 4496078_202407190702 2024年7月19日 07:02:59 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Jul/18/Dealing-with-Platform-Specific-Classes-and-Methods-in-CrossPlatform-NET#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4496078 .NET If you deal with old .NET library code that is sprinkled with some Windows specific code in places you've likely run into places where the Windows specific code is throwing up unwanted compiler warnings. Sometimes that matters, but often times these warnings are annoyances as these methods are highly unlikely to get called from x-platform code. In this post I describe some annoyances, how you can work around them and eventually fix the issues without throwing everything out the window. C# Version String Formatting https://weblog.west-wind.com/posts/2024/Jun/13/C-Version-String-Formatting 4445887_202406140612 2024年6月14日 06:12:37 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Jun/13/C-Version-String-Formatting#Comments 9 https://weblog.west-wind.com/commentrss.aspx?id=4445887 .NET C# I'm tired of trying to format versions for user facing interfaces after fumbling with it again and again. In this short post I show a small helper extension method that lets you configure how to form user friendly version strings to display to end users. Mime Base64 is a Thing? https://weblog.west-wind.com/posts/2024/May/26/Mime-Base64-is-a-Thing 4416829_202405270750 2024年5月27日 07:50:45 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/May/26/Mime-Base64-is-a-Thing#Comments 3 https://weblog.west-wind.com/commentrss.aspx?id=4416829 .NET Ran into an old legacy application recently that required that attached data was preformatted to Mime Base64 which I never even heard of before. Turns out it's a 'url-safe' version of base64 that replaces certain characters that can be present in base64 with 'safe' characters. In this short post I show a small helper that handles several Base64 Mime operations. Speed up your Start Menu by disabling Web Search https://weblog.west-wind.com/posts/2024/May/03/Speed-up-your-Start-Menu-by-disabling-Web-Search 4381163_202405040653 2024年5月04日 06:53:30 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/May/03/Speed-up-your-Start-Menu-by-disabling-Web-Search#Comments 2 https://weblog.west-wind.com/commentrss.aspx?id=4381163 Windows If you're like me, you've probably cursed the Windows Start menu from time to time, when it's either very slow to pop up, or in some instances fails to pop up at all when you press the Windows key. This simple tip can drastically improve performance of your Windows Start Menu by simply disabling Web search. ASP.NET Core Hosting Module with Shadow Copy Not Starting: Separate your Shadow Copy Folders! https://weblog.west-wind.com/posts/2024/Apr/28/ASPNET-Core-Hosting-Module-with-Shadow-Copy-Not-Starting-Separate-your-Shadow-Copy-Folders 4372790_202404290106 2024年4月29日 01:06:35 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Apr/28/ASPNET-Core-Hosting-Module-with-Shadow-Copy-Not-Starting-Separate-your-Shadow-Copy-Folders#Comments 4 https://weblog.west-wind.com/commentrss.aspx?id=4372790 ASP.NET IIS I recently ran into a major failure related to Shadow Copying for an ASP.NET Web app on IIS which was caused by corruption of the Shadow Copy directories. The app starts with the dreaded white ANCM Error page and event log entries that point at missing application folders. It turns out that this is caused by interference of multiple applications using the same shadow copy folder. In this post I describe the problem and how to work around it. Programmatic Html to PDF Generation using the WebView2 Control with .NET https://weblog.west-wind.com/posts/2024/Mar/26/Html-to-PDF-Generation-using-the-WebView2-Control 4319131_202403270759 2024年3月27日 07:59:52 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Mar/26/Html-to-PDF-Generation-using-the-WebView2-Control#Comments 14 https://weblog.west-wind.com/commentrss.aspx?id=4319131 .NET ASP.NET Windows In this post I describe how to use the Microsoft WebView2 control to automate HTML to PDF generation generically for any kind of Windows application, including services. We'll look at the WebView and it's printing functionality and some of the intricacies that are involved in hosting the WebView control outside of a desktop application context to provide unattended mode even in service context. Comparing Raw ASP.NET Request Throughput across Versions https://weblog.west-wind.com/posts/2024/Mar/08/Comparing-Raw-ASPNET-Request-Throughput-across-Versions 4285010_202403081021 2024年3月08日 10:21:08 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Mar/08/Comparing-Raw-ASPNET-Request-Throughput-across-Versions#Comments 2 https://weblog.west-wind.com/commentrss.aspx?id=4285010 ASP.NET When I set up a new machine I usually use a small ASP.NET test project to get a feel of performance of the machine and when that happens I also take a moment to compare performance across recent versions of .NET to see how things are improving - and improved they have. Both due to the new hardware I'm using but also ASP.NET continues to bump up performance in every new version that comes out. In this post I describe a simple project with minimal do nothing requests to test the ASP,.NET pipeline locally and how to test these request as well as discussing the results. Reading Raw ASP.NET Request.Body Multiple Times https://weblog.west-wind.com/posts/2024/Feb/20/Reading-Raw-ASPNET-RequestBody-Multiple-Times 4257209_202402202308 2024年2月20日 23:08:09 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Feb/20/Reading-Raw-ASPNET-RequestBody-Multiple-Times#Comments 4 https://weblog.west-wind.com/commentrss.aspx?id=4257209 ASP.NET Some time ago I wrote about accessing raw request body content in ASP.NET Core which ended up being one of the most popular posts on this blog. But I failed to mention one major caveat: By default Request.Body can only be read once. In this post I discuss why frequently when you need raw Request.Body access you actually need to read the body more than once, and you can enable that functionality and deal with the caveats of doing so. Sharing Tab Missing in Windows 11 Folder Properties https://weblog.west-wind.com/posts/2024/Jan/10/Sharing-Tab-Missing-in-Windows-11-Folder-Properties 4187850_202401102254 2024年1月10日 22:54:27 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Jan/10/Sharing-Tab-Missing-in-Windows-11-Folder-Properties#Comments 1 https://weblog.west-wind.com/commentrss.aspx?id=4187850 Windows For some unfathomable reason, Windows 11 has removed the Sharing Tab on the Explorer Properties Context menu by default. The Sharing Tab allows you to shared folders and drives for remote access. In this post I discuss how to get the Sharing Tab back and also touch on how to make sure your machine can actually accept remote connections so you can share your folders and drives. Working around the WPF ImageSource Blues https://weblog.west-wind.com/posts/2024/Jan/03/Working-around-the-WPF-ImageSource-Blues 4169641_202401040536 2024年1月04日 05:36:29 GMT Rick Strahl https://weblog.west-wind.com/posts/2024/Jan/03/Working-around-the-WPF-ImageSource-Blues#Comments 2 https://weblog.west-wind.com/commentrss.aspx?id=4169641 WPF Windows The WPF Image control and its ImageSource property can be problematic if you are loading a lot of images in a list. Depending on where you load images from, and how, you can very easily get bogged down with slow, blocking load operations, and memory leaks when the controls are released. In this post I describe a couple of specific problems I ran into loading a sizable list of images from files and show a few ways how to avoid the potential pitfalls related to ImageSource peculiarities. Integrating OpenAI Image Generation into a .NET Application https://weblog.west-wind.com/posts/2023/Dec/21/Integrating-OpenAI-Image-Generation-into-a-NET-Application 4146698_202312212222 2023年12月21日 22:22:07 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Dec/21/Integrating-OpenAI-Image-Generation-into-a-NET-Application#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4146698 .NET AI Image Generation AIs are proving to be very good at creating images that can be used for all sorts of purposes. In this article I discuss how you can integrate image generation right into your own .NET applications using the OpenAI REST API. In addition I'll show how you can integrated this functionality into a larger application and discuss some general thoughts on image AI usage based on some of the experiences from a developer/non-designer user perspective. Embedding a minimal ASP.NET Web Server into a Desktop Application https://weblog.west-wind.com/posts/2023/Nov/27/Embedding-a-minimal-ASPNET-Web-Server-into-a-Desktop-Application 4108957_202311280842 2023年11月28日 08:42:36 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Nov/27/Embedding-a-minimal-ASPNET-Web-Server-into-a-Desktop-Application#Comments 3 https://weblog.west-wind.com/commentrss.aspx?id=4108957 ASP.NET .NET WPF Windows Did you ever need to embed a Web Server into a non-Web application? In this post I describe how you can use host ASP.NET in a non-Web application and specifically in a WPF desktop app. What do you need, how is it different and some of the issues that you need to consider if you want to use ASP.NET in your non-Web applications. Save Files With Elevated Permissions on UnauthorizedAccessException https://weblog.west-wind.com/posts/2023/Nov/03/Save-Files-With-Elevated-Permissions-on-UnauthorizedAccessException 4073408_202311031948 2023年11月03日 19:48:56 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Nov/03/Save-Files-With-Elevated-Permissions-on-UnauthorizedAccessException#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4073408 Windows WPF If you have an application that generically allows you to edit and save files, you might on rare occasions need to save files in locations that where a regular user account does not have permissions to save. Rather than failing wouldn't it be nice to let the user know and optionally allow saving with elevated permissions? In this post I describe the workflow and implementation on how to do just that. Caching your WebView Environment to manage multiple WebView2 Controls https://weblog.west-wind.com/posts/2023/Oct/31/Caching-your-WebView-Environment-to-manage-multiple-WebView2-Controls 4068979_202310312210 2023年10月31日 22:10:59 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Oct/31/Caching-your-WebView-Environment-to-manage-multiple-WebView2-Controls#Comments 2 https://weblog.west-wind.com/commentrss.aspx?id=4068979 WebView WPF Windows I've been struggling with rare WebView initialization errors in one of my applications, that have been difficult to debug and track down. After a lot of trial and error I discovered that the problem is related to WebView Environment instantiations that might be stepping on each other. In this post I describe the problem and a solution that involves creating a single WebView Environment and reusing it for all WebView initialization. Rolling Forward to Major Versions in .NET https://weblog.west-wind.com/posts/2023/Oct/02/Rolling-Forward-to-Major-Versions-in-NET 4028801_202310030812 2023年10月03日 08:12:27 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Oct/02/Rolling-Forward-to-Major-Versions-in-NET#Comments 2 https://weblog.west-wind.com/commentrss.aspx?id=4028801 .NET .NET Core has sophisticated policies that allows your applications that are compiled to specific versions of the .NET Runtime can roll forward to newer versions. You can specify what part of the version to roll forward and that generally works well, except for preview releases which require an extra step. IIS Error 500.19 with ASP.NET Core Application https://weblog.west-wind.com/posts/2023/Sep/19/IIS-Error-50019-with-ASPNET-Core-Application 4009938_202309191002 2023年9月19日 10:02:02 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Sep/19/IIS-Error-50019-with-ASPNET-Core-Application#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=4009938 ASP.NET Running on IIS locally is pretty rare, but if for some odd reason you decide to run IIS locally on your dev machine you might find yourself getting a 500.19 error which relates to an issue in the web.config configuration. The solution is simple enough: Make sure the ASP.NET Core Hosting Module is installed. In this post I describe in more detail what the problem is and why you need a seemingly superfluous extra install to get IIS and ASP.NET Core to cooperate on local dev machine. Dotnet Tool Component not found on the Mac https://weblog.west-wind.com/posts/2023/Sep/11/Dotnet-Tool-Component-not-found-on-the-Mac 3999259_202309120205 2023年9月12日 02:05:47 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Sep/11/Dotnet-Tool-Component-not-found-on-the-Mac#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=3999259 .NET I've run into this problem a few times: I install a new Mac OS and then install the .NET SDK. A bit later I install a dotnet tool using `dotnet tool install -g` and then try to run it, only to find out that the SDK is not able find it. This post is a note to self on how to fix the pathing for .NET tools to be found correctly and to be able to run your dotnet tools. Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET https://weblog.west-wind.com/posts/2023/Aug/15/Map-Physical-Paths-with-an-HttpContextMapPath-Extension-Method-in-ASPNET 3964818_202308160202 2023年8月16日 02:02:14 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Aug/15/Map-Physical-Paths-with-an-HttpContextMapPath-Extension-Method-in-ASPNET#Comments 1 https://weblog.west-wind.com/commentrss.aspx?id=3964818 ASP.NET Core ASP.NET ASP.NET Core doesn't have a Server.MapPath() method as classic ASP.NET had, and getting at the root path in Core is a little bit more involved than in those older versions. In this post I describe how to find the application Content and Web root folders and describe a MapPath() helper that simulates the old behavior. A WPF Statusbar Control https://weblog.west-wind.com/posts/2023/Aug/08/A-WPF-Statusbar-Control 3954615_202308082321 2023年8月08日 23:21:45 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Aug/08/A-WPF-Statusbar-Control#Comments 1 https://weblog.west-wind.com/commentrss.aspx?id=3954615 WPF Statusbar controls are boring, but because of the way that they are used there are a number of caveats like ensuring the UI updates even in linear code, allowing for timeouts of status messages and resetting to default, and to provide some visual clues to draw attention to the status messages displayed. In this post I talk about a custom status bar control and helper that make it super easy to use a new status bar control or attach additional functionality to an existing status bar. Getting the .NET Desktop Runtime Installed with a Custom Runtime Checker and Installer https://weblog.west-wind.com/posts/2023/Jun/21/Getting-the-NET-Desktop-Runtime-Installed-with-a-Custom-Runtime-Checker-and-Installer 3892897_202306220429 2023年6月22日 04:29:41 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/Jun/21/Getting-the-NET-Desktop-Runtime-Installed-with-a-Custom-Runtime-Checker-and-Installer#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=3892897 .NET WPF Windows Desktop I've recently moved my Markdown Monster Desktop Application to .NET 7.0 and I had to make a decision on how to get the Runtime installed or packaged with my application. In this post I review the different deployment modes, the plus's and cons and the solution I ended up with which was to build a custom install wrapper that can check and install the runtime if not already present from an Installer or interactively., Getting .NET Library Projects to Output Dependent Assemblies https://weblog.west-wind.com/posts/2023/May/29/Getting-NET-Library-Projects-to-Output-Dependent-Assemblies 3862505_202305292216 2023年5月29日 22:16:40 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/May/29/Getting-NET-Library-Projects-to-Output-Dependent-Assemblies#Comments 0 https://weblog.west-wind.com/commentrss.aspx?id=3862505 .NET Recent .NET Core versions have changed how .NET Core Library projects output dependencies into the build folder. The new behavior doesn't output depdencies, unlike full framework .NET projects which always automatically dumped dependencies into the build output folder. This isn't a common requirement, but when you need it, the options are sufficiently obscure and this post discusses how you can make your dependencies output into the build folder Implementing Two-Factor Auth using an Authenticator App in ASP.NET https://weblog.west-wind.com/posts/2023/May/17/Implementing-TwoFactor-Auth-using-an-Authenticator-App-in-ASPNET 3847525_202305180908 2023年5月18日 09:08:39 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/May/17/Implementing-TwoFactor-Auth-using-an-Authenticator-App-in-ASPNET#Comments 8 https://weblog.west-wind.com/commentrss.aspx?id=3847525 ASP.NET Security Two factor authentication using Authenticator apps is getting more popular. One advantage of Authenticator 2FA is that you don't need to use a service nor do users have to provide additional bits of personal information. It's easy to implement, doesn't cost anything and also very secure as it uses one-time codes that can't easily be corrupted short of physical take over of a device. In this article I describe how Authenticator based 2FA works in the context of an application without using ASP.NET Identity. Removing the IIS Server Request Header from ASP.NET Core Apps (any version) https://weblog.west-wind.com/posts/2023/May/08/Removing-the-IIS-Server-Request-Header-from-ASPNET-Core-Apps-any-version 3834571_202305082102 2023年5月08日 21:02:50 GMT Rick Strahl https://weblog.west-wind.com/posts/2023/May/08/Removing-the-IIS-Server-Request-Header-from-ASPNET-Core-Apps-any-version#Comments 4 https://weblog.west-wind.com/commentrss.aspx?id=3834571 ASP.NET ASP.NET Core applications that run on IIS as InProcess output an Server name for IIS into the HTTP headers. If you want to remove the server header, you'll find that the process for IIS is different than for the internally created Kestrel header and you can't use the same approach to remove the header as with Kestrel applications. In this post I discuss why the header behaves differently in IIS and how to remove it regardless of ASP.NET version.

AltStyle によって変換されたページ (->オリジナル) /