将数组从 C++ 移动到 C#、修改它并将其传递回 C++ 的最简单方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/575455/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Simplest way to move an array from C++ to C#, modify it, and pass it back to C++
提问by Alex
I have a C# class library that contains methods that need to be used with an external application. Unfortunately this external application only supports external APIs in C/C++.
我有一个 C# 类库,其中包含需要与外部应用程序一起使用的方法。不幸的是,这个外部应用程序仅支持 C/C++ 中的外部 API。
Now I've managed to get a very simple COM example working between a C++ dll and a C# DLL, but I am stuck on how I can move around array data.
现在我已经成功地得到了一个在 C++ dll 和 C# DLL 之间工作的非常简单的 COM 示例,但是我被困在如何移动数组数据上。
This is what I've got so far, just as a little example I found on the web of communicating via 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();
}
This works fine to call the add method in my C# class. How can I modify this to take and return an array of doubles? (ill also need to do it with strings down the line).
这适用于在我的 C# 类中调用 add 方法。如何修改它以获取并返回双精度数组?(生病也需要用线来做这件事)。
I need to take an unmanaged array , pass this array to a C# class for some calculations, and then pass it back the results to the array reference specified in the original function call (unmanaged) C++.
我需要获取一个非托管数组,将此数组传递给 C# 类以进行某些计算,然后将结果传递回原始函数调用(非托管)C++ 中指定的数组引用。
I'll need to expose a function like this:
我需要公开这样的函数:
*calcin - reference to array of doubles
*calcin - 对双精度数组的引用
*calcOut - reference to array of doubles
*calcOut - 对双精度数组的引用
numIN - value of size of input array
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
}
I thinkI can use a C++\CLI DLL for the external application so if this is easier than straight COM then i'll be willing to look at that.
我想我可以将 C++\CLI DLL 用于外部应用程序,所以如果这比直接 COM 更容易,那么我愿意考虑一下。
Please be gentle as I am primarily a C# developer but have been thrown in the deep end of Interop and C++ .
请保持温和,因为我主要是一名 C# 开发人员,但已陷入 Interop 和 C++ 的深渊。
采纳答案by Patrick
I experimented with this a while ago but have unfortunately forgotten how it all fitted together... for my purpose it turned out to be woefully slow so I cut out the C# and went back to all C++. When you say you're primarily a C# developer I hope you understand pointers because if you don't there's no way to be gentle.
我不久前尝试过这个,但不幸的是忘记了它是如何组合在一起的......为了我的目的,结果证明它非常慢,所以我去掉了 C# 并回到了所有的 C++。当你说你主要是一名 C# 开发人员时,我希望你理解指针,因为如果你不理解,就没有办法保持温和。
Passing arrays basically came down to using CoTaskMemAlloc family of functions on the C++ side (http://msdn.microsoft.com/en-us/library/ms692727(VS.85).aspx) and the Marshal class on the C# side (http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.aspx- which has methods like AllocCoTaskMem). For C# I ended up with a utility class:
传递数组基本上归结为在 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;
}
}
Just to throw some more code at you: this c++
只是为了向您抛出更多代码:this 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;
}
called this c#
叫这个 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;
}
Sorry, but I don't have the time to work all this out and explain it properly, hopefully this will give you pointers in the right direction, at the very least some things to google. Good Luck
抱歉,但我没有时间解决所有这些问题并正确解释它,希望这会给您指明正确的方向,至少可以向 google 提供一些信息。祝你好运
PS : I got all the info to write this stuff off the net, so it is out there.
PS:我从网上得到了写这些东西的所有信息,所以它就在那里。
回答by Richard
I think I can use a C++\CLI DLL for the external application so if this is easier than straight COM then i'll be willing to look at that.
我想我可以将 C++\CLI DLL 用于外部应用程序,所以如果这比直接 COM 更容易,那么我愿意考虑一下。
If you don't have much COM experience (and arrays are significantly not simple in COM) then C++/CLI wrapper around the 3rdparty will likely be easier.
如果你没有太多的经验,COM(和阵列显著不是在COM简单),然后周围的3 C ++ / CLI包装RD党很可能会更容易。
It will also only involve a single marshalling stage (native <-> managed) rather than the extra step the necessary COM Callable Wrapper you will need for managed <-> COM interfacing).
它也将只涉及单个编组阶段(本机 <-> 托管),而不是额外的步骤(托管 <-> COM 接口所需的 COM 可调用包装器)。
回答by Patrick
Would this also work?
这也行吗?
In C#, 1. Call Marshal.PtrToStructure 2. Modify the value 3. Call Marshal.StructureToPtr
在 C# 中, 1. 调用 Marshal.PtrToStructure 2. 修改值 3. 调用 Marshal.StructureToPtr