You can use registration-free COM with .NET COM components - see here.
Another option is to use C++/CLI as a bridge. People are mostly familiar with using it to wrap unmanaged APIs to expose to managed code, but it actually works both ways - it is possible to compile with /clr
, and yet produce a .dll
assembly with plain unmanaged exports, which can be called from unmanaged code as usual. Here's a very simple example that exposes System::String::ToUpper
that way:
// compile with cl.exe /clr /LD wrapper.cpp ole32.lib
#include <windows.h>
__declspec(dllexport)
wchar_t* ToUpper(const wchar_t* wcs)
{
System::String^ s = gcnew System::String(wcs);
array<wchar_t>^ chars = s->ToUpper()->ToCharArray();
size_t size = chars->Length * 2;
wchar_t* dst = (wchar_t*)CoTaskMemAlloc(size + 2);
pin_ptr<wchar_t> src = &chars[0];
memcpy(dst, src, size);
dst[chars->Length] = 0;
return dst;
}
This will produce wrapper.dll
- hybrid managed/unmanaged assembly - and an export library wrapper.lib
. The latter can be used in a pure native application as follows:
// compile with cl.exe test.cpp ole32.lib wrapper.lib
// note, no /clr
#include <stdio.h>
#include <windows.h>
wchar_t* ToUpper(const wchar_t* wcs);
int main()
{
wchar_t* s = ToUpper(L"foo");
wprintf(L"%s", s);
CoTaskMemFree(s);
}
In practice it will load CLR runtime into the calling process (unless it's already loaded there) and dispatch from native code to managed code transparently - all the magic is done by C++/CLI compiler.