RE: safearray api calls and .Net problems
> From: owner-lisp-hug@lispworks.com [mailto:owner-lisp-
> hug@lispworks.com] On Behalf Of Guy Footring
> Two other interesting things - Safearrays of floats are only passed in
> one part of the code, whereas the more heavily used safearrays of BSTR
> is not giving any problems. The other difference is that the BSTR
> safearrays are received ByVal, whereas the VT_R8 safearrays are
> (required to be?) received ByRef.
Do you have some small sample, with which you can reproduce the problem?
I've written an ActiveX component in C++ with ATL, which sends events to
event sinks and it works with VB.NET and VB6 programs. I've mixed it with
std::string for easier interfacing with the rest of my program and it is
integrated in some other C++ classes, but the interesting part for you looks
like this:
SAFEARRAY* createSafearray(string data)
{
METHOD_DEBUG;
SAFEARRAYBOUND aDim[1];
aDim[0].lLbound = 0;
aDim[0].cElements = (ULONG) data.size();
char *pActualData = NULL;
SAFEARRAY* pSA = SafeArrayCreate(VT_UI1, 1, aDim);
if (pSA == NULL) return NULL; // out of memory
if (SafeArrayAccessData(pSA, (void **)&pActualData)) return NULL;
// can't lock
for (int j = 0; j < (int) data.size(); j++) pActualData[j] =
data[j];
if (SafeArrayUnaccessData(pSA)) return NULL; // can't unlock
return pSA;
}
Void someFunction(string foo, string data)
{
SAFEARRAY* pSA = createSafearray(data);
if (pSA) {
_bstr_t bar(foo.c_str());
Fire_OnFooEvent(bar, &pSA);
SafeArrayDestroy(pSA);
}
}
HRESULT Fire_OnFooEvent (BSTR bar, SAFEARRAY** data)
{
HRESULT hr = S_OK;
T * pThis = static_cast<T *>(this);
int cConnections = m_vec.GetSize();
for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();
IDispatch * pConnection = static_cast<IDispatch
*>(punkConnection.p);
if (pConnection)
{
CComVariant avarParams[2];
avarParams[1] = bar;
avarParams[1].vt = VT_BSTR;
avarParams[0].pparray = data;
avarParams[0].vt = VT_ARRAY | VT_UI1 | VT_BYREF;
CComVariant varResult;
DISPPARAMS params = { avarParams, NULL, 2, 0 };
hr = pConnection->Invoke(1, IID_NULL,
LOCALE_USER_DEFAULT, DISPATCH_METHOD, ¶ms, &varResult, NULL, NULL);
}
}
return hr;
}
The Fire_OnFooEvent was generated by the wizard from the IDL specification.
The data parameter was specified as "[in] SAFEARRAY(BYTE)* message", but the
brain damaged Visual Studio wizard creates non-compilable code from it, so I
had to search some time and read a lot to find the right way how to
implement SAFEARRAY callback parameters.
Regarding your problem: Specifying VT_BYREF should work, because then the
ownership of the object won't be transferred to the receiver, and you have
to free the object. Same with BSTR, but in this case even if you pass it by
value: the ownership is not transferred to the called function. In my code
this is handled by the smart pointer class _bstr_t (part of Visual Studio
comutil), which creates the OLECHAR object, passes a OLECHAR object to the
OnFooEvent (I think it does this with the function "operator wchar_t*()
const throw();") and destroys it automaticly after leaving the stack
context.
All this wizards and C++ code generation are a mess compared to some nice
Lisp macros. I don't know the LispWorks COM infrastructure, but maybe
someone wants to encode all the COM rules to some simple to use macros,
which automates marshalling, object life time etc.? It should be possible,
because if you write a COM component within VB.NET, you don't have to do all
the low level details yourself. And unlike in Common Lisp, where you have to
write anything in clear to read and understandable code, operator
overloading for typedefed variables in C++ is evil :-)