Самый простой способ переместить массив из C ++ в C #, изменить его и передать обратно в C ++ - PullRequest
6 голосов
/ 22 февраля 2009

У меня есть библиотека классов C #, которая содержит методы, которые необходимо использовать с внешним приложением. К сожалению, это внешнее приложение поддерживает только внешние API в C / C ++.

Теперь мне удалось получить очень простой пример COM, работающий между DLL C ++ и C # DLL, но я застрял в том, как я могу перемещаться по данным массива.

Это то, что я получил до сих пор, так же, как маленький пример, который я нашел в сети для общения через 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();

}

Это прекрасно работает для вызова метода add в моем классе C #. Как я могу изменить это, чтобы взять и вернуть массив пар? (плохо также нужно сделать это со строками вниз по линии).

Мне нужно взять неуправляемый массив, передать этот массив классу 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 ]

3 голосов
/ 23 февраля 2009

Я экспериментировал с этим некоторое время назад, но, к сожалению, забыл, как все это совмещалось ... для моей цели это оказалось ужасно медленным, поэтому я отключил C # и вернулся ко всему C ++. Когда вы говорите, что вы в первую очередь разработчик C #, я надеюсь, что вы понимаете указатели, потому что если вы этого не сделаете, нет способа быть мягким.

Передача массивов в основном сводилась к использованию семейства функций CoTaskMemAlloc на стороне C ++ (http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx) и класса Marshal на стороне C # (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;
    }
}

Просто, чтобы добавить к вам еще немного кода: это с ++

#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;
    }

Извините, но у меня нет времени, чтобы разобраться со всем этим и объяснить это должным образом, надеюсь, это даст вам указатели в правильном направлении, по крайней мере, некоторые вещи для Google. Удачи

PS: у меня есть вся информация, чтобы написать этот материал из сети, так что он там.

1 голос
/ 23 февраля 2009

Я думаю, что я могу использовать C ++ \ CLI DLL для внешнего приложения, так что если это проще, чем прямой COM, тогда я буду готов посмотреть на это.

Если у вас нет большого опыта работы с COM (а массивы в COM не так просты), то обертка C ++ / CLI вокруг 3 rd , скорее всего, будет проще.

Он также будет включать в себя только одну стадию сортировки (управляемый <->), а не дополнительный шаг, необходимый COM Callable Wrapper, который вам понадобится для управляемого <-> COM-взаимодействия).

0 голосов
/ 06 октября 2009

Будет ли это работать?

В C #, 1. Позвоните маршалу.PtrToStructure 2. Измените значение 3. Позвоните маршалу. StructureToPtr

...