c++ WINAPI Shared Memory array of structs - c++

I'm trying to share an array of structs through shared named memory using the WINAPI. I'm able to create and manage the shared memory but when trying to share an array of structs the size of the array is always 0 upon reading.
Below is test code i have written which should write/read an array of 10 entries, but even this is failing. My goal is however to write/read a dynamic array of structs containing 2 dynamic arrays and the info they already contain at the moment.
I'm aware i shouldn't share pointers between processes as they could point to a random value. Therefor i'm allocating memory for the arrays using new.
This is what i have so far:
Shared in both processes:
#define MEMSIZE 90024
typedef struct {
int id;
int type;
int count;
} Entry;
Process 1:
extern HANDLE hMapObject;
extern void* vMapData;
std::vector<Entry> entries;//collection of entries
BOOL DumpEntries(TCHAR* memName) {//Returns true, writing 10 entries
int size = min(10, entries.size());
Entry* eArray = new Entry[size];
for (int i = 0; i < size; i++) {
eArray[i] = entries.at(i);
}
::hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMSIZE, memName);
if (::hMapObject == NULL) {
return FALSE;
}
::vMapData = MapViewOfFile(::hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, MEMSIZE);
if (::vMapData == NULL) {
CloseHandle(::hMapObject);
return FALSE;
}
CopyMemory(::vMapData, eArray, (size * sizeof(Entry)));
UnmapViewOfFile(::vMapData);
//delete[] eArray;
return TRUE;
}
Process 2:
BOOL ReadEntries(TCHAR* memName, Entry* entries) {//Returns true reading 0 entries
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (hMapFile == NULL) {
return FALSE;
}
Entry* tmpEntries = (Entry*)(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 10 * sizeof(Entry)));
if (tmpEntries == NULL) {
CloseHandle(hMapFile);
return FALSE;
}
entries = new Entry[10];
for (int i = 0; i < 10; i++) {
entries[i] = tmpEntries[i];
}
UnmapViewOfFile(tmpEntries);
CloseHandle(hMapFile);
return TRUE;
}
Writing the 10 entries seems to be working but when trying to read the memory it returns successfully and the size
of the array is 0, like so:
Entry* entries = NULL;
if (ReadEntries(TEXT("Global\Entries"), entries)) {
int size = _ARRAYSIZE(entries);
out = "Succesfully read: " + to_string(size);// Is always 0
}
So my question is, what am I doing wrong? I'm sharing the same struct between 2 processes, i'm allocating new memory for the entries to be written to and copying the memory with a size of 10 * sizeof(Entry);. When trying to read I also try to read 10 * sizeof(Entry); bytes and cast the data to a Entry*. Is there something I'm missing? All help is welcome.

Based on cursory examination, this code appears to attempt to map structures containing std::strings into shared memory, to be used by another process.
Unfortunately, this adventure is doomed, before it even gets started. Even if you get the array length to pass along correctly, I expect the other process to crash immediately, as soon as it even smells the std::string that the other process attempted to map into shared memory segments.
std::strings are non-trivial classes. A std::string maintains internal pointers to a buffer where the actual string data is kept; with the buffer getting allocated on the heap.
You do understand that sizeof(std::string) doesn't change, whether the string contains five characters, or the entire contents of "War And Peace", right? Stop and think for a moment, how that's possible, in just a few bytes that it takes to store a std::string?
Once you think about it for a moment, it should become crystal clear why mapping one process's std::strings into a shared memory segment, and then attempting to grab them by another process, is not going to work.
The only thing that can be practically mapped to/from shared memory is plain old data; although you could get away with aggregates, in some cases, too.

I'm afraid the problem only lies in the _ARRAYSIZE macro. I could not really find it in MSDN, but I found references for _countof or ARRAYSIZE in other pages. All are defined as sizeof(array)/sizeof(array[0]). The problem is that it only make sense for true arrays defined as Entry entries[10], but not for a pointer to such an array. Technically when you declare:
Entry* entries;
sizeof(entries) is sizeof(Entry *) that is the size of a pointer. It is smaller than the size of the struct so the result of the integer division is... 0!
Anyway, there are other problems in current code. The correct way to exchange a variable size array through shared memory is to use an ancillary structure containing a size and the array itself declared as incomplete:
struct EntryArray {
size_t size;
Entry entries[];
};
You could dump it that way:
BOOL DumpEntries(TCHAR* memName) {//Returns true, writing 10 entries
int size = min(10, entries.size());
EntryArray* eArray = (EntryArray *) malloc(sizeof(EntryArray) + size * sizeof(Entry));
for (int i = 0; i < size; i++) {
eArray->entries[i] = entries.at(i);
}
eArray->size = size;
::hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMSIZE, memName);
if (::hMapObject == NULL) {
return FALSE;
}
::vMapData = MapViewOfFile(::hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, MEMSIZE);
if (::vMapData == NULL) {
CloseHandle(::hMapObject);
return FALSE;
}
CopyMemory(::vMapData, eArray, (sizeof(EntryArray) + size * sizeof(Entry)));
UnmapViewOfFile(::vMapData);
free(eArray);
return TRUE;
}
You can note that as the last member of the struct is an incomplete array, it is allocated 0 size, so you must allocate the size of the struct + the size of the array.
You can then read it from memory that way:
size_t ReadEntries(TCHAR* memName, Entry*& entries) {//Returns the number of entries or -1 if error
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (hMapFile == NULL) {
return -1;
}
EntryArray* eArray = (EntryArray*)(MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 10 * sizeof(Entry)));
if (eArray == NULL) {
CloseHandle(hMapFile);
return -1;
}
entries = new Entry[10]; // or even entries = new Entry[eArray->size];
for (int i = 0; i < 10; i++) { // same: i<eArray->size ...
entries[i] = eArray->entries[i];
}
UnmapViewOfFile(eArray);
CloseHandle(hMapFile);
return eArray.size;
}
But here again you should note some differences. As the number of entries is lost when eArray vanishes, it is passed as the return value from the function. And and you want to modify the pointer passed as second parameter, you must pass it by reference (if you pass it by value, you will only change a local copy and still have NULL in original variable after function returns).
There are still some possible improvement in your code, because the vector entries is global when it could be passed as a parameter to DumpEntries, and hMapObject is also global when it could be returned by the function. And in DumpObject you could avoid a copy by building directly the EntryArray in shared memory:
HANDLE DumpEntries(TCHAR* memName, const std::vector<Entry>& entries) {
//Returns HANDLE to mapped file (or NULL), writing 10 entries
int size = min(10, entries.size());
HANDLE hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, MEMSIZE, memName);
if (hMapObject == NULL) {
return NULL;
}
void * vMapData = MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, MEMSIZE);
if (vMapData == NULL) {
CloseHandle(hMapObject);
return NULL;
}
EntryArray* eArray = (EntryArray*) vMapData;
for (int i = 0; i < size; i++) {
eArray->entries[i] = entries.at(i);
}
eArray->size = size;
UnmapViewOfFile(vMapData);
return hMapObject;
}
And last but not least, the backslash \ is a special quoting character in a string litteral, and it must quote itself. So you should write .TEXT("Global\\Entries")

I did it some changes to your code:
PROCESS 1:
BOOL DumpEntries(TCHAR* memName)
{
int size = entries.size() * sizeof(Entry) + sizeof(DWORD);
::hMapObject = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, size, memName);
if (::hMapObject == NULL) {
return FALSE;
}
::vMapData = MapViewOfFile(::hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, size);
if (::vMapData == NULL) {
CloseHandle(::hMapObject);
return FALSE;
}
(*(DWORD*)::vMapData) = entries.size();
Entry* eArray = (Entry*)(((DWORD*)::vMapData) + 1);
for(int i = entries.size() - 1; i >= 0; i--) eArray[i] = entries.at(i);
UnmapViewOfFile(::vMapData);
return TRUE;
}
PROCESS 2:
BOOL ReadEntries(TCHAR* memName, Entry** entries, DWORD &number_of_entries) {
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, memName);
if (hMapFile == NULL) {
return FALSE;
}
DWORD *num_entries = (DWORD*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (num_entries == NULL) {
CloseHandle(hMapFile);
return FALSE;
}
number_of_entries = *num_entries;
if(number_of_entries == 0)
{
// special case: when no entries was found in buffer
*entries = NULL;
return true;
}
Entry* tmpEntries = (Entry*)(num_entries + 1);
*entries = new Entry[*num_entries];
for (UINT i = 0; i < *num_entries; i++) {
(*entries)[i] = tmpEntries[i];
}
UnmapViewOfFile(num_entries);
CloseHandle(hMapFile);
return TRUE;
}
PROCESS 2 (usage example):
void main()
{
Entry* entries;
DWORD number_of_entries;
if(ReadEntries(TEXT("Global\\Entries", &entries, number_of_entries) && number_of_entries > 0)
{
// do something
}
delete entries;
}
CHANGES:
I am not using a static size (MEMSIZE) when i map memory, i am calculating exactly memory requiered
I put a "header" to memory mapped, a DWORD for send to process 2 number of entries in buffer
your ReadEntries definition is wrong, i fix it changing Entry* to Entry**
NOTES:
you need to close ::hMapObject handle in process 1 before process 2 calls ReadEntries
you need to delete entries memory returned for ReadEntries in process 2, before you use it
this code works only under same windows user, if you want to communicate a services with user process (for example), you need to handle SECURITY_ATTRIBUTES member in CreateFileMapping procedure

Related

How to solve mesh corruption with staging buffer on Vulkan Api

I am found a bug in my code, that cause mesh data corruption in certain situation using staging buffer. I have:
temporary mesh data
staging buffer with certain size, that used simultaneously by command buffer and memcpy, but not same segment at a time.
Buffer allocator, that gives part of suitable vertex-index buffer, where mesh data transfers from staging by vkCmdCopyBuffer. Buffer contains many of segments, given for different meshes.
The issue that when I am using staging buffer simultaneously by command buffer and memcpy, mesh data writes incorrectly (become overwritten/corrupted) and even badly can cause VK_ERROR_DEVICE_LOST .
https://imgur.com/8p53SUW "correct mesh"
https://imgur.com/plJ8V0v "broken mesh"
[[nodiscard]] static Result writeMeshBuffer(TransferData &data, GpuMesh &buffer)
{
Result result; using namespace vkw;
auto &mesh = buffer.source;
size_t vSize = mesh.vertices_count * mesh.vertex_size;
size_t iSize = mesh.indices_count * mesh.index_size;
size_t mesh_size = vSize + iSize;
auto &staging_offset = data.stagingData.buffer_offset_unused;
// write data to staging buffer
{
// guaranteed that mesh_size will less or equal than staging buffer size
//FIXME false condition generate broken meshes somehow
bool is_wait_before = mesh_size > TransferStagingData::BUFFER_SIZE - staging_offset;
//will work correctly:
//bool is_wait_before = true;
if (is_wait_before) // if we need more memory on staging buffer than not used already
{
result = data.wait_transfer();
if (result != VK_SUCCESS)
return result;
staging_offset = 0;
}
uint8_t *pMemory = static_cast<uint8_t*>(data.stagingData.pMemory) + staging_offset;
memcpy(pMemory, mesh.vertices.pX, vSize);
memcpy(pMemory + vSize, mesh.indices.pXX, iSize);
if (not is_wait_before)
{
result = data.wait_transfer();
if (result != VK_SUCCESS)
return result;
}
}
// write data from staging buffer to mesh buffer
{
auto cmd_cpy_buff = [](CommandBuffer cmd, BufferCopy copy, Offsets offsets, DeviceSizeT size)
{
cmd.cmd_copy_buffer(copy, offsets, size);
};
// SRC DST
BufferCopy copy = { data.stagingData.buffer, buffer.info.buffer };
Offsets offsets = { staging_offset, buffer.info.region.offset };
result = data.transfer.prepare(cmd_cpy_buff, data.transfer.cmd_buffer, copy, offsets, mesh_size);
if (result != VK_SUCCESS)
return result;
data.reset_fence();
result = data.transfer.submit({&data.transfer.cmd_buffer,1},{}, {}, {}, data.transferFence);
if (result != VK_SUCCESS)
return result;
}
// save usused offset to data.stagingData.buffer_offset_unused;
staging_offset = staging_offset == 0 ? mesh_size : 0;
return result;
}
If I can't use staging buffer like this, than why.
If i have an error, idk where.
The issue was
staging_offset = staging_offset == 0 ? mesh_size : 0;
Need to change
staging_offset = staging_offset == 0 ? TransferStagingData::BUFFER_SIZE - mesh_size : 0;
And after change all works correctly.

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.

Storing std::map iteration object as itemdata in CGridCtrl

I have been trying to decide how to convey my problem.
I have some maps:
typedef struct tagDemoEntryAssign
{
COleDateTime datMeeting;
CString strAssignment;
int iAssignmentType; // AJT v16.0.9
int iStudyPoint; // AJT v16.0.3
int iNextStudyPoint; // AJT v16.0.9
} S_DEMO_ENTRY_ASSIGN;
typedef std::vector<S_DEMO_ENTRY_ASSIGN> PublisherAssignments;
typedef struct tagDemoEntryEx
{
CString strName;
E_GENDER eGender;
E_POSITION ePosition;
E_APPOINTED eAppointed;
BOOL bDemonstrations; // AJT v16.0.3
PublisherAssignments vectorItemAssign; // Sorted array of S_DEMO_ENTRY_ASSIGN structures.
} S_DEMO_ENTRY_EX;
typedef std::map<CString, S_DEMO_ENTRY_EX> PublisherMap;
typedef std::map<CString, S_DEMO_ENTRY_EX>::iterator PublisherMapIter;
I end up having some data, and filling in a CGridCtrl. I try to set the item data for the cells. Specifically:
m_Grid.SetItemData(iRowCount - 1, DEMO_COLUMN_NAME, (LPARAM)&iter->second);
When I try to access the LPARAM data is it not valid.
Why?
Update
I think the problem is related to this line of code:
S_DEMO_ENTRY_ASSIGN sAssign = iter->second.vectorItemAssign.back();
I assign it as item data:
m_Grid.SetItemData(iRowCount - 1, DEMO_COLUMN_LAST_USED, (LPARAM)&sAssign);
It subsequently gets used here:
int CALLBACK CDemoPickerDlg::pfnCellCompareDate(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
CGridCellBase* pCell1 = (CGridCellBase*)lParam1;
CGridCellBase* pCell2 = (CGridCellBase*)lParam2;
if (!pCell1 || !pCell2) return 0;
S_DEMO_ENTRY_ASSIGN *psItem1 = (S_DEMO_ENTRY_ASSIGN*)pCell1->GetData();
S_DEMO_ENTRY_ASSIGN *psItem2 = (S_DEMO_ENTRY_ASSIGN*)pCell2->GetData();
// If a name has never been used the structure pointer will be null.
if (psItem1 == NULL && psItem2 == NULL)
return 0;
else if (psItem1 == NULL)
return -1;
else if (psItem2 == NULL)
return 1;
else if (psItem1->datMeeting < psItem2->datMeeting)
return -1;
else if (psItem1->datMeeting == psItem2->datMeeting)
return 0;
else
return 1;
}
I think that this is wrong:
S_DEMO_ENTRY_ASSIGN sAssign = iter->second.vectorItemAssign.back();
If I understand the mechanics right, the above is making a copy of the structure. Then I am assigning the pointer of this copy. And the copy goes out of scope in the iteration loop of the map.
I need to store a pointer to the actual iter->second.vectorItemAssign.back() object instead I think.
I had to change one line of code to:
m_Grid.SetItemData(iRowCount - 1, DEMO_COLUMN_LAST_USED, (LPARAM)&iter->second.vectorItemAssign.back());

Getting digital signature from mmc.exe at windows 8

I have an application that tries to verify the mmc.exe (services) signature. (the context of the application I think is irrelevant) I am trying with winapi function which both fails with
WinVerifyTrust. I get TRUST_E_BAD_DIGEST when I am trying with verification from catalog, and
TRUST_E_NOSIGNATURE when trying from file info. it is very important to mention that my function succeeds on win7, XP but fails on win8.
this is the code snippet for the function
CATALOG_INFO InfoStruct = {0};
InfoStruct.cbStruct = sizeof(CATALOG_INFO);
WINTRUST_CATALOG_INFO WintrustCatalogStructure = {0};
WintrustCatalogStructure.cbStruct = sizeof(WINTRUST_CATALOG_INFO);
WINTRUST_FILE_INFO WintrustFileStructure = {0};
WintrustFileStructure.cbStruct = sizeof(WINTRUST_FILE_INFO);
GUID ActionGuid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
//Get a context for signature verification.
HCATADMIN Context = NULL;
if(!::CryptCATAdminAcquireContext(&Context, NULL, 0) ){
return false;
}
//Open file.
cx_handle hFile(::CreateFileW(filename_.c_str(), GENERIC_READ, 7, NULL, OPEN_EXISTING, 0, NULL));
if( INVALID_HANDLE_VALUE == (HANDLE)hFile )
{
CryptCATAdminReleaseContext(Context, 0);
return false;
}
//Get the size we need for our hash.
DWORD HashSize = 0;
::CryptCATAdminCalcHashFromFileHandle(hFile, &HashSize, NULL, 0);
if( HashSize == 0 )
{
//0-sized has means error!
::CryptCATAdminReleaseContext(Context, 0);
return false;
}
//Allocate memory.
buffer hashbuf(HashSize);
//Actually calculate the hash
if( !CryptCATAdminCalcHashFromFileHandle(hFile, &HashSize, hashbuf.data, 0) )
{
CryptCATAdminReleaseContext(Context, 0);
return false;
}
//Convert the hash to a string.
buffer MemberTag(((HashSize * 2) + 1) * sizeof(wchar_t));
for( unsigned int i = 0; i < HashSize; i++ ){
swprintf(&((PWCHAR)MemberTag.data)[i * 2], L"%02X", hashbuf.data[i ]);
}
//Get catalog for our context.
HCATINFO CatalogContext = CryptCATAdminEnumCatalogFromHash(Context, hashbuf, HashSize, 0, NULL);
if ( CatalogContext )
{
//If we couldn't get information
if ( !CryptCATCatalogInfoFromContext(CatalogContext, &InfoStruct, 0) )
{
//Release the context and set the context to null so it gets picked up below.
CryptCATAdminReleaseCatalogContext(Context, CatalogContext, 0);
CatalogContext = NULL;
}
}
//If we have a valid context, we got our info.
//Otherwise, we attempt to verify the internal signature.
WINTRUST_DATA WintrustStructure = {0};
WintrustStructure.cbStruct = sizeof(WINTRUST_DATA);
if( !CatalogContext )
{
load_signature_verification_from_file_info(WintrustFileStructure, WintrustStructure);
}
else
{
load_signature_verification_from_catalog(WintrustStructure, WintrustCatalogStructure, InfoStruct, MemberTag);
}
//Call our verification function.
long verification_res = ::WinVerifyTrust(0, &ActionGuid, &WintrustStructure);
//Check return.
bool is_success = SUCCEEDED(verification_res) ? true : false;
// if failed with CatalogContext, try with FILE_INFO
if(!is_success && CatalogContext && verification_res != TRUST_E_NOSIGNATURE)
{
//warning2(L"Failed verification with Catalog Context: 0x%x %s ; Retrying with FILE_INFO.", verification_res, (const wchar_t*)format_last_error(verification_res));
load_signature_verification_from_file_info(WintrustFileStructure, WintrustStructure);
verification_res = ::WinVerifyTrust(0, &ActionGuid, &WintrustStructure);
is_success = SUCCEEDED(verification_res) ? true : false;
}
if(perr && !is_success && verification_res != TRUST_E_NOSIGNATURE)
{
perr->code = verification_res;
perr->description = format_last_error(verification_res);
}
//Free context.
if( CatalogContext ){
::CryptCATAdminReleaseCatalogContext(Context, CatalogContext, 0);
}
//If we successfully verified, we need to free.
if( is_success )
{
WintrustStructure.dwStateAction = WTD_STATEACTION_CLOSE;
::WinVerifyTrust(0, &ActionGuid, &WintrustStructure);
}
::CryptCATAdminReleaseContext(Context, 0);
return is_success;
I don't think any thing had changed in this function from win7 to win 8 so what could possibly go wrong?
UPDATE
I did notice that my function does work for task manager at win 8.
but again for the mmc it does not work.
It appears that your general approach is correct and the functions themselves haven't changed. However there are subtle changes; namely the data on which they operate has changed. The hashes stored for files on Windows 8, according to comments on CryptCATAdminCalcHashFromFileHandle, are calculated using SHA-256 hashes.
The SHA-256 hashing algorithm is not supported by CryptCATAdminCalcHashFromFileHandle, so you must update the code to use CryptCATAdminAcquireContext2 and CryptCATAdminCalcHashFromFileHandle2 on Windows 8; the former allows you to acquire a HCATADMIN with a specified hash algorithm, and the latter allows using that HCATADMIN.
(Interestingly, WINTRUST_CATALOG_INFO also points this direction with its HCATADMIN hCatAdmin member, documented as "Windows 8 and Windows Server 2012: Support for this member begins.")

fFeatures needs to equal 2194?

Currently, I am trying hard to get a safearray of variants to work in my c++ code below. As you can see I call QueueInputReport which has a signature of (SAFEARRAY * psainputreport, UNIT timeoutduration):
CComSafeArray<VARIANT> savt;
//LONG j[5];
LONG length = 4;
//SafeArrayLock(psaValues);
for(LONG i = 0; i <= length; ++i)
{
//j[i] = i;
MessageBox(NULL,L"inputreport assigned to variable",NULL,NULL);
//VariantInit(&pDescriptorData[i]);
//pDescriptorData[i].vt = VT_UI1;
//pDescriptorData[i].bVal = inputreport[i];
//SafeArrayPutElement(psaValues,&i,&pDescriptorData[i]);
// VariantClear(&pDescriptorData[i]);
savt.Add(CComVariant(inputreport[i]));
}
//SafeArrayUnlock(psaValues);
MessageBox(NULL,L"data is successfully assigned to safearray",L"correct data format",NULL);
//FADF_STATIC+FADF_FIXEDSIZE+FADF_HAVEVARTYPE+FADF_VARIANT;
/* _TCHAR szBuffer2[100];
_stprintf_s(szBuffer2, _T("%i"),&psaValues->fFeatures);
MessageBox(NULL,L"safe array type",szBuffer2,NULL);*/
piSoftHidDevice1[devindex]->QueueInputReport(savt,8);
piSoftHidDevice1[devindex]->StartProcessing();
piSoftHidDevice1[devindex]->StopProcessing();
Edit: below is the code for queueinputreport which I needed to pass data to.
STDMETHODIMP CHIDDevice::QueueInputReport( SAFEARRAY* psaInputReport, UINT timeoutDuration )
/*++
Routine Description: Queues additional input reports
Arguments:
IdleTimeout - used to set the value of the log level
Return value:
S_OK
--*/
{
VARIANT * pArrayData = NULL;
UINT cbData = 5;
LONG lLBound = 0;
LONG lUBound = 0;
HRESULT hr = S_OK;
HID_INPUT_REPORT inputReport;
BOOLEAN result = TRUE;
// Initialize Structure
inputReport.timeout = timeoutDuration;
inputReport.cbInputReport = 0;
inputReport.pbInputReport = NULL;
MessageBox(NULL,L"report initialized",L"initial report",NULL);
// Get SAFEARRAY size
IfFailHrGo(SafeArrayGetLBound(psaInputReport, 1, &lLBound));
IfFailHrGo(SafeArrayGetUBound(psaInputReport, 1, &lUBound));
IfFalseHrGo(lUBound > lLBound, E_UNEXPECTED);
cbData = lUBound - lLBound + 1;
//psaInputReport->fFeatures = 0x892;
//psaInputReport->fFeatures = 0x892;
inputReport.pbInputReport = (BYTE*)CoTaskMemAlloc( cbData * sizeof(BYTE) );
_TCHAR szBuffer3[100];
_stprintf_s(szBuffer3, _T("%i"), &psaInputReport->fFeatures);
MessageBox(NULL,L"array content features",szBuffer3,NULL);
// If the memory Allocation fails, then fail and exit
if( inputReport.pbInputReport == NULL )
{
hr = E_UNEXPECTED;
goto Exit;
}
//void HUGEP** cast orginally
IfFailHrGo( SafeArrayAccessData( psaInputReport,
reinterpret_cast<void HUGEP**>(&pArrayData) ) );
// Step through the SAFEARRAY and populating only BYTE input report
// and bail out if the type is not correct
for( LONG i = 0; i <= lUBound; ++i )
{
if (pArrayData[i].vt == VT_UI1)
{
inputReport.pbInputReport[i] = pArrayData[i].bVal;
}
else
{
hr = E_FAIL;
goto Exit;
}
}
SafeArrayUnaccessData(psaInputReport);
inputReport.cbInputReport = cbData;
//MessageBox(NULL,L"report being sent next",L"sent report",NULL);
// Add the report to the input queue (does a shallow copy so no need to free the array data)
result = m_InputReportQueue.insert( inputReport );
if (result == FALSE)
{
MessageBox(NULL,L"failed to queue the input",NULL,NULL);
hr = E_FAIL;
}
return hr;
Exit:
SafeArrayUnaccessData(psaInputReport);
if( FAILED(hr) )
{
CoTaskMemFree(inputReport.pbInputReport);
}
return hr;
}
Edit: The problem is I need the fFeatures to equal 2194 and it is currently its a very high number. What could I be doing wrong?
In vbscript, I have some working code for the queueinputreport:
........(too numerous code to list here but it represents the device I am sending input to (i.e. device #1,#2,#3))
Here's some more information on the fFeatures Iam talking about:
http://msdn.microsoft.com/en-us/library/cc237824.aspx
Dim inputreport(5)
inputreport(0) = CByte(0)
inputreport(1) = CByte(100)
inputreport(2) = CByte(100)
inputreport(3) = CByte(0)
inputreport(4) = Cbyte(0)
pisofthiddevice1(i).QueueInputReport(inputreport, 8)
However, when I try replicating this in C++ above it does not work.