Website of Markus Griesser
Latest Update: 29.12.2007
Ay you have seen in Part 1 and
Part 2 connecting managed and
unmanaged code is not this complecated. But - yeah there is a but - we have done
nothing with data. As you might want to pass some variables to c# and also want
to get something back, you have to take care of a lot of things. I have slightly
ripped the topic of marshalling in Part 1. But nothing compared to the whole thing.
We now extend the interface from Part 1 with several new functions.
public interface IClass1 {
// Gets a string and displays it
void DisplayString(
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]string s);
// Gives back a string array
string[] GetStringArray();
// Gets a struct, change its content, and give it back
void DisplayStruct([In, Out]MFCStruct s);
}
Let's have a look at the above code. Function DisplayString is very
straight forward. It gets a UNICODE string and displays it in a MessageBox.
GetStringArray() is intended to return several strings as an array.
The most interresting function is DisplayStruct which gets a struct from
the c++ function, changes all of its content and gives it back. A complete round.
To make things more difficult, we will add a string member to our MFCStruct.
It now looks like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public class MFCStruct {
public MFCStruct() {
nI = 0;
dL = 0.0;
}
[MarshalAs(System.Runtime.InteropServices.UnmanagedType.BStr)]
public string s;
public int nI;
public double dL;
}
Now the implementation of the above declared functions in our super class Class1:
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Class1 : IClass1{
public void DisplayString(
[MarshalAs(
System.Runtime.InteropServices.UnmanagedType.BStr)]string s) {
MessageBox.Show(s);
}
public string[] GetStringArray() {
string[] arr = new string[]{
"From C# 1",
"From C# 2",
"From C# 3"
};
return arr;
}
public void DisplayStruct([In, Out]MFCStruct s) {
MessageBox.Show(
string.Format("String: {0}, Integer: {1}, Double: {2}",
s.s, s.nI, s.dL));
s.s = s.s.ToLower();
s.nI *= 2;
s.dL *= 3;
}
}
Looks very simple, isn't it?
Now the client side. How to call these functions:
void CMFCSharpeningDlg::OnBnClickedButton2()
{
CoInitialize(NULL);
IClass1Ptr pClass1(__uuidof(Class1));
CString csTest = _T("Hello World");
BSTR str = SysAllocString((BSTR)csTest.GetBuffer(0));
pClass1->DisplayString(str);
CoUninitialize();
}
As you can see, I prefer to use BSTR. But it is not problem to use LPTWSTR or
its ANSI brother LPTSTR. You only have to change the MarshalAs
attribute (the same is true for the struct). Now the second function, which
returns an array of strings:
void CMFCSharpeningDlg::OnBnClickedButton6()
{
CoInitialize(NULL);
IClass1Ptr pClass1(__uuidof(Class1));
SAFEARRAY *arr = new SAFEARRAY;
pClass1->GetStringArray(&arr);
long length = 0;
HRESULT hr = SafeArrayGetUBound(arr, 1, &length);
for(long j=0;j<=length;j++){
BSTR bstr;
SafeArrayGetElement(arr, &j, &bstr);
MessageBox(bstr);
}
CoUninitialize();
}
A bit more complicated, because arrays are exported as COM-type SAFEARRAY. So you have to write some code to strip it back. Finally our struct:
void CMFCSharpeningDlg::OnBnClickedButton7()
{
CoInitialize(NULL);
IClass1Ptr pClass1(__uuidof(Class1));
Sharp::MFCStruct newStruct;
newStruct.s = SysAllocString((BSTR)_T("Hallo"));
newStruct.nI = 12;
newStruct.dL = 13.45;
pClass1->DisplayStruct(&newStruct);
MessageBox(newStruct.s);
delete newStruct;
CoUninitialize();
}
I hope you got an impression on how the connection works. I have currently not solved every problem related to this topic for me, but every big achievment will be posted here.