The EX27C Example -- A Persistent Storage Client Program

This program is similar to EX27A in function—indeed, the storage files are compatible. Internally, however, both worker threads use the persistent COM class CText (EX27B) for loading and storing text.

To prepare EX27C, open the \vcpp32\ex27c\ex27c.dsw workspace and build the project. Run the program from the debugger, first choosing Write from the Storage menu and then choosing Read. Observe the output in the Debug window.

The menu, the view class, and the application class are the same as the EX27A versions. Only the thread code is different.

Figure 27-5 lists the code for both the WriteThread.cpp and the ReadThread.cpp files.

,

WRITETHREAD.CPP

 #include "StdAfx.h" #include "Thread.h" #include "itext.h" CLSID g_clsid; // for the Text server int g_nIndent = 0; const char* g_szBlanks = "                                          "; const char* g_szRootStorageName = "\\direct.stg"; UINT WriteThreadProc(LPVOID pParam) {     USES_CONVERSION;     LPSTORAGE pStgRoot = NULL;     g_nIndent = 0;     ::CoInitialize(NULL);     ::CLSIDFromProgID(L"EX27B.TEXT", &g_clsid);     VERIFY(::StgCreateDocfile(T2COLE(g_szRootStorageName),            STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE,            0, &pStgRoot) == S_OK);     ReadDirectory("\\", pStgRoot);     pStgRoot->Release();     AfxMessageBox("Write complete");     return 0; } void ReadDirectory(const char* szPath, LPSTORAGE pStg) {     // recursive function     USES_CONVERSION;     WIN32_FIND_DATA fData;     HANDLE h;     char szNewPath[MAX_PATH];     char szStorageName[100];     char szStreamName[100];     char szData[81];     char* pch = NULL;     LPSTORAGE pSubStg = NULL;     LPSTREAM pStream = NULL;     LPPERSISTSTREAM pPersistStream = NULL;     g_nIndent++;     strcpy(szNewPath, szPath);     strcat(szNewPath, "*.*");     h = ::FindFirstFile(szNewPath, &fData);     if (h == (HANDLE) 0xFFFFFFFF) return;  // can't find directory     do {             if (!strcmp(fData.cFileName, "..") ||                 !strcmp(fData.cFileName, ".") ) continue;             while((pch = strchr(fData.cFileName, `!')) != NULL) {                 *pch = `|';         }         if (fData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {             // It's a directory, so make a storage             strcpy(szNewPath, szPath);             strcat(szNewPath,fData.cFileName);             strcat(szNewPath, "\\");             strcpy(szStorageName, fData.cFileName);             szStorageName[31] = `\0';    // limit imposed by OLE             TRACE("%0.*sStorage = %s\n", (g_nIndent - 1) * 4,                   g_szBlanks, szStorageName);             VERIFY(pStg->CreateStorage(T2COLE(szStorageName),                    STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,                    0, 0, &pSubStg) == S_OK);             ASSERT(pSubStg != NULL);             ReadDirectory(szNewPath, pSubStg);             pSubStg->Release();         }         else {             if ((pch = strrchr(fData.cFileName, `.')) != NULL) {                 if (!stricmp(pch, ".TXT")) {                     // It's a text file, so make a stream                     strcpy(szStreamName, fData.cFileName);                     strcpy(szNewPath, szPath);                     strcat(szNewPath, szStreamName);                     szStreamName[32] = `\0'; // OLE max length                     TRACE("%0.*sStream = %s\n", (g_nIndent - 1) * 4,                           g_szBlanks, szNewPath);                     CStdioFile file(szNewPath, CFile::modeRead);                     // Ignore zero-length files                     if(file.ReadString(szData, 80)) {                         TRACE("%s\n", szData);                         VERIFY(pStg->CreateStream(T2COLE(szStreamName),                                STGM_CREATE | STGM_READWRITE |                                 STGM_SHARE_EXCLUSIVE,                                0, 0, &pStream) == S_OK);                         ASSERT(pStream != NULL);                         // Include the null terminator in the stream                         IText text;                         VERIFY(text.CreateDispatch(g_clsid));                         text.m_lpDispatch->QueryInterface                             (IID_IPersistStream,                             (void**) &pPersistStream);                         ASSERT(pPersistStream != NULL);                         text.SetText(COleVariant(szData));                         pPersistStream->Save(pStream, TRUE);                         pPersistStream->Release();                         pStream->Release();                     }                 }             }         }     } while (::FindNextFile(h, &fData));     g_nIndent—; } 

READTHREAD.CPP

 #include "StdAfx.h" #include "Thread.h" #include "itext.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE __; #endif UINT ReadThreadProc(LPVOID pParam) {     g_nIndent = 0;     ::CoInitialize(NULL);     ::CLSIDFromProgID(L"EX27B.TEXT", &g_clsid);     LPSTORAGE pStgRoot = NULL;     if(::StgOpenStorage(L"\\DIRECT.STG", NULL,                         STGM_READ|STGM_SHARE_EXCLUSIVE,                         NULL, 0, &pStgRoot) == S_OK) {         ASSERT(pStgRoot!= NULL);         ReadStorage(pStgRoot);         pStgRoot->Release();     }     else {         AfxMessageBox("Storage file not available or not readable.");     }     AfxMessageBox("Read complete");     return 0; } void ReadStorage(LPSTORAGE pStg) // reads one storage — recursive calls for substorages {     LPSTORAGE pSubStg = NULL;     LPSTREAM pStream = NULL;     LPENUMSTATSTG pEnum = NULL;     STATSTG statstg;     LPPERSISTSTREAM pPersistStream = NULL;     g_nIndent++;     if(pStg->EnumElements(0, NULL, 0, &pEnum) != NOERROR) {         ASSERT(FALSE);         return;     }     while(pEnum->Next(1, &statstg, NULL) == NOERROR) {         if(statstg.type == STGTY_STORAGE) {             VERIFY(pStg->OpenStorage(statstg.pwcsName, NULL,                    STGM_READ|STGM_SHARE_EXCLUSIVE,                    NULL, 0, &pSubStg) == S_OK);             ASSERT(pSubStg != NULL);             ReadStorage(pSubStg);             pSubStg->Release();         }         else if(statstg.type == STGTY_STREAM) {             VERIFY(pStg->OpenStream(statstg.pwcsName, NULL,                    STGM_READ|STGM_SHARE_EXCLUSIVE,                    0, &pStream) == S_OK);             ASSERT(pStream != NULL);             IText text;             VERIFY(text.CreateDispatch(g_clsid));             text.m_lpDispatch->QueryInterface(IID_IPersistStream,                  (void**) &pPersistStream);             ASSERT(pPersistStream != NULL);             pPersistStream->Load(pStream);             pPersistStream->Release();             COleVariant va = text.GetText();             ASSERT(va.vt == VT_BSTR);             CString str = va.bstrVal;             TRACE("%s\n", str);             pStream->Release();         }         else {             ASSERT(FALSE);  // LockBytes?         }         ::CoTaskMemFree(statstg.pwcsName);     }     pEnum->Release();     g_nIndent—; } 

Figure 27-5. The code listing for the two worker threads in EX27C.

Look at the second half of the ReadDirectory function in the WriteThread.cpp file in Figure 27-5. For each TXT file, the program constructs a CText object by constructing an IText driver object and then calling CreateDispatch. Then it calls the SetText member function to write the first line of the file to the object. After that, the program calls IPersistStream::Save to write the object to the compound file. The CText object is deleted after the IPersistStream pointer is released and after the IText object is deleted, releasing the object's IDispatch pointer.

Now look at the second half of the ReadStorage function in the ReadThread.cpp file. Like ReadDirectory, it constructs an IText driver object and calls CreateDispatch. Then it calls QueryInterface to get the object's IPersistStream pointer, which it uses to call Load. Finally, the program calls GetText to retrieve the line of text for tracing.

As you've learned already, a COM component usually implements IPersistStorage, not IPersistStream. The CText class could have worked this way, but then the compound file would have been more complex because each TXT file would have needed both a storage element (to support the interface) and a subsidiary stream element (to hold the text).

Now get ready to take a giant leap. Suppose you have a true creatable-by-CLSID COM component that supports the IPersistStorage interface. Recall the IStorage functions for class IDs. If a storage element contains a class ID, together with all the data an object needs, COM can load the server, use the class factory to construct the object, get an IPersistStorage pointer, and call Load to load the data from a compound file. This is a preview of compound documents, which you'll see in Chapter 28.



Programming Microsoft Visual C++
Programming Microsoft Visual C++
ISBN: 1572318570
EAN: 2147483647
Year: 1997
Pages: 332

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net