c++ windows service -main entry point - how to debug - c++

I created a service for Windows using a complete example found at https://msdn.microsoft.com/en-us/library/bb540476(VS.85).aspx.
I do not know if it is the best example. A lot of people say Microsoft's examples are not the best. But it works. I can install, start, stop and delete the service without problems.
But now I need to effectively put the part of the code that should be what the service will do.
Reading the code I understood that this part creates a list and in it is placed the main of the service:
// TO_DO: Add any additional services for the process to this table.
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) SvcMain },
{ NULL, NULL }
};
// This call returns when the service has stopped.
// The process should simply terminate when the call returns.
if (!StartServiceCtrlDispatcher( DispatchTable ))
{
SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
}
If I understood correctly when the service was started the SvcMain function would be executed.
// Purpose:
// Entry point for the service
//
// Parameters:
// dwArgc - Number of arguments in the lpszArgv array
// lpszArgv - Array of strings. The first string is the name of
// the service and subsequent strings are passed by the process
// that called the StartService function to start the service.
//
// Return value:
// None.
//
VOID WINAPI SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
{
// Register the handler function for the service
LOGD << "passing 1";
gSvcStatusHandle = RegisterServiceCtrlHandler(
SVCNAME,
SvcCtrlHandler);
LOGD << "passing 2";
if( !gSvcStatusHandle )
{
SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));
return;
}
// These SERVICE_STATUS members remain as set here
LOGD << "passing 3";
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
gSvcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
LOGD << "passing 4";
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );
// Perform service-specific initialization and work.
LOGD << "passing 5";
SvcInit( dwArgc, lpszArgv );
}
I put log messages on multiple lines and I did not get any results. Nothing was recorded in the log file.
Log messages work during the service installation, but nothing is logged from within the SvcMain function.
Note:: Everything has been tested and works fine when run as a program, including in Debug.
1 - Is this function that executes the service code?
2 - Is it possible to write a log file from within a service?
3 - What other option would I have to debug inside the service?

Related

WNetOpenEnum returns ERROR_NETWORK_UNREACHABLE for the "Microsoft Windows Network" node

Our program has a piece of code that calculates the list of computers on our local network. It uses the Windows Networking API (WNetOpenEnum/WNetEnumResource) to unwind the network. For many years, the resulting list was identical to the one that can be seen in Windows Explorer under the "Network" entry. However, recently we have noticed that the same code returns an empty list. During debugging I found that WNetOpenEnum returns error 1231 (ERROR_NETWORK_UNREACHABLE) when it is called for the "Microsoft Windows Network" under the root node.
I have to mention, though I'm pretty sure it has nothing to do with the matter, that the network unwinding is done multithreaded, to avoid possible delays in the main GUI thread. Each time a node of type RESOURCEUSAGE_CONTAINER is encountered, a new worker thread is launched. The thread function calls the following procedure:
DWORD WINAPI EnumNetwork(NETRESOURCE_M* lpNR)
{
const int BUF_SIZE = 16384; // 16K is a good size.
HANDLE hEnum;
DWORD Result;
// Call the WNetOpenEnum function to begin the enumeration.
Result = ::WNetOpenEnum(RESOURCE_GLOBALNET, // all network
RESOURCETYPE_ANY, // all resource types
0, // enumerate all
(LPNETRESOURCE)lpNR,// parent resource
&hEnum); // enumeration handle
if (Result != NO_ERROR) // -> for "Microsoft Windows Network" Result = 1231
return Result;
std::vector<std::wstring> SrvList;
// Allocate buffer for enumeration.
LPNETRESOURCE lpEnumNR = (LPNETRESOURCE)new char[BUF_SIZE];
if (lpEnumNR == 0)
Result = ERROR_OUTOFMEMORY;
else
{
while (1)
{
::ZeroMemory(lpEnumNR, BUF_SIZE); // Initialize the buffer.
DWORD NumEntries = -1; // Enumerate all entries.
DWORD BufSize = BUF_SIZE;
// Call WNetEnumResource to continue the enumeration.
Result = ::WNetEnumResource(hEnum, // enumeration handle
&NumEntries,// number of entries to enumerate
lpEnumNR, // array of resources to return
&BufSize); // buffer size
if (Result == NO_ERROR)
{
// If the call succeeds, loop through the array.
for (unsigned i = 0; i < NumEntries; ++i)
{
if (lpEnumNR[i].dwDisplayType == RESOURCEDISPLAYTYPE_SERVER)
{
// Collect servers.
LPCWSTR SrvName = lpEnumNR[i].lpRemoteName;
if (PathHelpers::IsFullPath(SrvName))
SrvList.push_back(SrvName);
}
else if ((lpEnumNR[i].dwUsage & RESOURCEUSAGE_CONTAINER) &&
lpEnumNR[i].lpRemoteName != 0)
{
TCHAR PathBuf[1024] = {0};
if (lpNR && lpNR->Path)
{
_tcscpy(PathBuf, lpNR->Path);
::PathAddBackslash(PathBuf);
}
_tcscat(PathBuf, lpEnumNR[i].lpRemoteName);
if (RegisterServer(PathBuf))
{
// Start new thread for recursive enumeration.
NETRESOURCE_M* lpChildNR = DeepCopyNR(&lpEnumNR[i], PathBuf);
ExploreNetwork(lpChildNR); // -> this starts a worker thread
}
else
{
GetLogger().LogMessage(
_T("Cycles found while unwinding network: %s"), PathBuf);
}
}
}
}
else
{
if (Result == ERROR_NO_MORE_ITEMS)
Result = NO_ERROR;
break;
}
} // end while
delete [] (char*)lpEnumNR;
} // end if
::WNetCloseEnum(hEnum);
if (!SrvList.empty())
NotifyServerAdded(SrvList);
return Result;
}
where NETRESOURCE_M is the structure
struct NETRESOURCE_M
{
NETRESOURCE NR;
LPTSTR Path;
};
Trying to figure out what could have caused such a sudden change in behavior, I found in Google that a few years ago Microsoft disabled the SMB1 protocol, which could affect Network Discovery. However, I can't believe they could have damaged their own API without saying a word in the documentation.
EDIT: At the same time, Windows Explorer has a bunch of computers under its "Network" node. In the network settings, the network type is "Domain", and the network discovery is ON. Services "Function Discovery Provider Host" and "Function Discovery Resources Publication" are running. Windows OS build is 19042.685.
Edit 2: The Sysinternals' "ShareEnum" tool also fails with the error: "No domains or workgroups where found on your network". Because of this, and also because some time ago our company moved all of its computers to a different network, I got the feeling that the problem is in the network configuration. Such as though the network is declared as "Domain", the computers were not enrolled to this domain. I do not understand much in that, but something like this.

OPCUA C++ Monitored item doest'n callback the function

I'm quite new in the opcua's world and I'm trying to monitor a server variable with a client in C++.
I'm on the 1.2.2 version of opcua
I have a boolean variable in the server at the node (1,6070) and when I run the following code I receive the LOG :
[2021-08-03 15:27:47.442 (UTC+0200)] info/session Connection 5 | SecureChannel 2 | Session ns=1;g=913a21de-f467-5bc9-ed9e-29b27b470490 | Subscription 2 | Created the Subscription with a publishing interval of 500.00 ms
But I've never reach the function 'handler_events_datachange' in which I only put an output for now. (I'm sure that the value in the node 6070 changed btw)
Thanks for helping!
int main(void) {
signal(SIGINT, stopHandler); /* catches ctrl-c */
UA_Client *client = UA_Client_new();
UA_ClientConfig *cc = UA_Client_getConfig(client);
UA_ClientConfig_setDefault(cc);
UA_Client_connect(client, "opc.tcp://localhost");
UA_MonitoredItemCreateResult result;
UA_CreateSubscriptionResponse response; // warning memory leak
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
UA_Int32 subId = response.subscriptionId;
if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
{
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND , "subscription succed");
} else {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND , "subscription UNsucced");
}
UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default(UA_NODEID_NUMERIC(1, 6070));
result = UA_Client_MonitoredItems_createDataChange(client, subId, UA_TIMESTAMPSTORETURN_BOTH, monRequest, NULL, handler_events_datachange, NULL);
while(running) {
}
}
I finally found the error !
The problem comme from the fact no means of handling asynchronous
events automatically is provided. However, some synchronous function
calls will trigger handling, but to ensure this happens a client
should periodically call UA_Client_run_iterate explicitly.
So the solution is to add UA_Client_run_iterate(client,100) in the while().
I didn't fully understand what the timeout was about but I'll complete this answer if I can

How to combine RegisterDragDrop, RoInitialize to work together in one thread?

I have a dilemma. My GUI-based C++ app requires to implement drag-and-drop functionality. At the same time, I'm converting this Win32 app to UWP to submit to Windows Store. But there's one issue:
To implement drag-and-drop I need to call these two methods:
OleInitialize(NULL);
//...
HRESULT hr = RegisterDragDrop(hMainWnd, pDropTarget);
and to init WinRT stuff to work with Windows Store, I need to call:
HRESULT hr = RoInitialize(RO_INIT_MULTITHREADED);
Unfortunately OleInitialize initialized COM as single-thread apartment and RoInitialize requires multi-threaded model, while RegisterDragDrop cannot function without calling OleInitialize.
Any idea how to resolve it? (apart from moving RoInitialize and all WinRT code into a worker thread, that will complicate things.)
Raymond Chen in his usual condescending way is pretty good at criticizing things but offers no fix to an existing problem. I'm posting this mostly for later self-reference and in case someone else stumbles upon the same issue as well. I just spent several days trying to resolve this bug, so maybe it will save time for someone else.
Problem
First off, this is a native Win32 code (no .NET or C++/CX.) It is C++ with a sprinkle of WRL for easier handling of WinRT/COM stuff.
In my case I have a Win32 GUI app that implements drag-and-drop of files into its main window. So to init it, one needs to do this from the main thread, right when the app starts:
OleInitialize(NULL);
//...
HRESULT hr = RegisterDragDrop(hMainWnd, pDropTarget);
The OleInitialize call above will initialize COM for the main thread to use single-thread apartment, which is required for RegisterDragDrop to succeed. Without it, the drag-and-drop function will not work.
Then, say you decide to convert this Win32 app to UWP using Microsoft's Project Centennial converter for inclusion into Windows 10 store.
When the app is converted and listed in the store under their trial-license scheme, you will employ the following logic to check if the user has a trial or an activated (i.e. purchased) copy of the app. You'll begin it as such:
//Init COM for WinRT
RoInitialize(RO_INIT_MULTITHREADED);
ComPtr<IStoreContextStatics> pStoreContextStatics;
if(SUCCEEDED(RoGetActivationFactory(
HStringReference(L"Windows.Services.Store.StoreContext").Get(),
__uuidof(pStoreContextStatics),
&pStoreContextStatics)) &&
pStoreContextStatics)
{
//Get store context for the app
ComPtr<IStoreContext> pStoreContext;
if(SUCCEEDED(pStoreContextStatics->GetDefault(&pStoreContext)) &&
pStoreContext)
{
//Got store context
//....
}
}
and then if you need to know trial vs. activated status of the app, using this logic, you'd call:
ComPtr<IAsyncOperation<StoreAppLicense*>> p_opAppLic;
if(SUCCEEDED(pStoreContext->GetAppLicenseAsync(p_opAppLic)) &&
p_opAppLic)
{
ComPtr<IAsyncOperationCompletedHandler<StoreAppLicense*>> p_onAppLicCallback =
Callback<Implements<RuntimeClassFlags<ClassicCom>, IAsyncOperationCompletedHandler<StoreAppLicense*>, FtmBase>>(
[](IAsyncOperation<StoreAppLicense*>* pOp, AsyncStatus status)
{
if (status == AsyncStatus::Completed)
{
ComPtr<IStoreAppLicense> pAppLicResult;
if(SUCCEEDED(pOp->GetResults(&pAppLicResult)) &&
pAppLicResult)
{
BYTE nActive = -1;
BYTE nTrial = -1;
pAppLicResult->get_IsActive(&nActive);
pAppLicResult->get_IsTrial(&nTrial);
//Get app's store ID with SKU
HString strStoreId;
pAppLicResult->get_SkuStoreId(strStoreId.GetAddressOf());
if(nActive == 1 &&
nTrial == 0)
{
//Activated, or purchased copy
}
else if(nActive == 1 &&
nTrial == 1)
{
//Trial copy
}
else
{
//Error -- store returned some gibberish
}
}
}
return S_OK;
});
if(SUCCEEDED(p_opAppLic->put_Completed(p_onAppLicCallback.Get())))
{
//Success initiating async call
}
}
So, if you do all this, your UWP-converted app will behave in a very strange way. Here's an example. Say a user purchases a license for the app thru Windows Store. In turn your app logic calls the code above to see if the app is activated, but what you get back is nActive=0 and nTrial=1. Then if you check strStoreId it will be your app store ID but without the SKU. WTF!?
I know, it's really confusing. As an aside, let me explain. When you first list your app in a Windows Store it will be assigned a Store ID. Something like: ABCDEFG12345. Then if you submit any follow-up update(s) to the first version of the same app, they will add a SKU number to it, that will make the whole app ID change to ABCDEFG12345/0010, then ABCDEFG12345/0011 for the next update, and so on.
Well, the WinRT code above would return my app store ID as ABCDEFG12345 without any SKU attached to it. Which was wrong, since it was a third or so update to the first version of the app. And thus any additional attributes for that app store ID were also wrong.
So that was the issue that I was faced with...
Cause
All the headache that I described above was caused by my omission to check the result code returned from the first RoInitialize call. I would be able to catch the problem much faster if I did this:
//Init COM for WinRT
if(FAILED(RoInitialize(RO_INIT_MULTITHREADED)))
{
//WinRT COM initialization failed
//Go scratch your head why....
}
In this case RoInitialize will fail with error code RPC_E_CHANGED_MODE. The documentation for it is as helpful as Windows Help (F1) option:
A previous call to RoInitialize specified the concurrency model for
this thread as multithread apartment (MTA). This could also indicate
that a change from neutral-threaded apartment to single-threaded
apartment has occurred.
What previous call? The only parameter anyone can call it with is RO_INIT_MULTITHREADED.
So I started digging deeper and by the process of elimination found that the OleInitialize call earlier was the reason why RoInitialize failed and caused the cascade of events that I described above.
Thus I was at the point of asking the question here.
Note on the side, that the bug ridden WinRT library (ref1, ref2, ref3, ref4, ref5) gave me no indications of a problem in all the calls following RoInitialize and somewhere internally silently failed to retrieve the app's SKU because of a single-thread apartment COM initialization.
Hack/Workaround
As was suggested by RbMm in the comments above, doing the following will work, but is a totally undocumented behavior:
if(SUCCEEDED(OleInitialize(0))
{
CoUninitialize();
}
CoInitializeEx(NULL, COINIT_MULTITHREADED);
So if you don't want your app to start crashing for no apparent reason, I would not use it.
Solution
My solution that I went with was to move all the WinRT COM stuff (code I listed above: 2nd and 3rd code segments) into a separate worker thread. It will work fine from there. The issue is marshalling calls between your main thread and this worker thread. It is doable, but requires some work, i.e. using mutexes and events for synchronized access, etc.
So if anyone finds an easier fix for this, please post your solution. I'll mark it as the answer.
solution to the IDsObjPicker crashes I mentioned in the comment ealier, quick code I wrote just now.
Use code below as:
TDsObjPicker lv_PickInfo;
memset(&lv_PickInfo, 0, sizeof(TDsObjPicker));
Sec_InitDsObjPicker(&lv_PickInfo, &lv_InitInfo);
Sec_InvokeDsObjPicker(&lv_PickInfo, 0, &lv_oData);
Solution is to run the dialog in another thread and init the thread without the Ole+Com combination:
// command codes
#define DSOPCMD_EXITTHREAD 1
#define DSOPCMD_INITIALIZE 2
#define DSOPCMD_INVOKE 3
// parameters of object picker via thread
typedef struct tagDsObjPicker
{
// thread handle
HANDLE hThread;
// events
HANDLE hCmdEvt;
HANDLE hRdyEvt;
// commands
UINT CmdCode;
HRESULT hResult;
// command parameters - DSOPCMD_INITIALIZE
DSOP_INIT_INFO *InitInfo;
// command parameters - DSOPCMD_INVOKE
HWND hWnd;
IDataObject **oData;
//
} TDsObjPicker;
DWORD CALLBACK _Sec_DsObjPickerThread(VOID *in_Param)
{
/* locals */
HRESULT lv_hCreateResult;
HRESULT lv_hResult;
TDsObjPicker *lv_PickInfo;
IDsObjectPicker *lv_oPicker;
// get info structure
lv_PickInfo = (TDsObjPicker*)in_Param;
// init COM
CoInitializeEx(NULL, COINIT_MULTITHREADED);
// preclear object pointer
lv_oPicker = NULL;
// create instance of picker
lv_hCreateResult = CoCreateInstance(
CLSID_DsObjectPicker, NULL, CLSCTX_INPROC_SERVER,
IID_IDsObjectPicker, (VOID**)&lv_oPicker);
// while thread is not aborted
while (lv_PickInfo->CmdCode != DSOPCMD_EXITTHREAD)
{
// wait for command event
if (WaitForSingleObject(lv_PickInfo->hCmdEvt, INFINITE) == 0)
{
// what command?
switch (lv_PickInfo->CmdCode)
{
// call init
case DSOPCMD_INITIALIZE:
{
// call object
if (lv_hCreateResult)
lv_hResult = lv_hCreateResult;
else
lv_hResult = lv_oPicker->Initialize(lv_PickInfo->InitInfo);
// done
break;
}
// call invoke
case DSOPCMD_INVOKE:
{
// call object
if (lv_hCreateResult)
lv_hResult = lv_hCreateResult;
else
lv_hResult = lv_oPicker->InvokeDialog(lv_PickInfo->hWnd, lv_PickInfo->oData);
// done
break;
}
// other command codes
default:
lv_hResult = E_FAIL;
break;
}
// store result
lv_PickInfo->hResult = lv_hResult;
// notify caller
SetEvent(lv_PickInfo->hRdyEvt);
}
}
// destroy the picker object
if (lv_oPicker)
lv_oPicker->Release();
// cleanup COM
CoUninitialize();
// leave the thread
return 0;
}
VOID Sec_DoneDsObjPicker(TDsObjPicker *in_PickInfo)
{
// is thread created?
if (in_PickInfo->hThread)
{
// set command code
in_PickInfo->CmdCode = DSOPCMD_EXITTHREAD;
// trigger the thread to process the code
SetEvent(in_PickInfo->hCmdEvt);
// wait for thread to finish
WaitForSingleObject(in_PickInfo->hThread, INFINITE);
// close thread handle
CloseHandle(in_PickInfo->hThread);
}
// close event handles
if (in_PickInfo->hCmdEvt) CloseHandle(in_PickInfo->hCmdEvt);
if (in_PickInfo->hRdyEvt) CloseHandle(in_PickInfo->hRdyEvt);
// clear
memset(in_PickInfo, 0, sizeof(TDsObjPicker));
}
HRESULT Sec_InitDsObjPicker(TDsObjPicker *in_PickInfo, DSOP_INIT_INFO *in_InitInfo)
{
/* locals */
DWORD lv_TID;
// thread not yet created?
if (!in_PickInfo->hThread)
{
// create events
in_PickInfo->hCmdEvt = CreateEvent(0,0,0,0);
in_PickInfo->hRdyEvt = CreateEvent(0,0,0,0);
// if ok
if (in_PickInfo->hCmdEvt && in_PickInfo->hRdyEvt)
{
// create the thread
in_PickInfo->hThread = CreateThread(
0, 0, _Sec_DsObjPickerThread, in_PickInfo, 0, &lv_TID);
}
// failed?
if (!in_PickInfo->hThread)
{
// cleanup
Sec_DoneDsObjPicker(in_PickInfo);
// return with error
return E_OUTOFMEMORY;
}
}
// store parameter
in_PickInfo->InitInfo = in_InitInfo;
// set command code
in_PickInfo->CmdCode = DSOPCMD_INITIALIZE;
// trigger the thread to process the code
SetEvent(in_PickInfo->hCmdEvt);
// wait for result
WaitForSingleObject(in_PickInfo->hRdyEvt, INFINITE);
// return the result
return in_PickInfo->hResult;
}
HRESULT Sec_InvokeDsObjPicker(TDsObjPicker *in_PickInfo, HWND in_hWnd, IDataObject **out_oData)
{
/* locals */
MSG lv_Msg;
// thread not yet created?
if (!in_PickInfo->hThread)
return E_FAIL;
// store parameters
in_PickInfo->hWnd = in_hWnd;
in_PickInfo->oData = out_oData;
// set command
in_PickInfo->CmdCode = DSOPCMD_INVOKE;
// trigger the thread
SetEvent(in_PickInfo->hCmdEvt);
// process messages of this thread while picker runs in other thread until event
while (MsgWaitForMultipleObjects(1, &in_PickInfo->hRdyEvt, 0, INFINITE, QS_ALLINPUT) != 0)
{
// get next message
while (PeekMessage(&lv_Msg, 0,0,0, PM_REMOVE))
{
// translate/dispatch the message
TranslateMessage(&lv_Msg);
DispatchMessage(&lv_Msg);
}
}
// return the result
return in_PickInfo->hResult;
}
You asked why calling OleInitialize() first, followed by CoUnintialize and then reinit COM via CoInitializeEx works and is safe, look at the code of the rewritten OLE server in WINE, https://github.com/wine-mirror/wine/blob/master/dlls/ole32/ole2.c it comes pretty close to the "real thing". The OleInitialize calls CoInitializeEx itself with COINIT_APARTMENTTHREADED and fails before doing the OLE-specific initializations upon a fail of CoInitializeEx. There is no reason to fail as the OLE code can run as well in MULTITHREADED mode. Remember MULTITHREADED means the caller must take care of synchronisation/locking while with APARTMENTTHREADED the COM libray will handle it for the code. So if you make sure you do not call the OLE code like dragdrop and clipboard at the same time from multiple threads then there is no problem. Keeping all UI in the main thread will do that. As you should already write multithreaded-aware code yourself using the requested MULTITHREADED mode.
I have the problem with directshow filters/drivers which lock the process when COM is initialized with APARTMENTTHREADED even when directshow is called from a thread with THREADED while the main UI thread runs in APARTMENTTHREADED.
Uninitializing COM after initializing OLE, then re-inititializing COM with MULTITHREAED during startup in the main UI thread makes you bypass the failure in OleInitialize. It is the best solution to make sure all runs well.

C++ Function Completing Before Other Function Finishes

I am coding a C++ program to interact with the internet using the C++ REST SDK. I have a main function and a webCommunication function. The code is similar to below:
void webCommunication(data, url)
{
//Communicate with the internet using the http_client
//Print output
}
int main()
{
//Obtain information from user
webCommunication(ans1, ans2);
system("PAUSE");
}
However, it seems that the main function is progressing before the webCommunication function is finished. If I make webCommunication a function type of string and have
cout << webCommunication(ans1, ans2) << endl;
But that still pauses and then prints the data retrieved. Normally, this would be fine, expect I am referring to the returned answer later on in the code. If the webCommunication isn't completed, the application crashes. Is there some kind of wait_until function I can use?
UPDATE: I have tried using a mutex suggested with no success. I also tried starting the function as a thread and then using the .join() with still no success.
If you declare your webCommunications() function as a
pplx::task<void> webCommunications()
{
}
Then you can use ".wait()" when calling the function. It will then wait until the function executes to continue. Looks like this:
pplx::task<void> webCommunications()
{
}
int main()
{
webCommunications().wait();
//Do other stuff
}
I think you are missing a keyword in the descriptions. ASYNCHRONOUS. This is indicating that it returns before finishing. If you need it to be synchronous, you should put a semaphore acquire right after the call and put a release into the callback code.
https://msdn.microsoft.com/en-us/library/jj950081.aspx
Modified code snippet from link above( added lock to callback ) :
// Creates an HTTP request and prints the length of the response stream.
pplx::task<void> HTTPStreamingAsync()
{
http_client client(L"http://www.fourthcoffee.com");
// Make the request and asynchronously process the response.
return client.request(methods::GET).then([](http_response response)
{
// Print the status code.
std::wostringstream ss;
ss << L"Server returned returned status code " << response.status_code() << L'.' << std::endl;
std::wcout << ss.str();
// TODO: Perform actions here reading from the response stream.
auto bodyStream = response.body();
// In this example, we print the length of the response to the console.
ss.str(std::wstring());
ss << L"Content length is " << response.headers().content_length() << L" bytes." << std::endl;
std::wcout << ss.str();
// RELEASE lock/semaphore/etc here.
mutex.unlock()
});
/* Sample output:
Server returned returned status code 200.
Content length is 63803 bytes.
*/
}
Note : Acquire the mutex after the function call to start web processing. Add to the callback code to release the mutex. In this way the main thread locks until the function actually finishes and then continues to 'pause'.
int main()
{
HttpStreamingAsync();
// Acquire lock to wait for complete
mutex.lock();
system("PAUSE");
}

How to reinitialize native code in managed dll

For testing an existing application I wrote a dll that can be loaded into our simulation application. All is working just fine until I want to reset the existing application from within the dll. Although the main() is restarted, it seems that the memory is not reset/initialized.
The goal is to change as little as possible in the existing application, so actually I don't want to rewrite the application to initialize its variables at startup. Besides that, all local static variables also keep their old values.
Below a sample of how I call the existing application from within the dll.
void TimerThread::Run(void)
{
while(true)
{
if ((nullptr != mpMainThread) && (mpMainThread->ThreadState == System::Threading::ThreadState::Stopped))
{
// Cleanup MainThread when thread has stopped
delete mpMainThread;
mpMainThread = nullptr;
}
if (nullptr == mpMainThread)
{
// (Re)create MainThread in which the existing application is executed
mpMainThread = gcnew System::Threading::Thread(gcnew System::Threading::ThreadStart(&Main));
mpMainThread->Priority = System::Threading::ThreadPriority::Highest;
mpMainThread->Start();
}
dtStartTime = System::DateTime::Now; // Keep track when started.
if (nullptr != mpMainThread)
{
//Simulate timertick in existing application
main_callback_timer_elapsed();
}
dtEndTime = System::DateTime::Now;
iDuration = dtEndTime->Millisecond - dtStartTime->Millisecond; // Determine execution time.
System::Threading::Thread::Sleep(((TIMER_INTERVAL - iDuration) > 0) ? (miInterval - iDuration) : 0); // Set sleep time depending on time spent
}
}
void TimerThread::Main(void)
{
main(); // Run main off existing application
}
void TimerThread::Reset(void)
{
mpMainThread->Abort(); // Reset existing application by aborting MainThread
}
The main of the existing application is quite common. Below an indication of the main().
int main(void)
{
static char test = 0;
init_stuff();
while(true)
{
test = 1;
do_stuff();
while(!timer_tick)
{
check_timer();
}
timer_tick = FALSE;
}
}
The static test variable is initialized at 0 and set to 1 in the infinite loop. When the application is reset from within the dll, the main restarts, but the test variable keeps the value 1. Obviously I want this variable to be reset to 0 when I reset the application.
Any ideas?
If the native DLL doesn't provide a function to reset its state then you will have to unload the DLL and then reload it. If you are using implicit linking that's not possible. You have to use explicit linking: LoadLibrary, GetProcAddress etc.
I'm assuming that the native code is contained in a separate DLL. If that's not the case then you are completely stuck.
Pretty simple.
static char test;
test = 0;