The author of the COM server saves an R object to a file so that it is available to future R sessions. Then, we define a COM server definition that knows where to find this file containing the serialized data and uses it to construct the object. Let's start by creating a simple S object that we want to provide as a COM server. We'll create a simple matrix with the numbers 1 through 35 and with dimensions 5 by 7. Then we save it to the file <file>matrix.rda</file> (in the current directory).
m = matrix(1:35, 5, 7) fn = paste(getwd(), "matrix.rda", sep=.Platform$file.sep) save(m, file = fn) |
---|
Now we can proceed to register this fixed S object as a COM server. We provide a UUID for the class, either by selecting a specific one or generating it anew. And then we directly add the entry to the Windows registry so that clients can find and instantiate the server.
library(RDCOMServer) clsid = getuuid("E9A94B2B-47C6-4CBA-D9A8-737577914171") registerClassID("Test.LocalMatrix", clsid, rda = fn, profile = gsub("/", "\\\\", system.file("tests", "profile.S", package = "RDCOMServer")), testKey = "my own value") |
---|
The rda gives the name of the file into which we serialized the S value. This will be deserialized and converted to a COM object via a call to createCOMObject. The profile value here gives the name of a file containing S source code. This is source'ed each time an instance of this server is needed. This can be used to do initialization of the system such as loading packages, etc. that provid functions and methods that will be used by the resulting COM server.
Note that we don't need to provide any additional information in this particular case. The GetCOMClassDef function will be called with the UUID of this class and it will look for these keys in the associated registry key. On finding them, it will source the file identified by profile and deserialize the rda file. Then it calls createCOMObject on the resulting object obtained from the deserialization. In our case, we have a built-in method for handling matrices. If we needed to provide a method to handle the particular type, we could add it to the rda file, or to the code in the profile file.
These two keys provide sufficient flexibility to create a COM server from a fixed object. However, it is sometimes useful for the resulting COM server to be able to be parameterized by additional values. We can do this by putting additional entries into the UUID's registry key which the createCOMObject method can query to customize the serve. As a slightly unrealistic example, suppose we wanted to provide access to only a fixed set of columns in a very large data frame. Rather than having a copy of the subset on disk, we might want to add an additional property to the COM server which restricted the columns to the specified subset. While we can write a functional server to do this, we can also specify the serialized file and a registry entry, say columns, that gives a comma-separated list of the column names to export.
Given the registration above, a client can use this in much the same way it would use a regular COM object from S. For examle, in Python, we can create an instance of the server and ask for its dimensions.
from win32com.client import Dispatch o = Dispatch("Test.LocalMatrix") d = o.dim() print d |
---|
o = Dispatch("Test.LocalMatrix") d = o.dim() print d |
---|
data(mtcars) |
---|
If we were to copy the mechanism for the matrix in the example above, we would also have to specify the location of the file containing the serialized mtcars data frame. Again, it is more convenient to tell GetCOMClassDef to fetch the variable by name and not have to find the object indirectly via file. To do this, we specify the name of the S variable to use to get the desired object. To do this, we use the Svariable key and assign it a value of "mtcars". We don't provide a value for the rda key and GetCOMClassDef realizes what to do.
So now we know how to register such a server quite simply. The following code is very similar to that for the matrix example above, but differs only in that we provide an S command for the value of profile and the name "mtcars" for Svariable.
library(RDCOMServer) clsid = getuuid("52d4f6bc-dd1c-45b3-0287-e469ede627f7") registerClassID("S.Mtcars", clsid, profile = "data(mtcars)", Svariable="mtcars") |
---|
use Win32::OLE; $mtcars = Win32::OLE->new("S.Mtcars"); @dim= @{$mtcars->dim()}; print "Cars: ", $mtcars, "\n"; print $#dim, "\n"; print $dim[0], "x ", $dim[1], "\n"; |
---|