Passing pointer array as void pointer to new thread in c++ - c++

I am currently working on a project where I have to build a shell for a C++ dll, so a new C# GUI can use its functions.
However I got the following problem, in the C++ portion I have to create a new thread for specific reasons, and I want to pass an int array to the new thread.
Note that the values assigned to the array in the function in which this happens are gained from the C# portion of the code.
__declspec( dllexport ) void CreateReportPane(int &id, int &what)
{
DWORD threadId;
int iArray[2] = { id, what};
HANDLE hThread = CreateThread( NULL, 0, CreateReportPaneThread, iArray, 0, &threadId);
if (hThread == NULL)
{
ExitProcess(3);
}
}
The problem arises in the new thread, I can reliably fetch the first value out of the array, but the 2nd value seems to be released, here is the code on the other side.
DWORD WINAPI CreateReportPaneThread(LPVOID lparam)
{
int id, what;
id = *(( int * )lparam);
what = *(((int *)lparam)+1) ;
CreateReportPaneOriginal(id, what);
return 0;
}
Is there any way to prevent the values in the array from getting released while not holding the original thread captive?
A big thank you in advance

int iArray[2] = { id, what};
HANDLE hThread = CreateThread(...,CreateReportPaneThread, iArray, ...);
The problem is that iArray is a local array which means this gets destroyed when the function CreateReportPane() returns. So what CreateReportPaneThread() refers to doesn't exist. You get the first value just by chance. There is no such guarantee that you would get even the first value.
Use dynamic array:
int * iArray = new int[2];
iArray[0] = id;
iArray[1] = what;
HANDLE hThread = CreateThread(...,CreateReportPaneThread, iArray, ...);
Remember to write deallocate the array once you're done with it in CreateReportPaneThread:
DWORD WINAPI CreateReportPaneThread(PVOID *data)
{
int *array = static_cast<int*>(data);
int id = array[0], what = array[1];
delete []array; //MUST DO IT to avoid memory leak!
//rest of your code
}

Dynamically allocate the array to prevent the array going out of scope when CreateReportPane() exits:
int* iArray = new int[2];
iArray[0] = id;
iArray[1] = what;
otherwise the thread is accessing an array that is no longer valid, which is undefined behaviour. The thread routine CreateReportPaneThread() must then delete[] the array when it is no longer required (note the use of delete[] and not delete).

Related

Pointer to array of structs pointers

I need create one pointer to a null-terminated array of pointers to key detail structures..
Struct: WFS_RESULT
typedef struct _wfs_result
{
REQUESTID RequestID;
HSERVICE hService;
SYSTEMTIME tsTimestamp;
HRESULT hResult;
union {
DWORD dwCommandCode;
DWORD dwEventID;
} u;
LPVOID lpBuffer;
} WFSRESULT, *LPWFSRESULT;
Struct: PINKEY
typedef struct _wfs_pin_key_detail_ex
{
LPSTR lpsKeyName;
DWORD dwUse;
BYTE bGeneration;
BYTE bVersion;
BYTE bActivatingDate[4];
BYTE bExpiryDate[4];
BOOL bLoaded;
} WFSPINKEYDETAILEX, * LPWFSPINKEYDETAILEX;
Program: How am i trying to do
LPWFSPINKEYDETAILEX* array[7];
LPWFSPINKEYDETAILEX Test;
WFSPINKEYDETAILEX Obj;
Test = &Obj;
Test->lpsKeyName = NULL;
array[0] = &Test;
array[1] = &Test;
array[2] = &Test;
array[3] = &Test;
array[4] = &Test;
array[5] = &Test;
array[6] = NULL;
LPWFSPINKEYDETAILEX** val = array;
lpWFSResult->lpBuffer = val;
The question is, is what I did above a pointer to an array of pointers? Because, I need to pass this Pointer Array Pointer to this parameter lpWFSResult-> lpBuffer = val; and in the final program (Bank Application) it gives error -15 (WFS_ERR_INTERNAL_ERROR).
Just as idea. Maybe they check if to be different objects. Maybe they are using the previous pointers and do not expect that value of val[0] will be changed after changing val[1].
Also check the API maybe they expects the rest of the fields filled up
lpsKeyName
bGeneration
bActivatingDate
bExpiryDate
This depends on how the array is created and where it is stored/used. based on the code provided, I assume the array has been generated on the memory stack but then used after that stack level has been popped (ie. function returns). The array memory will have been deallocated and the array pointer will be invalid (will cause unexpected behaviour). If you need to preserve the array outside of the stack, you will need to generate it on the heap using new. This way the memory will persist after the function exits and the memory stack level is popped.
LPWFSPINKEYDETAILEX** array = new LPWFSPINKEYDETAILEX*[7];
LPWFSPINKEYDETAILEX Test;
WFSPINKEYDETAILEX Obj;
Test = &Obj;
Test->lpsKeyName = NULL;
array[0] = &Test;
array[1] = &Test;
array[2] = &Test;
array[3] = &Test;
array[4] = &Test;
array[5] = &Test;
array[6] = NULL;
lpWFSResult->lpBuffer = array;
Don't forget to delete it later when you're done with the memory so you don't get a memory leak.
You need to read the API spec where it tells you how to allocate memory. I presume you are writing an SP, have six keys and the key names are in the array keyNames.
int numKeys=6;
LPSTR keyNames[6]={"key1","key2","key3","key4","key5","key6"};
LPWFSRESULT pResult;
LPWFSPINKEYDETAILEX* ppDetails;
WFMAllocateBuffer(sizeof(WFSRESULT), WFS_MEM_ZEROINIT, (LPVOID*)&pResult);
WFMAllocateMore(sizeof(LPWFSPINKEYDETAILEX)*(numKeys+1),pResult, (LPVOID*)&ppDetails);
for (int i=0;i<numKeys;i++)
{
WFMAllocateMore(sizeof(WFSPINKEYDETAILEX),pResult,(LPVOID*)&ppDetails[i]);
WFMAllocateMore(strlen(keyNames[i])+1,pResult,(LPVOID*)&ppDetail[i].lpsKeyName);
strcpy(ppDetails[i].lpsKeyName,keyNames[i]);
//TODO fill in other details
}
ppDetails[numKeys]=NULL;
pResult->lpBuffer=ppDetails;

Allocate multiple structs in memory

i need pass multiple values to memory, i need make various country to CEN/XFS.
This api: CashDispenser - CDM
Struct reference: WFSCDMCURRENCYEXP
How am i trying to do:
HRESULT WINAPI WFPGetInfo(HSERVICE hService, DWORD dwCategory, LPVOID lpQueryDetails, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID) {
WFSRESULT * lpWFSResult;
WFSCDMSTATUS CdmStatus;
WFSCDMCAPS CdmCapabilities;
WFSCDMCASHUNIT CdmCash;
WFSCDMCURRENCYEXP CdmCurrency;
HRESULT result;
result = WFMAllocateBuffer(sizeof(WFSRESULT), WFS_MEM_ZEROINIT | WFS_MEM_SHARE, (void**)&lpWFSResult);
if(result != WFS_SUCCESS){
return WFS_ERR_INTERNAL_ERROR;
}
if(dwCategory == WFS_INF_CDM_CURRENCY_EXP){
const int countCurrencies = 2;
WFSCDMCURRENCYEXP** ppCdmCurrencies;
result = WFMAllocateMore(sizeof(WFSCDMCURRENCYEXP*) * (countCurrencies+1), lpWFSResult, (void**)&ppCdmCurrencies);
lpWFSResult->hService=hService;
lpWFSResult->RequestID=ReqID;
lpWFSResult->u.dwEventID=WFS_INF_CDM_CURRENCY_EXP;
lpWFSResult->hResult=WFS_SUCCESS;
result = WFMAllocateMore(sizeof(WFSCDMCURRENCYEXP), lpWFSResult, (void**)&ppCdmCurrencies[0]);
WFSCDMCURRENCYEXP& cmdCurrency0(*ppCdmCurrencies[0]);
memcpy(cmdCurrency0.cCurrencyID, "AED", 3);
cmdCurrency0.sExponent = 0;
WFSCDMCURRENCYEXP& cmdCurrency1(*ppCdmCurrencies[1]);
memcpy(cmdCurrency1.cCurrencyID, "AFA", 3);
cmdCurrency1.sExponent = 0;
lpWFSResult->lpBuffer = ppCdmCurrencies;
logFile.close();
}
}
Your code looks very C-ish.
An idiomatic way to do this in c++ would be:
struct WFSCDMCURRENCYEXP
{
std::string cCurrencyID;
SHORT sExponent;
};
std::vector<WFSCDMCURRENCYEXP> CdmCurrencies {
{ "ARG", 3 } ,
{ "EUA", 3 } ,
// lots more countries
};
Update:
I just noticed you apparently interact with a c-style API, and using that struct might be required in its original form.
Though in c++ you still can use a std::vector to manage a dynamically allocated, contiguous array of that struct:
typedef struct _wfs_cdm_currency_exp
{
CHAR cCurrencyID[3];
SHORT sExponent;
} WFSCDMCURRENCYEXP, * LPWFSCDMCURRENCYEXP;
std::vector<WFSCDMCURRENCYEXP> CdmCurrencies {
{ { 'A', 'R', 'G' }, 3 } , // Note that the cCurrencyID is a non null terminated
// array here
{ { 'E', 'U', 'A' }, 3 } ,
// lots more countries
};
LPWFSCDMCURRENCYEXP pCdmCurrencies = &CdmCurrencies[0];
Declare an array:
WFSCDMCURRENCYEXP CdmCurrency[2];
memcpy( CdmCurrency[0].cCurrencyID, "ARG", 3);
CdmCurrency[0].sExponent = 0;
memcpy( CdmCurrency[1].cCurrencyID, "EUA", 3);
CdmCurrency[1].sExponent = 0;
memcpy(lpWFSResult->lpBuffer, CdmCurrency, 2*sizeof(WFSCDMCURRENCYEXP));
// ^^
Don't forget you will need to copy more memory.
Also note I have fixed the setting of .cCurrencyID - a character literal (with single quotes) can only contain a single character. To move multiple characters, you will need to call memcpy from a string. Normally I would suggest using std::string rather than char [3], but you can't use memcpy if you do, and it probably wouldn't be a good idea to pass a std::string across a DLL boundary.
I think you try to handle WFS_INF_CDM_CURRENCY_EXP message to get information about currencies in your CDM.
Read XFS specification carefully:
Output Param LPWFSCDMCURRENCYEXP *lppCurrencyExp; Pointer to a NULL-terminated array of pointers to WFSCDMCURRENCYEXP structures
This means that you must allocate an array of pointers to WFSCDMCURRENCYEXP with size N+1 and set last item to null.
In CEN/XFS you cannot use standard new or malloc for memory allocation.
You need to use WFMAllocateBuffer and WFMAllocateMore to allocate memory for XFS structures that you return to caller.
For you task yo need something like this:
HRESULT WINAPI WFPGetInfo(HSERVICE hService, DWORD dwCategory, LPVOID lpQueryDetails, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID) {
WFSRESULT * lpWFSResult;
WFSCDMSTATUS CdmStatus;
WFSCDMCAPS CdmCapabilities;
WFSCDMCASHUNIT CdmCash;
WFSCDMCURRENCYEXP CdmCurrency;
HRESULT result;
result = WFMAllocateBuffer(sizeof(WFSRESULT), WFS_MEM_ZEROINIT | WFS_MEM_SHARE, (void**)&lpWFSResult);
if(result != WFS_SUCCESS){
return WFS_ERR_INTERNAL_ERROR;
}
if(dwCategory == WFS_INF_CDM_CURRENCY_EXP){
const int countCurrencies = 2;
WFSCDMCURRENCYEXP** ppCdmCurrencies;
result = WFMAllocateBuffer(sizeof(WFSCDMCURRENCYEXP*) * (countCurrencies+1), WFS_MEM_ZEROINIT | WFS_MEM_SHARE, (void**)&ppCdmCurrencies);
lpWFSResult->hService=hService;
lpWFSResult->RequestID=ReqID;
lpWFSResult->u.dwEventID=WFS_INF_CDM_CURRENCY_EXP;
lpWFSResult->hResult=WFS_SUCCESS;
result = WFMAllocateMore(sizeof(WFSCDMCURRENCYEXP), lpWFSResult, (void**)&ppCdmCurrencies[0]);
WFSCDMCURRENCYEXP& cmdCurrency0(*ppCdmCurrencies[0]);
memcpy(cmdCurrency0.cCurrencyID, "AED", 3);
cmdCurrency0.sExponent = 0;
result = WFMAllocateMore(sizeof(WFSCDMCURRENCYEXP), lpWFSResult, (void**)&ppCdmCurrencies[1]);
WFSCDMCURRENCYEXP& cmdCurrency1(*ppCdmCurrencies[1]);
memcpy(cmdCurrency1.cCurrencyID, "AFA", 3);
cmdCurrency1.sExponent = 0;
lpWFSResult->lpBuffer = ppCdmCurrencies;
logFile.close();
return WFS_SUCCESS;
}
}
That's not so simple to manipulate with XFS. It's too many complex API structures with different rules of allocation and data representation. Please read carefully XFS manuals. In the first book ftp://ftp.cen.eu/CWA/CEN/WS-XFS/CWA16926/CWA%2016926-1.pdf many conceptual things is described. About configuration, memory mangement and so on.
You can call malloc to allocate multiple structs, if they are what is called POD - plain old data - or structures with no destructors, member functions, or member classes with these characteristics.
The only reason for doing that is to be compatible with C, however, or for writing very low level code.
As a general rule, you want to create an std::vector. The vector handles all the memory for you, and you can push_back as many members as you require. Use reserve (or resize) if efficiency is important. There's also a member called data() which is there for C compatibility (however C can't call your vector, at least not easily).
your code keeps throwing WFS_ERR_INTERNAL_ERROR because you havent got a fully functional XFS environment setup, setup a proper configuration, or emulate one (registry keys, sdk, dlls, etc) and then test it again, make sure your code includes the proper headers and make sure you are linking to msxfs.lib, xfs_conf.lib and xfs_supp.lib

Value getting changed after end of function

I am trying to set data which needs to be used in another file (layer) of the application. During debugging , I see that the value gets correctly set for the first time . But when I try to use this set variable after the function , the value is changed. I think the memory is getting released causing the variable to reset . Can anyone please help me with what I am trying to do.
void SetExpectedTabsData(_In_ PCWSTR tabUrls[], _In_ PCWSTR tabTitles[], _In_ UINT tabsCount, _In_ FakeCourier* courier)
{
wchar_t jsonPerTab[256];
wchar_t tabsDataJSON[c_jsonTabDataSize];
// tabsDataJSON should be large enough to hold any data.
StringCchPrintf(tabsDataJSON, c_jsonTabDataSize, L"\"tabs\":[");
bool isActiveTab = true;
// tabId starts from 1 and the tabIndex starts with 0. Manipulated the json string generation accordingly.
for (unsigned int i = 1; i <= tabsCount; ++i)
{
StringCchPrintf(jsonPerTab, ARRAYSIZE(jsonPerTab), L"{\"id\":%i,\"index\":%i,\"windowId\":1,\"active\":%s,\"status\":\"complete\",\"title\":\"%s\",\"url\":\"%s\"}", i, (i - 1), isActiveTab ? L"true" : L"false", tabTitles[i - 1], tabUrls[i - 1]);
StringCchCat(tabsDataJSON, c_jsonTabDataSize, jsonPerTab);
isActiveTab = false;
if (i != tabsCount)
{
StringCchCat(tabsDataJSON, c_jsonTabDataSize, L",");
}
}
StringCchCat(tabsDataJSON, c_jsonTabDataSize, L"],");
VERIFY_SUCCEEDED(courier->SetExpectedTabsData(tabsDataJSON));
}
The courier file where the data needs to be set is as
HRESULT FakeCourier::SetExpectedTabsData(_In_ wchar_t* tabsData)
{
m_tabsData = tabsData;
return S_OK;
}
Please suggest the correct approach to do this.
The variable tabsDataJSON is local in the function SetExpectedTabsData, so it can be overwritten after finishing of this function. And since it can happen, it happens.
When you call FakeCourier::SetExpectedTabsData(tabsDataJSON), this function just remembers pointer to that local variable tabsDataJSON. But right after finishing of the function SetExpectedTabsData this pointer becomes invalid.
To fix it you need to modify the function FakeCourier::SetExpectedTabsData: replace copying of the pointer be copying of the pointed data. Since your m_tabsData is a wchar_t*, just allocate this string and copy the data from tabsData into m_tabsData (read about wcsncpy and similar functions).
Try something like this:
HRESULT FakeCourier::SetExpectedTabsData(_In_ wchar_t* tabsData)
{
const size_t len = wcslen(tabsData);
if (m_tabsData)
delete m_tabsData;
m_tabsData = new wchar_t[len + 1];
wcsncpy(m_tabsData, tabsData, len + 1);
return S_OK;
}
And also you need to free this memory in the destructor of the FakeCourier (to avoid memory leak). And to initialize m_tabsData in the constructor (m_tabsData = 0;)

access violation reading location in c++

I have a thread which reads multicast data and updates certain data structures
and another thread which is handled by chai 3d library
when I just run my library code it works fine.
when I run y thread also
I get access violation in one of the routine inside the chai3d code.
my thread code
unsigned int __stdcall ThreadFunc(void* data)
{
char *timeOld;
int ID;
while(1)
{
char *position = _com_util::ConvertBSTRToString(cpi->getData());
ID = cpi->getMulticastDataID();
char* timeNew = _com_util::ConvertBSTRToString(cpi->getTime());
if(timeFirst == true)
{
timeOld = new char[strlen(timeNew) + 1];
strcpy(timeOld,timeNew);
timeFirst = false;
}
if((strcmp(timeNew,timeOld) != 0) && (AddItselToList == true) && ( ID != 99))
{handlePacket(position,ID);
strcpy(timeOld,timeNew);}
delete[] position;
delete[] timeNew;
}
delete[] timeOld;
}
cpi is a pointer to com c# object where getdata, gettime return strings and getmulticastid returns int.
is there something worn with my thread code?
_beginthreadex(NULL,0,ThreadFunc,NULL,0,NULL);
delete[] position and timeNew before the closing brace of the while loop. delete[] timeOld before the closing brace of ThreadFunc. Also, are you sure timeOld is long enough to copy timeNew into it? This may explain your access violation.

passing integer array in thread function

I am working on multiple producer and Single consumer problem.I wanted to pass Thread like 1,2,3 in the thread function so that individual thread can be named based on these number.
But the program is crashing after count 7 while creating thread.I think problem is due to
variable nThreadNo;
if i limit the count less than 7 it works fine.but if i make count more than this it crashes.
void CEvent1Dlg::CreateProducerThreads()
{
try
{
nThreadNo = new int20];
memset(nThreadNo,0,20);
if (nThreadNo ==NULL) return;
}catch(...)
{
MessageBox(_T("Memory allocation Failed"),_T("Thread"),1);
return ;
}
int i = 0;
for ( i = 0;i<20;i++)
{
//nThreadNo = i+1;
nThreadNo[i] = i+1;
hWndProducer[i] = CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ProducerThrdFunc,(void*)(nThreadNo+i),0,&dwProducerThreadID[i]);
if (hWndProducer[i] == NULL)
{
//ErrorHandler(TEXT("CreateThread"));
ExitProcess(3);
}
}
//WaitForMultipleObjects(20,hWndProducer,TRUE,INFINITE);
}
DWORD WINAPI ProducerThrdFunc ( LPVOID n )
{
int *nThreadNo = (int*)n;
char chThreadNo[33];
memset(chThreadNo,0,33);
while(1)
{
itoa(*nThreadNo,chThreadNo,10);
char* pMsg1 = new char[100];
char* pMsg2 = new char[100];
memset(pMsg1,0,100);
memset(pMsg2,0,100);
strcpy(pMsg1,"Producer ");
strcat(pMsg1," Thread No:");
strcat(pMsg1,chThreadNo);
if (stThreadInfoProd.pEventQueue->AddTail(pMsg1)==TRUE)
{
strcpy(pMsg2,"Producer ");
strcat(pMsg2," Thread No:");
strcat(pMsg2,chThreadNo);
strcat(pMsg2," Added the Msg");
}
else
{
strcpy(pMsg2,"Producer ");
strcat(pMsg2," Thread No:");
strcat(pMsg2,chThreadNo);
strcat(pMsg2,"failed to Add the Msg");
}
PostMessage(stThreadInfoProd.hWndHandle,UWM_ONUPDATEPRODUCERLIST,(WPARAM)pMsg2,0);
strcat(pMsg1," Adding Msg:");
//PostMessage(stThreadInfoProd.hWndHandle,UWM_ONUPDATEPRODUCERLIST,(WPARAM)pMsg2,0);
Sleep(3000);
}
return 0;
}
You are zeroing out the first 20 bytes of nThreadNo, not the first 20 * sizeof(int) bytes as you should be doing.
There are other arrays you are indexing into in this code: hWndProducer, dwProducerThreadID. Are there enough elements in those as well?
The CreateThread call is passing the integer value, but the thread function itself is treating it as a pointer to integer. Rather than this:
int *nThreadNo = (int*)n;
It should probably be:
int nThreadNo = (int)n;
Edit: I looked more closely at the call and I do see that it is passing an integer pointer. However, that value is stack data, which may not exist by the time the thread tries to read it. So it should probably just pass the integer value: (void*)(nThreadNo[i])
This line
if (nThreadNo ==NULL) return;
is worthless.
The new operator in modern C++ doesn't return NULL on failure, it throws a std::badalloc exception, but even if you were using an allocator that returns NULL to indicate failure, it's too late to detect it, you're already passed the NULL pointer to memcpy.