I'm wondering if it is possible to call .NET functions from R, via a COM call.
The library rcom
allows calls to COM objects, so this should be possible, in theory, for any .NET assembly that is exposed as a COM object.
To keep it simple, I'll see if I can call the .Reverse()
function in System.Text
, which is exposed by default as a COM object from the .NET framework.
This is what I have tried so far:
I obtained a list of ProgID's in my system (see link to C# code). Here is a list of the relevant ProgIDs in my system:
---start list of COM ProgID entries---
<snip>
System.SystemException -> mscoree.dll
System.Text.ASCIIEncoding -> mscoree.dll
System.Text.StringBuilder -> mscoree.dll
System.Text.UnicodeEncoding -> mscoree.dll
System.Text.UTF7Encoding -> mscoree.dll
System.Text.UTF8Encoding -> mscoree.dll
<snip>
---end list---
This R code loads a .NET .dll exposed as a COM object:
library('rcom')
x <- comCreateObject("System.Text.ASCIIEncoding")
Its definitely finding the COM object:
x attr(,"class") 1 "COMObject"
My question is - how do I call the .Reverse()
function within this COM object?
Update
In .NET, the call would be:
string x = "hello".Reverse();
So, in R, the call would be?
Update
For an example of R calling C#, see R calls C# in Embedding R in Applications on Windows on slide 61.
Note that ProgId
is ProjectName.ClassName
from .NET class.
I have just successfully called .NET code from R via COM, based on instructions from slide 61 to 65 of Embedding R in Applications on Windows.
Here is the R code:
# This is a once-off call.
install.packages("rcom")
library(rcom)
# This is a once-off call. See rcom user manual at:
# http://cran.r-project.org/web/packages/rcom/rcom.pdf
installstatconnDCOM()
x <- comCreateObject("InteropSample.MyClass32")
comSetProperty(x,"Text","xxx")
comSetProperty(x,"Connector",comThis())
comInvoke(x,"DoCallback")
Here is the output within R:
> DoCallback: xxxNULL
Here is the C# code for the .NET class:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
// Before running this, get rid of errors with "RCOMServerLib" by adding the rcom type library:
//
// Make sure everything is 32-bit (32-bit build in .NET, 32-bit run of Revolution R).
//
// Tick "Register for COM interop" in .NET project settings.
//
// 1.Browse to "C:\Revolution\R-Enterprise-6.1\R-2.14.2\library\rcom\libs\i386>", then execute:
// C:\Revolution\R-Enterprise-6.1\R-2.14.2\library\rcom\libs\i386> C:\Windows\Microsoft.NET\Framework\v4.0.30319\regtlibv12.exe rcom_srv.tlb
// Registration of rcom_srv.tlb successful.
//
// 2. Add reference to "rcom_srv.tlb", this gets rid of errors for RComServerLib.
// (browse to "C:\Revolution\R-Enterprise-6.1\R-2.14.2\library\rcom\libs\i386")
//
// 3. If we are using VS2012, this .NET assembly class will be automatically registered as COM server on build if we are using VS2012. If using VS2012, you must do this manually on the command line.
//
// See:
// http://generally.wordpress.com/2006/07/28/exposing-your-net-assembly-through-com/
// http://www.inside-r.org/packages/cran/rcom/docs/comCreateObject
// In R:
// comCreateObject("InteropSample.MyClass32")
// comSetProperty(x,"Text","xxx")
// comSetProperty(x,"Connector",comThis())
// comInvoke(x,"DoCallback")
namespace COM___called_from_R
{
[Guid("3ddfe021-a0c6-4218-a254-4fc4328c99a7"),
InterfaceType(ComInterfaceType.InterfaceIsDual)]
internal interface IMyComponent
{
RCOMServerLib.IStatConnector Connector { set; }
string Text { set; }
void DoCallback();
}
[Guid("133fee0e-9b32-4429-8a43-6e2a706a9beb"), ComVisible(true)]
[ProgIdAttribute("InteropSample.MyClass32")]
public class MyComponent : IMyComponent
{
private string mText;
private RCOMServerLib.IStatConnector mConnector;
public RCOMServerLib.IStatConnector Connector
{
set { mConnector = value; }
}
public string Text
{
set { mText = value; }
}
public string MyProperty;
public void DoCallback()
{
if (mConnector != null)
{
mConnector.EvaluateNoReturn("cat(\"DoCallback: "
+ mText + "\")\n");
}
}
}
}
Notes
In order for this to work, everything must be consistently 32-bit, or consistently 64-bit. I got it working in 32-bit mode, by using the following settings:
If you use Visual Studio 2012 (VS2012), then if you tick "Register for COM interop" in .NET project settings, it will automatically run C:\Windows\Microsoft.NET\Framework\v4.0.30319\regtlibv12.exe
to register your custom .NET class as a system wide COM component, on compile. However, if you use Visual Studio 2010 (VS2010), it will not automatically run regtlibv12.exe
, all this setting will do is create the .tlb file (you will have to run regtlibv12.exe
manually, yourself).
Can unregister a COM component by calling "regtlibv12.exe -u MyComDLL.tlb".
If you build your project, and VS2012 complains that it cannot write the output .dll, this means that R is locking it due to the call x <- comCreateObject("InteropSample.MyClass32")
. To unlock the .dll so it can be compiled VS2012, close R, compile C#, then restart R.
Additional Information
rcom
package.rcom
.