Roland Weigelt

Born to Code

  • A Stupid Little TypeScript Mistake

    For context: I am a C# developer who uses TypeScript just every now and then. Recently I added a few string constants to an existing TypeScript class. The program compiled just fine, but when I ran the program, the result was different from what I expected.

    For a repro scenario, copy the following script code into the Windows clipboard:

    class MyStrings {
        public static LoremIpsumDolorSitAmet = "Lorem ipsum dolor sit amet";
        public static ConsecteturAdipiscingElit = "consectetur adipiscing elit";
        public static VestibulumFeugiatLigulaEuOdioPosuereVelTristiqueDiamIaculis = "Vestibulum feugiat ligula eu odio posuere, vel tristique diam iaculis";
        public static FusceUrnaLiberoEfficiturNecTortorSed = "Fusce urna libero, efficitur nec tortor sed";
        public static UllamcorperFaucibusAugueProinUtPurusMetus = "ullamcorper faucibus augue. Proin ut purus metus";
        public static CurabiturAPosuereDiamSedElementumSedNislVitaeMaximus = "Curabitur a posuere diam. Sed elementum sed nisl vitae maximus";
        public static PraesentVitaeEnimVestibulumUltriciesNuncInGravidaSapien = "Praesent vitae enim vestibulum, ultricies nunc in, gravida sapien";
        public static ProinIaculisMiOrciUtRhoncusDuiVenenatisId : "Proin iaculis mi orci, ut rhoncus dui venenatis id";
        public static MorbiSedCongueLigulaSedFinibusNeque : "Morbi sed congue ligula, sed finibus neque";
        public static PellentesqueEuMolestieExIdFermentumEllus : "Pellentesque eu molestie ex, id fermentum tellus";
    }
    
    console.log(MyStrings.LoremIpsumDolorSitAmet);
    console.log(MyStrings.ConsecteturAdipiscingElit);
    console.log(MyStrings.VestibulumFeugiatLigulaEuOdioPosuereVelTristiqueDiamIaculis);
    console.log(MyStrings.FusceUrnaLiberoEfficiturNecTortorSed);
    console.log(MyStrings.UllamcorperFaucibusAugueProinUtPurusMetus);
    console.log(MyStrings.CurabiturAPosuereDiamSedElementumSedNislVitaeMaximus);
    console.log(MyStrings.PraesentVitaeEnimVestibulumUltriciesNuncInGravidaSapien);
    console.log(MyStrings.ProinIaculisMiOrciUtRhoncusDuiVenenatisId);
    console.log(MyStrings.MorbiSedCongueLigulaSedFinibusNeque);
    console.log(MyStrings.PellentesqueEuMolestieExIdFermentumEllus);
    
    

    Now head to the TypeScript Playground at https://www.typescriptlang.org/play, replace the text on the left side with the content of the clipboard and run the script.

    This will give you the following output:

    It took me a bit until it dawned on me why the last three strings are undefined – can you immediately figure out what the problem is?

    A hint: Just before I edited the class, I worked on a couple of JSON files.

  • Kostenloser Online-Workshop am 4.6.2021: User Interfaces und User Experience für Entwickler (Teil 2)

    Am 4. Juni 2021 bin ich ab 20:30 wieder auf dem Twitch-Kanal von Gregor Biswanger zu Gast. Um mehr Zeit für Fragen und Antworten zu haben, hatten Gregor und ich den Workshop am 7. Mai spontan aufgeteilt.

    Für diejenigen, die den ersten Teil verpasst haben:

    • Gregor hat eine Aufnahme des Streams auf YouTube hochgeladen und mit detaillierten Kapitelmarken versehen.
    • Ich werde mit einem groben Schnelldurchlauf starten.

    Inhalt

    Thema des zweiten Teils sind User Interface Patterns und andere Kochrezepte, die das Design von Bedienoberflächen vereinfachen. Die konkreten UI-Technologien spielen dabei keine Rolle, dementsprechend sind für den Workshop keine technischen Vorkenntnisse notwendig.

    Darüber hinaus stelle ich vor, mit welchen Tools und Methoden ich in meinem Alltag Ideen skizziere, GUIs entwerfe und die Abläufe ausprobiere.

    Wie beim ersten Mal wird die Interaktion mit Euch eine große Rolle spielen. Habt Ihr Fragen zum Was, Wann und Wie von UI/UX-Design im Umfeld der (agilen) Software-Entwicklung? Habt Ihr Screenshots von GUIs, die Ihr verbessern möchtet? Oder gibt es Designs, die Euch gefallen und die man gemeinsam auf wiederverwendbare Gestaltungsmittel und User Interface Patterns untersuchen könnte? Immer her damit, lasst uns darüber sprechen!

    Wie nehme ich teil?

    Wenn Ihr nur mal vorbeischauen möchtet, dann ruft am 7. Mai um 20:30 in Eurem Browser einfach https://www.twitch.tv/GregorBiswanger auf.

    Um auch am Chat teilnehmen zu können, benötigt Ihr einen (kostenlosen) Twitch Account.

  • Kostenloser Online-Workshop am 7.5.2021: User Interfaces und User Experience für Entwickler

    Am 7. Mai 2021 bin ich ab 20:30 auf dem Twitch-Kanal von Gregor Biswanger mit einem Workshop zu UI/UX-Grundlagen zu Gast.

    Zielgruppe sind Entwickler und andere Interessierte, die entweder

    • in einem Team ohne ein Budget für externe UI-Designer arbeiten, oder
    • mit einem besseren Verständnis in die Zusammenarbeit mit Externen gehen möchten.

    Inhalt

    Nach Grundlagen des Visual Designs stelle ich wichtige UI/UX-Fachbegriffe und -Konzepte in Theorie und Praxis vor, z.B. User Interface Patterns, Empathie, mentale Modelle oder Szenarien. In Live-Demos zeige ich Möglichkeiten zum Skizzieren von User Interfaces.

    Der Kanal von Gregor hat eine aktive Chat-Community, dementsprechend interaktiv werde ich den Workshop gestalten. Stellt Eure Fragen im Chat. Vielleicht habt Ihr eine UI, die Ihr gerne verbessern möchtet? Schickt Screenshots und wir wenden das Gelernte direkt live an.

    An einem Freitagabend darf natürlich der Spaß nicht fehlen: Problemlösungen à la Indiana Jones, Datenbanken für außerirdische Lebensformen oder Gedanken zum Einfluss von Rallye-Streifen auf die Geschwindigkeit von Sportwagen. Alles frei nach dem Motto “Wenn das Beispiel merkwürdig genug ist, dann ist die Idee dahinter merk-würdig”. Last-but-not-least: Kein UI-Vortrag ohne Beispiele für epische UI-Fails!

    Wie nehme ich teil?

    Wenn Ihr nur mal vorbeischauen möchtet, dann ruft am 7. Mai um 20:30 in Eurem Browser einfach https://www.twitch.tv/GregorBiswanger auf. Um am Chat teilnehmen zu können, benötigt Ihr einen (kostenlosen) Twitch Account.

  • Emaroo 4.7.0 - Support for VS Code 1.55

    Emaroo is a free utility for browsing most recently used (MRU) file lists of programs like Visual Studio, VS Code, Word, Excel, PowerPoint, Photoshop, Illustrator and more. Quickly open files, jump to their folder in Windows Explorer, copy them (and their path) to the clipboard - or run your own tools on files and folders with custom actions!

    About this Release

    • Updated: Support for Visual Studio Code 1.55 (changes regarding list of most recently used folders/workspaces). Previous versions of Visual Studio Code are still supported.
  • Adjust, Save and Restore Webcam Properties With CamProps

      In recent months, I wrote a few articles that hinted that I was developing a webcam-related utility (here, here, here and here). About two weeks ago, I released it to my colleagues who liked it and found it useful. Now, after some improvements, I am ready for a release to a larger audience.

      What is it?

      CamProps is a free utility for quickly adjusting and managing the properties of webcams and other video capture devices.

      Where can I get it?

      You can download CamProps at https://www.roland-weigelt.de/camprops/

      Why should I use it?

      The picture quality of many webcams can be improved by manually tweaking properties like focus, exposure or gain. When doing so, you may run into a few issues, though:

      • If you are in a room with changing lighting conditions, you have to adjust the settings each time you want to use the camera.
      • Some third-party software may decide to reset the camera back to default.
      • And some devices simply forget all settings after a reboot.

      CamProps lets you

      • quickly adjust camera properties,
      • manage different sets of configurations, and
      • restore the settings with a single click.

      Which devices can I use?

      CamProps works with all webcams and video capture devices that support opening the webcam driver property dialog (either the default provided by Windows or a specific dialog by the manufacturer's driver).

      For instance, when you press the “” button for a Logitech C920 without the Logitech software installed, this will open the following dialog:

      (This dialog may look different for other devices)

      How do I start?

      • Head to https://www.roland-weigelt.de/camprops/
      • Download and install the program.
      • Read the “How to…” section on setting up your first configuration, activating a configuration and backing up the automatically stored settings.
    • Compiling DirectShow.Net for use in .NET 5

      In a previous blog post, I wrote about how to access webcam properties from C# using DirectShow.Net. The last release of the library is version 2.1 from February 2010, with project files for Visual Studio 2008.

      Fortunately, porting the project to compile for .NET 5 went pretty quick.

      The project file

      <Project Sdk="Microsoft.NET.Sdk">
      
        <PropertyGroup>
          <TargetFramework>net5.0-windows</TargetFramework>
          <RootNamespace>DirectShowLib</RootNamespace>
          <SignAssembly>true</SignAssembly>
          <AssemblyOriginatorKeyFile>DShowNET.snk</AssemblyOriginatorKeyFile>
          <DelaySign>false</DelaySign>
          <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
          <PackageId>DirectShowLib.Net</PackageId>
          <Authors>http://sourceforge.net/projects/directshownet/</Authors>
        </PropertyGroup>
      
      </Project>

      Things to note:

      • The target framework is net5.0-windows because the code (obviously) uses Windows-specific features.
      • I added <GenerateAssemblyInfo>false<GenerateAssemblyInfo> to use the AssemblyInfo.cs file that came with the original project.

      AssemblyInfo.cs

      The following modifications were necessary:

      • Change AssemblyVersion from 2.1.0.* to 2.1.0.0 because the compiler complained about the wildcard.
      • Remove the lines related to the .snk file (now handled in the project file).
      • Add SupportedOSPlatform("windows").
      using System;
      using System.Reflection;
      using System.Runtime.InteropServices;
      using System.Security.Permissions;
      
      
      [assembly : AssemblyTitle("DirectShow Net Library")]
      [assembly : AssemblyDescription(".NET Interfaces for calling DirectShow.  See http://directshownet.sourceforge.net/")]
      [assembly : AssemblyConfiguration("")]
      [assembly : AssemblyCompany("")]
      [assembly : Guid("6D0386CE-37E6-4f77-B678-07C584105DC6")]
      [assembly : AssemblyVersion("2.1.0.0")]
      #if DEBUG
      [assembly : AssemblyProduct("Debug Version")]
      #else
      [assembly : AssemblyProduct("Release Version")]
      #endif
      [assembly : AssemblyCopyright("GNU Lesser General Public License v2.1")]
      [assembly : AssemblyTrademark("")]
      [assembly : AssemblyCulture("")]
      [assembly : AssemblyDelaySign(false)]
      // Path is relative to the resulting executable (\Bin\Debug)
      #if USING_NET11
      [assembly : AssemblyKeyFile("..\\..\\DShowNET.snk")]
      #endif
      [assembly : AssemblyKeyName("")]
      [assembly : ComVisible(false)]
      [assembly : CLSCompliant(true)]
      [assembly : SecurityPermission(SecurityAction.RequestMinimum, UnmanagedCode=true)]
      [assembly: System.Runtime.Versioning.SupportedOSPlatform("windows")]
      

      LGPL

      The DirectShow.Net library is licensed under the Lesser General Public License (LGPL) Version 2.1.

      To be able distribute my software at some point in the future, the LGPL license requires – in addition to proper attribution – to

      • ship the library as an individual DLL (instead of ripping out just the parts that I need), and to
      • make the source code available publicly.

      In order to comply with the LGPL, I published my fork of the library on https://github.com/RWeigelt/DirectShow.Net.

      Please note that I did not perform any tests of features that go beyond accessing webcam properties. The library compiles, I can use it in my project, and so far it did not break my code – that’s good enough for me at this moment…

    • WPF, Text Rendering and the Blues

      I am currently working on a small webcam utility written in C#/WPF that I intend to release as freeware later this year.

      On the main window, I use a ComboBox for selecting a webcam in a dropdown list, with the toggle button of the control styled to look like a heading:

      When you move the mouse pointer over the toggle button, the text and the glyph turn blue – nothing special.

      The application does not have a main menu, only a settings menu that you open by clicking a gear icon:

      The icon also turns blue when the pointer hovers over the general area:

      But wait a second… is this the same blue? A quick check:

      • The XAML says the text color is #0987c3.
      • The color picker in Photoshop tells me that the color of most icon pixels is… also #0987c3!

      What is going on?

      On a screenshot zoomed to 300%, the gear icon looks like this:

      And this is the text at 300% size:

      The colored pixels to the left and the right of the characters are caused by ClearType. You will also notice that many pixels “inside” the character shapes also have a different color. This is because less pixels than one might expect are fully covered in blue – which means that more pixels have their color modified by ClearType.

      What did I do wrong?

      It is not what I did do, but what I did not do. I did not tweak WPF’s text rendering to suit my needs. Which sounds curious, because writing a desktop application that looks good at typical application font sizes is not exactly an exotic usage scenario.

      When WPF was developed, it was designed to be device-independent and ready for a future of high pixel density displays (“HiDPI”). So WPF rendered text exactly as specified by the font metrics, which on normal density displays led to blurry characters. It took until .NET 4 for WPF to offer a way to render small text with an emphasis on readability instead of correctness (read this old article by Scott Hanselman for a look way back in time).

      What to do

      As a WPF developer, you can influence the text rendering by using the attached properties TextOptions.TextFormattingMode and TextOptions.TextRenderingMode. You apply them directly on an element, e.g., a TextBlock or set them on a window.

      TextOptions.TextFormattingMode

      The property TextOptions.TextFormattingMode can be set to Display or Ideal (which is the default).

      • With Ideal, text is rendered according to the font's metrics. Which is fine for large text, but not so much for small text on a non-HiDPI display.
      • With Display, the text rendering favors of aligning letters to pixels. This is especially important for small text on normal density screens.

      TextOptions.TextRenderingMode

      The property TextOptions.TextRenderingMode controls the way rendered text will be anti-aliased. Possible values are Aliased (which we will see can be confusing), Auto (the default), ClearType, and Grayscale.

      To show off the possible combinations, I placed the gear icon directly in front of the text (where it does not make any sense, just for comparing colors):

      Display Aliased
      Display Auto
      Display ClearType
      Display Grayscale
      Ideal Aliased
      Ideal Auto
      Ideal ClearType
      Ideal Grayscale

      So many options…

      In a first round, I eliminated:

      • Display&Aliased (no anti-aliasing),
      • Ideal&ClearType (the combination that caused me to write this article in the first place),
      • Ideal&Auto (which in my simple scenario looks the same as Ideal&ClearType), and
      • Display&ClearType (even though it looks better than Ideal&ClearType)

      Which left me with the following options:

      Display Grayscale
      Ideal Aliased
      Ideal Grayscale

      In the end, I decided to use Ideal&Aliased because I wanted the bolder text without a larger font size. And Segoe UI in “real” bold does not look good at this font size. Interesting that with Ideal, the setting Aliased is actually anti-aliased…

      I set TextOptions.TextFormattingMode="Display" on my application window but did not specify TextOptions.TextRenderingMode there.

      For the dropdown list, I set TextOptions.TextFormattingMode="Ideal" and TextOptions.TextRenderingMode="Aliased" on the ContentPresenter of the toggle button.

      A note on “Display” vs. “Ideal” used with “Grayscale”

      While writing this blog post, I had to juggle a larger number of image files than usual. When I saw the letter “P” in the enlarged versions of Display&Grayscale and Ideal&Grayscale, I first suspected I had mixed up the screenshots. But that was not the case; it is just a coincidence that the “H” and the “P” look sharper.

      This becomes clear when you look at a different text:

      Display Grayscale
      Ideal Grayscale

      Note the way the “m” is rendered and how more vertical lines are aligned to full pixels in Display mode.

      A good example for small sample sizes leading to wrong conclusions…

    • How to Access Webcam Properties from C#

      My Logitech C920 webcam requires some tweaking of settings like exposure, gain, focus, etc. to get good image quality. I uninstalled the “feature rich” Logitech software and now change the settings using the bare-bones Windows webcam properties dialog. This works well for me; unfortunately, the settings are not persisted reliably. After a cold-boot, or sometimes after simply starting an application that uses the webcam, I have to make the adjustments again.

      That is why I became interested in what would it take to read and write the webcam properties and to open the properties dialog from C#. The result of a web search was a bit intimidating as I came across multimedia APIs that go way beyond what I intended to do. After all, I only wanted to access the properties, not write a full-blown video capture suite.

      In the end I settled on DirectShow.Net, a C# wrapper around DirectShow under LPGL license. Even though DirectShow is an old API and the DirectShow.Net project seems to be no longer active, I found one very important reason to use it: A working sample that opened the webcam properties dialog.

      This blog post starts with a guided tour through the sample, with the intended side-effect of making the sample easier to discover on the web. Additionally, I will describe how to access the properties from your code.

      Step 1: Download DirectShow.Net

      • Visit  http://directshownet.sourceforge.net/
      • Go to “Downloads”
      • Download the latest version of the library (DirectShowLibV2-1.zip at the time of this writing)
      • Download the samples (DirectShowSamples-2010-February.zip)
      • Unpack the ZIP files so that the folder Samples is in the same directory as Docs, lib and src.
        • The lib folder contains the file DirectShowLib-2005.dll which the samples reference.

      Step 2: Run the “DxPropPages” demog

      • Open Samples\Capture\DxPropPages\DxPropPages-2008.sln in Visual Studio and let the “One-way upgrade” finish.
      • Run the project.
      • In the window that appears,
        • select your webcam and
        • press the “Show property pages” button.

         
      • On my computer, a properties dialog with two tabs appears. Depending on your drivers/webcam software, the dialog may have been replaced. But the generic, low-level dialog looks like this:



        (Remember “Video Proc Amp” and “Camera Control”, we will come across these names later)

      Step 3: Look at the code

      • Stop the program.
      • Open the code view of the file Form1.cs.
      • Set a breakpoint at the start of
        • the constructor e Form1(),
        • the comboBox1_SelectedIndexChanged() event handler, and
        • the DisplayPropertyPage() method.
      • Run the program in the debugger.

      How to get the available webcam(s)

      When the first breakpoint is hit, you will see the following lines:

      foreach (DsDevice ds in DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice))
      {
          comboBox1.Items.Add(ds.Name);
      }

      The code gets all available “video input devices” (which include webcams) and fills the dropdown that you used in step 2 to choose your webcam.

      A DsDevice instance has two important properties for identifying a device:

      • Name returns a human-readable name (i.e., what you saw in the dropdown list)
      • DevicePath returns a unique identifier.

      At this point, the sample does not store the instances, only the names, even though we need the DsDevice instance for the selected webcam later. I am not sure whether there is a reason for this other than keeping the sample code short and to be able to re-use the CreateFilter() method (which we will look at soon).

      How to open the properties dialog

      Now continue to run the program. The comboBox1_SelectedIndexChanged event handler gets called automatically during startup. If your webcam is not the first device, let the program continue and select the webcam in the dropdown.

      After the breakpoint has been hit, look at the code.

      • The purpose of the event handler is to set the field theDevice (of type IBaseFilter) which we need later.
      • The call of Marshal.ReleaseComObject(theDevice) when switching between devices is a reminder that we are dealing with COM and its reference counting (instead of relying on garbage collection).
      • Note that the variable name devicepath is misleading; the dropdown contains the display names of the devices. This becomes clear when we look at the CreateFilter() method: The second parameter is called friendlyname which is more appropriate.

      Inside the CreateFilter() method, some “COM stuff” happens. The important bit for us is that the returned IBaseFilter is assigned to the field theDevice, which is used in the button1_Click handler when calling DisplayPropertyPage().

      The method DisplayPropertyPage() contains even more COM stuff that we can ignore for now, because the method does exactly what its name says. We will see later that we need some basic understanding what is happening inside, though.

      How to make the controls in the dialog appear “Windows 10”-like

      The steps described my blog post “Windows 10 Theme for a Properties Dialog” for a WPF application are also valid for WinForms. In the case of the sample application the change also affects the controls of the main window.

      Step 4: Start to experiment

      The code inside DisplayPropertyPage() uses the ISpecifyPropertyPages interface. Two other interesting interfaces are IAMVideoProcAmp and IAMCameraControl. The names correspond to the pages of the properties dialog. Using the two interfaces, you can access the properties you see in the dialog.

      How to read or write the webcam properties from your code

      The interfaces IAMVideoProcAmp and IAMCameraControl both offer GetRange(), Get() and Set() methods.

      For IAMCameraControl, these methods are defined like this:

      int GetRange(
      	[In] CameraControlProperty Property,
      	[Out] out int pMin,
      	[Out] out int pMax,
      	[Out] out int pSteppingDelta,
      	[Out] out int pDefault,
      	[Out] out CameraControlFlags pCapsFlags
      	);
      
      int Set(
      	[In] CameraControlProperty Property,
      	[In] int lValue,
      	[In] CameraControlFlags Flags
      	);
      
      int Get(
      	[In] CameraControlProperty Property,
      	[Out] out int lValue,
      	[Out] out CameraControlFlags Flags
      	);

      When using the methods:

      • You specify the property you want to access via an enum value of type CameraControlProperty. Your device may not support all properties, though – if you look at the screenshots above, you will notice that some sliders are disabled. Therefore it is important to check the return value to be 0 (zero) for a successful call.
      • The CameraControlFlags value contains information whether the property is (or should be) set automatically and / or manually.

      Let us say you want to access the “exposure” property of your webcam (this may or may not work on your webcam; if not, you can try another property).

      For a quick-and-dirty test, resize the “Show property pages” button so can add another button next to it, double click the new button and insert the following code into the “Click” event handler:

      var cameraControl = theDevice as IAMCameraControl;
      if (cameraControl == null) return;
      
      cameraControl.GetRange(CameraControlProperty.Exposure,
      	out int min, out int max, out int steppingDelta,
      	out int defaultValue, out var flags);
      
      Debug.WriteLine($"min: {min}, max: {max}, steppingDelta: {steppingDelta}");
      Debug.WriteLine($"defaultValue: {defaultValue}, flags: {flags}");

      When I run the program, select my Logitech C920 webcam and press the button I added above, the following appears in the debug output window in Visual Studio:

      min: -11, max: -2, steppingDelta: 1
      defaultValue: -5, flags: Auto, Manual

      This means that the exposure can be adjusted from -11 to -2, with -5 being the default. The property supports both automatic and manual mode.

      Not all properties have a stepping delta of 1. For the Logitech C920, for instance, the focus property (CameraControlProperty.Focus) has a range from 0 to 250 with a stepping delta of 5. This is why setting the property value to e.g. 47 has the same effect on the hardware as setting the value to 45.

      Calling the Get() and Set() methods is simple. For instance, setting the focus to a fixed value of 45 looks like this:

      cameraControl.Set(CameraControlProperty.Focus, 45, CameraControlFlags.Manual);

      The CameraControlFlags.Manual tells the webcam to switch off aufo-focus.

      Where to go from here

      Note the COM pitfall in the sample

      If you are as inexperienced working with COM interop as I am and look at the original sample code inside DisplayPropertyPage(), you may notice that the line

      ISpecifyPropertyPages pProp = dev as ISpecifyPropertyPages;
      

      seems to have a corresponding

      Marshal.ReleaseComObject(pProp);

      Does this mean that we need a similar call in our experimental code we added above?

      No, because if you add the (only supposedly) “missing”  Marshal.ReleaseComObject(cameraControl) to your code and click the button repeatedly, you will run into this exception:

      System.Runtime.InteropServices.InvalidComObjectException
        HResult=0x80131527
        Message=COM object that has been separated from its underlying RCW cannot be used.
        Source=DxPropPages
      …
      

      What is happening here? The answer is that simply “casting” to a COM interface in C# is not something that has to be “cleaned up”. The code may imply that, but you could change the line

      Marshal.ReleaseComObject(pProp);

      to

      Marshal.ReleaseComObject(dev); // oDevice would work, too

      and it still would run without leaking references.

      How do I know? Because Marshal.ReleaseComObject() returns the new reference count and changing the line to

      Debug.WriteLine(Marshal.ReleaseComObject(oDevice));

      will output 1 each time we opened and close the properties dialog. The value of 1 is correct, because want to continue to be able to access the device object.

      Placing a copy of that line in front of the call of the external function OleCreatePropertyFrame() obviously does not make sense and will lead to an exception. But if you do it anyway, just for testing, the debug output will show 0 instead of 1. This shows us that passing the object as a parameter in COM interop – at least in this case – caused the reference count to be increased. This is why Marshal.ReleaseComObject() is called after OleCreatePropertyFrame(), not because of the cast to ISpecifyPropertyPages.

      Practice defensive coding

      As already mentioned, not all webcams support all properties. And if a webcam supports a property, the allowed values may differ from other devices. That is why you should use GetRange() to determine

      • whether a property is supported (return value 0),
      • the range of the allowed values, and
      • whether the property can be set to “auto”.

      Last, but not least: When you access a USB webcam – like any other detachable device – be prepared for it not being available. Not only at startup, but also while your program is running, because the device could have been unplugged unexpectedly.

    • Tiny Difference, Big Consequences Reloaded: SignalR in .NET Core 3.1 vs. .NET 5

      In a past blog post I wrote about a surprising change I encountered during the migration of a .NET Core 2.2 application to .NET Core 3.0. I have just migrated that same application to .NET 5 and guess what, I stumbled across another “tiny difference with big consequences”.

      The situation: I have a WPF desktop client that communicates with an ASP.NET Core server that in turn communicates with a small TypeScript browser application. I use Web API for accessing files and meta data, and SignalR to call remote functions.

      After the migration I ran the application and things were working fine – until a certain point, where I received the following exception in a call of SignalR’s HubConnection.InvokeCoreAsync() (actual function name replaced with “doSomething”):

      Microsoft.AspNetCore.SignalR.HubException
         HResult=0x80131500
         Message=Failed to invoke 'doSomething' due to an error on the server.
         Source=System.Private.CoreLib

      On the server side, my own code was not even called.

      It took me a while to notice that one of the parameters of the function had the type of a class with a specific constellation of constructors:

      • A public constructor with parameters
      • A private default constructor.

      It turned out while SignalR in .NET Core 3.1 has no problem (de)serializing an instance of a class with a private default constructor, SignalR in .NET 5 does. So simply removing that constructor fixed the problem. In terms forcing the usage of the other constructor when explicitly creating objects of that type, nothing changed; I did not need a private default constructor for that.

      P.S. Do not ask me why I had a private default constructor in the first place. But the project is a couple of years old now, and things like this do happen.

    • Windows 10 Theme for a Properties Dialog

      Today I played around with some code in an WPF application that opens a (COM-style) properties dialog.

      When opened outside my application, the dialog looked like this:

      20201012_C920_Windows

      But when I opened the dialog from my code, it looked like this:

      20201012_C920_WPF

      The solution is simple but appears blog-worthy to me because I had a hard time searching the Web using rather unspecific search terms like “properties dialog”, theme, style, etc.

      In the end I came across a theme-related Microsoft article for C++ mentioning a manifest file and connected the dots to a StackOverflow question/answer about changing the Windows User Account Control level for a C#/WPF application using a custom manifest.

      What to do

      Create the custom manifest file

      • In Visual Studio (2019), open the context menu of the project for the WPF application.
      • Add > New item.
      • Ctrl+E (to jump to the search box), enter “manifest”.
      • Press the “Add” button to add the “app.manifest” file to the project.
      • Visual Studio selects the file automatically (see project properties > Application > Resources > Icon and manifest > Manifest)

      Edit the manifest

      • Open the “app.manifest” file.
      • Scroll to the bottom.
      • Uncomment the section behind the “Enable themes for Windows common controls and dialogs (Windows XP and later)” comment.
      • Rebuild.

      That’s it!

      In case you are wondering about my (webcam) experiment: This will be the topic of an upcoming blog post.