Programming in C#, Java, and god knows what not

Using Null in the Face of CS0121

Sometime you might want to use null as a parameter but you get the annoying CS0121.

The call is ambiguous between the following methods or properties…

One could just adjust constructor but that would be an easy way out.

Proper way would be to use default operator. For example, if you want to use null string, you can use something like default(string):

var dummy = new Dummy(default(string));

If we’re using nullable reference types, just add question mark:

var dummy = new Dummy(default(string?));

And this simple trick selects the correct constructor every time.

Parsing GZip Stream Without Looking Back

Some files can exist in two equivalent forms - compressed and uncompressed. One excellent example is .pcap. You can get it as standard .pcap we all know and love but it also comes compressed as .pcap.gz. To open a compressed file in C#, you could pass it to GZipStream - it works flawlessly. However, before doing that you might want to check if you’re dealing with compressed or uncompressed form.

Check itself is easy. Just read first 2 bytes and, if they’re 0x1F8B, you’re dealing with a compressed stream. However, you just consumed 2 bytes and simply handing over file stream to GZipStream will no longer work. If you are dealing with file on a disk, just seek backward and you’re good. But what if you are dealing with streaming data and seeking is not possible?

For .pcap and many more transparently compressed formats, you can simply decide to skip into bread-and-butter of encryption - deflate algorithm. You see, GZip is just a thin wrapper over deflate stream. And quite often it only has a fixed size header. If you move just additional 8 bytes (thus skipping a total of 10), you can use DeflateStream and forget about “rewinding.”

Wanna see example? Check constructor of PcapReader class.

SignTool and Error -2146869243/0x80096005

As I was trying out my new certificate, I got the following error:

SignTool Error: An unexpected internal error has occurred.
Error information: "Error: SignerSign() failed." (-2146869243/0x80096005)

Last time I had this error, I simply gave up and used other timeserver. This time I had a bit more time and wanted to understand from where the error was coming. After a bit of checking, I think I got it now. It’s the digest algorithm.

SignTool still uses SHA-1 as default. Some servers (e.g. timestamp.digicert.com) are ok with that. However, some servers (e.g. timestamp.comodoca.com and timestamp.sectigo.com) are not that generous. They simply refuse to use weak SHA-1 for their signature.

Solution is simple - just add /td sha256 to the list of codesign arguments.

Merging Two Git Repositories

As I went onto rewriting QText, I did so in the completely new repository. It just made more sense that way. In time, this new code became what the next QText version will be. And now there’s a question - should I still keep it in a separate repository?

After some thinking, I decided to bring the new repository (QTextEx) as a branch in the old repository (QText). That way I have a common history while still being able to load the old C# version if needed.

All operations below are to executed in the destination repository (QText).

The first step is to create a fresh new branch without any common history. This will ensure Git doesn’t try to do some “smart” stuff when we already know these repositories are unrelated.

git switch --discard-changes --orphan ^^new^^

This will leave quite a few files behind. You probably want to clean those before proceeding.

The next step is to add one repository into the other. This can be done by adding remote into destination, pointing toward the source. Remote can be anywhere but I find it easiest if I use it directly from my file system. After fetching the repository, a simple merge is all it takes to get the commits. Once that’s done, you can remove the remote.

git remote add ^^QTextEx^^ ^^../QTextEx^^
git fetch --tags ^^QTextEx^^
git merge --ff --allow-unrelated-histories ^^QTextEx^^/main
git remote remove ^^QTextEx^^

After a push, you’ll see that all commits from the source repository are now present in destination too.


PS: Do make backups and triple verify all before pushing it upstream.

PPS: This is a permanent merge of histories. Consider using subtree if you want to keep them separate.

PPPS: Things get a bit more complicated if you want to transfer multiple branches from the other repository.

Background Worker in Qt

Coming from C#, Qt and its C++ base might not look the friendliest. One example is ease of BackgroundWorker and GUI updates. “Proper” way of creating threads in Qt is simply a bit more involved.

However, with some lambda help, one might come upon solution that’s not all that different.

#include <QFutureWatcher>
#include <QtConcurrent/QtConcurrent>
``…``
QFutureWatcher watcher = new QFutureWatcher<bool>();
connect(watcher, &QFutureWatcher<bool>::finished, [&]() {
    //do something once done
    bool result = watcher->future().result();
});
QFuture<bool> future = QtConcurrent::run([]() {
    //do something in background
});
watcher->setFuture(future);

QFuture is doing the heavy lifting but you cannot update UI from its thread. For that we need a QFutureWatcher that’ll notify you within the main thread that processing is done and you get to check for result and do updating then.

Not as elegant as BackgroundWorker but not too annoying either.

Stripping Diacritics in Qt

As someone dealing with languages different than English, I quite often need to deal with diacritics. You know, characters such as č, ć, đ, š, ž, and similar. Even in English texts I can sometime see them, e.g. voilà. And yes, quite often you can omit them and still have understandable text. But often you simply cannot because it changes meaning of the word.

One place where this often bites me is search. It’s really practical for search to be accent insensitive since that allows me to use English keyboard even though I am searching for content in another language. Search that would ignore diacritics would be awesome.

And over the time I implemented something like that in all my apps. As I am building a new QText, it came time to implement it in C++ with a Qt flavor. And, unlike C#, C++ was not really built with a lot of internationalization in mind.

Solution comes from non-other than now late Michael S. Kaplan. While his blog was deleted by Microsoft (great loss!), there are archives of his work still around - courtesy of people who loved his work. His solution was in C# (that’s how I actually rembered it - I already needed that once) and it was beautifully simple. Decompose unicode string, remove non-spacing mark characters, and finally combine what’s left back to a unicode string.

In Qt’s C++, that would be something like this:

QString stripDiacritics(QString text) {
    QString formD = text.normalized(QString::NormalizationForm_D);

    QString filtered;
    for (int i = 0; i &lt; formD.length(); i++) {
        if (formD.at(i).category() != QChar::Mark_NonSpacing) {
            filtered.append(formD.at(i));
        }
    }

    return filtered.normalized(QString::NormalizationForm_C);
}

Renaming Master Branch to Main

Illustration

GitHub is making a major change to the default branch name. As of October 1st, the default branch will be called main instead of master. While this is done just for the new repositories and the official recommendation is to wait until the end of year for the existing ones, I was never the one to follow the rules.

To locally change the name of the branch, you just need to move it.

git branch -m master main

Next step is telling GitHub you have a new branch:

git push -u origin main

If you go to GitHub now, youl’ll see both main and master present with master still being the default. To change this you’ll need to go into the repository settings and switch default branch there.

Only once that step is done, you can delete master branch forever.

git push origin --delete master

Now the existing repository now has main as the default branch name.

As you can see, currently this process is a bit involved and I am sure that GitHub will automate it reasonably soon. You might want to wait with local renames until they do. My plan is to update branch names as I push updates to my projects. Active repositories will get the update sooner while some old repositories might stay with master forever.

That’s all fine and dandy but what about the new repositories? Well, there’s a setting for that too. Just adjust init.defaultBranch Git property.

git config --global init.defaultBranch main

And now you’re ready to roll.


PS: I will not get into politics whether this change was necessary or not. As a Croat, I will never fully understand the slavery and the emotional impact having the master branch might have. For me this change is more of pragmatism. The exact name doesn’t matter much to me and main is a better choice anyhow.

On-screen Measurement Grid

Illustration

When shopping for gloves it is really useful to know hand size. Normal person would find a measuring tape. Me? I decided to make a on-screen measurement grid.

First step is just figuring pixel dimensions in relation to my diagonal. A bit of Pythagorean later, the following proved to be what I needed:

var diagonal = 15.6;
var pixelWidth = 1920;
var pixelHeight = 1080;

var ratio = 16.0 / 9;
var height = diagonal / Math.Sqrt(ratio * ratio + 1);
var width = height * ratio;  // not really needed

var pixelsPerInch = pixelHeight / height;

var inchWidth = pixelWidth / pixelsPerInch;
var inchHeight = pixelHeight / pixelsPerInch;

Notice here that I am using “witchcraft” units instead of millimetres as I normally would. Reason for that is simple - I was buying gloves on USA site and all measurements were in inches. My screen measurement was also in inches. With both these units being the same, it made no sense to convert into something else first.

Also notice I am only using height to determine pixel density thus making an assumption pixel is a perfect square. Unless you are dealing with something really strange, this assumption is perfectly good.

With these basic calculations done, it’s time to draw. Notice I have a few multiplications/divisions by 4 hear - the only reason for these is due to me finding inch-based grid way too coarse. A quarter-inch grid gives a bit more flexibility here.

using (var bmp = new Bitmap(pixelWidth, pixelHeight))
{
    using (var g = Graphics.FromImage(bmp))
    {
        g.FillRectangle(Brushes.White, 0, 0, pixelWidth, pixelHeight);

        for (var i = 0; i &lt; (int)Math.Ceiling(inchWidth) * 4; i++) {
            var pen = (i % 4 == 0) ? Pens.Black : Pens.LightBlue;
            var x = (int)(i / 4.0 * pixelsPerInch);
            g.DrawLine(pen, x, 0, x, pixelHeight);
        }

        for (var i = 0; i &lt; (int)Math.Ceiling(inchHeight) * 4; i++)
        {
            var pen = (i % 4 == 0) ? Pens.Black : Pens.LightBlue;
            var y = (int)(i / 4.0 * pixelsPerInch);
            g.DrawLine(pen, 0, y, pixelWidth, y);
        }
    }
    bmp.Save("Background.png");
}

I made this image my background and voila! Now I can measure my hand without ever leaving my chair.

Dark Mode for QT Application

With dark mode becoming quite fast the default setup for many, I figured having it in the new QText wouldn’t hurt. And with QT it’s easy - surprisingly so. Just start with a good style (i.e. Fusion) and adjust its palette. And that’s all it took.

qApp-&gt;setStyle(QStyleFactory::create("Fusion"));

QPalette newPalette;
newPalette.setColor(QPalette::Window,          QColor( 37,  37,  37));
newPalette.setColor(QPalette::WindowText,      QColor(212, 212, 212));
newPalette.setColor(QPalette::Base,            QColor( 60,  60,  60));
newPalette.setColor(QPalette::AlternateBase,   QColor( 45,  45,  45));
newPalette.setColor(QPalette::PlaceholderText, QColor(127, 127, 127));
newPalette.setColor(QPalette::Text,            QColor(212, 212, 212));
newPalette.setColor(QPalette::Button,          QColor( 45,  45,  45));
newPalette.setColor(QPalette::ButtonText,      QColor(212, 212, 212));
newPalette.setColor(QPalette::BrightText,      QColor(240, 240, 240));
newPalette.setColor(QPalette::Highlight,       QColor( 38,  79, 120));
newPalette.setColor(QPalette::HighlightedText, QColor(240, 240, 240));

newPalette.setColor(QPalette::Light,           QColor( 60,  60,  60));
newPalette.setColor(QPalette::Midlight,        QColor( 52,  52,  52));
newPalette.setColor(QPalette::Dark,            QColor( 30,  30,  30) );
newPalette.setColor(QPalette::Mid,             QColor( 37,  37,  37));
newPalette.setColor(QPalette::Shadow,          QColor( 0,    0,   0));

newPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));

qApp-&gt;setPalette(newPalette);

PS: Ok, chances are that you will need another iconset but that’s the story for some other time.

SignTool Failing with 0x80096005

After creating a new setup package I noticed my certificate signing wasn’t working. I kept getting error while running the same signing command I always had.

sign -s "My" -sha1 $CERTIFICATE_THUMBPRINT -tr ^^http://timestamp.comodoca.com/rfc3161^^ -v App.exe
 SignTool Error: An unexpected internal error has occurred.
 Error information: "Error: SignerSign() failed." (-2146869243/0x80096005)

A bit of troubleshooting later and I narrowed my problem to the timestamping server as removing /tr option made it work as usually (albeit without the timestamping portion). There were some certificate changes for the timestamp server but I don’t believe this was the issue as the new certificate was ok and I remember their server occasionally not working for days even before this.

And then I remembered what I did the last time Comodo’s timestamp server crapped out. Quite often you can use other, more reliable, timestamp server. In my case I went with timestamp.digicert.com.

sign -s "My" -sha1 $CERTIFICATE_THUMBPRINT -tr ^^http://timestamp.digicert.com^^ -v App.exe
 Successfully signed: App.exe

PS: This same error might happen due to servers refusing SHA-1.