问题
I am working on an enterprise application development. the entire application is developed in c++, except the UI, which is developed in c#, now its time to hookup the UI with c++ code. After detailed study i choose PInvoke in order to do so. All is successful, the only case where i stuck is that how can i pass collection to C++ code. e.g:
C# Side Code
List<string> lst = new List<string>();
lst.Add("1");
lst.Add("2");
lst.Add("3");
lst.Add("4");
C++ Side Code
std::vector<std::string> vStr;
Now how do i pass lst to native C++ code
回答1:
As mzabsky mentioned, you cannot marshal these types. You can, however, marshal an array:
The theoretical C++ export:
extern "C" __declspec(dllexport) void __stdcall Foo(wchar_t const* const* values, int length)
{
// Check argument validity...
// If you really need a vector
std::vector<std::wstring> vStr(values, values + length);
//...
}
The P/Invoke signature:
[DllImport("foo.dll")]
static extern void Foo([MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.LPWStr)] string[] values, int length);
The call from C#:
Foo(lst.ToArray(), lst.Count);
Note that I'm using std::wstring here; you could instead use char instead of wchar_t, LPStr instead of LPWStr, and std::string instead of std::wstring.
Note that this will allocate an array from the list and then the vector will copy the array's contents. If the original list is small in size, this should be of negligible concern.
Edit: fixing markup (< and >).
回答2:
You can't do this, only C types can be marshalled. You will have to write a C++/CLI wrapper (or a C wrapper around the C++ vector).
See this answer.
回答3:
Yes. You can. Actually, not just std::string
, std::wstring
, any standard C++ class or your own classes can be marshaled or instantiated and called from C#/.NET.
Wrapping a std::vector<any_type>
in C# is indeed possible with just regular P/Invoke Interop, it is complicated though. even a std::map
of any type can be done in C#/.NET.
The basic idea of instantiating a C++ object from .NET world is to allocate exact size of the C++ object from .NET, then call the constructor which is exported from the C++ DLL to initialize the object, then you will be able to call any of the functions to access that C++ object, if any of the method involves other C++ classes, you will need to wrap them in a C# class as well, for methods with primitive types, you can simply P/Invoke them. If you have only a few methods to call, it would be simple, manual coding won't take long. When you are done with the C++ object, you call the destructor method of the C++ object, which is a export function as well. if it does not have one, then you just need to free your memory from .NET.
Here is an example.
public class SampleClass : IDisposable
{
[DllImport("YourDll.dll", EntryPoint="ConstructorOfYourClass", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void SampleClassConstructor(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomething", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject);
[DllImport("YourDll.dll", EntryPoint="DoSomethingElse", CharSet=CharSet.Ansi, CallingConvention=CallingConvention.ThisCall)]
public extern static void DoSomething(IntPtr thisObject, int x);
IntPtr ptr;
public SampleClass(int sizeOfYourCppClass)
{
this.ptr = Marshal.AllocHGlobal(sizeOfYourCppClass);
SampleClassConstructor(this.ptr);
}
public void DoSomething()
{
DoSomething(this.ptr);
}
public void DoSomethingElse(int x)
{
DoSomethingElse(this.ptr, x);
}
public void Dispose()
{
Marshal.FreeHGlobal(this.ptr);
}
}
For the detail, please see the below link,
C#/.NET PInvoke Interop SDK
(I am the author of the SDK tool)
来源:https://stackoverflow.com/questions/5102436/how-to-marshal-collection-in-c-sharp-to-pass-to-native-c-code