PyObject *PyObject_FromSAFEARRAYRecordInfo(SAFEARRAY *psa) { PyObject *ret = NULL, *ret_tuple = NULL; IRecordInfo *info = NULL; BYTE *source_data = NULL, *this_dest_data = NULL; long lbound, ubound, nelems, i; ULONG cb_elem; PyRecordBuffer *owner = NULL; HRESULT hr = SafeArrayGetRecordInfo(psa, &info); if (FAILED(hr)) goto exit; hr = SafeArrayAccessData(psa, (void **)&source_data); if (FAILED(hr)) goto exit; // Allocate a new chunk of memory hr = SafeArrayGetUBound(psa, 1, &ubound); if (FAILED(hr)) goto exit; hr = SafeArrayGetLBound(psa, 1, &lbound); if (FAILED(hr)) goto exit; nelems = ubound-lbound; hr = info->GetSize(&cb_elem); if (FAILED(hr)) goto exit; owner = new PyRecordBuffer(nelems * cb_elem); if (PyErr_Occurred()) goto exit; owner->AddRef(); // unref'd at end - for successful failure cleanup ret_tuple = PyTuple_New(nelems); if (ret_tuple==NULL) goto exit; this_dest_data = (BYTE *)owner->data; for (i=0;i<nelems;i++) { hr = info->RecordInit(this_dest_data); if (FAILED(hr)) goto exit; hr = info->RecordCopy(source_data, this_dest_data); if (FAILED(hr)) goto exit; PyTuple_SET_ITEM(ret_tuple, i, new PyRecord(info, this_dest_data, owner)); this_dest_data += cb_elem; source_data += cb_elem; } ret = ret_tuple; Py_INCREF(ret); // for decref on cleanup. exit: if (FAILED(hr)) { if (info) PyCom_BuildPyException(hr, info, IID_IRecordInfo); else PyCom_BuildPyException(hr); Py_XDECREF(ret); ret = NULL; } if (owner != NULL) owner->Release(); Py_XDECREF(ret_tuple); if (info) info->Release(); if (source_data!=NULL) SafeArrayUnaccessData(psa); return ret; }
unsigned char * WINAPI LPSAFEARRAY_UserMarshal(unsigned long *pFlags, unsigned char *Buffer, LPSAFEARRAY *ppsa) { HRESULT hr; TRACE("("); dump_user_flags(pFlags); TRACE(", %p, &%p\n", Buffer, *ppsa); ALIGN_POINTER(Buffer, 3); *(ULONG_PTR *)Buffer = *ppsa ? TRUE : FALSE; Buffer += sizeof(ULONG_PTR); if (*ppsa) { VARTYPE vt; SAFEARRAY *psa = *ppsa; ULONG ulCellCount = SAFEARRAY_GetCellCount(psa); wireSAFEARRAY wiresa; SF_TYPE sftype; GUID guid; *(ULONG *)Buffer = psa->cDims; Buffer += sizeof(ULONG); wiresa = (wireSAFEARRAY)Buffer; wiresa->cDims = psa->cDims; wiresa->fFeatures = psa->fFeatures; wiresa->cbElements = psa->cbElements; hr = SafeArrayGetVartype(psa, &vt); if (FAILED(hr)) RpcRaiseException(hr); wiresa->cLocks = (USHORT)psa->cLocks | (vt << 16); Buffer += FIELD_OFFSET(struct _wireSAFEARRAY, uArrayStructs); sftype = SAFEARRAY_GetUnionType(psa); *(ULONG *)Buffer = sftype; Buffer += sizeof(ULONG); *(ULONG *)Buffer = ulCellCount; Buffer += sizeof(ULONG); *(ULONG_PTR *)Buffer = (ULONG_PTR)psa->pvData; Buffer += sizeof(ULONG_PTR); if (sftype == SF_HAVEIID) { SafeArrayGetIID(psa, &guid); memcpy(Buffer, &guid, sizeof(guid)); Buffer += sizeof(guid); } memcpy(Buffer, psa->rgsabound, sizeof(psa->rgsabound[0]) * psa->cDims); Buffer += sizeof(psa->rgsabound[0]) * psa->cDims; *(ULONG *)Buffer = ulCellCount; Buffer += sizeof(ULONG); if (psa->pvData) { switch (sftype) { case SF_BSTR: { BSTR* lpBstr; for (lpBstr = (BSTR*)psa->pvData; ulCellCount; ulCellCount--, lpBstr++) Buffer = BSTR_UserMarshal(pFlags, Buffer, lpBstr); break; } case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: FIXME("marshal interfaces\n"); break; case SF_VARIANT: { VARIANT* lpVariant; for (lpVariant = (VARIANT*)psa->pvData; ulCellCount; ulCellCount--, lpVariant++) Buffer = VARIANT_UserMarshal(pFlags, Buffer, lpVariant); break; } case SF_RECORD: { IRecordInfo* pRecInfo = NULL; hr = SafeArrayGetRecordInfo(psa, &pRecInfo); if (FAILED(hr)) RpcRaiseException(hr); if (pRecInfo) { FIXME("write record info %p\n", pRecInfo); IRecordInfo_Release(pRecInfo); } break; } case SF_I8: ALIGN_POINTER(Buffer, 7); /* fallthrough */ case SF_I1: case SF_I2: case SF_I4: /* Just copy the data over */ memcpy(Buffer, psa->pvData, ulCellCount * psa->cbElements); Buffer += ulCellCount * psa->cbElements; break; default: break; } } } return Buffer; }
unsigned long WINAPI LPSAFEARRAY_UserSize(unsigned long *pFlags, unsigned long StartingSize, LPSAFEARRAY *ppsa) { unsigned long size = StartingSize; TRACE("("); dump_user_flags(pFlags); TRACE(", %ld, %p\n", StartingSize, *ppsa); ALIGN_LENGTH(size, 3); size += sizeof(ULONG_PTR); if (*ppsa) { SAFEARRAY *psa = *ppsa; ULONG ulCellCount = SAFEARRAY_GetCellCount(psa); SF_TYPE sftype; HRESULT hr; size += sizeof(ULONG); size += FIELD_OFFSET(struct _wireSAFEARRAY, uArrayStructs); sftype = SAFEARRAY_GetUnionType(psa); size += sizeof(ULONG); size += sizeof(ULONG); size += sizeof(ULONG_PTR); if (sftype == SF_HAVEIID) size += sizeof(IID); size += sizeof(psa->rgsabound[0]) * psa->cDims; size += sizeof(ULONG); switch (sftype) { case SF_BSTR: { BSTR* lpBstr; for (lpBstr = (BSTR*)psa->pvData; ulCellCount; ulCellCount--, lpBstr++) size = BSTR_UserSize(pFlags, size, lpBstr); break; } case SF_DISPATCH: case SF_UNKNOWN: case SF_HAVEIID: FIXME("size interfaces\n"); break; case SF_VARIANT: { VARIANT* lpVariant; for (lpVariant = (VARIANT*)psa->pvData; ulCellCount; ulCellCount--, lpVariant++) size = VARIANT_UserSize(pFlags, size, lpVariant); break; } case SF_RECORD: { IRecordInfo* pRecInfo = NULL; hr = SafeArrayGetRecordInfo(psa, &pRecInfo); if (FAILED(hr)) RpcRaiseException(hr); if (pRecInfo) { FIXME("size record info %p\n", pRecInfo); IRecordInfo_Release(pRecInfo); } break; } case SF_I8: ALIGN_LENGTH(size, 7); /* fallthrough */ case SF_I1: case SF_I2: case SF_I4: size += ulCellCount * psa->cbElements; break; default: break; } } return size; }
int main() { CoInitializeEx(0, COINIT_MULTITHREADED); HRESULT hr = S_OK; //GUID guid; //hr = CLSIDFromString(L"MyNamespace.MyClass", &guid); //hr = CLSIDFromString(L"{7884998B-0504-4CBE-9FF9-6BBAA9776188}", &guid); //hr = CoCreateInstance(guid, NULL, CLSCTX_ALL, IID_IUnknown, reinterpret_cast<void**>(&p)); NetCom::IMyClassPtr ptr; hr = ptr.CreateInstance(__uuidof(NetCom::Class1)); long l; hr = ptr->GetData(&l); SAFEARRAY *ppData = NULL; hr = ptr->GetStructs(&ppData); if (SUCCEEDED(hr) && ppData) { #pragma region Validate received datatype VARTYPE vt; hr = SafeArrayGetVartype(ppData, &vt); if (SUCCEEDED(hr) && (vt != VT_RECORD)) { //Not a sturcture hr = E_POINTER; } if (SUCCEEDED(hr)) { IRecordInfoPtr spIRecordInfo = NULL; hr = SafeArrayGetRecordInfo(ppData, &spIRecordInfo); if (SUCCEEDED(hr) && spIRecordInfo) { GUID guid; hr = spIRecordInfo->GetGuid(&guid); if (guid != __uuidof(NetCom::TICKSTRUCT)) { //Invalid record type hr = E_POINTER; } } } #pragma endregion NetCom::TICKSTRUCT *pVals = NULL; hr = SafeArrayAccessData(ppData, (void**)&pVals); if (SUCCEEDED(hr)) { long lowerBound, upperBound; // get array bounds SafeArrayGetLBound(ppData, 1, &lowerBound); SafeArrayGetUBound(ppData, 1, &upperBound); long count = upperBound - lowerBound + 1; for (int i = 0; i < count; ++i) // iterate through returned values { printf("[%d] Date: %f Price: %f Volume: %d\n", i, pVals->Date, pVals->Price, pVals->Volume); pVals++; } hr = SafeArrayUnaccessData(ppData); } hr = SafeArrayDestroy(ppData); } //Control safearray lifetime by _variant_t wrapper _variant_t vt; if (SUCCEEDED(ptr->GetStructs(&vt.parray))) { vt.vt = VT_ARRAY | VT_RECORD; SAFEARRAY* ppData2 = vt.parray; //ppData2 can be used in same way as ppData above with exception: //1. Do not call SafeArrayDestroy. array will be destroyed by VariantClear function //2. Make sure ppData2 is not locked (SafeArrayAccessData) before vt.Clear or ~vt will be called. // In this case VariantClear will not able to clean locked data } return 0; }
/************************************************************************* * SafeArrayGetElement (OLEAUT32.25) * * Get an item from a SafeArray. * * PARAMS * psa [I] SafeArray to get from * rgIndices [I] Indices to get from * pvData [O] Destination for data * * RETURNS * Success: S_OK. The item data is returned in pvData. * Failure: An HRESULT error code indicating the error. * * NOTES * See SafeArray. */ HRESULT WINAPI SafeArrayGetElement(SAFEARRAY *psa, LONG *rgIndices, void *pvData) { HRESULT hRet; TRACE("(%p,%p,%p)\n", psa, rgIndices, pvData); if (!psa || !rgIndices || !pvData) return E_INVALIDARG; hRet = SafeArrayLock(psa); if (SUCCEEDED(hRet)) { PVOID lpvSrc; hRet = SafeArrayPtrOfIndex(psa, rgIndices, &lpvSrc); if (SUCCEEDED(hRet)) { if (psa->fFeatures & FADF_VARIANT) { VARIANT* lpVariant = lpvSrc; VARIANT* lpDest = pvData; /* The original content of pvData is ignored. */ V_VT(lpDest) = VT_EMPTY; hRet = VariantCopy(lpDest, lpVariant); if (FAILED(hRet)) FIXME("VariantCopy failed with 0x%x\n", hRet); } else if (psa->fFeatures & FADF_BSTR) { BSTR* lpBstr = lpvSrc; BSTR* lpDest = pvData; if (*lpBstr) { *lpDest = SysAllocStringByteLen((char*)*lpBstr, SysStringByteLen(*lpBstr)); if (!*lpBstr) hRet = E_OUTOFMEMORY; } else *lpDest = NULL; } else if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) { IUnknown **src_unk = lpvSrc; IUnknown **dest_unk = pvData; if (*src_unk) IUnknown_AddRef(*src_unk); *dest_unk = *src_unk; } else if (psa->fFeatures & FADF_RECORD) { IRecordInfo *record; SafeArrayGetRecordInfo(psa, &record); hRet = IRecordInfo_RecordCopy(record, lpvSrc, pvData); IRecordInfo_Release(record); } else /* Copy the data over */ memcpy(pvData, lpvSrc, psa->cbElements); } SafeArrayUnlock(psa); } return hRet; }
/************************************************************************* * SafeArrayPutElement (OLEAUT32.26) * * Put an item into a SafeArray. * * PARAMS * psa [I] SafeArray to insert into * rgIndices [I] Indices to insert at * pvData [I] Data to insert * * RETURNS * Success: S_OK. The item is inserted * Failure: An HRESULT error code indicating the error. * * NOTES * See SafeArray. */ HRESULT WINAPI SafeArrayPutElement(SAFEARRAY *psa, LONG *rgIndices, void *pvData) { HRESULT hRet; TRACE("(%p,%p,%p)\n", psa, rgIndices, pvData); if (!psa || !rgIndices) return E_INVALIDARG; hRet = SafeArrayLock(psa); if (SUCCEEDED(hRet)) { PVOID lpvDest; hRet = SafeArrayPtrOfIndex(psa, rgIndices, &lpvDest); if (SUCCEEDED(hRet)) { if (psa->fFeatures & FADF_VARIANT) { VARIANT* lpVariant = pvData; VARIANT* lpDest = lpvDest; hRet = VariantCopy(lpDest, lpVariant); if (FAILED(hRet)) FIXME("VariantCopy failed with 0x%x\n", hRet); } else if (psa->fFeatures & FADF_BSTR) { BSTR lpBstr = (BSTR)pvData; BSTR* lpDest = lpvDest; SysFreeString(*lpDest); *lpDest = SysAllocStringByteLen((char*)lpBstr, SysStringByteLen(lpBstr)); if (!*lpDest) hRet = E_OUTOFMEMORY; } else if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) { IUnknown *lpUnknown = pvData; IUnknown **lpDest = lpvDest; if (lpUnknown) IUnknown_AddRef(lpUnknown); if (*lpDest) IUnknown_Release(*lpDest); *lpDest = lpUnknown; } else if (psa->fFeatures & FADF_RECORD) { IRecordInfo *record; SafeArrayGetRecordInfo(psa, &record); hRet = IRecordInfo_RecordCopy(record, pvData, lpvDest); IRecordInfo_Release(record); } else /* Copy the data over */ memcpy(lpvDest, pvData, psa->cbElements); } SafeArrayUnlock(psa); } return hRet; }
/* Copy data items from one array to another. Destination data is freed before copy. */ static HRESULT SAFEARRAY_CopyData(SAFEARRAY *psa, SAFEARRAY *dest) { HRESULT hr = S_OK; if (!psa->pvData) return S_OK; if (!dest->pvData || psa->fFeatures & FADF_DATADELETED) return E_INVALIDARG; else { ULONG ulCellCount = SAFEARRAY_GetCellCount(psa); dest->fFeatures = (dest->fFeatures & FADF_CREATEVECTOR) | (psa->fFeatures & ~ignored_copy_features); if (psa->fFeatures & FADF_VARIANT) { VARIANT *src_var = psa->pvData; VARIANT *dest_var = dest->pvData; while(ulCellCount--) { HRESULT hRet; /* destination is cleared automatically */ hRet = VariantCopy(dest_var, src_var); if (FAILED(hRet)) FIXME("VariantCopy failed with 0x%08x, element %u\n", hRet, ulCellCount); src_var++; dest_var++; } } else if (psa->fFeatures & FADF_BSTR) { BSTR *src_bstr = psa->pvData; BSTR *dest_bstr = dest->pvData; while(ulCellCount--) { SysFreeString(*dest_bstr); if (*src_bstr) { *dest_bstr = SysAllocStringByteLen((char*)*src_bstr, SysStringByteLen(*src_bstr)); if (!*dest_bstr) return E_OUTOFMEMORY; } else *dest_bstr = NULL; src_bstr++; dest_bstr++; } } else if (psa->fFeatures & FADF_RECORD) { BYTE *dest_data = dest->pvData; BYTE *src_data = psa->pvData; IRecordInfo *record; SafeArrayGetRecordInfo(psa, &record); while (ulCellCount--) { /* RecordCopy() clears destination record */ hr = IRecordInfo_RecordCopy(record, src_data, dest_data); if (FAILED(hr)) break; src_data += psa->cbElements; dest_data += psa->cbElements; } SafeArraySetRecordInfo(dest, record); /* This value is set to 32 bytes by default on descriptor creation, update with actual structure size. */ dest->cbElements = psa->cbElements; IRecordInfo_Release(record); } else if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) { IUnknown **dest_unk = dest->pvData; IUnknown **src_unk = psa->pvData; /* release old iface, addref new one */ while (ulCellCount--) { if (*dest_unk) IUnknown_Release(*dest_unk); *dest_unk = *src_unk; if (*dest_unk) IUnknown_AddRef(*dest_unk); src_unk++; dest_unk++; } } else { /* Copy the data over */ memcpy(dest->pvData, psa->pvData, ulCellCount * psa->cbElements); } if (psa->fFeatures & FADF_HAVEIID) { GUID guid; SafeArrayGetIID(psa, &guid); SafeArraySetIID(dest, &guid); } else if (psa->fFeatures & FADF_HAVEVARTYPE) { SAFEARRAY_SetHiddenDWORD(dest, SAFEARRAY_GetHiddenDWORD(psa)); } } return hr; }
/* Free data items in an array */ static HRESULT SAFEARRAY_DestroyData(SAFEARRAY *psa, ULONG ulStartCell) { if (psa->pvData && !(psa->fFeatures & FADF_DATADELETED)) { ULONG ulCellCount = SAFEARRAY_GetCellCount(psa); if (ulStartCell > ulCellCount) { FIXME("unexpted ulcellcount %d, start %d\n",ulCellCount,ulStartCell); return E_UNEXPECTED; } ulCellCount -= ulStartCell; if (psa->fFeatures & (FADF_UNKNOWN|FADF_DISPATCH)) { LPUNKNOWN *lpUnknown = (LPUNKNOWN *)psa->pvData + ulStartCell; while(ulCellCount--) { if (*lpUnknown) IUnknown_Release(*lpUnknown); lpUnknown++; } } else if (psa->fFeatures & FADF_RECORD) { IRecordInfo *lpRecInfo; if (SUCCEEDED(SafeArrayGetRecordInfo(psa, &lpRecInfo))) { PBYTE pRecordData = psa->pvData; while(ulCellCount--) { IRecordInfo_RecordClear(lpRecInfo, pRecordData); pRecordData += psa->cbElements; } IRecordInfo_Release(lpRecInfo); } } else if (psa->fFeatures & FADF_BSTR) { BSTR* lpBstr = (BSTR*)psa->pvData + ulStartCell; while(ulCellCount--) { SysFreeString(*lpBstr); lpBstr++; } } else if (psa->fFeatures & FADF_VARIANT) { VARIANT* lpVariant = (VARIANT*)psa->pvData + ulStartCell; while(ulCellCount--) { HRESULT hRet = VariantClear(lpVariant); if (FAILED(hRet)) FIXME("VariantClear of element failed!\n"); lpVariant++; } } } return S_OK; }
PyObject *PyRecord::getattro(PyObject *self, PyObject *obname) { PyObject *res; PyRecord *pyrec = (PyRecord *)self; char *name=PYWIN_ATTR_CONVERT(obname); if (name==NULL) return NULL; if (strcmp(name, "__members__")==0) { ULONG cnames = 0; HRESULT hr = pyrec->pri->GetFieldNames(&cnames, NULL); if (FAILED(hr)) return PyCom_BuildPyException(hr, pyrec->pri, IID_IRecordInfo); BSTR *strs = (BSTR *)malloc(sizeof(BSTR) * cnames); if (strs==NULL) return PyErr_NoMemory(); hr = pyrec->pri->GetFieldNames(&cnames, strs); if (FAILED(hr)) { free(strs); return PyCom_BuildPyException(hr, pyrec->pri, IID_IRecordInfo); } res = PyList_New(cnames); for (ULONG i=0;i<cnames && res != NULL;i++) { PyObject *item = PyWinCoreString_FromString(strs[i]); SysFreeString(strs[i]); if (item==NULL) { Py_DECREF(res); res = NULL; } else PyList_SET_ITEM(res, i, item); // ref count swallowed. } free(strs); return res; } res = PyObject_GenericGetAttr(self, obname); if (res != NULL) return res; PyErr_Clear(); WCHAR *wname; if (!PyWinObject_AsWCHAR(obname, &wname)) return NULL; VARIANT vret; VariantInit(&vret); void *sub_data = NULL; PY_INTERFACE_PRECALL; HRESULT hr = pyrec->pri->GetFieldNoCopy(pyrec->pdata, wname, &vret, &sub_data); PyWinObject_FreeWCHAR(wname); PY_INTERFACE_POSTCALL; if (FAILED(hr)) { if (hr == TYPE_E_FIELDNOTFOUND){ // This is slightly suspect - throwing a unicode // object for an AttributeError in py2k - but this // is the value we asked COM for, so it makes sense... // (and PyErr_Format doesn't handle unicode in py2x) PyErr_SetObject(PyExc_AttributeError, obname); return NULL; } return PyCom_BuildPyException(hr, pyrec->pri, IID_IRecordInfo); } // Short-circuit sub-structs and arrays here, so we dont allocate a new chunk // of memory and copy it - we need sub-structs to persist. if (V_VT(&vret)==(VT_BYREF | VT_RECORD)) return new PyRecord(V_RECORDINFO(&vret), V_RECORD(&vret), pyrec->owner); else if (V_VT(&vret)==(VT_BYREF | VT_ARRAY | VT_RECORD)) { SAFEARRAY *psa = *V_ARRAYREF(&vret); int d = SafeArrayGetDim(psa); if (sub_data==NULL) return PyErr_Format(PyExc_RuntimeError, "Did not get a buffer for the array!"); if (SafeArrayGetDim(psa) != 1) return PyErr_Format(PyExc_TypeError, "Only support single dimensional arrays of records"); IRecordInfo *sub = NULL; long ubound, lbound, nelems; int i; BYTE *this_data; PyObject *ret_tuple = NULL; ULONG element_size = 0; hr = SafeArrayGetUBound(psa, 1, &ubound); if (FAILED(hr)) goto array_end; hr = SafeArrayGetLBound(psa, 1, &lbound); if (FAILED(hr)) goto array_end; hr = SafeArrayGetRecordInfo(psa, &sub); if (FAILED(hr)) goto array_end; hr = sub->GetSize(&element_size); if (FAILED(hr)) goto array_end; nelems = ubound-lbound; ret_tuple = PyTuple_New(nelems); if (ret_tuple==NULL) goto array_end; this_data = (BYTE *)sub_data; for (i=0;i<nelems;i++) { PyTuple_SET_ITEM(ret_tuple, i, new PyRecord(sub, this_data, pyrec->owner)); this_data += element_size; } array_end: if (sub) sub->Release(); if (FAILED(hr)) return PyCom_BuildPyException(hr, pyrec->pri, IID_IRecordInfo); return ret_tuple; } // This default conversion we use is a little slow (but it will do!) // For arrays, the pparray->pvData member is *not* set, since the actual data // pointer from the record is returned in sub_data, so set it here. if (V_ISARRAY(&vret) && V_ISBYREF(&vret)) (*V_ARRAYREF(&vret))->pvData = sub_data; PyObject *ret = PyCom_PyObjectFromVariant(&vret); // VariantClear(&vret); return ret; }