Why is this COM code leaking? - c++

I'm maintaining an application which uses Windows Explorer overlay icons. Occasionally some operations require me to forcibly refresh explorers view for a particular folder. I do so using the following function which uses COM:
void RefreshExplorerView(CString strPath)
{
CComPtr<IShellWindows> pShellWindows;
CoInitialize(NULL);
if(SUCCEEDED(pShellWindows.CoCreateInstance(CLSID_ShellWindows)))
{
IDispatch* pFolder=NULL;
VARIANT variant;
V_VT(&variant) = VT_I4;
for(V_I4(&variant) = 0; pShellWindows->Item(variant, &pFolder) == S_OK; V_I4(&variant)++)
{
CComPtr<IWebBrowserApp> pWebBrowserApp;
if(SUCCEEDED(pFolder->QueryInterface(IID_PPV_ARGS(&pWebBrowserApp))))
{
BSTR LocationURL = NULL;
pWebBrowserApp->get_LocationURL(&LocationURL);
if(LocationURL != NULL && strPath.CompareNoCase(LocationURL) == 0)
{
CComPtr<IServiceProvider> pServiceProvider;
if(SUCCEEDED(pWebBrowserApp->QueryInterface(IID_PPV_ARGS(&pServiceProvider))))
{
CComPtr<IShellBrowser> pShellBrowser;
if(SUCCEEDED(pServiceProvider->QueryInterface(IID_PPV_ARGS(&pShellBrowser))))
{
IShellView* pShellView;
if(SUCCEEDED(pShellBrowser->QueryActiveShellView(&pShellView)))
{
pShellView->Refresh();
pShellView->Release();
}
}
}
}
SysFreeString(LocationURL);
}
pFolder->Release();
pFolder = NULL;
}
}
CoUninitialize();
}
I've noticed that when my program does this refresh regularly it slowly grows in size and UMDH has shown me that I appear to be leaking pFolder and pShellWindow instances every time this runs. I can't work out why on earth this happens since as far as I can tell these are released properly. Can anyone see what I'm missing?

You release pShellWindows after CoUninitialize, which is incorrect.
The rest of interfaces seem to be released fine. Note that you could improve cleanless and readability greatly by using CComQIPtr instead of QueryInterface, and not using raw pointers at all (BSTR, IFoo*) and replace them with smart auto-releasing wrappers.
pFolder might be leaking too, if Item call is successful but returns code other than S_OK. Again, use of CComPtr<IFolder> instead of IFolder* would immediately resolve this problem without even drawing any attention to it.

CoInitialize(NULL);
There's more than one problem with this statement. #Roman explained how you can leak by uninitializing too soon. But this will also go bad in more than one way, the apartment state of a thread is a Really Big Deal in COM:
You are not checking the return value of CoInitialize(). This will blow up the client app that calls this function if it has already called CoInitializeEx() and selected MTA instead of STA. That will make CoInitialize() fail, you cannot change the thread state after it was committed. Your CoUninitialize() call will blow the client app to smithereens, making all of its subsequent COM calls fail.
Selecting STA also requires that you implement the contract for a single threaded apartment. Which states that you never block the thread, you're okay with that. And that you pump a message loop. The message loop is crucial to marshaling calls to the single-threaded apartment. You are not okay with that, nor can you reasonably ensure that this is taken care of in a function like this. Particularly important for the shell interfaces, the vast majority of them are not thread-safe. The consequence of not pumping is deadlock. You may get away with not pumping, it is not a guaranteed deadlock. You'll get a bit of leeway here since these are probably out-of-process interfaces.
Particularly the last requirement can only be met by the code that created the thread that calls this function, only it is in control over what the thread does beyond calling your function. If you cannot get the guarantee that the client app initializes COM correctly then the only truly safe thing to do is to create a thread yourself.

Related

C++ SetConsoleCtrlHandler, passing data for cleanup without globals

I'm trying to check when the console is closed through the close button on Windows. I read about SetConsoleCtrlHandler and I thought I'd use that, but there's some cleanup I want to do in my main function. I'll make a small example describing what I want to do for my larger program.
BOOL CtrlHandler( DWORD fdwCtrlType )
{
switch( fdwCtrlType )
{
//Cleanup exit
case CTRL_CLOSE_EVENT:
bool* programIsOn = &???; //How do I pass the address to that variable in this function?
*programIsOn = false;
return( TRUE );
default:
return FALSE;
}
}
int main(){
MyObject obj = new MyObject();
bool programIsOn = true;
//How do I pass the address of programIsOn here?
if(!SetConsoleCtrlHandler( (PHANDLER_ROUTINE) CtrlHandler, TRUE )){
cout << "Could not set CtrlHandler. Exiting." << endl;
return 0;
}
while(programIsOn){
//...
}
//CLEANUP HERE
delete obj;
return 0;
}
I want to perform cleanup when my program closes via the console close event, however if I just close the console the main function doesn't terminate and is forced to stop. I thought of passing in programIsOn's address to the CtrlHandler callback but I have no idea how to do this without using a global variable.
TL;DR: Proper handling of this control signal is complicated. Don't bother with any 'clean-up' unless it's absolutely necessary.
The system creates a new thread (see the Remarks) in your application, which is then used to execute the handler function you registered. That immediately causes a few issues and forces you in a particular design direction.
Namely, your program suddenly became multi-threaded, with all the complications that brings. Just setting a 'program should stop' (global) boolean variable to true in the handler is not going to work; this has to be done in a thread-aware manner.
Another complication this handler brings is that the moment it returns the program is terminated as per a call to ExitProcess. This means that the handler should wait for the program to finish, again in a thread-aware manner. Queue the next complication, where the OS gives you only 10 seconds to respond to the handler before the program is terminated anyway.
The biggest issue here, I think, is that all these issues force your program to be designed in a very particular way that potentially permeates every nook and cranny of your code.
It's not necessary for your program to clean up any handles, objects, locks or memory it uses: these will all be cleaned up by Windows when your program exits.
Therefore, your clean-up code should consists solely of those operations that need to happen and otherwise wouldn't happen, such as write the end of a log file, delete temporary files, etc.
In fact, it is recommended to not perform such clean-up, as it only slows down the closing of the application and can be so hard to get right in 'unexpected termination' cases; The Old New Thing has a wonderful post about it that's also relevant to this situation.
There are two general choices here for the way to handle the remaining clean-up:
The handler routine does all the clean-up, or
the main application does all the clean-up.
Number 1 has the issue that it's very hard to determine what clean-up to perform (as this depends on where the main program is currently executing) and it's doing so 'while the engine is still running'. Number 2 means that every piece of code in the the main application needs to be aware of the possibility of termination and have short-circuit code to handle such.
So if you truly must, necessarily, absolutely, perform some additional clean-up, choose method 2. Add a global variable, preferably a std::atomic<bool> if C++11 is available to you, and use that to track whether or not the program should exit. Have the handler set it to true
// Shared global variable to track forced termination.
std::atomic<bool> programShouldExit = false;
// In the console handler:
BOOL WINAPI CtrlHandler( DWORD fdwCtrlType )
{
...
programShouldExit = true;
Sleep(10000); // Sleep for 10 seconds; after this returns the program will be terminated if it hasn't already.
}
// In the main application, regular checks should be made:
if (programShouldExit.load())
{
// Short-circuit execution, such as return from function, throw exception, etc.
}
Where you can pick your favourite short-circuiting method, for instance throwing an exception and using the RAII pattern to guard resources.
In the console handler, we sleep for as long as we think we can get away with (it doesn't really matter); the hope is that the main thread will have exited by then causing the application to exit. If not, either the sleep ends, the handler returns and the application is closed, or the OS became impatient and killed the process.
Conclusion: Don't bother with clean-up. Even if there is something you prefer to have done, such as deleting temporary files, I'd recommend you don't. It's truly not worth the hassle (but that's my opinion). If you really must, then use thread-safe means to notify the main thread that it must exit. Modify all longer-running code to handle the exit status and all other code to handle the failure of the longer-running code. Exceptions and RAII can be used to make this more manageable, for instance.
And this is why I feel that it's a very poor design choice, born from legacy code. Just being able to handle an 'exit request' requires you to jump through hoops.

Multithreaded C++ application using Matlab Engine

I open Matlab engine in an initialization thread, doing :
bool MY_MATLAB_ENGINE_o::Open()
{
// Handle the case where engine is already open
if( MatlabEngine )
{
return true;
}
else if( !( MatlabEngine = engOpen( 0 ) ) )
{
return false;
}
IsEngineOpen.SetValue( true );
return true;
}
Function engOpen() opens a COM channel to Matlab.
Once the engine is open, the thread falls in wait event mode.
Then, in another thread, I do this :
bool MY_MATLAB_ENGINE_o::ChangeDirectory( QString strPath )
{
QString strPathToScript = "cd('" + strPath + "');";
QByteArray ba = strPathToScript.toLatin1();
const char* cPathToScript = ba.data();
if( MatlabEngine )
{
engEvalString( MatlabEngine, cPathToScript );
return true;
}
return false;
}
I get a CoInitialize has not been called first-chance exception on engEvalString( MatlabEngine, cPathToScript ); which seems to tell me that Matlab COM server isn't available (but Matlab engine is still up and running).
When I put everything in the same thread it works fine though, but that's not the kind of design I had in mind.
I find Matlab engine documentation lack information about engine+COM. Any idea how to have engine initialization and function calls in separated threads ?
Thanks !
EDIT following RobH's answer
I added this method to my class (instantiated in the second thread) :
bool MY_MATLAB_FUNCTION_CALL_o::PostThreadCreationHook()
{
HRESULT hr;
hr = CoInitializeEx(0, COINIT_MULTITHREADED);
if (FAILED(hr))
{
return false;
}
return true;
}
And now when I call engEvalString( MatlabEngine, cPathToScript ); I get The application called an interface that was marshalled for a different thread first-chance exception :) I have so much fun this morning ! :)
So, CoMarshalInterface() ?
CoInitialize has to be called from every thread where you use a COM object, not just the main thread.
It has been a decade since I last automated Matlab, so excuse rustiness in what follows. That you have received the CoInitialize error suggests that the engOpen call wraps underlying COM calls. Unfortunately, this exposes you unawares to the can of worms that is COM. I guess you are right and that engOpen includes a call to CoInitialize, which initialises the COM library on the current thread. To access COM objects from a thread CoInitialize must always have been called on that thread before any calls to COM (other than one permitted COM function, I forget which.)
My advice is to isolate all of your Matlab calls onto one thread now. If you do that you won't have to make the explicit CoInitialize call, and you avoid any later multithreaded COM issues. You might get your program working today by calling CoInitialize on the second thread but one day you'll be bitten by another COM problem.
[I spent about a decade elbows-deep on COM and it is full of bear traps. You could spend a few weeks reading up on a technology which Microsoft tried to hide / kill with .Net, but it is better just to take the easy (single-thread) path now and forget about it.]
Update
I'm afraid your edit has tripped you into the mire of COM threading models. COINIT_MULTITHREADED effectively tells COM that you're going to take care of all of the little nuances of threading, which is almost certainly not what you want to do. COM operates with several (last time I paid attention it was three) threading models and the parameter you pass to CoInitializeEx declares which of those models you wish to use.
Apologies to all if what follows is slightly off.
If you specify COINIT_MULTITHREADED you need to either know that the COM object you are calling is thread-safe or do the appropriate locking (and marshalling of interfaces and data between threads) yourself.
COINIT_APARTMENTTHREADED, which is probably what engOpen uses as in my experience it's the most common, lets the COM library deal with multithreading for you. The library may, for instance, create proxy and stub objects to mediate calls across threads (or process boundaries, which is what will happen when you call to Matlab.)
engOpen created a Matlab proxy object on your main thread. This proxy object can be called from the thread where it was created and, if I recall correctly, from any other thread in the 'apartment' (where CoInitializeEx has been called with COINIT_APARTMENTTHREADED.) You have tried to call through the proxy from a thread in a different threading model, the COM library has noticed, and has issued the error you mention.
In many ways COM is amazing, but the intricacies are a pain in the arse. Be thankful you are never likely to have to use Distributed COM, which is truly nasty!
Update 2
My ancient memory of COM threading models is wrong. This MSDN page states that there is one thread per apartment with COINIT_APARTMENTTHREADED. COM objects can be accessed using the same interface pointer from all threads in the apartment where they were created. For COINIT_APARTMENTTHREADED that means just the thread where the object was created. In COINIT_MULTITHREADED that would be all the threads in the multithreaded apartment but (1) you don't get to choose which thread the Matlab engine is created on if you use engOpen and (2) trying to call a COM object that you didn't write from a multithreaded apartment is risky. The original OLE threading model only allowed COM calls from the main GUI thread, BTW.

CoInitializeEx returning S_OK when called inside a COM object

Some time ago, I had to modify an old COM DLL (Visual C++ 2010, ATL) migrating it from "Apartment" threading model to "Both", i.e. it can now be called from both STA and MTA threads without serializing calls (of course, I had to add internal synchronization for shared data). This in turn caused problems when translating COM events (connection points) to .NET events when my DLL is called via Interop from a .NET application (I must support both STA and MTA even in .NET applications).
To tackle these problems, I changed the way events are fired.
1) If the DLL is called in a STA context, it works like before, i.e. it creates an invisible window, then, when the event must be raised, it calls PostMessage to that window, then the main STA thread calls the actual event-firing code, i.e. CProxy_IMyEventFiringInterface member functions (CProxy_IMyEventFiringInterface derives from IConnectionPointImpl).
2) if the DLL is called in a MTA context, I don't have a main COM thread and I cannot do PostMessage, so I use a custom thread I create and let that thread call IConnectionPointImpl functions.
But AFAIK there is no Windows API that detects if the calling thread is STA or MTA. Many websites suggest to use CoInitializeEx like this:
HRESULT hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
switch(hr)
{
case S_FALSE: // we are in a Multi-Threaded Apartment (MTA)
CoUninitialize(); // each successful call to CoInitialize or CoInitializeEx, including any call that returns S_FALSE, must be balanced by a corresponding call to CoUninitialize
break;
case RPC_E_CHANGED_MODE: // we are in a Single-Threaded Apartment (STA)
break;
default: // IMPOSSIBLE!!!!
}
I decided to put this call to CoInitializeEx in CMyComComponent::FinalConstruct.
Everything worked fine... till today. In a customer scenario, I see from my tracing tool that for a certain .NET EXE application (I do not have the source code) the above code ends up in the default branch because CoInitializeEx returned S_OK.
How can this be possible? Microsoft documentation says that S_OK means "The COM library was initialized successfully on this thread", but I am INSIDE a COM object, the COM library MUST be already initalized!
By the way, the default branch does not shut down the application, but, since S_OK was returned, it called CoUninitialize (each successful call to CoInitialize or CoInitializeEx, including any call that returns S_FALSE, must be balanced by a corresponding call to CoUninitialize) and then the DLL goes on assuming STA (a wrong move in hindsight). But it's not STA: in fact, a later PostMessage returns FALSE.
I can simply change the code to use MTA as the default if CoInitializeEx(NULL, COINIT_MULTITHREADED) returns S_OK, I should have done it right from the start.
But I also want to be SURE that this is the right thing to do, to avoid further problems in the future.
Thank you very much
Demetrio
This is possible when you are on implicit MTA thread. The correct function is like this:
BOOL IsMultiThreadedApartment() throw()
{
HRESULT nResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if(SUCCEEDED(nResult))
CoUninitialize();
return SUCCEEDED(nResult);
}
You are hitting this unusual situation because the caller application failed to initialize COM on the thread it instantiates your class on. However be aware that if later the caller initializes the thread as STA, you might get into tricky situation having your class running in MTA mode on STA thread. Doing CoInitializeEx yourself on the other hand might cause failure of the caller to do erroneously late COM initialization.

C++ - Should data passed to a thread be volatile?

In Microsoft Visual C++ I can call CreateThread() to create a thread by starting a function with one void * parameter. I pass a pointer to a struct as that parameter, and I see a lot of other people do that as well.
My question is if I am passing a pointer to my struct how do I know if the structure members have been actually written to memory before CreateThread() was called? Is there any guarantee they won't be just cached? For example:
struct bigapple { string color; int count; } apple;
apple.count = 1;
apple.color = "red";
hThread = CreateThread( NULL, 0, myfunction, &apple, 0, NULL );
DWORD WINAPI myfunction( void *param )
{
struct bigapple *myapple = (struct bigapple *)param;
// how do I know that apple's struct was actually written to memory before CreateThread?
cout << "Apple count: " << myapple->count << endl;
}
This afternoon while I was reading I saw a lot of Windows code on this website and others that passes in data that is not volatile to a thread, and there doesn't seem to be any memory barrier or anything else. I know C++ or at least older revisions are not "thread aware" so I'm wondering if maybe there's some other reason. My guess would be the compiler sees that I've passed a pointer &apple in a call to CreateThread() so it knows to write out members of apple before the call.
Thanks
No. The relevant Win32 thread functions all take care of the necessary memory barriers. All writes prior to CreateThread are visible to the new thread. Obviously the reads in that newly created thread cannot be reordered before the call to CreateThread.
volatile would not add any extra useful constraints on the compiler, and merely slow down the code. In practice thiw wouldn't be noticeable compared to the cost of creating a new thread, though.
No, it should not be volatile. At the same time you are pointing at the valid issue. Detailed operation of the cache is described in the Intel/ARM/etc papers.
Nevertheless you can safely assume that the data WILL BE WRITTEN. Otherwise too many things will be broken. Several decades of experience tell that this is so.
If thread scheduler will start thread on the same core, the state of the cache will be fine, otherwise, if not, kernel will flush the cache. Otherwise, nothing will work.
Never use volatile for interaction between threads. It is an instruction on how to handle data inside the thread only (use a register copy or always reread, etc).
First, I think optimizer cannot change the order at expense of the correctness. CreateThread() is a function, parameter binidng for function calls happens before the call is made.
Secondly, volatile is not very helpful for the purpose you intend. Check out this article.
You're struggling into a non-problem, and are creating at least other two...
Don't worry about the parameter given to CreateThread: if they exist at the time the thread is created they exist until CreateThread returns. And since the thread who creates them does not destroy them, they are also available to the other thread.
The problem now becomes who and when they will be destroyed: You create them with new so they will exist until a delete is called (or until the process terminates: good memory leak!)
The process terminate when its main thread terminate (and all other threads will also be terminated as well by the OS!). And there is nothing in your main that makes it to wait for the other thread to complete.
Beware when using low level API like CreateThread form languages that have thir own library also interfaced with thread. The C-runtime has _beginthreadex. It call CreateThread and perform also other initialization task for the C++ library you will otherwise miss. Some C (and C++) library function may not work properly without those initializations, that are also required to properly free the runtime resources at termination. Unsing CreateThread is like using malloc in a context where delete is then used to cleanup.
The proper main thread bnehavior should be
// create the data
// create the other thread
// // perform othe task
// wait for the oter thread to terminate
// destroy the data
What the win32 API documentation don't say clearly is that every HANDLE is waitable, and become signaled when the associate resource is freed.
To wait for the other thread termination, you main thread will just have to call
WaitForSingleObject(hthread,INFINITE);
So the main thread will be more properly:
{
data* pdata = new data;
HANDLE hthread = (HANDLE)_beginthreadex(0,0,yourprocedure, pdata,0,0);
WaitForSingleObject(htread,INFINITE);
delete pdata;
}
or even
{
data d;
HANDLE hthread = (HANDLE)_beginthreadex(0,0,yourprocedure, &d,0,0);
WaitForSingleObject(htread,INFINITE);
}
I think the question is valid in another context.
As others have pointed out using a struct and the contents is safe (although access to the data should by synchronized).
However I think that the question is valid if you hav an atomic variable (or a pointer to one) that can be changed outside the thread. My opinion in that case would be that volatile should be used in this case.
Edit:
I think the examples on the wiki page are a good explanation http://en.wikipedia.org/wiki/Volatile_variable

WinINet asynchronous mode disaster

Sorry for such a long question. It's just I've spent several days trying to solve my problem, and I'm exhausted.
I'm trying to use WinINet in an asynchronous mode. And I must say... this is simply insane. I really can't understand this. It does so many things, but unfortunately its asynchronous API is so much poorly designed that it just can't be used in a serious application with high stability demands.
My problem is the following: I need to do a lot of HTTP/HTTPS transactions serially, whereas I also need to be able to abort them immediately at request.
I was going to use WinINet in the followig way:
Initialize WInINet usage via InternetOpen function with INTERNET_FLAG_ASYNC flag.
Install a global callback function (via InternetSetStatusCallback).
Now, in order to perform a transaction that's what I thought to do:
Allocate a per-transaction structure with various members describing the transaction state.
Call InternetOpenUrl to initiate the transaction. In the asynchronous mode it usually immediately returns with an error, which is ERROR_IO_PENDING. One of its parameters is the 'context', the value which will be passed to the callback function. We set it to the pointer to the per-transaction state structure.
Very shortly after this the global callback function is called (from another thread) with status INTERNET_STATUS_HANDLE_CREATED. At this moment we save the WinINet session handle.
Eventually callback function is invoked with INTERNET_STATUS_REQUEST_COMPLETE when the transaction is complete. This allows us to use some notification mechanism (such as setting an event) to notify the originating thread that the transaction is complete.
The thread that issued the transaction realizes that it's complete. Then it does the cleanup: closes the WinINet session handle (by InternetCloseHandle), and deletes the state structure.
So far there seems to be no problem.
How to abort a transaction which is in the middle of execution? One way is to close the appropriate WinINet handle. And since WinINet doesn't have functions such as InternetAbortXXXX - closing the handle seems to be the only way to abort.
Indeed this worked. Such a transaction completes immediately with ERROR_INTERNET_OPERATION_CANCELLED error code.
But here all the problems begin...
The first unpleasant surprise that I've encountered is that WinINet tends to invoke sometimes the callback function for the transaction even after it has already been aborted.
According to the MSDN the INTERNET_STATUS_HANDLE_CLOSING is the last invocation of the callback function. But it's a lie. What I see is that sometimes there's a consequent INTERNET_STATUS_REQUEST_COMPLETE notification for the same handle.
I also tried to disable the callback function for the transaction handle right before closing it, but this didn't help. It seems that the callback invocation mechanism of the WinINet is asynchronous. Hence - it may call the callback function even after the transaction handle has been closed.
This imposes a problem: as long as WinINet may call the callback function - obviously I can't free the transaction state structure. But how the hell do I know whether or not WinINet will be so kind to call it? From what I saw - there's no consistency.
Nevertheless I've worked this around. Instead I now keep a global map (protected by the critical section of course) of allocated transaction structures. Then, inside the callback function I ensure that the transaction indeed exists and lock it for the duration of the callback invocation.
But then I've discovered another problem, which I couldn't solve so far. It arises when I abort a transaction very shortly after it's started.
What happens is that I call InternetOpenUrl, which returns the ERROR_IO_PENDING error code. Then I just wait (very short usually) until the callback function will be called with the INTERNET_STATUS_HANDLE_CREATED notification. Then - the transaction handle is saved, so that now we have an opportunity to abort without handle/resource leaks, and we may go ahead.
I tried to do the abort exactly after this moment. That is, close this handle immediately after I receive it.
Guess what happens? WinINet crashes, invalid memory access! And this is not related to whatever I do in the callback function. The callback function isn't even called, the crash is somewhere deep inside the WinINet.
On the other hand if I wait for the next notification (such as 'resolving name') - usually it works. But sometimes crashes as well!
The problem seems to disappear if I put some minimal Sleep between obtaining the handle and closing it. But obviously this can't be accepted as a serious solution.
All this makes me conclude: The WinINet is poorly designed.
There's no strict definition about the scope of the callback function invocation for the specific session (transaction).
There's no strict definition about the moment from which I'm allowed to close WinINet handle.
Who knows what else?
Am I wrong? Is that something that I don't understand? Or WinINet just can't be safely used?
EDIT:
This is the minimal code block that demonstrates the 2nd issue: crash.
I've removed all the error handling and etc.
HINTERNET g_hINetGlobal;
struct Context
{
HINTERNET m_hSession;
HANDLE m_hEvent;
};
void CALLBACK INetCallback(HINTERNET hInternet, DWORD_PTR dwCtx, DWORD dwStatus, PVOID pInfo, DWORD dwInfo)
{
if (INTERNET_STATUS_HANDLE_CREATED == dwStatus)
{
Context* pCtx = (Context*) dwCtx;
ASSERT(pCtx && !pCtx->m_hSession);
INTERNET_ASYNC_RESULT* pRes = (INTERNET_ASYNC_RESULT*) pInfo;
ASSERT(pRes);
pCtx->m_hSession = (HINTERNET) pRes->dwResult;
VERIFY(SetEvent(pCtx->m_hEvent));
}
}
void FlirtWInet()
{
g_hINetGlobal = InternetOpen(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, INTERNET_FLAG_ASYNC);
ASSERT(g_hINetGlobal);
InternetSetStatusCallback(g_hINetGlobal, INetCallback);
for (int i = 0; i < 100; i++)
{
Context ctx;
ctx.m_hSession = NULL;
VERIFY(ctx.m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL));
HINTERNET hSession = InternetOpenUrl(
g_hINetGlobal,
_T("http://ww.google.com"),
NULL, 0,
INTERNET_FLAG_NO_UI | INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_RELOAD,
DWORD_PTR(&ctx));
if (hSession)
ctx.m_hSession = hSession;
else
{
ASSERT(ERROR_IO_PENDING == GetLastError());
WaitForSingleObject(ctx.m_hEvent, INFINITE);
ASSERT(ctx.m_hSession);
}
VERIFY(InternetCloseHandle(ctx.m_hSession));
VERIFY(CloseHandle(ctx.m_hEvent));
}
VERIFY(InternetCloseHandle(g_hINetGlobal));
}
Usually on first/second iteration the application crashes. One of the thread created by the WinINet generates an access violation:
Access violation reading location 0xfeeefeee.
Worth to note that the above address has special meaning to the code written in C++ (at least MSVC).
AFAIK when you delete an object that has a vtable (i.e. - has virtual functions) - it's set to the above address.
So that it's an attempt to call a virtual function of an already-deleted object.
declaration of Context ctx is the source of problem, it is declared within a for(;;) loop, so it's a local variable created for each loop, it will be destroyed and no longer accessible at end of each loop.
as a result, when a callback is invoked, ctx has been destroyed already, pointer being passed to callback points to a destroyed ctx, invalid memory pointer causes the crash.
Special thanks to Luke.
All the problems disappear when I explicitly use InternetConnect + HttpOpenRequest + HttpSendRequest instead of all-in-one InternetOpenUrl.
I don't receive any notifications on the request handle (not to confuse with the 'connection' handle). Plus no more crashes.