wayside
asked on
How do you pass an array to a COM object using interop?
I need to extend an existing COM object to add a function that takes as input an array of bytes, which is readonly. I haven't done COM in a while and I don't remember the gory details so hopefully someone here can help me fill in the blanks.
The interface definition looks something like this:
[object, dual, uuid("12345678-1234-1234-1 234-123456 789ABC") ]
__interface IMyFoo {
[id(1), helpstring("method foo1")] HRESULT foo1([in] BSTR str, [out,retval] BSTR* result);
[id(2), helpstring("method foo2")] HRESULT foo2([in] BYTE buf[], [in] LONG numBytes, [out,retval] BSTR* result); // <-- #1 below
};
[ coclass, uuid("87654321-4321-4321-4 321-CBA987 654321"), default(IMyFoo) ]
class CMyFoo : public IMyFoo
{
public:
CMyFoo(void);
~CMyFoo void);
STDMETHOD(foo1)(BSTR str, LONG* result);
STDMETHOD(foo2)(BYTE []buf, LONG numBytes, BSTR* result); // <--#1 below
};
The implementation of foo2 is conceptually something like:
STDMETHODIMP CMyFoo::foo2(BYTE []buf, LONG numBytes, BSTR* result)
{
int sum = 0;
for (int i=0; i<numBytes; i++) {
// process buf here
sum += buf[i]; // <--- #2 below
}
char outstr[50];
sprintf(outstr, "answer is %ld", sum);
*result = ::SysAllocString((OLECHAR *) outstr);
return S_OK;
}
From the C# side, I want to do something like:
byte[] buf = new byte[1234];
// buf gets data in it, not important how
mycom.CMyFooClass myfoo = new mycomCMyFooClass();
string sum= myfoo.foo2(buf, buf.length); // <--- #3 below
I've already got everything working to call the foo1() function in C#, but I need help with
#1) setting up the interface to be able to pass an array of bytes in foo2()
#2) accessing that array within the COM object function foo2() - don't need to modify it, just access it
#3) how do I call foo2() from the C# side to pass the array
Thanks!
The interface definition looks something like this:
[object, dual, uuid("12345678-1234-1234-1
__interface IMyFoo {
[id(1), helpstring("method foo1")] HRESULT foo1([in] BSTR str, [out,retval] BSTR* result);
[id(2), helpstring("method foo2")] HRESULT foo2([in] BYTE buf[], [in] LONG numBytes, [out,retval] BSTR* result); // <-- #1 below
};
[ coclass, uuid("87654321-4321-4321-4
class CMyFoo : public IMyFoo
{
public:
CMyFoo(void);
~CMyFoo void);
STDMETHOD(foo1)(BSTR str, LONG* result);
STDMETHOD(foo2)(BYTE []buf, LONG numBytes, BSTR* result); // <--#1 below
};
The implementation of foo2 is conceptually something like:
STDMETHODIMP CMyFoo::foo2(BYTE []buf, LONG numBytes, BSTR* result)
{
int sum = 0;
for (int i=0; i<numBytes; i++) {
// process buf here
sum += buf[i]; // <--- #2 below
}
char outstr[50];
sprintf(outstr, "answer is %ld", sum);
*result = ::SysAllocString((OLECHAR *) outstr);
return S_OK;
}
From the C# side, I want to do something like:
byte[] buf = new byte[1234];
// buf gets data in it, not important how
mycom.CMyFooClass myfoo = new mycomCMyFooClass();
string sum= myfoo.foo2(buf, buf.length); // <--- #3 below
I've already got everything working to call the foo1() function in C#, but I need help with
#1) setting up the interface to be able to pass an array of bytes in foo2()
#2) accessing that array within the COM object function foo2() - don't need to modify it, just access it
#3) how do I call foo2() from the C# side to pass the array
Thanks!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I am glad it is working for you. The article I pointed out suggested using safesarrays and variants rather than byte array. The other article does the same but gave example code which I agree is better
ASKER
Sorry, I should have closed this before.
ASKER
That article isn't very helpful.
This article is much more helpful, it shows actual sample code:
http://www.codeguru.com/Cpp/COM-Tech/activex/misc/article.php/c2577/
So I set up my code like this, and it works great. If you see any issues please let me know.
[object, dual, uuid("12345678-1234-1234-1
__interface IMyFoo {
[id(1), helpstring("method foo1")] HRESULT foo1([in] BSTR str, [out,retval] BSTR* result);
[id(2), helpstring("method foo2")] HRESULT foo2([in] VARIANT buf, [in] LONG numBytes, [out,retval] BSTR* result);
};
[ coclass, uuid("87654321-4321-4321-4
class CMyFoo : public IMyFoo
{
public:
CMyFoo(void);
~CMyFoo void);
STDMETHOD(foo1)(BSTR str, LONG* result);
STDMETHOD(foo2)(VARIANT buf, LONG numBytes, BSTR* result);
};
STDMETHODIMP CMyFoo::foo2(VARIANT buf, LONG numBytes, BSTR* result)
{
void *pData;
if (buf.vt == (VT_ARRAY | VT_UI1)) {
SafeArrayAccessData(buf.pa
SafeArrayUnaccessData(buf.
}
else
return S_FALSE;
unsigned char pUChar = (unsigned char *)pData;
int sum = 0;
for (int i=0; i<numBytes; i++) {
// process buf here
sum += pUChar [i];
}
char outstr[50];
sprintf(outstr, "answer is %ld", sum);
*result = ::SysAllocString((OLECHAR *) outstr);
return S_OK;
}
From the C# side:
byte[] buf = new byte[1234];
// buf gets data in it, not important how
mycom.CMyFooClass myfoo = new mycomCMyFooClass();
string sum= myfoo.foo2(buf, buf.Length);