|
|
/*++
Copyright (c) 1997 Microsoft Corporation
awd library
Routines for reading from an AWD file.
Author: Brian Dewey (t-briand) 1997-7-2 --*/
#include <stdio.h>
#include <stdlib.h>
#include <ole2.h> // AWD is an OLE compound document.
#include <assert.h>
#include <awdlib.h> // Header file for this library.
// ------------------------------------------------------------
// Auxiliary routines
// OpenAWDFile
//
// Opens an AWD file and fills in the psStorages structure.
//
// Parameters:
// pwcsFilename name of file to open (UNICODE)
// psStorages Pointer to structure that will hold
// the major storages used in an AWD file.
//
// Returns:
// TRUE on success, FALSE on failure. One or more storages may be
// NULL even when the routine returns TRUE. The client needs to
// check for this.
//
// Author:
// Brian Dewey (t-briand) 1997-6-30
BOOL OpenAWDFile(const WCHAR *pwcsFilename, AWD_FILE *psStorages) { HRESULT hStatus; // Status indicator for reporting errors.
hStatus = StgOpenStorage(pwcsFilename, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &psStorages->psAWDFile); if(FAILED(hStatus)) { return FALSE; } // If we get here, we've succeeded. Now open the related storages.
psStorages->psDocuments = OpenAWDSubStorage(psStorages->psAWDFile, L"Documents"); psStorages->psPersistInfo = OpenAWDSubStorage(psStorages->psAWDFile, L"Persistent Information"); psStorages->psDocInfo = OpenAWDSubStorage(psStorages->psPersistInfo, L"Document Information"); psStorages->psPageInfo = OpenAWDSubStorage(psStorages->psPersistInfo, L"Page Information"); psStorages->psGlobalInfo = OpenAWDSubStorage(psStorages->psPersistInfo, L"Global Information"); return TRUE; }
// CloseAWDFile
//
// Closes an AWD file.
//
// Parameters:
// psStorages Pointer to the AWD file.
//
// Returns:
// TRUE on success, FALSE otherwise.
//
// Author:
// Brian Dewey (t-briand) 1997-6-27
BOOL CloseAWDFile(AWD_FILE *psStorages) { // This should probably use some exception mechanism.
BOOL success = TRUE; if(FAILED(psStorages->psGlobalInfo->lpVtbl->Release(psStorages->psGlobalInfo))) { success = FALSE; } if(FAILED(psStorages->psPageInfo->lpVtbl->Release(psStorages->psPageInfo))) { success = FALSE; } if(FAILED(psStorages->psDocInfo->lpVtbl->Release(psStorages->psDocInfo))) { success = FALSE; } if(FAILED(psStorages->psPersistInfo->lpVtbl->Release(psStorages->psPersistInfo))) { success = FALSE; } if(FAILED(psStorages->psDocuments->lpVtbl->Release(psStorages->psDocuments))) { success = FALSE; } if(FAILED(psStorages->psAWDFile->lpVtbl->Release(psStorages->psAWDFile))) { success = FALSE; } return success; }
// OpenAWDSubStorage
//
// Get a substorage from a parent storage. Checks for errors
// and exits on error conditions. Note that it's not an error if
// the substorage doesn't exist, so the caller should still check for NULL.
//
// Parameters:
// psParent Pointer to the parent storage.
// pwcsStorageName Name of the substorage (UNICODE).
//
// Returns:
// A pointer to the substorage, or NULL if the substorage doesn't exist.
//
// Author:
// Brian Dewey (t-briand) 1997-6-27
IStorage * OpenAWDSubStorage(IStorage *psParent, const WCHAR *pwcsStorageName) { IStorage *psSubStorage; // The substorage.
HRESULT hStatus; // Status of the call.
if(psParent == NULL) return NULL; hStatus = psParent->lpVtbl->OpenStorage(psParent, pwcsStorageName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &psSubStorage); if(FAILED(hStatus)) { if(hStatus == STG_E_FILENOTFOUND) { fwprintf(stderr, L"OpenAWDSubStorage:No such substorage '%s'.\n", pwcsStorageName); return NULL; } // use the wide-printf() to get the UNICODE filename.
fwprintf(stderr, L"OpenAWDSubStorage:Unable to open substorage %s.\n", pwcsStorageName); exit(1); } return psSubStorage; }
// OpenAWDStream
//
// This function opens an AWD stream for exclusive read access. It
// checks for errors and exits on an error condition. Not found is
// not considered a fatal error.
//
// Parameters:
// psStorage Pointer to the storage holding the stream.
// pwcsStreamName Name of the stream (UNICODE).
//
// Returns:
// A pointer to the stream. If no such stream exists, returns NULL.
// It will abort on any other error.
//
// Author:
// Brian Dewey (t-briand) 1997-6-27
IStream * OpenAWDStream(IStorage *psStorage, const WCHAR *pwcsStreamName) { HRESULT hStatus; IStream *psStream;
assert(psStorage != NULL); // Sanity check.
fwprintf(stderr, L"OpenAWDStream:Opening stream '%s'.\n", pwcsStreamName); hStatus = psStorage->lpVtbl->OpenStream(psStorage, pwcsStreamName, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &psStream); if(FAILED(hStatus)) { if(hStatus == STG_E_FILENOTFOUND) return NULL; fwprintf(stderr, L"OpenAWDStream:Error %x when opening stream %s.\n", hStatus, pwcsStreamName); exit(1); } return psStream; }
// AWDViewed
//
// This function tests if the AWD file has previously been viewed by
// a viewer. It does this by checking for the presence of a stream
// called "BeenViewed." See AWD specs.
//
// Parameters:
// psStorage Pointer to the "Persistent Information"
// substorage.
//
// Returns:
// TRUE if the file has been viewed, FALSE otherwise.
//
// Author:
// Brian Dewey (t-briand) 1997-6-27
BOOL AWDViewed(AWD_FILE *psStorages) { IStream *psStream; // Pointer to the been-viewed stream.
HRESULT hStatus; // Holds the status of the call.
// Attempt to open the BeenViewed stream.
hStatus = psStorages->psPersistInfo->lpVtbl->OpenStream(psStorages->psPersistInfo, L"BeenViewed", NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &psStream); // If succeeded, then definately found.
if(SUCCEEDED(hStatus)) return TRUE; // If not found, then definately hasn't been viewed.
if(hStatus == STG_E_FILENOTFOUND) return FALSE; fprintf(stderr, "AWDViewed:Unexpected status %x.\n", hStatus); // Assume that we've been viewed.
return TRUE; }
// DumpAWDDocuments
//
// This function prints out the name of the fax documents contained in the
// file in their display order. Output is to stdout.
//
// New AWD files have a "Display Order" stream in the psGlobalInfo that defines
// all of the documents. Old AWD files need to enumerate through the
// "Documents" substorage.
//
// Parameters:
// psStorages Pointer to the storages of an AWD file.
//
// Returns:
// Nothing.
//
// Author:
// Brian Dewey (t-briand) 1997-6-27
void DumpAWDDocuments(AWD_FILE *psStorages) { printf("Document list:\n"); printf("-------- -----\n"); EnumDocuments(psStorages, DisplayDocNames);
}
// EnumDocuments
//
// This function enumerates through all of the things in the "Documents"
// substorage and prints their names. It's a helper routine to DumpAWDDocuments().
//
// Parameters:
// psStorages Pointer to the storages in the AWD file.
// pfnDocProc Pointer to function that should be called
// with the names of the documents in the
// AWD file.
//
// Returns:
// TRUE if all iterations succeeded, FALSE otherwise.
//
// Author:
// Brian Dewey (t-briand) 1997-6-30
BOOL EnumDocuments(AWD_FILE *psStorages, AWD_DOC_PROCESSOR pfnDocProc) { IEnumSTATSTG *psEnum; STATSTG sData; WCHAR awcNameBuf[MAX_AWD_NAME]; // 32 == longest possible name.
UINT uiNameOffset; IStream *psDisplayOrder; // Points to the display order stream.
char chData; // A single byte of data.
ULONG cbRead; // Count of bytes read.
//[RB]assert(psGlobalInfo != NULL); // Sanity check.
psDisplayOrder = OpenAWDStream(psStorages->psGlobalInfo, L"Display Order"); if(psDisplayOrder == NULL) { fprintf(stderr, "There is no 'Display Order' stream. This is an old AWD file.\n"); if(FAILED(psStorages->psDocuments->lpVtbl->EnumElements(psStorages->psDocuments, 0, NULL, 0, &psEnum))) { return FALSE; } sData.pwcsName = awcNameBuf; while(psEnum->lpVtbl->Next(psEnum, 1, &sData, NULL) == S_OK) { // We succeeded!
if(!(*pfnDocProc)(psStorages, sData.pwcsName)) return FALSE; // The enumeration has been aborted.
} psEnum->lpVtbl->Release(psEnum); return TRUE; }
// The display order list is a stream of document names. Each
// name is null-terminated, and a second null ends the stream.
// The document names are ANSI characters.
//
// The easy way to read this, which is what I do, is to read
// the stream a byte at a time. For efficiency, this should be
// changed to reading larger blocks.
// Prime the loop by reading the first character.
psDisplayOrder->lpVtbl->Read(psDisplayOrder, &chData, 1, &cbRead); while(chData) { // Until I've read a null...
// This inner loop prints out a single string.
uiNameOffset = 0; while(chData) { awcNameBuf[uiNameOffset++] = chData; psDisplayOrder->lpVtbl->Read(psDisplayOrder, &chData, 1, &cbRead); }; awcNameBuf[uiNameOffset] = 0; // We've now read & printed a whole string. Call the enumerator.
if(!(*pfnDocProc)(psStorages, awcNameBuf)) { psDisplayOrder->lpVtbl->Release(psDisplayOrder); return FALSE; // The enumeration has been aborted.
} // And re-prime the engine.
psDisplayOrder->lpVtbl->Read(psDisplayOrder, &chData, 1, &cbRead); }
psDisplayOrder->lpVtbl->Release(psDisplayOrder); return TRUE; }
// DisplayDocNames
//
// This is a simple little routine that prints out the names of all of the
// documents in an AWD file. Used in conjunction w/ EnumDocuments.
//
// Parameters:
// psStorages Pointer to the storages in the AWD file.
// pwcsDocName Name of a document (UNICODE).
//
// Returns:
// TRUE.
//
// Author:
// Brian Dewey (t-briand) 1997-6-30
BOOL DisplayDocNames(AWD_FILE *psStorages, const WCHAR *pwcsDocName) { wprintf(L"Document '%s'.\n", pwcsDocName); return TRUE; }
// DetailedDocDump
//
// This function displays lots of information about a particular document.
//
// Parameters:
// psStorages Pointer to the storages in the AWD file.
// pwcsDocName Name of a document (UNICODE).
//
// Returns:
// TRUE on success; FALSE on error.
//
// Author:
// Brian Dewey (t-briand) 1997-6-30
BOOL DetailedDocDump(AWD_FILE *psStorages, const WCHAR *pwcsDocName) { IStream *psDocInfoStream; // Stream containing doc information.
DOCUMENT_INFORMATION sDocInfo; // Document information.
ULONG cbRead; // Count of bytes read.
wprintf(L"Information for document '%s' --\n", pwcsDocName); psDocInfoStream = OpenAWDStream(psStorages->psDocInfo, pwcsDocName); if(psDocInfoStream == NULL) { fprintf(stderr, "DetailedDocDump:No document info stream.\n"); // This is not a fatal error, so don't exit.
} else { psDocInfoStream->lpVtbl->Read(psDocInfoStream, &sDocInfo, sizeof(sDocInfo), &cbRead); if(sizeof(sDocInfo) != cbRead) { fwprintf(stderr, L"DetailedDocDump:Error reading document information " L"for %s.\n", pwcsDocName); } else { printf("\tDocument signature = %x.\n", sDocInfo.Signature); printf("\tDocument version = %x.\n", sDocInfo.Version); } } PrintPageInfo(&sDocInfo.PageInformation); return TRUE; }
// PrintPageInfo
//
// This function displays the fields of a PAGE_INFORMATION structure to standard
// output.
//
// Parameters:
// psPageInfo The PAGE_INFORMATION structure to display.
//
// Returns:
// nothing.
//
// Author:
// Brian Dewey (t-briand) 1997-6-30
void PrintPageInfo(PAGE_INFORMATION *psPageInfo) { printf("\tStructure signature = %x\n", psPageInfo->Signature); printf("\tStructure version = %x\n", psPageInfo->Version);
if(psPageInfo->awdFlags & AWD_FIT_WIDTH) printf("\tAWD_FIT_WIDTH flag is set.\n"); if(psPageInfo->awdFlags & AWD_FIT_HEIGHT) printf("\tAWD_FIT_HEIGHT flag is set.\n"); if(psPageInfo->awdFlags & AWD_INVERT) printf("\tAWD_INVERT flag is set.\n"); if(psPageInfo->awdFlags & AWD_IGNORE) printf("\tAWD_IGNORE flag is set.\n");
printf("\tRotation = %d degrees counterclockwise.\n", psPageInfo->Rotation); printf("\tScaleX = %d.\n", psPageInfo->ScaleX); printf("\tScaleY = %d.\n", psPageInfo->ScaleY); }
// DumpData
//
// A simple utility function that will write the specified data to a file
// for post-mortem examining.
//
// Parameters
// pszFileName Name of the output file.
// pbData Pointer to the data.
// cbCount Number of bytes to write.
//
// Returns:
// nothing.
//
// Author:
// Brian Dewey (t-briand) 1997-7-7
void DumpData(LPTSTR pszFileName, LPBYTE pbData, DWORD cbCount) { HANDLE hFile; DWORD cbWritten;
hFile = CreateFile( pszFileName, // Open this file...
GENERIC_WRITE, // We want to write.
0, // Don't share.
NULL, // No need to inherit.
CREATE_ALWAYS, // Always create a new file.
FILE_ATTRIBUTE_COMPRESSED, // Save disk space... might want to change this later.
NULL); // No template file.
if(hFile != INVALID_HANDLE_VALUE) { WriteFile(hFile, pbData, cbCount, &cbWritten, NULL); CloseHandle(hFile); } }
|