Feeds:
Posts
Comments

Posts Tagged ‘C++/CLI’

It is not always possible to rewrite your C or C++ legacy applications in .NET languages in one shot. Sometimes you have to do it module by module, step by step. In other words, you may have to use unmanaged DLLs in your .NET applications.Suppose you want to an unmanaged DLL, name Foo.dll, in your C# applications. What approach would your choose?

If you only want to use a small number of C functions expose by the DLL and the parameters can be marshalled across the managed and unmanaged boundary, you could use P/Invoke.

If you want to use a large number of the functions exposed by the DLL, and/or the some function parameters are hard to map to C# structs, or if the DLL exposes C++ interfaces or C++ classes, you will have to create another layer using C++/CLI as the bridge between the managed application and the unmanaged DLL. The bridge layer would be a DLL that expose .NET classes that you can use in the .NET application. The .NET classes normally delegate the real jobs to the unmanaged DLL.

The approach works great until you want to replace the Foo.dll with a purely managed DLL. The new managed DLL must provide exactly the same classes of the C++/CLI bridge, otherwise, you will have to modify the managed application. Even if you provide the same classes in the managed DLL, you still have to recompile the .NET application. This is all because the .NET application and the bridge are tightly coupled.

I would propose another approach, illustrated in the following component diagram. Foo.Contract.dll defines the interface that the bridge should implement. Foo.Bridge.dll implements the interface by delegating the real jobs to Foo.dll. There is no link-time dependency between the .NET application and Foo.Bridge.dll. The .NET application will load Foo.Bridge.dll at runtime.

Foo.Contract.dll should only defines interfaces, enums, structs, exception classes, and sealed classes if necessary. We should define the interfaces as if we are designing a reusable API, focusing on how to make it easier for the clients to use, rather than how to make it easy to be implemented. Ideally, the contract should define a rich domain model to make it easier to understand and evolve. And there should be a façade interface (or a factory interface), acting as the entry point into the domain model.

Foo.Bridge.dll implements the interfaced defined in Foo.Contract.dll by delegating to Foo.dll.

As mentioned earlier, the .NET application doesn’t link to Foo.Bridge.dll directly, instead it loads the DLL at runtime. All it needs to know are the DLL name and the name of the class that implements the façade interface. We could add an entry in the appSettings section of the configuration file. For example:

<configuration>
  <appSettings>
    <add  key="Foo.IFacade" value="Foo.Bridge.dll,  FacadeImpl"  />
  </appSettings>
</configuration>

The key is the façade interface name. The value contains the DLL name and the class name. At runtime, the .NET application loads the DLL, finds the Type object representing  the Façade class, and calls the Activator.CreateInstance() method to create an instance of the class and casts to Foo.IFacade. From then on, you can use all functionality defined in Foo.Contract.dll.

If, in future, we want to replace Foo.dll with a pure .NET DLL, say, Foo.Managed.dll (that implements the interfaces defined in Foo.Contract.dll), we can simply modify the setting to

<add  key="Foo.IFacade" value="Foo.Managed.dll,  FooFacade"  />

And the application should just work without re-complication.

This approach allows us to replace unmanaged DLLs with managed DLLs without having to modify or even recompile the application, thus achieving “close to modification and open to extension” (the open-closed principle). And it forces us to design a sound API, which also makes the bridge unit-testable.

Advertisements

Read Full Post »

In one of my C++/CLI projects, I need to use a class exported from a unmanaged DLL. When I build the C++/CLI project, I get two linking errors: LNK2028 and LNK2029. The linker cannot find the GetObject() method of a class.

First, I made sure that the export library of the unmanaged DLL was linked with the managed DLL.

Then I used the dumpbin tool to dump the exports of the unamanged DLL, I found that the GetObject() method was renamed as GetObjectW(), and the linker tries to look for GetObject() when linking the managed project, hence the errors.

We know that Microsoft implemented two sets of Win32 functions, one set for ANSI with the function names postfixed with A, one set for Unicode with function names postfixed with W. We call the functions without specifying which version we want to use, the compiler will figure it out depending on whether the UNICODE macro is defined. Here is an example (copied from wingdi.h)

WINGDIAPI int   WINAPI GetObjectA(__in HANDLE h, __in int c,
        __out_bcount_opt(c) LPVOID pv);
WINGDIAPI int   WINAPI GetObjectW(__in HANDLE h, __in int c,
        __out_bcount_opt(c) LPVOID pv);
#ifdef UNICODE
#define GetObject  GetObjectW
#else
#define GetObject  GetObjectA
#endif // !UNICODE
#if defined(_M_CEE)
#undef GetObject
__inline
int
GetObject(
    HANDLE h,
    int c,
    LPVOID pv
    )
{
#ifdef UNICODE
    return GetObjectW(
#else
    return GetObjectA(
#endif
        h,
        c,
        pv
        );
}
#endif  /* _M_CEE */

If the UNICODE macro is defined, GetObject will be replaced with GetObjectW regardless whether it is used as a member method name or a function name. That is why the GetObject() method gets renamed as GetObjectW.

However, when compiling a C++/CLI project, the _M_CEE macro is defined, causing the GetObject macro to be undefined, and an inline function GetObject() defined. And that is why the linker looks for GetObject() instead of GetObjectW().

There isn’t a clean solution when you have a method whose name conflicts with a Win32 function. You can use the push_macro/pop_macro directives to temporarily undefine the macro defined for the Win32 function, to prevent the method name from being changed:

class __declspec (dllexport) UnmanagedClass
{
public:

#pragma push_macro("GetObject")
#undef GetObject
	std::wstring GetObject();
#pragma pop_macro("GetObject")

	void CallGetObject();
};

The problem is whenever you want to call this method in a unmanaged project, you have to undefine the macro, making the code quite messy.

To avoid these kind of linking errors in C++/CLI projects, 1) make sure the export library of unmanaged DLLs are added to the C++/CLI projects, 2) avoid naming a method with the same name of a Win32 system function.

Here is the code that I used to reproduce the issue.

1. The header file of the unamanged class.

#pragma once

#include "string"

class __declspec (dllexport) UnmanagedClass
{
public:

#pragma push_macro("GetObject")
#undef GetObject
	std::wstring GetObject();
#pragma pop_macro("GetObject")

	void CallGetObject();
};

2. The cpp file of the unmanaged class.

#include "stdafx.h"

#include "UnmanagedClass.h"

#pragma push_macro("GetObject")
#undef GetObject

std::wstring UnmanagedClass::GetObject()
{
	return L"GetObject() is called";
}

void UnmanagedClass::CallGetObject()
{
	// The GetObject macro must remain to be undefined here.
	this-&gt;GetObject();
}

#pragma pop_macro("GetObject")

3. The cpp file in the managed project that calls the UnmangedClass::GetObject() method.

UnmanagedClass uc;
std::wstring str = uc.GetObject();

String^ mstr = System::Runtime::InteropServices::Marshal::PtrToStringAuto(
		System::IntPtr::IntPtr((void*)str.c_str()));

Console::WriteLine(mstr);

Read Full Post »

C++/CLI Cheat Sheet

In the C++ CLI Cheat Sheet, I tried to list side by side how to do certain things in C++/CLI and C#.

Read Full Post »

Compile Error C3918

In C#, we can check if an event variable is null before firing the event. For example:

public class MyClass
{
    public event EventHandler MyEvent;

    public void FireEvent()
    {
        // Check if there is any event handler registered.
        if (MyEvent != null)
        {
            MyEvent(this, new EventArgs());
        }
    }
}

But if we do the same thing in C++/CLI, we will get an compile error C3918.

ref class MyClass
{
public:
    event EventHandler^ MyEvent;

    void FireEvent()
    {
        if(MyEvent != nullptr) // C3918
        {
            MyEvent(this, gcnew EventArgs());
        }
    }
}

Here is the solution:

public ref class MyClass
{
    EventHandler^ m_myEvent;

public:
    event EventHandler^ MyEvent
    {
        void add(EventHandler^ handler)
        {
            m_myEvent += handler;
        }

        void remove(EventHandler^ handler)
        {
            m_myEvent -= handler;
        }

        void raise(Object^ sender, EventArgs^ e)
        {
            // Check if there is any event handler registered.
            if (m_myEvent != nullptr) 
            {
                m_myEvent->Invoke(sender, e);
            }
        }
    }
};

Read Full Post »