Pass byte array within struct to a com object - c++

I wrote a C++ COM server (out-of-proc) and client so:
idl (interface is IDispatch):
typedef[uuid(0952A366-20CC-4342-B590-2D8920D61613)]
struct MyStruct{
LONG id;
BYTE* data;
} MyStruct;
[helpstring("")] HRESULT foo([out] MyStruct* pStreamInfo);
server:
STDMETHODIMP foo(MyStruct* myStruct)
{
myStruct.id = 7;
myStruct.data = pData; // pData is a pointer to some data of variable length
return S_OK;
}
client:
MyStruct ms;
hr = comObj->foo(&ms);
The code will work fine except when adding the myStruct.data = pData; line which crashes the server. Assigning memory in the client e.g. ms.data = new BYTE[1000] does not help as the pointer still arrives to foo as NULL.
What would be a solution, 1. preferably most simple one for client side since interface will be used by various users 2. Would there by a different solution if interface is used by C# client 3. If data needs to be out of the struct (I hope not) is there a reference to a complete example.

As others have mentioned in the comments, you cannot pass a raw array this way. Minimally, you have to copy the byte array into a SAFEARRAY of bytes (SAFEARRAY(BYTE) in IDL). I just tested the code below with a custom proxy/stub (compiled from the P/S code generated by midl.exe), and I was able to get my data across the wire.
If you want to use a standard P/S such as PSDispatch ({00020420-0000-0000-C000-000000000046}) or PSOAInterface ({00020424-0000-0000-C000-000000000046}), or if you want to use VBA as a client, then you may have to convert this to a SAFEARRAY(VARIANT) and/or put the resulting safearray into a VARIANT. Try the simplest approach of just using SAFEARRAY(BYTE) first, because that's the one with the least overhead. (A SAFEARRAY(VARIANT) uses 16x more memory than a SAFEARRAY(BYTE) because a VARIANT is 16 bytes long. And you would have to manually convert each byte to/from a VARIANT, as opposed to the simple memcpy calls show below.)
Packing the byte array into a SAFEARRAY: (Note that this copies the byte array into the SAFEARRAY. You could muck around with the internals of the SAFEARRAY struct to prevent the copy, but you'd be doing things in a non-standard way.)
/// <summary>Packs an array of bytes into a SAFEARRAY.</summary>
/// <param name="count">The number of bytes.</param>
/// <param name="pData">A reference to the byte array. Not read if count is 0.</param>
/// <param name="pResult">Receives the packed LPSAFEARRAY on success.</param>
HRESULT PackBytes(ULONG count, const BYTE* pData, /*[ref]*/ LPSAFEARRAY* pResult)
{
// initialize output parameters
*pResult = NULL;
// describe the boundaries of the safearray (1 dimension of the specified length, starting at standard index 1)
SAFEARRAYBOUND bound{ count, 1 };
// create the safearray
LPSAFEARRAY safearray = SafeArrayCreate(VT_UI1, 1, &bound);
if (!safearray)
return E_OUTOFMEMORY;
// when there is actually data...
if (count > 0)
{
// begin accessing the safearray data
BYTE* safearrayData;
HRESULT hr = SafeArrayAccessData(safearray, reinterpret_cast<LPVOID*>(&safearrayData));
if (FAILED(hr))
{
SafeArrayDestroy(safearray);
return hr;
}
// copy the data into the safearray
memcpy(safearrayData, pData, count);
// finish accessing the safearray data
hr = SafeArrayUnaccessData(safearray);
if (FAILED(hr))
{
SafeArrayDestroy(safearray);
return hr;
}
}
// set output parameters
*pResult = safearray;
// success
return S_OK;
}
Unpacking the byte array from the SAFEARRAY: (Note that this copies the byte array from the SAFEARRAY. You could muck around with the internals of the SAFEARRAY struct to prevent the copy, but you'd be doing things in a non-standard way. Also, you could choose to use the data directly from the SAFEARRAY by putting the consuming code between SafeArrayAccessData and SafeArrayUnaccessData.)
/// <summary>Unpacks an array of bytes from a SAFEARRAY.</summary>
/// <param name="safearray">The source SAFEARRAY.</param>
/// <param name="pCount">A pointer to a ULONG that will receive the number of bytes.</param>
/// <param name="ppData">A pointer to a BYTE* that will receive a reference to the new byte array.
/// This array must be deallocated (delete []) when the caller is done with it.</param>
HRESULT UnpackBytes(LPSAFEARRAY safearray, /*[out]*/ ULONG* pCount, /*[out]*/ BYTE** ppData)
{
// initialize output parameters
*pCount = 0;
*ppData = NULL;
// validate the safearray element type (must be VT_UI1)
VARTYPE vartype;
HRESULT hr = SafeArrayGetVartype(safearray, &vartype);
if (FAILED(hr))
return hr;
if (vartype != VT_UI1)
return E_INVALIDARG;
// validate the number of dimensions (must be 1)
UINT dim = SafeArrayGetDim(safearray);
if (dim != 1)
return E_INVALIDARG;
// get the lower bound of dimension 1
LONG lBound;
hr = SafeArrayGetLBound(safearray, 1, &lBound);
if (FAILED(hr))
return hr;
// get the upper bound of dimension 1
LONG uBound;
hr = SafeArrayGetUBound(safearray, 1, &uBound);
if (FAILED(hr))
return hr;
// if the upper bound is less than the lower bound, it's an empty array
if (uBound < lBound)
return S_OK;
// calculate the count of the bytes
ULONG count = uBound - lBound + 1;
// create buffer
BYTE* pData = new BYTE[count];
if (!pData)
return E_OUTOFMEMORY;
// begin accessing the safearray data
BYTE* safearrayData;
hr = SafeArrayAccessData(safearray, reinterpret_cast<LPVOID*>(&safearrayData));
if (FAILED(hr))
{
delete[] pData;
return hr;
}
// copy the data
memcpy(pData, safearrayData, count);
// finish accessing the safearray data
hr = SafeArrayUnaccessData(safearray);
if (FAILED(hr))
{
delete[] pData;
return hr;
}
// set output parameters
*pCount = count;
*ppData = pData;
// success
return S_OK;
}

Related

Should I delete vector<short>?

I have the following code to pass the values of a float vector. The vector holds audio data.
However, the receiving object expects a temporary vector, so I convert the floats to shorts, then pass this vector along:
HRESULT CApp::PassAudio(ISpTTSEngineSite * pOutputSite, const SPVTEXTFRAG * uFragList, vector<float>&uFloats,int &uSamplesWritten)
{
vector<short>nShortsFromFloats;
nShortsFromFloats.resize(uFloats.size());
for (int i = 0; i < (int)uFloats.size(); i++)
{
nShortsFromFloats[i] = (int)(uFloats[i] * 32767);
}
//Pass the audio (shorts now!) back to the requesting site
ULONG iSizeShorts = nShortsFromFloats.size();
uSamplesWritten += (iSizeShorts * 2);
HRESULT hr;
hr = pOutputSite->Write((short *)&nShortsFromFloats[0], (iSizeShorts * 2), NULL);
if (hr == S_OK)
{
//fine :-)
}
else
{
//some warning
}
return hr;
}
I would like to know if I need to delete the vector afterwards or if this is done automatically.
I'm not sure which other info is needed. I'm using this code in a DLL.
Thank you.
Nothing is being allocated with new, so there is nothing to delete. The vector will be destructed automatically when it goes out of scope when the function exits.

How to pass string array from VBScript to COM+

I have written a C++ COM which is running as COM+ application.
I am trying to access COM functionality from VBScript (ASP application).
I am able to call a function of COM from VBScript which takes a string. But when I try to call a COM function which takes an array of string, I could get length of array but I could not retrieve elements from that array at COM side.
VBScript (ASP application)
dim myComObj
Set myComObj = Server.CreateObject("ProgId_PerlCOMSimple.1")
Dim myArray(3)
myArray(0) = "Clean Underwear"
myArray(1) = "Vacuum Cleaner"
myArray(2) = "New Computer"
myArray(3) = "Talking Bass"
strDfStatus = myComObj.TestArray1 (myArray)
C++ COM which runs as COM+ application (through dllHost.exe)
STDMETHODIMP CPerlCOMSimple::TestArray1(VARIANT* testArray, LONG* lResult)
{
// TODO: Add your implementation code here
*lResult = testArray->parray->rgsabound->cElements;
BSTR** StrPtr = 0;
//LONG* pVals;
long LowerBound = 0;
long UpperBound = 0;
int i;
SafeArrayGetLBound(testArray->parray, 1, &LowerBound);
SafeArrayGetUBound(testArray->parray, 1, &UpperBound);
SafeArrayAccessData(testArray->parray, (void**)&pVals);
for (i = LowerBound; i <= UpperBound; ++i)
{
BSTR* lVal = StrPtr[i];
lVal++;
}
SafeArrayUnaccessData(testArray->parray);
return S_OK;
}
VBScript will not generate a SAFEARRAY with vartype VT_BSTR, which is what you are expecting. It will have VARTYPE VT_VARIANT.
// check all your parameters
if(testarray == null) return E_INVALIDARG;
if(testarray->vartype != VT_ARRAY|VT_BSTR
&& testarray->vartype != VT_ARRAY|VT_VARIANT)
return E_INVALIDARG;
if(testarray->parray == null) return E_INVALIDARG;
// Now we have established we have an array, and that it
// is either a string array or a variant array.
VARTYPE vt = VT_EMPTY;
SafeArrayGetVarType(testarray->pArray, &vt);
// Now we need to take different actions based on the vartype.
if(vt == VT_BSTR){
// we have an array of strings
// Proceed as above.
}else if(vt == VT_VARIANT){
// we have an array of variants, probably supplied by VBScript
// Read them one by one and use VariantChangeType to get a string
}else{
// We have some other array type we don't support
return E_INVALIDARG;
}

WinRT C++ (Win10) Accessing bytes from SoftwareBitmap / BitmapBuffer

To process my previewFrames of my camera in OpenCV, I need access to the raw Pixel data / bytes. So, there is the new SoftwareBitmap, which should exactly provide this.
There is an example for c#, but in visual c++ I can't get the IMemoryBufferByteAccess (see remarks) Interface working.
Code with Exceptions:
// Capture the preview frame
return create_task(_mediaCapture->GetPreviewFrameAsync(videoFrame))
.then([this](VideoFrame^ currentFrame)
{
// Collect the resulting frame
auto previewFrame = currentFrame->SoftwareBitmap;
auto buffer = previewFrame->LockBuffer(Windows::Graphics::Imaging::BitmapBufferAccessMode::ReadWrite);
auto reference = buffer->CreateReference();
// Get a pointer to the pixel buffer
byte* pData = nullptr;
UINT capacity = 0;
// Obtain ByteAccess
ComPtr<IUnknown> inspectable = reinterpret_cast<IUnknown*>(buffer);
// Query the IBufferByteAccess interface.
Microsoft::WRL::ComPtr<IMemoryBufferByteAccess> bufferByteAccess;
ThrowIfFailed(inspectable.As(&bufferByteAccess)); // ERROR ---> Throws HRESULT = E_NOINTERFACE
// Retrieve the buffer data.
ThrowIfFailed(bufferByteAccess->GetBuffer(_Out_ &pData, _Out_ &capacity)); // ERROR ---> Throws HRESULT = E_NOINTERFACE, because bufferByteAccess is null
I tried this too:
HRESULT hr = ((IMemoryBufferByteAccess*)reference)->GetBuffer(&pData, &capacity);
HRESULT is ok, but I can't access pData -> Access Violation reading Memory.
Thanks for your help.
You should use reference instead of buffer in reinterpret_cast.
#include "pch.h"
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
MIDL_INTERFACE("5b0d3235-4dba-4d44-865e-8f1d0e4fd04d")
IMemoryBufferByteAccess : IUnknown
{
virtual HRESULT STDMETHODCALLTYPE GetBuffer(
BYTE **value,
UINT32 *capacity
);
};
auto previewFrame = currentFrame->SoftwareBitmap;
auto buffer = previewFrame->LockBuffer(BitmapBufferAccessMode::ReadWrite);
auto reference = buffer->CreateReference();
ComPtr<IMemoryBufferByteAccess> bufferByteAccess;
HRESULT result = reinterpret_cast<IInspectable*>(reference)->QueryInterface(IID_PPV_ARGS(&bufferByteAccess));
if (result == S_OK)
{
WriteLine("Get interface successfully");
BYTE* data = nullptr;
UINT32 capacity = 0;
result = bufferByteAccess->GetBuffer(&data, &capacity);
if (result == S_OK)
{
WriteLine("get data access successfully, capacity: " + capacity);
}
}
Based on answer from #jeffrey-chen and example from #kennykerr, I've assembled a tiny bit cleaner solution:
#include <wrl/client.h>
// other includes, as required by your project
MIDL_INTERFACE("5b0d3235-4dba-4d44-865e-8f1d0e4fd04d")
IMemoryBufferByteAccess : ::IUnknown
{
virtual HRESULT __stdcall GetBuffer(BYTE **value, UINT32 *capacity) = 0;
};
// your code:
auto previewFrame = currentFrame->SoftwareBitmap;
auto buffer = previewFrame->LockBuffer(BitmapBufferAccessMode::ReadWrite);
auto bufferByteAccess= buffer->CreateReference().as<IMemoryBufferByteAccess>();
WriteLine("Get interface successfully"); // otherwise - exception is thrown
BYTE* data = nullptr;
UINT32 capacity = 0;
winrt::check_hresult(bufferByteAccess->GetBuffer(&data, &capacity));
WriteLine("get data access successfully, capacity: " + capacity);
I'm currently accessing the raw unsigned char* data from each frame I obtain on a MediaFrameReader::FrameArrived event without using WRL and COM...
Here it is how:
void MainPage::OnFrameArrived(MediaFrameReader ^reader, MediaFrameArrivedEventArgs ^args)
{
MediaFrameReference ^mfr = reader->TryAcquireLatestFrame();
VideoMediaFrame ^vmf = mfr->VideoMediaFrame;
VideoFrame ^vf = vmf->GetVideoFrame();
SoftwareBitmap ^sb = vf->SoftwareBitmap;
Buffer ^buff = ref new Buffer(sb->PixelHeight * sb->PixelWidth * 2);
sb->CopyToBuffer(buff);
DataReader ^dataReader = DataReader::FromBuffer(buffer);
Platform::Array<unsigned char, 1> ^arr = ref new Platform::Array<unsigned char, 1>(buffer->Length);
dataReader->ReadBytes(arr);
// here arr->Data is a pointer to the raw pixel data
}
NOTE: The MediaCapture object needs to be configured with MediaCaptureMemoryPreference::Cpu in order to have a valid SoftwareBitmap
Hope the above helps someone

How to pass an array of doubles from VB6 to VC++..What is wrong with this code?

How to pass an array of doubles from VB6 to VC++..What is wrong with this code?
VB Code: dSelfCdArr is my array of double values
Public Sub FilterDocTypeByPriv(colEventSets As Collection)
Dim lCount As Long
Dim oColItem As Object
Dim objDBEventSetRow As DB_EventSetRow
Dim evYes As Boolean
Dim dSelfCdArr() As Double
For lCount = 1 To colEventSets.Count
Set objDBEventSetRow = colEventSets(lCount)
ReDim Preserve dSelfCdArr(1 To lCount)
dSelfCdArr(lCount) = CDbl(objDBEventSetRow.dSelf_cd)
Next
Call m_dtsAppForm.DocController.HasPrivCreateResultEventCode(m_dUserId, m_dPositionCd, m_dPPRCd, dSelfCdArr)
End Sub
C++ Idl file:
[id(51), helpstring("method HasPrivCreateResultEventCode")] HRESULT HasPrivCreateResultEventCode([in]double dUserId,[in]double dPosCd,[in]double dPPRCd, [in, out] VARIANT* pEventCode);
C++ Code: I get bad pointers in the first line for VARIANT* pEventCode
STDMETHODIMP CDocumentController::HasPrivCreateResultEventCode(double dUserId,double dPosCd,double dPPRCd, VARIANT* pEventCode)
{
HRESULT hr = E_FAIL;
AFX_MANAGE_STATE(AfxGetStaticModuleState())
if (V_VT(pEventCode) == VT_ARRAY | VT_R8)
{
CComSafeArray<double> arrECode;
arrECode.Attach(pEventCode->parray);
double pVals;
int iCount = arrECode.GetCount();
CMap<double,double,bool,bool> mapEventCds;
for(int iIndex = 0; iIndex < iCount; iIndex++)
{
double pVals = arrECode.GetAt(iIndex);
mapEventCds.SetAt(pVals, false);
std::cout << "element " << iIndex << ": value = " << pVals << std::endl;
}
CheckPrivViewResultEventCds(dUserId, dPosCd, dPPRCd, mapEventCds);
//pEventCode->c
double dEventCd(0.0);
bool bPriv(false);
POSITION pos(mapEventCds.GetStartPosition());
INT_PTR nEventCnt(mapEventCds.GetCount());
CComSafeArray<double> pSafeArraypEventCode = NULL;
for(INT_PTR count(0); count < nEventCnt; ++count)
{
mapEventCds.GetNextAssoc(pos, dEventCd, bPriv);
if (bPriv)
{
pSafeArraypEventCode.Add(dEventCd);
}
}
pEventCode->parray = pSafeArraypEventCode.Detach();
// Empty the CMap
mapEventCds.RemoveAll();
}
return S_OK;
}
Your problem is here:
if (V_VT(pEventCode) == VT_ARRAY | VT_R8)
The VB equivalent to that would be:
If V_VT(pEventCode) = VT_ARRAY Or True Then
//Do stuff
End If
The | VT_R8 is evaluating to boolean true because:
1) == takes precedence over |, so the comparison is performed, THEN VT_R8 is evaluated.
And
2) Anything that is non-zero equals "true" in C. Since VT_R8 gets evaluated by itself (and not as part of the comparison), it is always true.
You need to use parentheses so that your statements are evaluated in the order you want.
Here is the answer..I had to destroy the original safearray data, create a new safearray to fill the data from maps and then copy the new safearray data to the original safearray using SafeArrayCopy.. It worked.
STDMETHODIMP CDocumentController::GetEventCodesWithAddDocumentationPriv(double dUserId,double dPosCd,double dPPRCd,SAFEARRAY** pArrayOfEventCode)
{
HRESULT lResult(S_OK); // return code for OLE functions
// checking if it is a one-dimensional array
if ( (*pArrayOfEventCode)->cDims != 1 )
{
MsgWrite(MSG_DEFAULT, eMsgLog_Commit, _T("PVClinDocMiscCom"), eMsgLvl_Error, _T("CDocumentController::GetEventCodesWithAddDocumentationPriv() SafeArray pEventCode is not one dimensional"));
return(E_FAIL);
}
// locking the array before using its elements
lResult=SafeArrayLock(*pArrayOfEventCode);
if (lResult != S_OK)
{
MsgWrite(MSG_DEFAULT, eMsgLog_Commit, _T("PVClinDocMiscCom"), eMsgLvl_Error, _T("CDocumentController::GetEventCodesWithAddDocumentationPriv() SafeArray pEventCode is not locked"));
SafeArrayUnlock(*pArrayOfEventCode);
return(E_FAIL);
}
double *pArrayOfElements; // pointer to the elements of the array
// using the array
pArrayOfElements=(double*) (*pArrayOfEventCode)->pvData;
CMap<double,double,bool,bool> mapEventCds;
// number of elements in the array
long lElements=(*pArrayOfEventCode)->rgsabound[0].cElements;
double lVal(0);
for (long lCount=0; lCount<lElements; lCount++)
{
lVal = pArrayOfElements[lCount];
mapEventCds.SetAt(lVal, false);
}
CheckPrivViewResultEventCds(dUserId, dPosCd, dPPRCd, mapEventCds);
SafeArrayUnlock(*pArrayOfEventCode);
lResult = SafeArrayDestroyData(*pArrayOfEventCode);
if (lResult != S_OK)
{
MsgWrite(MSG_DEFAULT, eMsgLog_Commit, _T("PVClinDocMiscCom"), eMsgLvl_Error, _T("CDocumentController::GetEventCodesWithAddDocumentationPriv() SafeArray could not be destroyed"));
return(E_FAIL);
}
SAFEARRAYBOUND rgsabound[1]; //Create a one dimensional array
rgsabound[0].lLbound = (*pArrayOfEventCode)->rgsabound->lLbound; //Set the lowerbound for the array
rgsabound[0].cElements = (*pArrayOfEventCode)->rgsabound->cElements; //Set the upperbound for the array
//Create a new safearray of double to fill from the mapeventcodes
SAFEARRAY* newArray = SafeArrayCreate(VT_R8, 1, rgsabound);
double dEventCd(0.0);
bool bPriv(false);
//Get the starting index of the SafeArray
long lEventCdIdx = (*pArrayOfEventCode)->rgsabound->lLbound;
POSITION pos(mapEventCds.GetStartPosition());
while(pos != NULL)
{
mapEventCds.GetNextAssoc(pos, dEventCd, bPriv);
if (bPriv)
{
lResult = SafeArrayPutElement(newArray, &lEventCdIdx, &dEventCd);
if (lResult != S_OK)
{
MsgWrite(MSG_DEFAULT, eMsgLog_Commit, _T("PVClinDocMiscCom"), eMsgLvl_Debug, _T("CDocumentController::GetEventCodesWithAddDocumentationPriv() Failed to add element to array"));
}
lEventCdIdx++;
}
}
// Empty the CMap
mapEventCds.RemoveAll();
//Copy the contents from new safearray to the existing safearray
SafeArrayCopy(newArray, pArrayOfEventCode);
//Destroy the new safearray
SafeArrayDestroy(newArray);
// releasing the array
return S_OK;
}

Why I got crash when allocating buffer?

I am developing a driver with Filter. So when I write the SendNetBufferListsComplete function in filter.cpp I got a crash (bluescreen). WinDbug pointed to some buffer allocation. The code is here:
Edited:
sendNetBufferListsComplete(
IN PNET_BUFFER_LIST NetBufferLists,
IN ULONG SendCompleteFlags) {
PNET_BUFFER_LIST pNetBufferList = NetBufferLists;
PNET_BUFFER_LIST pNextNetBufferList = NULL;
while (pNetBufferList)
{
pNextNetBufferList = NET_BUFFER_LIST_NEXT_NBL(pNetBufferList);
NET_BUFFER_LIST_NEXT_NBL(pNetBufferList) = NULL;
PNET_BUFFER_LIST pParentNetBufferList = pNetBufferList->ParentNetBufferList;
if (pParentNetBufferList != NULL)
{
NDIS_STATUS status = NET_BUFFER_LIST_STATUS(pNetBufferList);
NdisFreeNetBufferList(pNetBufferList);
if (NdisInterlockedDecrement(&pParentNetBufferList->ChildRefCount) == 0) {
NET_BUFFER_LIST_STATUS(pParentNetBufferList) = status;
NdisFSendNetBufferListsComplete(m_hFilter, pParentNetBufferList, SendCompleteFlags);
}
}
else
{
if(pNetBufferList != NULL)
{
**---windbug pointed here---****
PVOID pBuffer = *(PVOID*) NET_BUFFER_LIST_CONTEXT_DATA_START(pNetBufferList);
PMDL pMdl = NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(pNetBufferList));
if(pMdl)
NdisFreeMdl(pMdl);
if(pBuffer)
delete[] (UCHAR*) pBuffer;
NdisFreeNetBufferList(pNetBufferList);
}
}
NdisInterlockedDecrement(&m_nSendNetBufferListCount);
pNetBufferList = pNextNetBufferList;
}
What is the actual problem? Is it overflow? Or NULL check problems?
In ndish.h
#define NET_BUFFER_LIST_CONTEXT_DATA_START(_NBL) ((PUCHAR)(((_NBL)->Context)+1)+(_NBL)->Context->Offset)
like this . and in Wdm.h
//
// I/O system definitions.
//
// Define a Memory Descriptor List (MDL)
//
// An MDL describes pages in a virtual buffer in terms of physical pages. The
// pages associated with the buffer are described in an array that is allocated
// just after the MDL header structure itself.
//
typedef
_Struct_size_bytes_(_Inexpressible_(sizeof(struct _MDL) + // 747934
(ByteOffset + ByteCount + PAGE_SIZE-1) / PAGE_SIZE * sizeof(PFN_NUMBER)))
struct _MDL {
struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPROCESS *Process;
PVOID MappedSystemVa; /* see creators for field size annotations. */
PVOID StartVa; /* see creators for validity; could be address 0. */
ULONG ByteCount;
ULONG ByteOffset;
} MDL, *PMDL;