In the previous article, I have demonstrated how to call C/C++ libraries from C#. In this post, I want to deal with the inverse action : call C# method from C++.
Be constrained to grammar, it is not possible to call C# from C, it is
only C++ could achieve this purpose.
一. Create the C# library
I create the C# library donned as "CSharpLibrary"
And enable the unsafe selection:
The C# code, name as CSharpLibrary.cs, be :
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace CSharpLibraryNameSpace
{
// Interface declaration.
public delegate
int NativeDelegateType(IntPtr pStrInUnicode, int strlen, IntPtr pArgs);
[ComVisible(true)]
public interface ManagedInterface
{
int ArraySum(IntPtr pIntArray, int len, ref int sum);
int CalltheCallbackFun(IntPtr callbackFnPtr, IntPtr pArgs);
};
// Interface implementation.
[ComVisible(true)]
public class ManagedCSharpClass : ManagedInterface
{
public int ArraySum(IntPtr pIntArray, int len, ref int sum)
{
Console.Write("{0}\n",
System.Reflection.MethodBase.GetCurrentMethod().Name);
sum = 0;
unsafe
{
int* pArray = (int*)pIntArray;
for (int i = 0; i < len; i++)
sum += pArray[i];
}
return 0;
}
public int CalltheCallbackFun(IntPtr callbackFnPtr, IntPtr pArgs)
{
string str;
str = "牛羊豬雞貓狗";
unsafe
{
fixed (char* pStr = str)
{
Console.Write("before call callbackFun ptr={0}\n",
callbackFnPtr);
//Convert IntPtr to Delegate
NativeDelegateType callback =
Marshal.GetDelegateForFunctionPointer(callbackFnPtr,
typeof(NativeDelegateType))
as NativeDelegateType;
callback(Marshal.StringToHGlobalUni(str), str.Length, pArgs);
}
}
return 0;
}
}
}
Note that
there is a delegate (callback ) interface for C++ registering.
In the code I have write some Chinese words in a string, to demonstrate how to pass Unicode string through C# to C++.
二. Prepare a type library (tlb) for C++ linking with.
In the Build Event part, of project setting :
Add the comment line in post-build column :
%SystemRoot%\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe $(SolutionDir)bin\$(ConfigurationName)\$(ProjectName).dll /tlb:$(ProjectName).tlb /codebase
%SystemRoot%\system32\xcopy.exe /Y $(SolutionDir)bin\$(ConfigurationName)\$(ProjectName).tlb $(SolutionDir)
The two lines are to generate file CSharpLibrary.tlb for C++ binary linking with.
You could press CTRL + SHIFT + B to build the library, the binary CSharpLibrary.tlb would be present under the project root folder.
Then Close current VS2005 project.
三. Create a empty C++ project and solution:
Create a blank C++ project, named CCallCCsharp:
After your creating has done, put all the C# library project and code and so forth stuff under the CCallCCsharp folder:
Add a file, main.cpp under project , the code be :
#include <locale.h>
#include <windows.h>
// Import the type library.
#import "CSharpLibrary.tlb" raw_interfaces_only
using namespace CSharpLibrary;
int __stdcall CCallbackFunction(wchar_t *pStrInUnicode, int strlen, void *pArgs)
{
wprintf(TEXT("%s\n"), pStrInUnicode);
wprintf(TEXT("%d\n"), *((int*)pArgs));
return 0;
}/*CCallbackFunction*/
int main(int argc, char *argv[])
{
HRESULT hr;
/*Set current unicode*/
setlocale(LC_ALL,"");
// Initialize COM.
hr = CoInitialize(NULL);
// Create the interface pointer.
ManagedInterfacePtr CSharpDLLPtr(__uuidof(ManagedCSharpClass));
{
long lResult = 0;
int intArray[10], sum;
int i;
for(i = 0; i< 10; i++)
intArray[i] = i;
// Call the ArraySum method
CSharpDLLPtr->ArraySum((long)&intArray[0],
sizeof(intArray), (long*)&sum, &lResult);
wprintf(L"The result is %d\n", lResult);
}/*local variables*/
{
long nCallbackResult;
int someNum;
someNum = 4;
CSharpDLLPtr->CalltheCallbackFun((long)CCallbackFunction, (long)&someNum,
&nCallbackResult);
wprintf(L"The nCallbackResult is %d\n", nCallbackResult);
}/*local variables*/
// Uninitialize COM.
CoUninitialize();
return 0;
}
And Set the character Set as Unicode :
四. Link C# library with C++ main :
Include CSharpLibrary project into the solution, CCallCCsharp. And set the dependence:
Now you could press the "play" button to run the code.
The result :
ArraySum
The result is 0
before call callbackFun ptr=4264216
牛羊豬雞貓狗
4
The nCallbackResult is 0
五. Key to reach the linking between C# and C++:
Under the Debug/Release folder, there is a file csharplibrary.tlh
generated by C++ compiler, which is as the header of C# methods for C++:
// Created by Microsoft (R) C/C++ Compiler Version 14.00.50727.762 (92c42779).
//
// c:\users\gaiger\desktop\ccallccsharp\debug\csharplibrary.tlh
//
// C++ source equivalent of Win32 type library CSharpLibrary.tlb
// compiler-generated file created 05/08/16 at 01:10:19 - DO NOT EDIT!
#pragma once
#pragma pack(push, 8)
#include <comdef.h>
namespace CSharpLibrary {
//
// Forward references and typedefs
//
struct __declspec(uuid("406b133d-a839-43fe-882c-d126830250b2"))
/* LIBID */ __CSharpLibrary;
struct __declspec(uuid("ce8f3e90-5777-330d-9d1b-73e8fb398f2a"))
/* dual interface */ ManagedInterface;
struct /* coclass */ ManagedCSharpClass;
struct __declspec(uuid("b00d90a9-5151-380e-aae8-de1ba2b632e5"))
/* dual interface */ _ManagedCSharpClass;
//
// Smart pointer typedef declarations
//
_COM_SMARTPTR_TYPEDEF(ManagedInterface, __uuidof(ManagedInterface));
_COM_SMARTPTR_TYPEDEF(_ManagedCSharpClass, __uuidof(_ManagedCSharpClass));
//
// Type library items
//
struct __declspec(uuid("ce8f3e90-5777-330d-9d1b-73e8fb398f2a"))
ManagedInterface : IDispatch
{
//
// Raw methods provided by interface
//
virtual HRESULT __stdcall ArraySum (
/*[in]*/ long pIntArray,
/*[in]*/ long len,
/*[in,out]*/ long * sum,
/*[out,retval]*/ long * pRetVal ) = 0;
virtual HRESULT __stdcall CalltheCallbackFun (
/*[in]*/ long callbackFnPtr,
/*[in]*/ long pArgs,
/*[out,retval]*/ long * pRetVal ) = 0;
};
struct __declspec(uuid("585bb79e-7d64-3d56-ab11-70c238098a18"))
ManagedCSharpClass;
// [ default ] interface _ManagedCSharpClass
// interface _Object
// interface ManagedInterface
struct __declspec(uuid("b00d90a9-5151-380e-aae8-de1ba2b632e5"))
_ManagedCSharpClass : IDispatch
{};
} // namespace CSharpLibrary
#pragma pack(pop)
The function declare is very explicit.
Note that there is namespace and inheritance syntax, therefore, It is impossible to link C with C# directly.
You could find that,: In C#, there is type IntPtr ; in the C++ part, it is long type. One could comprehend IntPtr as void*, the generic pointer. So it is very nature to pass a pointer as long integer (in 32 bit, long is length of 4 bytes, and VC++ of VS2005 for 32 bit only)
The most key of the line :
NativeDelegateType callback =
Marshal.GetDelegateForFunctionPointer(callbackFnPtr,
typeof(NativeDelegateType))
as NativeDelegateType;
It is to cast the function pointer from C++ as C# delegate method.
Warning : the line:
using namespace CSharpLibrary;
should be the same as C# library name(actually, it should be the same as the tlb file name).