| Tenouk C & C++ | MFC Home | Structured Storage, DLL & COM 3 | OLE & Containers 1 | Download | Site Index |


 

 

 

 

 

Structured Storage 4

 

 

 

 

 

 

Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. The supplementary notes for this tutorial are statstg, Thread.h, ReadThread.cpp and WriteThread.cpp.

  1. The MYMFC31C Example: A Persistent Storage Client Program

  2. The MYMFC31C From Scratch

  3. Compound File Fragmentation

  4. Other Compound File Advantages

 

 

The MYMFC31 Project Example: A Persistent Storage Client Program

 

This program is similar to MYMFC31A in function, indeed, the storage files are compatible. Internally, however, both worker threads use the persistent COM class CText (MYMFC31B) for loading and storing text. To prepare MYMFC31C, open the mymfc31c.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 or you can start it from scratch.

 

The MYMFC31C From Scratch

 

This is an SDI application without Automation and ActiveX Controls support.

 

Figure 32: MYMFC31C – Visual C++ new project dialog.

 

Figure 32: MYMFC31C – Visual C++ new project dialog.

 

Figure 33: MYMFC31C – AppWizard step 1 of 6, select Single document.

 

Figure 33: MYMFC31C – AppWizard step 1 of 6, select Single document.

 

Figure 34: MYMFC31C – AppWizard step 2 of 6.

 

Figure 34: MYMFC31C – AppWizard step 2 of 6.

 

Figure 35: MYMFC31C – AppWizard step 3 of 6, deselect Automation and ActiveX Controls.

 

Figure 35: MYMFC31C – AppWizard step 3 of 6, deselect Automation and ActiveX Controls.

 

Figure 36: MYMFC31C – AppWizard step 4 of 6.

 

Figure 36: MYMFC31C – AppWizard step 4 of 6.

 

Figure 37: MYMFC31C – AppWizard step 5 of 6.

 

Figure 37: MYMFC31C – AppWizard step 5 of 6.

 

Figure 38: MYMFC31C – AppWizard step 6 of 6.

 

Figure 38: MYMFC31C – AppWizard step 6 of 6.

 

Figure 39: MYMFC31C project summary.

 

Figure 39: MYMFC31C project summary.

 

Add the following menu items.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ID

Caption

-

Storage

ID_STORAGE_READ

Read

ID_STORAGE_WRITE

Write

 

Table 12.

 

Figure 40: Adding Write menu item.

 

Figure 40: Adding Write menu item.

 

Figure 41: Adding Read menu item.

 

Figure 41: Adding Read menu item.

 

Figure 42: A completed MYMFC31C menus.

 

Figure 42: A completed MYMFC31C menus.

 

Use ClassWizard to add command handlers for the previous menu items as shown in the following Table to CMymfc31cView class.

 

ID

Type

Function

ID_STORAGE_READ

Command

OnStorageRead()

ID_STORAGE_WRITE

Command

OnStorageWrite()

 

Table 13.

 

Figure 43: Adding command handlers to CMymfc31cView class.

 

Figure 43: Adding command handlers to CMymfc31cView class.

 

Add codes to the command handlers.

 

void CMymfc31cView::OnStorageRead()

{

      // TODO: Add your command handler code here

      CWinThread* pThread = AfxBeginThread(ReadThreadProc, GetSafeHwnd());

}

 

void CMymfc31cView::OnStorageWrite()

{

      // TODO: Add your command handler code here

      CWinThread* pThread = AfxBeginThread(WriteThreadProc, GetSafeHwnd());

}

 

 

MFC C++ code snippet - Compound document/structured storage

 

Listing 8.

 

Add the #include directive to mymfc31aView.cpp.

 

#include "Thread.h"

 

MFC C++ code snippet - Compound document/structured storage

 

Listing 9.

 

Add Thread.h, WriteThread.cpp and ReadThread.cpp files, the modified version of MYMFC31A to the project. Copy and paste the codes (provided in the following Listings) to the respective files.

 

Figure 44: Adding new files to project.

 

Figure 44: Adding new files to project.

 

Figure 45: Adding Thread.h file to MYMFC31C. Repeat for other two source files.

 

Figure 45: Adding Thread.h file to MYMFC31C. Repeat for other two source files.

 

Add the following lines in your StdAfx.h file:

 

#include <afxole.h>

#include <afxpriv.h> // for wide-character conversion

 

Then delete the following line (or commented out):

 

#define VC_EXTRALEAN

 

MFC C++ code snippet - Compound document/structured storage

 

Listing 10.

 

Add MYMFC31B class from the type library to the project. Click Add Class button in ClassWizard and select From a type library button.

 

Figure 46: Adding new class from type library.

 

Figure 46: Adding new class from type library.

 

Figure 47: Browsing and selecting the mymfc31b.tlb type library file.

 

Figure 47: Browsing and selecting the mymfc31b.tlb type library file.

 

Figure 48: IText class from MYMFC31B addition confirmation dialog.

 

Figure 48: IText class from MYMFC31B addition confirmation dialog.

 

 

You can verify the added class through ClassView.

 

Figure 49: Verifying the added class through ClassView.

 

Figure 49: Verifying the added class through ClassView.

 

Build and run in debug mode. Click the Write menu.

 

Figure 50: MYMFC31C in action.

 

Figure 50: MYMFC31C in action.

 

Figure 51: The write operation message box displayed.

 

Figure 51: The write operation message box displayed.

 

Then, click the Read menu.

 

Figure 52: testing the Read menu, provided the previous write operation was successful.

 

Figure 52: testing the Read menu, provided the previous write operation was successful.

 

Figure 53: The completed read operation message box.

 

Figure 53: The completed read operation message box.

 

Check the Debug window. It will look something like this (the storage and stream should be different with yours).

 

Storage = mfcjadik

    Storage = final

        Stream = \mfcjadik\final\log.txt

 

Loaded 'C:\WINDOWS\system32\xpsp2res.dll', no matching symbolic information found.

Warning: CreateDispatch returning scode = REGDB_E_CLASSNOTREG ($80040154).

    Storage = myscribble

        Storage = Debug

        Stream = \mfcjadik\myscribble\ReadMe.txt

========================================================================

 

Warning: CreateDispatch returning scode = REGDB_E_CLASSNOTREG ($80040154).

        Storage = res

Storage = mfcproject

    Storage = bkup

        Storage = mymfc29A

            Storage = Debug

            Stream = \mfcproject\bkup\mymfc29A\ReadMe.txt

========================================================================

[Trimmed]

========================================================================

 

Warning: CreateDispatch returning scode = REGDB_E_CLASSNOTREG ($80040154).

                                          Storage = res

                                          Storage = myproject

                                          Storage = childprocess

Storage = Debug

                                          Storage = console

Storage = Debug

                                          Storage = crtexecprog

[Trimmed]

Storage = Debug

Storage = Release

                                          Storage = testmydllruntime

Storage = Debug

                                          Storage = testsharedll

Storage = Debug

                                          Storage = win32prog

Storage = Debug

                                          Storage = myuserprog

                                          Storage = myuserprog

Storage = Debug

                                          Storage = RECYCLER

                                          Storage = NPROTECT

                                          Storage = S-1-5-21-1343024091-1647877149-

                                          Storage = S-1-5-21-1547161642-1580818891-

Storage = Df1

    Storage = Debug

    Stream = \RECYCLER\S-1-5-21-1547161642-1580818891-725345543-1003\Df1\ReadMe.txt

========================================================================

 

Warning: CreateDispatch returning scode = REGDB_E_CLASSNOTREG ($80040154).

    Storage = res

                                          Storage = S-1-5-21-1547161642-1580818891-

                                          Storage = S-1-5-21-1757981266-1202660629-

                                          Storage = S-1-5-21-448539723-1364589140-8

                                          Storage = S-1-5-21-448539723-1364589140-8

                                          Storage = S-1-5-21-602162358-1682526488-8

                                          Storage = System Volume Information

The thread 0x968 has exited with code 0 (0x0).

The thread 0x970 has exited with code 0 (0x0).

The thread 0x8C has exited with code 0 (0x0).

The thread 0xBCC has exited with code 0 (0x0).

The thread 0x8E4 has exited with code 0 (0x0).

The thread 0x8DC has exited with code 0 (0x0).

The program 'F:\mfcproject\mymfc31c\Debug\mymfc31c.exe' has exited with code 0 (0x0).

 

Verify the directdll.stg file creation.

 

Figure 54: Verifying the directdll.stg file creation during the previous write operation.

 

Figure 54: Verifying the directdll.stg file creation during the previous write operation.

 

The menu, the view class, and the application class are the same as the MYMFC31A versions. Only the thread code is different. Listing 11 lists the code for both the WriteThread.cpp and the ReadThread.cpp files.

 

WRITETHREAD.CPP

 

// WriteThread.cpp (MYMFC31C)

 

#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 = "\\directdll.stg";

 

UINT WriteThreadProc(LPVOID pParam)

{

    USES_CONVERSION;

    LPSTORAGE pStgRoot = NULL;

    g_nIndent = 0;

       ::CoInitialize(NULL);

       ::CLSIDFromProgID(L"MYMFC31B.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

            if(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

 

// ReadThread.cpp (MYMFC31C)

 

#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"MYMFC31B.TEXT", &g_clsid);

       LPSTORAGE pStgRoot = NULL;

       if(::StgOpenStorage(L"\\DIRECTDLL.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

                     if(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--;

}

 

 

Listing 11: The code listing for the two worker threads in MYMFC31C.

 

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 Module 27.

 

Compound File Fragment

 

Structured storage has a dark side. Like the disk drive itself, compound files can become fragmented with frequent use. If a disk drive becomes fragmented, however, you still have the same amount of free space. With a compound file, space from deleted elements isn't always recovered. This means that compound files can keep growing even if you delete data. Fortunately, there is a way to recover unused space in a compound file. You simply create a new file and copy the contents. The IStorage::CopyTo function can do the whole job in one call if you use it to copy the root storage. You can either write a stand-alone utility or build a file regeneration capability into your application.

 

Other Compound File Advantages

 

You've seen how compound files add a kind of random access capability to your programs, and you can appreciate the value of transactioning. Now consider the brave new world in which every program can read any other program's documents. We're not there yet, but we have a start. Compound files from Microsoft applications have a stream under the root storage named \005SummaryInformation. This stream is formatted as a property set, as defined for ActiveX controls. If you can decode the format for this stream, you can open any conforming file and read the summary. Visual C++ comes with a compound file viewing utility named DocFile Viewer (Dfview.exe), which uses a tree view to display the file's storages and streams.

 

Figure 55: Using DocFile Viewer to view structured storage file.

 

Figure 55: Using DocFile Viewer to view structured storage file.

 

Here is the DocFile Viewer output for the structured storage file generated by MYMFC31C (directdll.stg).

 

Figure 56: MYMFC31C’s directdll.stg structured storage file content.

 

Figure 56: MYMFC31C’s directdll.stg structured storage file content.

 

As a matter of fact, you can use DocFile Viewer to view the structure of any compound file. Are you starting to see the potential of this "universal file format?"

 

 

---------------------End------------------

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Further reading and digging:

  1. DCOM at MSDN.

  2. COM+ at MSDN.

  3. COM at MSDN.

  4. Win32 process, thread and synchronization story can be found starting from Module R.

  5. MSDN MFC 7.0 class library online documentation.

  6. MSDN MFC 9.0 class library online documentation - latest version.

  7. MSDN Library

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multibyte character set: Story and program examples.

 

 


 

| Tenouk C & C++ | MFC Home | Structured Storage, DLL & COM 3 | OLE & Containers 1 | Download | Site Index |