Alternative title: How the Windows API almost made me vomit
Chapter 1: Writing a native library for Java
Alright, here we go. This is my first blog post on this beautiful site - The name was not my idea by the way.
My name is Max and I'm a professional Java fetishist. In this post I'm going to tell you about my recent experiences with C++ and the Windows API.
It all started with a simple project idea - I wanted to write a program in Java that interacts with data from a Euro Truck Simulator 2 plugin. There was a small problem though: The plugin publishes this data through shared memory, also known as memory mapped files. This in itself would not be a problem, but Java does not have an API for interacting with shared memory. So, I did what anyone would do in this situation: I searched for an answer to this problem on StackOverflow. Unfortunately though there was no answer.
I slowly realized that I would have to write a native library for this on my own. Did I mention that I have pretty much never worked with C++? At this point I knew that this project was going to be a huge shit show, but I didn't quite anticipate how much of a shit show it was going to be.
Fortunately for me Java has this neat little feature called JNI, or Java Native Interface. It allows you to define method signatures using the
native keyword and implement those methods in C/C++. I was able to get this JNI thing up and running pretty quickly using this guide from Baeldung. Now I was facing a new problem though: How do I implement shared memory?
I learned that I would have to write different implementations for Windows and Linux (and MacOS probably too) and to be honest, I really did not have the nerve to do that. So, I looked for an alternative and I quickly found something promising: The Boost interprocess library. It's a header only library which meant that I did not have to link it. I have no clue what that means, but it meant less work for me, so I rolled with it.
Working with the Boost interprocess library was pretty easy: I just copied the shared memory example and modified it for my needs. Some folks might call that "stealing", but I like to call it "clever recycling". In under three days I was able to build a little shared memory library for Java. I was having a few problems here and there of course, but in comparison to what's following they were a walk in the park. So, if you need to interact with shared memory in Java on Windows and / or Linux, feel free to check out sharedmem.
This story is not over though. No, it is just getting started. Remember that I built this whole library just so I could read data from a ETS2 plugin? Yeah, well, there was a whole new problem.
Chapter 2: Wine and shared memory
I created a little test project, implemented my new sharedmem library, started ETS2 and attempted to read the plugin data. And now imagine my face when I got a 'File not found' error.
While I was busy with my little shared memory project I overlooked one minuscule detail: I'm playing ETS2 through Wine (Proton), which means that the plugin data was inside of some Wine process. I also couldn't just switch to a native ETS2 installation, because the plugin I'm using is only compatible with Windows.
So, tell me... What the f*ck am I supposed to do now?
I spent hours looking for a solution on Google and as a result my search history is now filled with things like "wine expose shared memory to host" and "wine read shared memory". Eventually I found this thing called wineshm-go by LeonB which put me on the right track: I had to write a program that would read shared memory and print the data to stdout. I then had to start this program with Wine using the same Wineprefix as my ETS2 installation. Well, this was easier said than done.
The Boost interprocess library worked perfectly last time, so I decided I would use it again for this project. I implemented the library, wrote a quick program that would read the data, compiled it, launched it with Wine and boom, there it was - A
boost::interprocess_exception::library_error. Wait, what?
One hour of googling this error later I still had no clue what was wrong. To this day I don't know the exact reason why it just doesn't work under Wine, but I assume it relies on some Windows thing that Wine just does not support. A horrifying realization hit me: Since I couldn't use the Boost library anymore I had to directly access the shared memory myself using the Windows API.
I have heard bad things about the Windows API, but I never thought it would be this bad. The whole API is a confusing mess of code. Maybe I just suck, but I needed some time to kind of understand what was going on. The thing that I hated the most though was the naming: It is the most inconsistent thing I have ever seen. Some methods are PascalCase, some are snake_case with a leading underscore.. It was horrible.
After many nervous breakdowns I finally managed to use the Windows API to read shared memory. I was so incredibly happy once I got that piece of garbage working. I think I don't have to mention that the program contains many pieces of stolen StackOverflow and MS Docs code which are held together with duct tape. It works though, and that was my main intention for this project.
The following code snipped was the C++ file before I started cleaning everything up. That's probably the most horrendous thing I have ever written - Even my production code at work is not as bad as this thing.
The only thing I still had to do was write a Java app that would launch the program through Wine and read the process output. That however was probably the easiest task of this whole project.
Thank you for reading this blog post. I had a ton of fun writing this thing (even though it took me a couple of hours) and I hope you stick around for our future posts.
Just for your information, I'm not a native English speaker, so please excuse any potential mistakes. I did my best :)
And just to clarify: My hatred towards the Windows API in this blog post is a bit exaggerated. I'm still not a huge fan, but to be completely honest, I still had fun figuring everything out.
Anyways, hope you had fun - See ya!