A Practical Question of Programming Language Choice
While my last post dealt with the somewhat abstract ideas around language choice, reader Nathan needs some concrete answers for some practical, real-world issues...
I am a VB turned C# developer who is responsible for combining a text retrieval API (Folio) with an audio API (NCT), as well as text/audio synchronization database, to create a multimedia application.
The problem lies with the text retrieval API, as it is not COM compliant.
I assume that I must use C++ because the search API must be linked in. As this app will have a complex GUI, what windowing framework should be used?
From what I have been able to dissect from sample applications showing how to use the search API, the c++ application passes the window handle to an API call, which draws and handles the display of the data on the given form.
I suppose this approach might work with any framework, but I would like to know what one I should use. I will be making a very large number of native calls, and fast loading times would be a bonus. The mp3 decoder and cd/dvd burning API is written in Delphi.
Should I use MFC and go native? Or should I use native while interfacing with Folio and managed for the GUI? Or should I write an COM interop layer for Folio?
Please advise. I know nothing about C++ and native code.
Okay, so let me summarize the landscape here:
- Single developer project
- Developer is experienced with C# and, less recently, VB
- Developer is unfamiliar with C/C++ and native code
- Must integrate with native C-style and COM-based APIs
- Performance is important
- Must choose a language and windowing framework
Here's my recommendation: write the application in C# using WinForms and use COM interop to access the COM APIs. For your C-style APIs, you have two choices:
- Use p/invoke to access the APIs directly from C#. This basically amounts to translating some of the header file from C/C++ to C#, and it can be pretty boring and error prone. As such, this is only recommended if you have a handful of API functions and types that you need to access.
- Use C++/CLI to write a manage wrapper around the native API. Using C++/CLI you can natively access the C-style API and provide a .NET interface on top of it that is directly consumable by C# (or any other CLR language). This is the preferred method if you have to work with a lot of functions/types because it's type safe, less error prone, and more maintainable. It's also pretty much your only option if your API exposes any native C++ objects.
I recommend C#/WinForms over C++/MFC in this case because I feel it would be too steep of a learning curve to take on for this project, as it would involve learning both C++ *and* MFC. Both are complex technologies, so this would be a lot to bit off at once. I think if you chose option 2 above, you would have the opportunity to learn some C++ at a more friendly pace without immediately being thrown into the deep end, so to speak.
Also, as options 1 and 2 illustrate above, you don't *have to* use C++ to access those C-style APIs. You can use C#'s [dllimport] syntax for p/invoke to access native code, but - again - I only recommend that option when you need access to a small number of native bits.
Regarding the GUI, WinForms does provide you with access to the underlying HWNDs that you can pass around to your external libraries. If you're ambitious, you could even create a custom WinForm control that wrapped the API and did all of it's work in a self-contained, componentized manner.
As to performance, you will pay a performance penalty at startup when you choose native vs. managed code, because managed must load the CLR and any dependent libraries into your process and JIT compile your code. However, once your app is loaded and JIT'd, it's executing as native code, so performance should be just fine. You note, though, that you make a great number of native calls. This is a potential performance issue because every call from managed to native means a managed-native transition, and each transition bears an associated performance cost. The worst case scenario would be something like calling a native function from a managed code loop, where many repeated transitions are made in a short period. In these cases, the transitions will add up and may visibly affect performance.
One technique you can use to overcome this repeated managed-native transition problem is to wrap those instances of repeated or close-together native API calls in a piece of native C++ code and then use C++/CLI to expose the functionality to your C# code. In this way, you only have to do the managed-native transition once, when your C# code calls your C++/CLI code, and the repeated native API calls all happen very efficiencly within the context of your native C++ code.