Friday, April 06, 2007

Now that the book is written and all urgent tasks I had to defer due to the book are done, I find some time to blog about technical topics.

Recently, a customer asked me how to marshal function pointers across managed / unmanaged interop boundaries. If you know a simple API and two attributes and if you are aware of a pitfall specific to marshaling function pointers, the job can be quite easy.

To discuss this topic, consider the following simple API:

namespace NativeAPI
{
  struct CallbackData
  {
    int i;
    double d;
  };

  typedef void (*PFNCallback)(CallbackData* p);

  class SampleClass
  {
    PFNCallback _pfn;
  public:
    SampleClass(PFNCallback pfn);
    void F();
  };
}

In my book I focus on wrapping class libraries that use virtual functions for callbacks instead of function pointers, because virtual functions are the typical C++-like approach for callbacks. But obviously, a lot of C++ class libraries have their roots in C which can force you to care about arguments of function pointer types.

The managed equivalent to a native function pointer is a delegate. There are different ways to map between delegates and function pointers. The following code shows a delegate that can be mapped to the native function pointer of type PFNCallback:

namespace ManagedWrapper {   using namespace System::Runtime::InteropServices;

  [StructLayout(LayoutKind::Sequential)]
  public value struct CallbackData
  {
    int i;
    double d;
  };

  [UnmanagedFunctionPointer(CallingConvention::Cdecl)]
  public delegate void CallbackDelegate(CallbackData% p);
}

Notice that I first define a managed wrapper type for CallbackData that has the same binary layout as its native counterpart. This is done with the StructLayout attribute. The attribute is only used for documentation purposes, because sequential layout is the default setting for custom value types defined in C++/CLI (and C#). The delegate type has the UnmanagedFunctionPointerAttribute, which is not optional in this case. Using this attribute, you can specify the calling convention of the native function pointer type. In the native API's header file, the PFNCallback is defined without an explicit calling convention specification – this is not recommended, but it occurs quite often. In this case, the function pointer type has the default calling convention, which depends on compiler switches. If no compiler switch is used, the default calling convention for C-style function pointers is __cdecl. Using the compiler switches /Gz or /Gr, the default calling convention can be changed to __stdcall or __fastcall. In the concrete scenario that my customer faced, neither /Gz nor /Gr are used. To express that the CallbackDelegate should be marshaled to a __cdecl function pointer, the UnmanagedFunctionPointerAttribute is necessary. If the native function pointer is a __fastcall function, you can not provide a simple mapping, because calling __fastcall functions from managed code is not supported by the current version (2.0) of the CLR. Once you have properly defined the delegate, you can use the function Marshal::GetFunctionPointerForDelegate to receive a pointer to a native->managed thunk for the delegate. This pointer can then be passed to native code. If native code uses this pointer for function calls, the thunk performs the native->managed transition and invokes the delegate. To wrap NativeAPI::SampleClass, you can implement the following wrapper class:

namespace ManagedWrapper
{
  public ref class SampleClass
  {
    NativeAPI::SampleClass* _pWrappedObject;
    CallbackDelegate^ _callbackDelegate;
  public:
    SampleClass(CallbackDelegate^ cbd);
    void F();
    ~SampleClass();
  };
}

In the constructor of this wrapper class, you can use Marshal::GetFunctionPointerForDelegate to determine the function pointer that is passed to the constructor of NativeLib::SampleClass. The following code shows the implementation of ManagedWrapper::SampleClass.

namespace ManagedWrapper
{
  SampleClass::SampleClass(CallbackDelegate^ callbackDelegate)
  : _callbackDelegate(callbackDelegate),
  _pWrappedObject(nullptr)
  {
    IntPtr p = Marshal::GetFunctionPointerForDelegate(_callbackDelegate);
    _pWrappedObject = new NativeAPI::SampleClass((NativeAPI::PFNCallback)p.ToPointer());
    if (!_pWrappedObject)
      throw gcnew OutOfMemoryException("Could not allocate memory on C++ free store");
  }

  void SampleClass::F()
  {
    _pWrappedObject->F();
  }

  SampleClass::~SampleClass()
  {
    NativeAPI::SampleClass* pWrappedObject = _pWrappedObject;
    _pWrappedObject = nullptr;
    delete _pWrappedObject;
  }
}

Notice that in the member initialization list of the constructor, I first store a handle to the target delegate in a member variable. This is important, to control the lifetime of the thunk. At the first view, one might think that the thunk manages a tracking handle to the target delegate, which would keep the delegate alive as long as the thunk exists. However, the opposite is the case. It is not the thunk that keeps the delegate alive; the delegate keeps the thunk alive: The thunk is guaranteed to exist only as long as the delegate exists. The thunk only contains a weak reference to the target delegate which it can use for invocation if the delegate is not garbage collected. Due to this implementation, the following code would definitely be a bug:

SampleClass::SampleClass(CallbackDelegate^ callbackDelegate)
: _pWrappedObject(nullptr)
{
  IntPtr p = Marshal::GetFunctionPointerForDelegate(callbackDelegate);
  _pWrappedObject = new NativeAPI::SampleClass((NativeAPI::PFNCallback)p.ToPointer());
  if (!_pWrappedObject)
    throw gcnew OutOfMemoryException("Could not allocate memory on C++ free store");
}

Since the delegate that was used to create the thunk (and the function pointer referring to the thunk) is not stored in a member variable, it can be GCed unless other references to the delegate exist. When this delegate is GCed, the thunk can be removed as well. Notice that the thunk is not automatically removed when its target delegate is deleted, because the thunk and the delegate are created on different heaps: the delegate is instantiated on the GC heap, whereas the thunk is created on a heap that I like to call the CLR's code heap. An important difference of these heaps is that the code heap consists of memory pages that have the attribute PAGE_EXECUTE_READWRITE. The following code shows you some internals about Marshal::GetDelegateForFunctionPointer

// GFPFDTests.cpp
// build with "CL /clr GFPFDTests.cpp"

#include "windows.h"

using namespace System;
using namespace System::Diagnostics;
using namespace System::Runtime::InteropServices;

typedef void (__cdecl* PFN)();

[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
public delegate void PFNDelegate();

// managed function has __cdecl calling convention
// therefore, a PFN can point to it.
void __cdecl F()
{
Console::WriteLine("::F called");
}
int main(array ^args)
{
  PFNDelegate^ d1 = gcnew PFNDelegate(F);
  IntPtr p1 = Marshal::GetFunctionPointerForDelegate(d1);
  IntPtr p2 = Marshal::GetFunctionPointerForDelegate(d1);
  // calling M::GFPFD twice for same delegate returns same thunk
  Debug::Assert(p1 == p2);

  PFNDelegate^ d2 = gcnew PFNDelegate(F);
  p2 = Marshal::GetFunctionPointerForDelegate(d2);
  // calling M::GFPFS twice for different delegates returns
  // different thunks even if delegate target is the same
  Debug::Assert(p1 != p2);
  MEMORY_BASIC_INFORMATION mbi;
  VirtualQuery(p1.ToPointer(), &mbi, sizeof(mbi));
  // thunks are allocated on a heap that consits of pages   // with the PAGE_EXECUTE_READWRITE flag
  Debug::Assert((mbi.Protect & PAGE_EXECUTE_READWRITE) ==
                 PAGE_EXECUTE_READWRITE);

  void* pD1 = *(void**)&d1; // hack to get native pointer to delegate
  VirtualQuery(pD1, &mbi, sizeof(mbi));
  // .NET objects are allocated on the GC heap which consists of
  // pages with the PAGE_READWRITE flag
  Debug::Assert((mbi.Protect & PAGE_READWRITE) ==
                PAGE_READWRITE);

  PFN pfn = (PFN)p1.ToPointer();
  pfn();
  WeakReference wr(d1);
  d1 = nullptr;
  GC::Collect(2);
  Debug::Assert(!wr.IsAlive);
  try
  {
    pfn();
    // since the target delegate does not exist any more,
    // an exception is thrown here and the line below is
    // not executed
    // if you execute this in a debugger, you will likely see
    // a "Managed Debugging Assistant" instead.
    // MDAs are CLR internal assertion-like constructs     Debug::Assert(FALSE);
  }
  catch (System::AccessViolationException^ ex)
  {
    Debug::WriteLine("Expected exception");
  }
}

In this post I have discussed how to treat marshal delegates to native function pointers. However, I have not discussed what you should do if the signature of the native function requires parameter marshalling. This will be addressed in my next post.

4/6/2007 10:13:08 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |   |  Trackback
 Saturday, January 13, 2007
A long project is comming to an end soon. After one year of writing, my book will be finished soon. 3 chapters need some minor changes. All other chapters of my book are now going to be copy edited. The book will summarize essentail parts of my research on C++/CLI in the last two years. Notice that the announcement in amazon.com is not 100% correct. The title of the book will be "Expert C++/CLI: .NET for Visual C++ programmers" and the release date will be Mid March.
1/13/2007 9:00:07 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |   |  Trackback
 Tuesday, April 11, 2006

1) Since April, 1st I am an MVP for Visual C++.

2) My first article in the MSDN Magazine has been published: http://msdn.microsoft.com/msdnmag/issues/06/05/MixAndMatch/default.aspx

4/11/2006 1:15:43 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |   |  Trackback
 Tuesday, March 21, 2006

In news://msnews.microsoft.com/microsoft.public.dotnet.languages.vc Edward Diener asked an very interesting question: Why can't I call a function having arguments of a native type like std::string from another assembly?

Assume you have some code like this one:
 
// Conversions.cpp
// compile with "CL /clr /LD conversions.cpp"
// output: mixed code assembly Conversions.dll
#include <string>
 
public ref class Conversions
{
public:
  static void S2S(System::String^ s1, std::string& s2) { /* ... */ }
};
 
This code should compile as expected, however, it would not give you the expected result!
 
The code below looks like a suitable client:
 
// ConversionsClient.cpp
// compile with "CL /clr ConversionsClient.cpp"
#using "Conversions.dll" 
#include <string>
int main()
{
  std::string s;
  Conversions::S2S("asdf", s);
}
 
If you try to compile this code, you will get a disappointing error message:
 
error C3767: 'Conversions::S2S': candidate function(s) not accessible
 
Why is a public static function S2S of a public type Convesions not accessible?
 
To use the native type std::string in managed code, the compiler generates a managed value type std::string in the assembly where std::string is used. This managed wrapper value type is private, therefore, the Conversions::S2S cannot be called from outside the assembly even though it is a public function of a public type.
 
At the first view it seems, key to the solution is to make sure the compiler generates a public type for std::string, in theory this is possible, however it would not help to solve the problem. In fact the native wrapper type has been defined as a private type for some good reasons.
 
Assume the native wrapper type for std::string was public. To call S2S, one would have to pass a tracking handle to a System::String, defined in mscolib.dll, and a value of the type std::string defined in the assembly Conversions.dll. The std::string type that we pass in ConversionsClient.cpp is a different one! It is the native wrapper type defined in ConversionsClient.cpp - not the native wapper type defined in Conversions.dll. Therefore, the parameters would not match.
 
So how can we solve this problem?
 
The origin of the problem is the fact, that type identity rules of .NET do not allways mix well with the type identity rules of native C++. To solve this problem, you can switch back to the world of native code sharing without loosing you managed code features. Simply create a mixed code static library:
 
Create a static mixed code library from the code :
 
// ConversionsLib.cpp
// compile with "CL /c /clr ConversionsLib.cpp"
// make lib with "LIB ConversionsLib.obj"
// output: mixed code static library ConversionsLib.lib
 
#include <string>
void S2S(System::String^ s1, std::string& s2) { /* ... */ }
Create a client from the code below.
 
// ConversionsLibClient.cpp
// compile with "CL /clr ConversionsLibClient.cpp"
#include <string>
 
#pragma comment (lib, "ConversionsLib.lib")
void S2S(System::String^ s1, std::string& s2);
 
int main()
{
  std::string s;
  S2S("asdff", s);
}
 
Conclusion: Beware the different type identity rules. Native types are identifies by their namespace-qualified typename, managed types are identified by their assembly- and namespace-qualifies typename. If you need native type identity rules, use native code sharing features, if you need managed type identity, use managed code sharing features.
3/21/2006 5:36:54 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |   |  Trackback
 Friday, January 20, 2006
1/20/2006 11:07:12 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |   |  Trackback
 Thursday, January 12, 2006

When a cpp file is compiled with /clr, a managed object file is created. In most scenarios, such a managed object file contains only managed code. However, there are two scenarios that end up in a managed object file with managed and native code:

  1. When #pragma unmanaged is used
  2. When the C++ file contains a function with C++ constructs that are not mappable to IL code.

Managed object files with native code imply a certain danger: They can end up in uninitialized state, as shown in the following sample:

In the code below, i is a global variable initialized with the return value of getValue(). There are two exported functions that simply return i. One is the managed function fManaged and the other one is the unmanaged function fUnmanaged.

<code language=”C++/CLI” filename=”MixedLib.cpp” compileWith=”CL /LD /clr MixedLib.cpp”>
#pragma unmanaged
__declspec(noinline) int getValue() {
  return 42;
}

int i = getValue();

__declspec(dllexport) int fUnmanaged()
{
  return i;
}

#pragma managed
__declspec(dllexport) int fManaged()
{
  return i;
}
</code>

Before fManaged is executed the first time, the module constructor is called. The module constructor calls getValue to initialize the global variable i. However, if fUnmanaged is called before fManaged is called the first time, the variable i will be returned before it is initialized. To reproduce this scenario, you can use the client application below.

<code language=”C++/CLI” filename=”TestApp.cpp” compileWith=”CL /clr TestApp.cpp”>
#include <stdio.h>

#pragma comment(lib, "testlib.lib")

__declspec(dllimport) int fUnmanaged();
__declspec(dllimport) int fManaged();

int main()
{
  printf("fUnmanaged returns %d\n", fUnmanaged());
  printf("fManaged returns %d\n", fManaged());
  printf("fUnmanaged returns %d\n", fUnmanaged());
}
</code>

If you start TestApp.exe, you will get the following output:

fUnmanaged returns 0
fManaged returns 42
fUnmanaged returns 42

The easiest way to avoid these problems is to make sure that files compiled with /clr contain only managed code and to leave the native code in cpp files compiled without /clr. If you consider using #pragma unmanaged or unmappable C++ constructs in files compiled with /clr, you should be aware that all global variables and static member variables of that file, are initialized by the module initializer. This is even true if the variable is of a native type.

The compiler's ability to automatically compile a function with unmappable C++ constructs to native code can cause a scenario where you get mixed code object files without even realizing it. Fortunately a compiler warning C4793 can be emitted in this scenario. C4793 is a level 2 warning. To get it, you either have to set the warning level to 2, or you have to use a compiler switch to make it a level 1 warning, as shown in the following code.

<code language="C++/CLI"
     filename=" UnmappableConstructs.cpp"
     compileWith="cl /c /clr /w14793 UnmappableConstructs.cpp">
void f()
{
  __asm int 3;
}
</code>

When you compile this code, the C++ compiler will emit the warning C4793 with the following message:

UnmappableConstructs.cpp(3) : warning C4793: '__asm' : causes native code generation for function 'void f(void)'
        UnmappableConstructs.cpp(1) : see declaration of 'f'

1/12/2006 3:29:45 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |   |  Trackback
 Saturday, November 26, 2005

.NET has a very interesting new feature regarding type identity. When I tested this feature the last time (I think it was with the RC, it did not work, so it is likely you have not yet heared about it). But let's start from the beginning:

Type identity is a very important concept of a programming infrastructure. In classic C++, types are identified by their names. COM uses GUIDs to identitfy types. To achieve a strong type safety, type identity in .NET is bound to assembly identity and assembly identity can be bound to developers using a public/private key pair. Type identity is bound to assembly identity means that 2 types with the same name in two different assemblies have two different identities. Assembly identity can be bound to developers using a public/private key pair means that developers can use unique public/private key pairs to give their assemblies unique and uncloneable names. This also gives all types in the assembly unique and uncloneable names.

The following expression returns a string containing the unique and uncloneable type identity, Microsoft has given to the 32 bit signed integer type:

int::typeid->AssemblyQualifiedName

If you are not familiar with the chosen language, the equivalent C# expression would be typeof(int).AssemblyQualifiedName.

While this type identity is very helpful to achieve verifiability, it is also had a drawback: This assembly bound type identity ment that a type could not leave the assembly where it was defined in without losing it's identity. You can define a type from exactly the same code in another assembly, but to the runtime, it would be a different type. To understand this, let's have a look at the following example.

Let's assume you have an assembly lib1.dll created from the following code:

public ref class TheType {};

Let's further assume, you have a client application like this one:

#using "lib1.dll"
int main() {
  using namespace System;
  Console::WriteLine(TheType().GetType()->AssemblyQualifiedName);

  using System::Reflection::Assembly;
  for each (Assembly^ a in AppDomain::CurrentDomain->GetAssemblies())
    Console::WriteLine(a->FullName);
}

Notice the elegant way to add an assembly reference inside your code, and the neat alternative for creating a temporary object of type TheType - two out of so many reasons why I call C++/CLI the chosen language.

If you run the application, you will see that TheType is defined in Lib1 and that the assemblies mscorlib, app, and lib1 are loaded.

Let's further assume that for the next version, you are redesigning your application and you find out, that TheType would now better fit into another assembly. Due to the type identity rules I have mentioned, this would mean that TheType would get another identity. Therefore this would be a breaking change for lib1.dll: The new version of Lib1 would not be backwards compatible with the old one, since it didn't have the public type TheType any more.

Using the type identity mapping feature in 2.0, you can reorganize your libraries without causing a backwards incompatibility in your new lib1.dll. To explain the mechanics, let's assume the source for lib2.dll now contains TheType:

//lib2.cs
public ref class TheType {};

To allow the old version of app.exe to execute even though TheType is no longer in the assembly where it is expected (lib1), you can define TheType in lib1.dll with a type identity mapping to the type in lib2.dll. These two lines are enough to achieve this:

#using "lib2.dll"
[assembly: TypeForwardedTo(TheType::typeid)];

Notice that this code creates lib1.dll with an assembly depencency to lib2.dll that defines TheType. When using this code, the compiler emits the folowing metadata for TheType in lib1.dll:

.class extern forwarder TheType
{
  .assembly extern lib2
  .class 0x02000002
}

.class 0x02000002 is to the metadata token for TheType in lib2.dll.

Almost the same metadata can be emitted by C#, too; however there is a slight difference:

[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(TheType))]

C# expects you to use the pseudo custom attribute System::Runtime::CompilerServices::TypeForwardedToAttribute, whereas C++/CLI uses a compiler-internal attriubte TypeForwardedTo. The philosophies of the two languages differ here: C# tries to be consistent in the way attributes are used. Regarding attributes, C++/CLI has different roots anyway: .NET attributes are, attributed ATL are two examples. Instead of trying to be consistent with one or the other attribute model, C++/CLI tries to avoid that the programmer has to explicitly use features of the runtime that are intended to be used by compiler builders only.

Whatever language you use, this simple attribute allows you to reorganize your libraries without breaking compatibility with old applications.

11/26/2005 11:06:15 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |   |  Trackback
 Friday, November 11, 2005

For most Visual Studio .NET language integrations, app.config files are treated specially. Before the target application is started, (with or without a debug session), the language package ensures that the app.config file is automaticallly copied to the target application's configuration file. Visual C++ does not have this feature, however it is easy to get the same:

Add a file named app.coinfig to a Visial C++ project and choose the following project settings:

Command line: type app.config > "$(TargetPath).config"

Description: "Updating target's configuration file"

Outputs: "(TargetPath).config"

Notice that the file is copied via the command type and piping. I prefer this to using the copy command here, sbecause this command ensures that the date of the compiled file is automatically adapted whenever the file is copied.

 

11/11/2005 11:34:30 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |   |  Trackback