Considering Xojo alternatives -- Windows Forms

In looking at alternatives to Xojo, by virtue of my working pretty much as I please on internal admin tools for the system I’m building, I have a few thoughts for Xojo devs who might be pondering the many alternatives.

In general I’m not interested in building up a nontrivial UI without a WYSWIG editor of some kind. I’m also at a Microsoft / Windows shop.

Microsoft desktop development is something of a hot mess at the moment. Windows Forms is the spiritual heir to the enormously successful VB6 but MSFT is clearly ambivalent about it. For awhile it appeared to have been supplanted by Windows Presentation Framework (WPF), a rather clunky XAML-based system that now appears to be getting the same benign neglect that WinForms has “enjoyed” for several years (it’s been turned over to the Asian division for support where it’s said that “Microsoft technologies go to die”). As a probably temporary side effect of this, WInForms has gotten some love in recent months, but if I had to guess (and guess is all I can do) where Microsoft is going with desktop is some form of Electron-like browser-in-the desktop via Blazor. Blazor is primarily for WASM-based browser deployment, so yeah that’s probably not going to end well either.

For now, needing only tooling for a small handful of people to work with in-house (never more than a half dozen, in practice more like 2 or 3) and with the option for browser deployment “nice to have” but not crucial, I’ve settled on WinForms using C# as the language (you can of course use VB.NET).

I’ve found it at least as fast and enjoyable to create a GUI as Xojo promised to be on a good day, but there are some troubling flies in the ointment.

Probably 95% of the code I write is console apps and near cousins like Windows services. The tooling for that is super mature and reliable. The technical debt around WinForms is palpable by comparison:

  • The forms designer UI is not DPI-aware, whereas the rest of Visual Studio 2022 is. The practical result of this is that the forms designer sometimes gets itself into funketized states, particularly if, as I do, you are constantly switching between a laptop and a large monitor. I’m talking about closing all designers and stopping / restarting Visual Studio and sometimes even then the only reliable way to get steady work done is on my large monitor, if the “form” being worked on is fairly large and complex.

  • In general the fake-happy path for working with the designer is, work on a form, close the designer, compile and test, then reopen the designer. If you just leave it open it may be unresponsive or wonky after running the app. Mercifully if you forget, just close the designer tab, go to the corresponding code tab, and choose “View Designer” (although it can take a few seconds for the designer to come back up).

  • On rare occasions, Visual Studio gets out of sync with the code, giving spurious compiler errors pointing to lines that don’t have anything to do with the error, refusing to navigate to the line where the error is, etc. The only fix I’ve found for that is to stop and restart Visual Studio.

  • Round-trip designer (changes in designer properly reflected in code and vice-versa) mostly works correctly, but it’s not hard to accidentally create an empty event handler by fat-fingering something in the UI and it takes a little experience to know how to properly remove it as the round-tripping breaks down here a little (basically if you create an event in the UI, delete it in the UI)

  • If you report such bugs you’ll probably run into the M$FT culture of “not enough people care about this bug so it’s not a priority” (translation, we’re not going to fix it). They are catering to web devs and API devs and other cool kids.

All that said, while annoying, the workarounds aren’t bad and the apps themselves are easy to construct and work correctly at runtime.

Would I use WinForms for commercial software? No, I don’t think so, although there are some pretty mature 3rd party control libraries that probably help with some of the above issues, God help you if you want to do a MacOS version or a web version; your WinForms code is a mostly un-reusable island unto itself. I’m not sure what kind of support, if any, Ryder has for WinForms – probably zero, although they have said they intend to support and extend VB.NET as a language despite MSFT deprecating it, I don’t think they’re committed to WInForms itself.

It is surprising that M$, despite all the resources they have at their disposal, is still struggling to get that straight for developers while Apple has it up and running since years.

For Apple, yes. And AFIAK without a GUI / WYSIWYG designer. It’s always a proprietary tradeoff.

The reason I never used .Net was because you can download a free tool from github and decompile an EXE or DLL to source code in like 2 seconds. real C# sourcecode that can be altered and recompiled again. :expressionless:

Yes, I know, lots of people that can dissasemble them and HEX patch, but is not the same as any average people having the sourcecode.

You can always obfuscate the code to make it a bit harder to follow how your code works. I personally don’t see it as a big problem.

I haven’t bothered with it because I’m not doing commercial software, but .NET 8 (official release in November I believe) offers Ahead of Time (AOT) compiling … it will produce a native code EXE just as obtuse as a C++ compiler will. Just as fast, too. I don’t know if it’s limited to C# at least in the first iteration but the resulting .exe is self contained and is pretty granular about only having whatever lib support is actually used by the app. None of the CLR decompilers will work with it.

I think you rather overstate what the CLR compilers (particularly the free ones) can do, especially without the debug files (.PDBs) which you would not ordinarily distribute to users. I haven’t looked in a long while but examples I saw a few years back did not recover meaningful variable names for example. Maybe they’ve gotten better – but again I only do in-house software that we already have the source to, so there’s nothing to protect.

Java has/had many of the same issues at one point
But, also with AOT compilers, you can crank out an app that is nothing but machine code thats obtuse just like any other machine code
And you could decompile class files but since those carry no information about variable names etc, like C#, you’d get “source” but it was far from readable
It is, & was, of VERY limited use

Xcode provides a graphical UI design interface with SwiftUI:

No, I speak from experience actually, Last time, I didt it a couple years ago with a comercial software, it had a problem, devs refuse to fix it, so I used a free decompiler, get the source code of the exe and some DLLs, examine the code, comented a couple lines of C# code in a DLL, recompiled to a new DLL and all was working fine. Took me like 45 minutes.

Maybe I have been lucky but every time I have peaked onto some comercial app, the full source code was extracted, Variable names, function names, classes, etc.

If devs dont take advantage of AOT then you get assemblies that can be decompiled

And this ISNT the only option for making it difficult to recover source from a .Net app

Much the same stuff exists the java world since it also runs on a VM
But it also can be compiled AOT to nothing but an executable and no VM or byte code etc

Even then a dedicated person could disassemble an app but they wont get back the same source you or any dev wrote

I’ve decompiled .NET .dll’s and Java .jar’s and made use of it. That being said, if you are building internal software, who cares?

I would argue that AOT .NET won’t be as fast as C++ though. There are still performance implications of the runtime, GC, etc. A .NET app that has been JIT’ed will perform the same as an AOT one without the pause/delays for JIT.

Were the .pdb files present, or not?

One article I read made the claim it’s as fast as C++ but I seriously doubt the claim. It might be substantially as fast as C++ using one of those bolt-on garbage collectors. Also the .NET GC has improved a great deal in recent years. It’s generally not the old “stop the world” scenario that it used to be.

Apart from the GC, I don’t know that there are performance penalties inherent in the CLR vs the standard C++ libs. The Common Language Runtime has also gotten significantly faster and more optimized – that was much of the reason for the whole .NET Core project, to lay the groundwork for better performance, and they have continued to refine it with each release. And the CLR would benefit from AOT the same as your own code.

Some of this does require code changes from traditional .NET Framework to take full advantage, though not major and pervasive ones. I make a lot of use of ReadOnlySpan when manipulating strings on hot paths for example. someString.AsSpan(2,5) is way faster than someString.Substring(2,5) when you can pass it to something that can take a span. It avoids a string allocation, and minimizing allocations is important to avoiding GC overhead even in cases where it doesn’t help raw performance all that much. In my work I frequently have to parse substrings out of strings and convert to other types or re-concatenate portions together, and these come in very handy. Spans are an example of new functionality in .NET Core / 5+ that is already baked into the CLR to great advantage … if you leverage them in your own code it helps that as well.

You may very well be right.

I dont think so :thinking: it was an installed app in the PC, the exe and all the DLL gave the full source code.

Interesting. I can’t see reason for actual variable names to be kept in the binary. I seem to recall reading about a decompiler years ago that used some heuristic to produce something less ugly and occasionally helpful than var1234 for a variable name but it was still imperfect. Maybe the state of the art is much better or there are compile options that leave at least some variable names in the binary for better error messaging or something. IDK.

Maybe if you try it?

Lol yeah admittedly I have paid very little attention to it … have always built in house software so not an issue.

Did you tried?

The problem I “solved”, was the licence manager of the app not working when the PC had 2 (or more) Network Adapters. I jut fix the problem but having the source code, adding a “Return True” as first line in the function that validates the licence was a posibility, also the code in that same function could be used to create valid licence code. :crazy_face: