如果COM对象从.NET程序集中公开,是否可以从R中调用COM对象?

问题描述:

我想知道是否可以通过COM调用从R调用.NET函数。如果COM对象从.NET程序集中公开,是否可以从R中调用COM对象?

rcom允许调用COM对象,因此理论上应该可以将任何.NET程序集作为COM对象公开。

为了简单起见,我会看看我是否可以调用System.Text中的.Reverse()函数,该函数默认作为.NET框架中的COM对象公开。

这是我到目前为止已经试过:

  1. 我在我的系统中获得的ProgID的列表(见link to C# code)。这是在我的系统相关的ProgID的列表:

    ---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--- 
    
  2. 该R代码加载公开为COM对象.NET .dll文件:

    library('rcom') 
    x <- comCreateObject("System.Text.ASCIIEncoding") 
    
  3. 它肯定找到COM对象:

    X ATTR( “类”) 1 “COMObject”

  4. 我的问题是 - 如何在此COM对象中调用.Reverse()函数?

更新

在.NET中,电话是:

string x = "hello".Reverse(); 

所以,在R,通话会是什么?

更新

对于R电话C#示例,请参见R于滑动调用C#中Embedding R in Applications on Windows 61.

注意ProgId是从.NET类ProjectName.ClassName

+1

'系统。 Text.ASCIIEncoding'没有'Reverse'方法。你的意思是'string.Reverse'? – 2013-02-13 21:06:51

+0

@D Stanley是的,想要调用string.Reverse。我想知道如何列出COM对象已经暴露的方法? – Contango 2013-02-13 21:09:04

+1

使用['comGetObjectInfo'](http://www.inside-r.org/packages/cran/rcom/docs/comGetObjectInfo) – 2013-02-13 21:13:04

基于从幻灯片61到幻灯片Embedding R in Applications on Windows的说明,我刚刚通过COM成功地从R调用了.NET代码。

这里是R代码:

# 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") 

这里为R内的输出:

> DoCallback: xxxNULL 

下面是.NET类C#代码:

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"); 
      } 
     } 
    } 
} 

笔记

  1. 为了达到此目的,所有内容必须始终如一地保持32位或一致的64位。我通过使用以下设置获得了它在32位模式下的工作:

    • C#程序集(设置为32位)。
    • R的版本(我在32位模式下使用了Revolution R)。
  2. 如果您使用Visual Studio 2012(VS2012),然后如果你勾选“注册为COM互操作”,在.NET项目设置,它会自动运行C:\Windows\Microsoft.NET\Framework\v4.0.30319\regtlibv12.exe注册您的自定义.NET类作为全系统COM组件,进行编译。但是,如果您使用Visual Studio 2010(VS2010),它将自动运行而不是regtlibv12.exe,所有此设置将执行的操作是创建.tlb文件(您将不得不亲自手动运行regtlibv12.exe)。

  3. 可以通过调用“regtlibv12.exe -u MyComDLL.tlb”注销COM组件。

  4. 如果你建立你的项目,VS2012抱怨说,它不能写输出.dll文件,这意味着r是它锁定,由于通话x <- comCreateObject("InteropSample.MyClass32")。为了解锁该.dll,因此它可以被编译VS2012,靠近R,编译C#,然后重新启动R.

附加资料

  1. 参见R help on rcom package
  2. 请参阅User manual for rcom
  3. 查看statconn Wiki page
  4. 如果它不适用于R v2.15,请使用R v2.14进行试用。
+1

不能相信我是唯一一个upvote这个梦幻般的答案! – 2013-03-25 05:33:56

+0

我一直在为这个问题烦恼几个小时,但我无法添加参考。我做了'regtlibv12.exe rcom_srv.tlb',但在VS中的COM引用中没有对它的引用。当我浏览到tlb时,它给我一个引用它的错误。你是如何参考'rcom_srv.tlb'的? – 2013-03-26 03:48:35

+0

最有可能您的.NET程序集未设置为32位。如果将其设置为64位(“ALL”),那么它将无法引用32位COM组件。让我知道这是否解决了这个问题。如果没有,我会在新电脑上按照自己的指示进行操作,并相应地调整说明。 – Contango 2013-03-27 10:53:12

那么一般使用comInvoke

s <- comInvoke(x,"Reverse") 

然而,由于没有System.Text.ASCIIEncoding也不stringReverse方法,你需要选择一个不同的方法来执行。

+0

啊!因此,其中一个问题是识别system.string所公开的COM对象的名称。 – Contango 2013-02-13 21:24:08

+1

这不是 - 从COM的角度来看''system.string'被标记为'noncreatable'。你会有更好的运气建立一个简单的COM类自己,试图找到一个COM可见的.NET框架类 – 2013-02-13 22:49:05

我知道这个问题很老,我报告我的经验,以帮助未来的.Net/R开发人员。

不管是什么我想我不能参考rcom_srv.tlb

不能添加到C:\Program Files\R\R-2.15.3\library\rcom\libs\i386\rcom_srv.tlb的引用。请确保该文件是可访问的,并且它是有效的程序集或COM组件。

enter image description here

我发现this article,他们同时使用RCOMServerLib和STATCONNECTORSRVLib:

public STATCONNECTORSRVLib.StatConnectorClass rdcom = null; 
//public RCOMServerLib.InternalConnectorClass rdcom = null; // Use 'rcom' for debugging 

我没能充分利用任何进步,所以我最终我做到了没有RcomServerLib:

namespace XYZ.dotNetProject_R 
{ 
    [Guid("FA6F70DD-CDD0-4FF3-94BA-E2B94E68321D"), 
    InterfaceType(ComInterfaceType.InterfaceIsDual)] 
    public interface IDataHelper 
    { 
     string[,] GetdotNetProject2DArray(string code, DateTime fromDate, DateTime toDate);   
    } 

    [ComVisible(true)] 
    [ProgId("XYZ.dotNetProject_R")] 
    public class DataHelper : IDataHelper 
    { 
     public string[,] GetdotNetProject2DArray(string code, DateTime fromDate = default(DateTime), DateTime toDate = default(DateTime)) 
     { 

     } 
    } 
} 

而我通过R

# On some PC's it wont download the Package until you set it to use your IE Proxy Settings: 
setInternet2(TRUE) 
# This is a once-off call. 
install.packages("rcom") 
# This is a once-off call. 
installstatconnDCOM() 

#Resusable calls 
> library('rcom') 
Loading required package: rscproxy 
> dll = comCreateObject("XYZ.dotNetProject_R") 
> dll 
<pointer: 0x2079002c> 
attr(,"class") 
[1] "COMObject" 
> series = comInvoke(dll,"GetdotNetProject2DArray","abc123","2000-01-01","2010-01-01") 
> series 
    [,1]   [,2]     
[1,] "2000-01-01" "1236.1" 

COM不支持泛型,所以我干脆返回一个字符串数组。我发现ř仅支持基本/原始.NET类型,例如字符串,日期时间,整数&等。当我试图返回一个对象数组,它没有和.Net调用返回NULL到R.

+0

感谢您的补充。 – Contango 2013-03-28 23:49:23

+0

现在我想起来了,我可能会首先尝试R v2.15,然后放弃并回到v2.14以使其开箱即用。我已经在上面更新了我的答案。 – Contango 2013-03-28 23:50:22