Now that I've made my decision about where to work, I can finally come out and say it: Windows Programming Sucks! It's okay when everything is packaged up nicely, or when there are step-by-step instructions or examples for exactly what I need to do... but when I'm trying to do something just a bit odd, it's hopeless. Listen, I can RTFM with the best of them, but with these giant Microsoft products, RTFM ends up referring to dozens of msdn articles, book chapters, forum posts, and worst of all, tiny "help" strings (as if) in a multitude of property inspectors. Those articles etc depend on a deep knowledge of how Windows programming worked in the last two or three iterations: COM vs COM+ vs ATL vs MFC vs OMFG!. I swear to god, the solution to my problem seems to be "Just use P/Invoke." Then it turns out that P/Invoke is actually pinvoke and it's something that gets called by IL (Intermediate Language) which is generated by the C++ compiler. So "using P/Invoke" actually means something like "In these six dialog boxes, set these nine options to these values: '/Gz', 'Native', 'Never', 'Yes', and 'Only on Tuesdays.'" Except the documentation doesn't say so directly.
So why on earth am I using Windows, anyways? I'm working on a project which makes heavy use of the Tablet PC digitizer and Microsoft Handwriting Recognition. No other platform delivers these capabilities. The Tablet PC digitizer, when used with the Microsoft.Ink api's, generates an order of magnitude more samples of stylus location than external Wacom tablets, or any other device I've used. This level of detail yields much better handwriting recognition results. So, yes, thank you for that, Microsoft Tablet PC team.
My task for the last few days has been trying to switch renderers for this application, from a pure-C#, non-hardware-accelerated, non-standard chemistry renderer, to an OpenGL renderer, specifically, G3D. G3D is a multi-platform OpenGL abstraction written in C++. It's sweet, fast, well-documented, and I've been using it for about a year for various projects on Windows and Linux. So I want to use the G3D OpenGL renderer in my existing C# .NET application. In other words, I want to use a legacy library to enhance my .NET application; surely this is a common situation. Turns out that it's such a common situation that there are at least four ways to do it, and none of them are explained clearly, anywhere.
The best two options seem to be making a managed C++ wrapper which calls the "unmanaged" G3D code, or recompiling G3D with the "/clr" flag, which means, compile it to intermediate language, to run on the common language runtime. Sounds good, but the documentation for the first option is all about calling code in an existing DLL, or dynamically loaded library. G3D only exists as a statically loaded library. In Windows, a variety of insane keywords (__cdeclspec dllimport!) are required to tag any parts of an API which should be available to users of a DLL; those keywords are not required for users of a statically-linked library. G3D doesn't have those keywords, and so G3D doesn't make a useful dll. I haven't found documentation on calling statically-linked unmanaged C++ code from managed C++.
Recompililng G3D with the /clr flag sounds good, except that it just pushes the problem off by one step; G3D statically links with other unmanaged C++ libraries, like libpng and libjpeg. So I'd actually have to compile all of these satellite libraries with /clr, too. I haven't tried this yet, but I have a feeling that those libraries will try to do things which aren't permitted in managed C++; they're tuned for fast image manipulation. Even leaving out that worry, adding /clr to the compiler options for the G3D code triggers a cascade of incompatible compiler flags: run-time checking, exceptions, and certain kinds of optimizations, don't work with the /clr option. To turn off those incompatible compiler flags, though, requires the use of, I kid you not, three levels of hierarchical UI: per-project, per-category, and per-subcategory, with a dozen options for each subcategory. The subcategory options have names that don't necessarily correspond to the flag they modify; ie, to get the /clr flag, I set "Use managed C++ extensions" to "Yes," and to turn off /Gz I actually have to set two separate run-time type checking properties to some unknown pair of values. In fact, this is where I'm currently hung up; I have to turn off /Gz to compile with /clr, but I can't figure out how to turn off /Gz. The options in the three-deep UI for setting one of the two run-time type checking flags are something like "Auto," "Default," and "Yes," but what I need is "No."
I'm disheartened. None of the approaches I try quite exactly fail, they just descend into a combinatorial explosion of options and an ever-widening mass of documentation.
My remaining options:
- using an existing C# OpenGL library: Tao, which is part of Mono, which is a whole 'nother barrel of fish; it seems to include its own compiler, and so I have no idea whether the Microsoft.Ink stuff will work with it. I dobut it.
- using another existing C# OpenGL library: SharpGL. This seems like it might work, but the license looks very casual, and I got into this whole mess because I wasn't paranoid enough about the Java Research License.
- Switching my application to be mostly unmanaged C++, with the Ink stuff in a managed C++ component. This is probably the most straightforward architecturally, but a) it requires rewriting a fair amount of code just before I turn maintenance over to a colleague and leave this job, b) it seems like an admission of defeat, and backwards, and c) it just might send me into the aforementioned dll hell.
I have to complete this task before I can leave this job and move to California. My frustration with Windows programming is unbounded.