将数组从C ++移动到C#的最简单方法,修改它,并将其传递回C ++

时间:2009-02-22 18:52:26

标签: c# c++ com interop

我有一个C#类库,其中包含需要与外部应用程序一起使用的方法。不幸的是,这个外部应用程序仅支持C / C ++中的外部API。

现在我已经设法在C ++ dll和C#DLL之间运行一个非常简单的COM示例,但我仍然坚持如何移动数组数据。

这是我到目前为止所做的,就像我通过COM在网络上找到的一个小例子:

DLL_EXPORT(void) runAddTest(int add1,long *result) {
    // Initialize COM.
    HRESULT hr = CoInitialize(NULL);

    // Create the interface pointer.
    IUnitModelPtr pIUnit(__uuidof(UnitModel));

    long lResult = 0;

    // Call the Add method.
    pIUnit->Add(5, 10, &lResult);

    *result = lResult;

    // Uninitialize COM.
    CoUninitialize();

}

这可以在我的C#类中调用add方法。如何修改它以获取并返回双精度数组? (生病也需要用绳子做下去)。

我需要获取一个非托管数组,将此数组传递给C#类进行一些计算,然后将结果传递回原始函数调用(非托管)C ++中指定的数组引用。

我需要公开这样的函数:


* calcin - 对双精度数组的引用

* calcOut - 对双精度数组的引用

numIN - 输入数组大小的值

DLL_EXPORT(void) doCalc(double *calcIn, int numIn, double *calcOut)
{
      //pass the calcIn array to C# class for the calcuations

      //get the values back from my C# class

      //put the values from the C# class 
      //into the array ref specified by the *calcOut reference 


}

认为我可以为外部应用程序使用C ++ \ CLI DLL,所以如果这比直接COM更容易,那么我愿意看一下。

请保持温和,因为我主要是一名C#开发人员,但已被抛入Interop和C ++的深层。

3 个答案:

答案 0 :(得分:3)

我前一段时间对此进行了实验,但不幸的是忘记了它们是如何组合在一起的......为了我的目的,它结果是非常慢,所以我剪掉了C#并回到了所有C ++。当你说你主要是一名C#开发人员时,我希望你理解指针,因为如果你不这样做,就没有办法保持温和。

传递数组基本上归结为在C ++端使用CoTaskMemAlloc系列函数(http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx)和在C#端使用Marshal类(http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.aspx - 它具有类似AllocCoTaskMem的方法)。对于C#,我最终得到了一个实用程序类:

public class serviceUtils
{
    unsafe public long stringToCoTaskPtr( ref str thestring )
    {
        return (long)Marshal.StringToCoTaskMemAnsi(thestring.theString).ToPointer();//TODO : what errors occur from here? handle them
    }

    unsafe public long bytesToCoTaskPtr( ref bytes thebytes, ref short byteCnt)
    {
        byteCnt = (short)thebytes.theArray.Length;
        IntPtr tmpptr = new IntPtr();
        tmpptr = Marshal.AllocCoTaskMem(byteCnt);
        Marshal.Copy(thebytes.theArray, 0, tmpptr, byteCnt);
        return (long)tmpptr.ToPointer();
    }

    public void freeCoTaskMemPtr(long ptr)
    {
        Marshal.FreeCoTaskMem(new IntPtr(ptr));//TODO : errors from here?
    }

    public string coTaskPtrToString(long theptr)
    {
        return Marshal.PtrToStringAnsi(new IntPtr(theptr));
    }

    public byte[] coTaskPtrToBytes(long theptr, short thelen)
    {
        byte[] tmpbytes = new byte[thelen];
        Marshal.Copy(new IntPtr(theptr), tmpbytes, 0, thelen);
        return tmpbytes;
    }
}

向您抛出更多代码: 这个c ++

#import "..\COMClient\bin\Debug\COMClient.tlb" named_guids raw_interfaces_only
int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);   //Initialize all COM Components
COMClient::IComCalculatorPtr pCalc;
// CreateInstance parameters
HRESULT hRes = pCalc.CreateInstance(COMClient::CLSID_ComCalculator);
if (hRes == S_OK) {
    long size = 5;
    LPVOID ptr = CoTaskMemAlloc( size );
    if( ptr != NULL )
    {
        memcpy( ptr, "12345", size );
        short ans = 0;
        pCalc->changeBytes( (__int64*)&ptr, &size, &ans );
        CoTaskMemFree(ptr);
    }
}

CoUninitialize ();   //DeInitialize all COM Components

return 0;
}

称之为c#

    public short changeBytes(ref long ptr, ref int arraysize)
    {
        try
        {
            IntPtr interopPtr = new IntPtr(ptr);                
            testservice.ByteArray bytes = new testservice.ByteArray();
            byte[] somebytes = new byte[arraysize];
            Marshal.Copy(interopPtr, somebytes, 0, arraysize);
            bytes.theArray = somebytes;

            CalculatorClient client = generateClient();
            client.takeArray(ref bytes);
            client.Close();
            if (arraysize < bytes.theArray.Length)
            {
                interopPtr = Marshal.ReAllocCoTaskMem(interopPtr, bytes.theArray.Length);//TODO : throws an exception if fails... deal with it
            }
            Marshal.Copy(bytes.theArray, 0, interopPtr, bytes.theArray.Length);
            ptr = interopPtr.ToInt64();

            arraysize = bytes.theArray.Length;

            //TODO : do we need to free IntPtr? check all code for memory leaks... check for successful allocation
        }
        catch(Exception e)
        {
            return 3;
        }

        return 2;
    }

很抱歉,但我没有时间完成所有这些并正确解释,希望这会给你指向正确的方向,至少有些东西可以谷歌。祝你好运

PS:我从网上得到了所有的信息,所以它就在那里。

答案 1 :(得分:1)

  

我想我可以为外部应用程序使用C ++ \ CLI DLL,所以如果这比直接COM更容易,那么我愿意看看它。

如果您没有太多的COM经验(并且COM中的数组非常简单),那么围绕3 rd 方的C ++ / CLI包装器可能会更容易。

它也只涉及单个编组阶段(本地&lt; - &gt;托管),而不是额外的步骤,您需要托管&lt; - &gt;所需的COM Callable Wrapper。 COM接口)。

答案 2 :(得分:0)

这会起作用吗?

在C#中, 1.致电Marshal.PtrToStructure 2.修改值 3.调用Marshal.StructureToPtr