I have done this before using a somewhat convoluted approach but it works!
First, you will need to create a C# DLL containing your function. You can do this in visual studio by selecting "class library" as the option when creating a new project. The code in the .cs file should look like this
namespace MyNamespace
{
//expose an interface with functions to be called from R
public interface MyInterface
{
string MyFunction(string name);
}
//create a class that implements the above interface
public class MyClass : MyInterface
{
public string MyFunction(string name)
{
return "Hello " + name;
}
}
}
Now compile your project and you will get a C# DLL. This DLL is a managed DLL which is different from a native C/C++ DLL. It cannot be directly consumed from non .Net languages and so needs to be exposed as a COM object. You can do this in one of two ways
- In Visual Studio, you can go to the project properties, click on "Assembly information" button under "Application" tab and select the checkbox that says "Make assembly COM visible".
- Alternatively, you can use regasm.exe which can be found in the .net installation folder to register the DLL as a COM component. Here is a link describing this process. http://msdn.microsoft.com/en-us/library/tzat5yw6(v=vs.71).aspx. The command is usually "regasm myTest.dll /tlb:myTest.tlb"
The COM registration process will now have created a .tlb file in the same folder as your DLL. Keep this .tlb file. We will need it in the next step
The next step is to create a C++ DLL which can call the COM DLL. This step is needed because R can call a C++ DLL directly but cannot call COM directly (correct me if I am wrong or skip this step if you know a better way to call COM from R). The code for the C++ DLL in dllmain.cpp is shown below (make sure to scroll to see the full code)
#include "stdafx.h"
#include <iostream>
//import the .tlb file create by COM registration
#import "path_to_Dll\MyDll.tlb" named_guids raw_interfaces_only
void _cdecl MyCPPFunction(char ** strName)
{
//Initialize COM
HRESULT hr = CoInitialize(NULL);
//create COM interface pointer
MyNamespace::MyInterfacePtr myPtr;
//create instance of COM class using the interface pointer
HRESULT hr2 = myPtr.CreateInstance(MyNamespace::CLSID_MyClass);
//create variable to hold output from COM.
BSTR output;
//call the COM function
myPtr->MyFunction(_bstr_t(strName[0]), &output);
//convert the returned BSTR from .net into char*
int length = (int) SysStringLen(output);
char *tempBuffer;
tempBuffer = (char *) malloc(1 + length);
WideCharToMultibyte(CP_ACP, 0, output, -1, tempBuffer, length, NULL, NULL);
tempBuffer[length] = '\0';
//release interface
myPtr->Release();
//uninitialize COM
if(hr == S_OK)
CoUninitialize();
//set output in input for returning to R (this is weird but the only way I could make it work)
strName[0] = tempBuffer;
}
Now compile your project and you will get a C++ DLL. We are almost there now! Dont give up yet :). Now, you have a C# DLL exposed as a COM object and a C++ DLL that can call this COM object. The final step is to call the C++ function from R. Here is the R code to do that
#load C++ DLL
dyn.load("path_to_C++_DLL")
#call function in C++ DLL and pass in a test name.
# The C++ DLL will in turn call the C# function
output <- .C("MyCPPFunction", as.character("Peter"))
#print output
print(output)
And you should see "Hello Peter" displayed from C# in the R console! One very important point to note.
Always, compile the COM DLL using "any CPU" option. Always compile the C++ DLL to match your R installation! For example, if you have 32 bit R installed, then make sure you compile the C++ DLL as 32 bit DLL. If you have 64 bit R installed and want to use it, then make sure you compile your C++ DLL as a 64 bit DLL. Best of luck!