• (nodebb)
    RegistryKey RK = Registry.LocalMachine.OpenSubKey("SOFTWARE\\XXXXX\\YYYYY");
    

    That's horrible because RegistryKey is an IDisposable object; not only will you run into memory leak issues, the numbers of handles are also limited.

    The correct code would be:

    using var Rk = Registry.LocalMachine.OpenSubKey("SOFTWARE\\XXXXX\\YYYYY");
    
  • TheCPUWizard (unregistered)

    @MaxiTB - MOST IDisposable do not actually keep a reference to an unmanaged object. Therefor implementing IDisposable is the WRONG approach. Also implementing IDisposable does not do anything... it merely requires you write a method named Dispose...the system will NEVER call it automatically... You could write a method call FooMe and call it in all of the places where you would have called Dispose... Further, it must be legal to call Dispose multiple times, but accessing ANY other ember must throw an ObjectDisposedException... and ANY class which has an IDisposable Member must also implement IDisposable...

    In 2022 I led a team of 3 developers, took 19 man months!!!! to eliminate incorrect IDisposable from their code, and encapsulate/abstract all of the incorrect implementations in the .Net Framework and NuGet package..

  • Brian Boorman (unregistered)

    Add to the first example, I'd be extremely surprised to find a compiler that didn't convert any pure mathematical expression with known values to a constant during compilation. This syntax is common as it clearly indicates the bit position being tested in a bit field. Guess must be fresh out of the college dorm.

  • LXE (unregistered)

    Users who have been instructed to create HTML pages should have been instructed which editor to use. So, still a WTF.

  • (nodebb) in reply to MaxiTB

    I was going to comment that the finalizer will dispose it for you (upon GC), but it looks like RegistryKey doesn't have a finalizer? In .NET Framework, there's a Dispose(bool) method, but no finalizer with a Dispose(false) call??? And in .NET Core, they've removed that indirection!

    .NET Framework: https://referencesource.microsoft.com/#mscorlib/microsoft/win32/registrykey.cs,7fd1b519e6923ca5

    .NET 8: https://github.com/dotnet/runtime/blob/v8.0.0/src/libraries/Microsoft.Win32.Registry/src/Microsoft/Win32/RegistryKey.cs#L128-L163

  • 56indepndent (unregistered)

    "Sometimes, we have a broken world that we can just do our best no navigate"

    I guess i'll navigate past the n-t mixup just before "navigate" :^)

  • Darren (unregistered)

    What these submissions - and to be fair, almost everything that ends up on this site - really show is that the world seems to be filled with people doing things that they don't really understand - they're just fudging their way through in a blind panic, randomly changing things until the errors go away.

    Has it always been this way or has it just reached a point where it's become the norm rather than the exception?

  • (author) in reply to Darren

    I'd argue that it's always been that way. A good rule for life, which I learned from TDWTF, is this: if you show up, do the work, and are halfway competent, you are light years beyond the competition already. The world doesn't need more geniuses, it just needs more competent people.

  • Conradus (unregistered)

    #define FEATURE_SENSE_CHAN (1 << 0)

    Aaaand now I'm visualizing an anime starring "Feature Sense-chan".

  • (nodebb) in reply to colejohnson66

    I think it's because they realized that since they're using a SafeHandle already, there's no need for a finalizer in a class that doesn't manipulate directly an unmanaged resource's lifetime.

    Only the SafeHandle class should have a finalizer (that takes care of calling CloseHandle) since it's the one managing the lifetime of an unmanaged resource.

  • Jason Stringify (unregistered) in reply to 56indepndent

    It goes with the mangled grammar in the first sentence of Remy's intro.

  • PedanticRobot (unregistered)

    It may be minor, but C# has very simple and clearly defined naming conventions, so an upper camel cased local variable like "BoolLog" is definitely a WTF.

  • (nodebb)

    (BoolLog == "true" || BoolLog == "yes" || BoolLog == "1")

    I know exactly how this happens, because it happened to me.

    We had a legacy application that was a hodgepodge of C#, VB6, and a few minor scripts.

    It would save settings in various places. INI files, database, .config files, and yes, registry.

    If you changed a boolean setting value and converted it to a string, the result would be different. For example, "true" in VB6 is "-1". Which makes a weird kind of sense - all bits are set to 1.

    And then you'd have people manually changing values. Telling someone "set that value to true" could result in "true", "yes", "1" or something else.

    So deep in the code, in a "GetSettingValue<bool>" method, I had to handle booleans a bit differently. "ON", "TRUE", "T", "YES", "Y", "1" and "-1" would return true. Anything else returned false.

  • Mick Moignard (unregistered)

    Could it be that rather than people getting dimmer, this stuff is getting over complex? I realise that the current world of development is far more complex than the world I started my career in (Mailframe PLI) as we are developing on, with and for more capable environments and creating far more complex solutions, but I do wonder if these modern tools and languages and environments are not just making stuff too complex for humans to understand and explot - and debug - and I lean on the whole of WTF as my evidence for this hypothesis.

  • (nodebb) in reply to Darren

    What these submissions - and to be fair, almost everything that ends up on this site - really show is that the world seems to be filled with people doing things that they don't really understand - they're just fudging their way through in a blind panic, randomly changing things until the errors go away.

    It has been my experience that most developers work this way. I really appreciate it when I see bad code and it has the same variable names as in the Stack Overflow highest rated answer for a question vaguely related.

    I just had an issue this morning where a developer used a pile of global variables in a chuck of code that processes a file. Shockingly, under an uncommon set of circumstances, one of the variables carried over a value from the previous record. We couldn't get it through the developer's head that the actual problem was the global variables, not the specific bug that happened this time.

    When I see new feature introduced in my development ecosystem, I can usually see the progression from problem to solution. 90% of the people I interact with didn't consider the original problem and don't see the value of the solution.

    For example, C# fairly recently introduced default interface implementations. The first thing I thought was that for my frequently-added-to interfaces, such as database repositories, I can just add an interface method with a default implementation of "throw new NotImplementedException();", and I can avoid compile errors from other consumers of the interface. In the past, I would have had to make a base class that implements everything and have consumers inherit the base class rather than directly implement the interface. The new pattern is less work, less likely for people to get wrong, and looks more correct.

  • (nodebb) in reply to colejohnson66

    The point of IDisposable is to dispose unmanaged resources as soon as possible. Finalizers are called by the garbage collector - there is no guarantee when, in fact, if the objects ends up as a gen-2 object, it will only be called when the process gets terminated. The disposable pattern recommends overwriting the finalizer and recommends allowing multiple disposing, but those are not mandatory - in fact many old IDisposable objects in the framework didn't implement the recommended parts of the pattern, most famously the old XML library. When an object is IDisposible, you have to dispose it as a consumer as soon as possible in an exception safe way, period. That is the rule, it doesn't matter what Dispose implements (see https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/using-objects) ;-)

    In .net Framework days RegistryKey relied on the Win32 SDK for access to the registry, using the registry handles. Those get cached, so it is critical to release them to allow the cache to flush.

    With netcore Registry parts got reimplemented in a open source non-properitary way, so it's implementation is slightly different. But the usage is the same and as mentioned above, it's not about the implementation, IDispose is a contract, you have to correctly dispose the object.

  • (nodebb) in reply to TheCPUWizard

    MOST IDisposable do not actually keep a reference to an unmanaged object. Therefor implementing IDisposable is the WRONG approach

    It doesn't matter how it is implemented. IDispose is a contract, it has to be disposed correctly (see https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/using-objects). It's up to the creator to the component to make use of the contract the way the creator sees fit. As a consumer of the object, it is your responsibility to follow the contract because you don't even know that will be behind Dispose tomorrow.

    Also implementing IDisposable does not do anything... it merely requires you write a method named Dispose

    IDisposable is a contract that exist since .net 1.0 for handling eager cleanup operations. It doesn't matter what the method implements at this point, if the object has an IDisposable, you have to dispose of it correctly as a consumer of said object (see https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/using-objects). It's exactly the same way as you have to free memory after you alloc it in C; those are also just functions, the contract demands of the consumer to free the object as soon as no longer required.

    Further, it must be legal to call Dispose multiple times, but accessing ANY other ember must throw an ObjectDisposedException

    This is incorrect. Dispose SHOULD allow to be called multiple times, but there is no requirement for this and never has been. In fact it is wrong to rely on this. Since netcore it is best practice to do it, but not required (see basic dispose pattern https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern). There is still a ton of Disposable objects around in various libraries that don't support multi-disposing, especially when it's thin COM+ wrappers.

  • Loren Pechtel (unregistered)

    I have a gripe with the article rather than the presented code.

    I don't believe the Registry was ever intended to actually be an optimum solution for storing configuration. Rather, it was intended to make it impossible to copy an existing software installation--an anti-piracy measure.

  • A Nonny Moose (unregistered)

    My understanding is the Windows Registry was created to solve the problem that FATnn filesystems on 90s hard drives meant even a small .ini file would use a relatively large amount of space. That benefit hasn't been remotely relevant for at least a decade and all that remain are the problems.

  • (nodebb)

    The first one is a WTF

    Here's a fix

    #define FEATURE_SENSE_CHAN      (1 << 0)
    #define FEATURE_SENSE_PEER      (1 << 1)
    #define FEATURE_SENSE_FILE_NOT_FOUND     ( (1 << 1) || (1 << 0))
    

    Joking aside, I think I need more context as somebody who knows nothing about Internet telephony. Is it possible for both to be true or for neither? If not, I would argue that the state is modelled in a WTF way.

  • (nodebb) in reply to Loren Pechtel

    I don't believe the Registry was ever intended to actually be an optimum solution for storing configuration. Rather, it was intended to make it impossible to copy an existing software installation--an anti-piracy measure.

    This seems unlikely. In what way does the Registry combat piracy?

    According to Raymond Chen [1], the advantages of the Registry over INI files are:

    • Hierarchical format
    • Strongly typed data
    • Atomic operations
    • Simultaneous access by multiple users
    • Centralized backup and restore
    • Granular security control
    • Unicode API support

    [1] https://devblogs.microsoft.com/oldnewthing/20071126-00/?p=24383

  • (nodebb)

    IDisposable has always been a difficult contract to do right. Looks simple, but not so much in reality with complex objects which may hold quite a graph of IDisposable objects but themselves do not hold unmanaged resources directly. Collections of IDisposable whatevers are especially un-fun.

    Beyond that an object upon which Dispose() has been called must fire an ObjectDisposedException on every single property access or method call. And for completeness, must have no non-private fields. The compiler provides no help for implementing that, and all the handy shortcuts for property definition must not be used if the object implements IDisposable.

    @CPUWizard misses the point that the system never calls Dispose(). It's not expected to. It's part of the object consumer contract to call Dispose(). Not part of the GC's contract, nor any other backstage process.

  • (nodebb)

    Oops. Meant to have more in the second paragraph. The "useless after Dispose" feature is a misfeature for lots of use cases where you might want a long-lived object which populates and depopulates various either IDisposable objects or unmanaged references. Essentially you're forced to build things that can be Close()ed, but never re-Open()ed. Which depending on how heavy they are may be utterly the wrong design decision.

    I totally get why IDisposable was needed even from .NET Framework 1.0. It's a shame they haven't come up with a different IDisposable2 that encompasses the lessons learned in the intervening 20+ years.

  • xtal256 (unregistered) in reply to Gearhead

    Can someone explain why people think the Windows registry is "a nightmare" to work with? Especially given all the above benefits.

    In all my years of working with Windows desktop apps, I've never had a problem with the registry. It's a wonderful place to store your app's config in a typed (well, except for boolean), hierarchical way.

    What's the alternative? Text files?

  • (nodebb) in reply to xtal256

    Works fine for Linux and MacOS.

    Also worked just fine for Windows prior to 95.

    Fun fact, Windows 3.11 actually had a registry too, it had regedit.exe and reg.dat, but the only thing MS stored in the registry was file associations.

  • xtal256 (unregistered) in reply to ray73864

    What works fine? Linux and MacOS don't have a registry?

    Or are you just suggesting that text files are the way to go? Because I hear a lot of hate about JSON and other formats from people on this site and on that I can agree. Text files have no defined format (so you end up re-inventing the wheel every decade or so - xml, json, yaml, etc) and have no data types so you have to decide how a date, number, etc should be stored.

    Again, why is the Windows registry horrible?

  • (nodebb)

    Can someone explain why people think the Windows registry is "a nightmare" to work with? Especially given all the above benefits.

    It's not the registry per se, but I don't care for the fact that RegEdit saves changes automatically, and does not show you a diff of the changes before saving.

Leave a comment on “What the Hmm?”

Log In or post as a guest

Replying to comment #:

« Return to Article