Posted
Comments None

(Originally written October 5, 2009)

In a previous post I pointed out some resources to help write PInvoke signatures to call platform functions from C# or VB.NET code. This type of interop can also be used to introduce C#/VB.NET code to an application not using the .NET Framework. For example perhaps a new GUI can be written in C# that reads and writes data using an existing C++ database layer. This form of interop is useful if you:

  1. Need to call unmanaged code from C# or VB.NET
  2. Have control over the unmanaged C/C++ codebase
  3. Don’t have or don’t want to use a COM interface
  4. Can reduce your module’s interface to C functions (PInvoke provides limited object oriented support, essentially whatever you can do with structs and no classes)

Below is very well near the simplest possible example.

First create a Win32 DLL project in Visual Studio and add the code below, a single trivial function. The “__declspec(dllexport)” exports the function from the DLL allowing outside modules to call it and the “extern “C”” prevents C++ name mangling.

extern "C" __declspec(dllexport) int Add(int a, int b)
{
    return a + b;
}

Next create a C# project (or VB.NET if you prefer). If you are already experienced with platform-invoking Windows functions this code will be familiar. Lines 8 and 9 import the function from the DLL created above. See my previous post linked above for more resources on PInvoke signatures.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(Add(10, 20));
    }
 
    [DllImport("My_CPP_Project.dll")]
    static extern int Add(int a, int b);
}

To run the code both the DLL from the first step and the EXE from the second step must be in the same folder. However its possible to dynamically load the unmanaged DLL from a different folder using LoadLibrary from the C# EXE. Simply ensure that you call LoadLibrary to correctly load the DLL before making any calls to the functions exported by the DLL.

Author

Posted
Comments None

(Originally written August 2, 2011)

Earlier this year my company was preparing to ship a new product. Part of the software used some new data files that would also be installed on the user’s system or on the user’s network and shared among multiple users. It was convenient to put this data into a relational database so we used Microsoft SQL Compact Edition.

Our plan was to build the databases, install the files on the user system, open readonly at runtime and query the data. Everyone involved agreed SQLCE would be a fine solution. And it would have been except for one surprising problem.

If the database is built on XP or Vista/Windows 7 it simply cannot be opened readonly on the other operating system. The first time it is opened SQLCE needs to re-index the database because the National Language Support sort order is different on these two operating systems. This was a problem for us for three reasons.

  1. We install this data file (and many others) readonly. There is no need for a user to change the files.
  2. We install the data files with an elevated account. The user currently running the software may not be able to elevate to administrator.
  3. The files could be shared over a network and accessed by different client operating systems. The database would constantly be going back and forth between XP or Windows 7 NLS sort order.

Here is a Microsoft Connect entry that mentions the problem with Windows Server and Windows Mobile. The same problem exists for XP and Vista/Windows 7.

Without any known workaround for this problem I have to conclude that SQLCE databases are just not portable. Yes, they can be converted back-and-forth. Yes, that conversion will happen automatically (whether you want it to or not). But no, the data is just not usable on both XP and Vista/Windows 7 without first being converted – and then it is no longer usable on the other OS!

For us it was a show stopper. Our solution was to use Sqlite instead. Fortunately the code change was minimal because the database access layer was properly hidden behind a higher level API. Only one module had to be changed and both our software and internal tools were back up and running (hooray for good architecture!). Sqlite databases are truly portable, not just with different versions of Microsoft Windows but also with Linux and MacOSX.

Author

Posted
Comments None

(Originally written February 4, 2010)

Most of the .NET code I write is in C# but occasionally I need to write mixed mode C++/CLI code to expose some existing unmanaged code to our new .NET code. C++/CLI works very well for this kind of interop but the language is much less familiar to me than C#.

For example today I wanted to implement a property on a C++/CLI class that returned IEnumerable to expose an unmanaged collection. In C# this is a very easy with the yield keyword but there is no equivalent in C++/CLI so the enumerator classes have to implemented manually. I got hung up on the syntax and it to took me far to many google searches before finally piecing it together. So here is a complete but simple example implementation of IEnumerable in C++/CLI.


ienumerable_cppcli.cpp[2.77kB]

  1. #pragma once
  2. using namespace System;
  3. namespace CppLibrary
  4. {
  5. public ref class Earth
  6. {
  7. private:
  8.     ref class EarthContinents : System::Collections::Generic::IEnumerable<String^>
  9.     {
  10.     private:
  11.         Earth^ _earth;
  12.  
  13.     public:
  14.         EarthContinents(Earth^ Earth)
  15.         {
  16.             _earth = Earth;
  17.         }
  18.  
  19.         virtual System::Collections::Generic::IEnumerator<String^>^ GetEnumerator_Generic() sealed
  20.             = System::Collections::Generic::IEnumerable<String^>::GetEnumerator
  21.         {
  22.             return gcnew EarthContinentsEnumerator(this);
  23.         }
  24.            
  25.         virtual System::Collections::IEnumerator^ GetEnumerator_NonGeneric() sealed
  26.             = System::Collections::IEnumerable::GetEnumerator
  27.         {
  28.             return GetEnumerator_Generic();
  29.         }
  30.  
  31.     private:
  32.         ref class EarthContinentsEnumerator : System::Collections::Generic::IEnumerator<String^>
  33.         {
  34.         private:
  35.             EarthContinents^ _continents;
  36.             int _index;
  37.  
  38.         public:
  39.             EarthContinentsEnumerator(EarthContinents^ Continents)
  40.             {
  41.                 _continents = Continents;
  42.                 Reset();
  43.             }
  44.  
  45.             ~EarthContinentsEnumerator() { }
  46.  
  47.             virtual bool MoveNext()
  48.             {
  49.                 _index += 1;
  50.                 return _index < _continents->_earth->_arr->Length;
  51.             }
  52.  
  53.             property String^ Current_Generic
  54.             {
  55.                 virtual String^ get() sealed
  56.                     = System::Collections::Generic::IEnumerator<String^>::Current::get
  57.                 {
  58.                     return _continents->_earth->_arr[_index];
  59.                 }
  60.             }
  61.  
  62.             property Object^ Current_NonGeneric
  63.             {
  64.                 virtual Object^ get() sealed
  65.                     = System::Collections::IEnumerator::Current::get
  66.                 {
  67.                     return Current_Generic;
  68.                 }
  69.             }
  70.  
  71.             virtual void Reset()
  72.             {
  73.                 _index = -1;
  74.             }
  75.         };
  76.     };
  77.  
  78. private:
  79.     array<String^>^ _arr;
  80.  
  81. public:
  82.     Earth()
  83.     {
  84.         _arr = gcnew array<String^>(7);
  85.         _arr[0] = gcnew String(L"Asia");
  86.         _arr[1] = gcnew String(L"Africa");
  87.         _arr[2] = gcnew String(L"North America");
  88.         _arr[3] = gcnew String(L"South America");
  89.         _arr[4] = gcnew String(L"Antarctica");
  90.         _arr[5] = gcnew String(L"Europe");
  91.         _arr[6] = gcnew String(L"Australia");
  92.     }
  93.  
  94.     property System::Collections::Generic::IEnumerable<String^>^ Continents
  95.     {
  96.         System::Collections::Generic::IEnumerable<String^>^ get()
  97.         {
  98.             return gcnew EarthContinents(this);
  99.         }
  100.     }
  101. };
  102. }

Author

Posted
Comments None

(Originally written September 14, 2009)

Just about every .NET developer will eventually need to call some magic Windows function to do something that just isn’t exposed by the .NET framework. Often the hardest part of invoking a platform function from C# or VB.NET is figuring out the signature and structure declarations to pass to the unmanaged API. There are two resources I use to make this process less painful.

First is the public wiki at http://pinvoke.net/. This website contains PInvoke signatures for thousands of Win32 platform functions contributed by the community at large. The quality is across the spectrum but generally good and because its a wiki you can fix mistakes, add new signatures and samples. I’ve contributed a few minor things myself although I can’t remember exactly what anymore.

The second resource is the open source PInvoke Interop Assistant. This very useful tool lets you copy and paste function and structure declarations directly from Windows header files and automatically generates the PInvoke signatures. It even has a command line version that generates interop signatures for an entire header file in one step.

While neither of these resources is 100% foolproof they have been enough to get me through every time I’ve needed to PInvoke something.

Author

Posted
Comments None

(Originally written July 3, 2009)

With Visual Studio 2002 and 2003 Microsoft released the .NET Framework 1.0 and 1.1 respectively. These releases included the new languages C# and VB.NET and also included managed extensions for C++. With Visual Studio 2005 Microsoft released the .NET Framework 2.0 and C++/CLI which is essentially an improved version of the managed extensions for C++. C++/CLI feels more natural for experienced C++ developers and the new syntax is much cleaner than the old syntax of 2002/2003. Visual Studio 2008 finally deprecates the old syntax with a compiler warning stating the old syntax will be removed in a future version.

The application I work on at the office had some old syntax managed C++. Originally this code was developed before the .NET framework existed and later converted to managed C++ using the old syntax. Recently we had time to convert it to the new syntax as part of routine maintenance. The code had a number of enumeration values that had to be converted. Under the old syntax an enumeration looks like this:

  1. __value enum TestEnum
  2. {
  3.     Foo,
  4.     Bar,
  5. };

Under the new syntax of C++/CLI an enumeration looks like this:

  1. enum class TestEnum
  2. {
  3.     Foo,
  4.     Bar,
  5. };

The only difference between the two is on the first line which declares the name of the enumeration.

This conversion is correct according to Microsoft’s syntax upgrade checklist but it caused a number of bugs elsewhere in the code. It has to do with how the enum values are used. Consider the managed C++ code below.

  1. Console::WriteLine(String::Format("The value of TestEnum::Foo is '{0}'",
  2.     Convert::ToString(TestEnum::Foo)));
  3.  
  4. Console::WriteLine(String::Format("The value of TestEnum::Bar is '{0}'",
  5.     Convert::ToString(TestEnum::Bar)));

This code is valid under both the old managed extensions syntax and the new C++/CLI syntax. What does it print? The answer depends on whether you compile this under old syntax or the new syntax.

Output under the old syntax:

Output under the new syntax:

Under the old syntax the compiler seems to be treating the enum value more like an integer and generates code to call one of the overloads of Convert::ToString() to convert a number to a string. Under the new syntax the compiler appears to treat the enum value like an object and calls a different version of the overloaded function Convert::ToString(). Both of these examples were created using Visual Studio 2008.

Lets use the ildasm.exe tool to take a look at the generated MSIL to see if this is really what’s happening. Here is part of the MSIL generated under the old syntax. You don’t have to be an expert in MSIL to clearly see that it is in fact calling the Convert::ToString(int) overload function.

  1. IL_000c:  ldc.i4.0
  2. IL_000d:  call       string [mscorlib]System.Convert::ToString(int32)
  3. ...
  4. IL_0026:  ldc.i4.1
  5. IL_0027:  call       string [mscorlib]System.Convert::ToString(int32)

The MSIL generated by the same version of Visual Studio 2008 but using the new syntax looks quite different. Clearly this code is calling the Convert::ToString(object) overload function.

  1. IL_0007:  ldc.i4.0
  2. IL_0008:  box        TestEnum
  3. IL_000d:  call       string [mscorlib]System.Convert::ToString(object)
  4. ...
  5. IL_0021:  ldc.i4.1
  6. IL_0022:  box        TestEnum
  7. IL_0027:  call       string [mscorlib]System.Convert::ToString(object)

But that is not quite the end of the story. Under both the old and new syntax the enumeration is a value type despite the use of the word ‘class’ in the declaration in the new syntax. However the MSIL under the new syntax shows that it is first boxing the enum value which converts it from a value type to an object. The new behavior of implicit boxing of value types with C++/CLI was ultimately the cause of our bugs.

Author

← Older Newer →