Is it possible to call a R statistics function to optimize C# function

后端 未结 3 1042
挽巷
挽巷 2020-12-16 05:51

I want to know if it\'s possible to call r statistics optimization function (here I want to use regnoud) from C# while the function to be optimized is written in C#. I fou

3条回答
  •  一个人的身影
    2020-12-16 06:37

    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

    1. 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".
    2. 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 
    
    //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!

提交回复
热议问题