【问题标题】:Simplest way to move an array from C++ to C#, modify it, and pass it back to C++将数组从 C++ 移动到 C#、对其进行修改并将其传回 C++ 的最简单方法
【发布时间】:2010-10-09 04:40:28
【问题描述】:

我有一个 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++ 的深渊。

【问题讨论】:

    标签: c# c++ com interop


    【解决方案1】:

    我不久前对此进行了实验,但不幸的是忘记了它们是如何组合在一起的……出于我的目的,结果证明它非常缓慢,所以我删掉了 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 : 我从网上得到了写这些东西的所有信息,所以它就在那里。

    【讨论】:

      【解决方案2】:

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

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

      它也只涉及单个编组阶段(本机 托管),而不是托管 COM 接口所需的必要 COM 可调用包装器的额外步骤。

      【讨论】:

        【解决方案3】:

        这也行吗?

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

        【讨论】:

          猜你喜欢
          • 2015-06-13
          • 2013-10-09
          • 2011-11-29
          • 2023-04-05
          • 1970-01-01
          • 1970-01-01
          • 2011-07-15
          • 2013-10-27
          • 1970-01-01
          相关资源
          最近更新 更多