Just when you thought you knew all the ways to download a file from
the Internet, you're going to learn about another one. With asynchronous
moniker files, you'll be doing all your programming in your application's main
thread without blocking the user interface. Sounds like magic, doesn't it? The
magic is inside the Windows URLMON DLL, which depends on WinInet and is
used by Microsoft Internet Explorer. The MFC
CAsyncMonikerFile class makes the programming easy, but you should know a little theory first.
Monikers
A moniker is a "surrogate" COM object that holds the name (URL) of the "real" object, which could be an embedded component but more often is just an Internet file (HTML, JPEG, GIF, and so on). Monikers implement the IMoniker interface, which has two important member functions: BindToObject and BindToStorage. The BindToObject function puts an object into the running state, and the BindToStorage function provides an IStream or an IStorage pointer from which the object's data can be read. A moniker has an associated IBindStatusCallback interface with member functions such as OnStartBinding and OnDataAvailable, which are called during the process of reading data from a URL.
The callback functions are called in the thread that created the
moniker. This means that the URLMON DLL must set up an invisible window in
the calling thread and send the calling thread messages from another thread,
which uses WinInet functions to read the URL. The window's message handlers
call the callback functions.
The MFC CAsyncMonikerFile Class
Fortunately, MFC can shield you from the COM interfaces described above.
The CAsyncMonikerFile class is derived from
CFile, so it acts like a regular file. Instead of opening a disk file, the class's
Open member function gets an
IMoniker pointer and encapsulates the
IStream interface returned from a call to
BindToStorage. Furthermore, the class has virtual functions that are tied to the
member functions of IBindStatusCallback. Using this class is a breeze; you
construct an object or a derived class and call the
Open member function, which returns immediately. Then you wait for calls to overridden virtual functions such
as OnProgress and OnDataAvailable, named, not coincidentally, after their
IBindStatusCallback equivalents.
Using the CAsyncMonikerFile Class in a Program
Suppose your application downloads data from a dozen URLs but has only one class derived from CAsyncMonikerFile. The overridden callback functions must figure out where to put the data. That means you must associate each derived class object with some UI element in your program. The steps listed below illustrate one of many ways to do this. Suppose you want to list the text of an HTML file in an edit control that's part of a form view. This is what you can do:
void CMyMonikerFile::OnDataAvailable(DWORD dwSize, DWORD bscfFlag) { try { UINT nBytesRead = Read(m_buffer, MAXBUF - 1); TRACE("nBytesRead = %d\n", nBytesRead); m_buffer[nBytesRead] = `\0'; // necessary for edit control // The following two lines add text to the edit control m_edit.SendMessage(EM_SETSEL, (WPARAM) 999999, 1000000); m_edit.SendMessage(EM_REPLACESEL, (WPARAM) 0, (LPARAM) m_buffer); } catch(CFileException* pe) { TRACE("File exception %d\n, pe->m_cause"); pe->Delete(); } }
m_myEmbeddedMonikerFile.m_edit.SubClassDlgItem(ID_MYEDIT, this);
m_myEmbeddedMonikerFile.Open("http://host/filename");
For a large file, OnDataAvailable will be called several times, each time adding text to the edit control. If you override OnProgress or OnStopBinding in your derived moniker file class, your program can be alerted when the transfer is finished. You can also check the value of bscfFlag in OnDataAvailable to determine whether the transfer is completed. Note that everything here is in your main thread andmost importantthe moniker file object must exist for as long as the transfer is in progress. That's why it's a data member of the view class.
In the WinInet examples earlier in this chapter, you started a worker thread that made blocking calls and sent a message to the main thread when it was finished. With asynchronous moniker files, the same thing happensthe transfer takes place in another thread, which sends messages to the main thread. You just don't see the other thread. There is one very important difference, however, between asynchronous moniker files and WinInet programming: with blocking WinInet calls, you need a separate thread for each transfer; with asynchronous moniker files, only one extra thread handles all transfers together. For example, if you're writing a browser that must download 50 bitmaps simultaneously, using asynchronous moniker files saves 49 threads, which makes the program much more efficient.
Of course, you have some extra control with WinInet, and it's easier to get information from the response headers, such as total file length. Your choice of programming tools, then, depends on your application. The more you know about your options, the better your choice will be.