util.cpp
/*****************************************************************************
*
* File: Util.cpp
*
* Description: This file contains definitions for helper classes and functions.
*
* ------------------------------------
*
* This file is part of the Microsoft Windows SDK Code Samples.
*
* Copyright (C) Microsoft Corporation. All rights reserved.
*
* This source code is intended only as a supplement to Microsoft
* Development Tools and/or on-line documentation. See these other
* materials for detailed information regarding Microsoft code samples.
*
* THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
****************************************************************************/
#include <stdio.h>
#include <windows.h>
#include <shlobj.h>
#include <msopc.h> // For Packaging APIs
#include <strsafe.h>
#include "util.h"
///////////////////////////////////////////////////////////////////////////////
// Description:
// Returns the last error as an HRESULT.
///////////////////////////////////////////////////////////////////////////////
HRESULT
GetLastErrorAsHResult(
VOID
)
{
DWORD dwError = GetLastError();
return HRESULT_FROM_WIN32(dwError);
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Gets the target part of a relationship with the 'Internal' target mode.
///////////////////////////////////////////////////////////////////////////////
HRESULT
GetRelationshipTargetPart(
IOpcPartSet *partSet, // Set of the parts in the package.
IOpcRelationship *relationship,// Relationship that targets the required part.
LPCWSTR expectedContentType, // Content type expected for the target part.
IOpcPart **targetPart // Receives pointer to target part. Method may return a valid
// pointer even on failure, and the caller must always release
// if a non-NULL value is returned.
)
{
OPC_URI_TARGET_MODE targetMode;
IOpcUri * sourceUri = NULL;
IUri * targetUri = NULL;
IOpcPartUri * targetPartUri = NULL;
BOOL partExists = FALSE;
LPWSTR contentType = NULL;
HRESULT hr = relationship->GetTargetMode(&targetMode);
if (SUCCEEDED(hr) && targetMode != OPC_URI_TARGET_MODE_INTERNAL)
{
// The relationship's target is not a part.
LPWSTR relationshipType = NULL;
relationship->GetRelationshipType(&relationshipType);
fwprintf(
stderr,
L"Invalid music bundle package: relationship with type %s must have Internal target mode.\n",
(PWSTR)relationshipType
);
// Set the return code to an error.
hr = E_FAIL;
CoTaskMemFree(static_cast<LPVOID>(relationshipType));
}
// Relationship's target is a part; the target mode is 'Internal'.
if (SUCCEEDED(hr))
{
// Get the URI of the relationship source.
hr = relationship->GetSourceUri(&sourceUri);
}
if (SUCCEEDED(hr))
{
// Get the URI of the relationship target.
hr = relationship->GetTargetUri(&targetUri);
}
if (SUCCEEDED(hr))
{
// Resolve the target URI to the part name of the target part.
hr = sourceUri->CombinePartUri(targetUri, &targetPartUri);
}
if (SUCCEEDED(hr))
{
// Check that a part with the resolved part name exists in the
// part set.
hr = partSet->PartExists(targetPartUri, &partExists);
}
if (SUCCEEDED(hr) && !partExists)
{
// The part does not exist in the part set.
fwprintf(
stderr,
L"Invalid music bundle package: the target part of relationship does not exist.\n"
);
// Set the return code to an error.
hr = E_FAIL;
}
if (SUCCEEDED(hr))
{
// Get the part.
hr = partSet->GetPart(targetPartUri, targetPart);
}
if (SUCCEEDED(hr))
{
// Get the content type of the part.
hr = (*targetPart)->GetContentType(&contentType);
}
if (SUCCEEDED(hr))
{
if (wcscmp(contentType, expectedContentType) != 0)
{
// Content type of the part did not match the expected content
// type.
fwprintf(
stderr,
L"Invalid music bundle package: the target part does not have correct content type.\n"
);
// Set the return code to an error.
hr = E_FAIL;
}
}
if (sourceUri)
{
sourceUri->Release();
sourceUri = NULL;
}
if (targetUri)
{
targetUri->Release();
targetUri = NULL;
}
if (targetPartUri)
{
targetPartUri->Release();
targetPartUri = NULL;
}
CoTaskMemFree(static_cast<LPVOID>(contentType));
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Gets the relationship of the specified type from a specified relationship
// set.
// Note: Method expects exactly one relationship of the specified type. This
// limitation is described in the Music Bundle Package specification--and is not
// imposed by the Packaging APIs or the OPC.
///////////////////////////////////////////////////////////////////////////////
HRESULT
GetRelationshipByType(
IOpcRelationshipSet *relsSet, // Relationship set that contains the relationship.
LPCWSTR relationshipType, // The relationship type of the relationship.
IOpcRelationship **targetRelationship // Receives the relationship. Method may return a valid
// pointer even on failure, and the caller must always release
// if a non-NULL value is returned.
)
{
IOpcRelationshipEnumerator * relsEnumerator = NULL;
BOOL hasNext;
int count = 0;
// Get an enumerator of all relationships of the required type.
HRESULT hr = relsSet->GetEnumeratorForType(relationshipType, &relsEnumerator);
// Enumerate through relationships, ensuring that there is exactly one
// relationship that has the specified type.
while (SUCCEEDED(hr) &&
SUCCEEDED(hr = relsEnumerator->MoveNext(&hasNext)) &&
hasNext)
{
count++;
if (count > 1)
{
// There is more than one relationship of the specified type.
fwprintf(
stderr,
L"Invalid music bundle package: cannot have more than 1 relationship with type: %s.\n",
relationshipType
);
// Set the return code to an error.
hr = E_FAIL;
// An error was encountered; stop enumerating.
break;
}
if (SUCCEEDED(hr))
{
// Get the relationship at the current position of the enumerator.
hr = relsEnumerator->GetCurrent(targetRelationship);
}
}
if (SUCCEEDED(hr))
{
if (count == 0)
{
// There were no relationships in the set that had the specified
// type.
fwprintf(
stderr,
L"Invalid music bundle package: relationship with type %s does not exist.\n",
relationshipType
);
// Set the return code to an error.
hr = E_FAIL;
}
}
if (relsEnumerator)
{
relsEnumerator->Release();
relsEnumerator = NULL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Forms and retrieves a full file name by combining the file name with the
// base directory path. Memory for the full file name is allocated by calling
// CoTaskMemAlloc, and the caller free the memory by calling CoTaskMemFree.
///////////////////////////////////////////////////////////////////////////////
HRESULT
GetFullFileName(
LPCWSTR filePath, // The base directory path.
LPCWSTR fileName, // Name of the file.
LPWSTR *fullFileName // The resultant full file name. The caller must free the memory allocated
// for this string buffer.
)
{
HRESULT hr = S_OK;
size_t cchFileName = 0;
size_t cchLength = 0;
WCHAR * szDest = NULL;
if (fullFileName == NULL)
{
// The fullFileName out parameter is required.
// Set the return code to an error.
hr = E_INVALIDARG;
}
// Calculate the length of the full file name buffer.
if (SUCCEEDED(hr))
{
// Get the length of the base directory string buffer.
hr = StringCchLength(filePath, STRSAFE_MAX_CCH, &cchLength);
}
if (SUCCEEDED(hr))
{
// Store the length of the base directory string buffer.
cchFileName += cchLength;
// Get the length of the file name string buffer.
hr = StringCchLength(fileName, STRSAFE_MAX_CCH, &cchLength);
}
if (SUCCEEDED(hr))
{
// Calculate the length of the full file name string buffer by adding
// the length of the base directory buffer to the length of the file
// name buffer, and adding 1 to account for the terminating null
// character.
cchFileName += cchLength;
cchFileName += 1;
// Allocate memory for the full file name buffer of the length in
// cchFileName.
*fullFileName = (LPWSTR)CoTaskMemAlloc(cchFileName*sizeof(WCHAR));
if (*fullFileName == NULL)
{
// Error encountered during allocation
// Set the return code to an error.
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr))
{
// Copy the base directory string to the full file name buffer;
// retrieve a pointer to the end of the base directory string in the
// full file name buffer; retrieve the number of unused characters in
// the full file name buffer; finally, set the flag to fill the
// remainder of the buffer with '0's if the copy succeeds, or fill the
// entire buffer with '0's if the copy fails.
szDest = *fullFileName;
hr = StringCchCopyEx(szDest, cchFileName, filePath, &szDest, &cchFileName, 0);
}
if (SUCCEEDED(hr))
{
// Append file name to base path in the full file name string
// buffer.
hr = StringCchCat(szDest, cchFileName, fileName);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Creates and retrieves a full file name by combining the part name with the
// base directory path. The part name is converted to a file name by replacing
// the leading '/' character with '\'. Method creates the directory
// structure to the full file name. Memory for the full file name is allocated
// by calling CoTaskMemAlloc, and the caller free the memory by calling
// CoTaskMemFree.
///////////////////////////////////////////////////////////////////////////////
HRESULT
CreateDirectoryFromPartName(
LPCWSTR filePath, // The base directory path.
LPCWSTR partName, // Part name to use to create full file name.
LPWSTR *fullFileName // The resultant full file name. The caller must free the memory allocated
// for this string buffer.
)
{
HRESULT hr = S_OK;
size_t cchFileName = 0;
size_t cchLength = 0;
LPWSTR szDest = NULL;
if (fullFileName == NULL)
{
// The fullFileName out parameter is required.
// Set the return code to an error.
hr = E_INVALIDARG;
}
// Calculate the length of the full file name buffer.
if (SUCCEEDED(hr))
{
// Get the length of the base directory string buffer.
hr = StringCchLength(filePath, STRSAFE_MAX_CCH, &cchLength);
}
if (SUCCEEDED(hr))
{
// Store the length of the base directory string buffer.
cchFileName += cchLength;
// Get the length of the part name string buffer.
hr = StringCchLength(partName, STRSAFE_MAX_CCH, &cchLength);
}
if (SUCCEEDED(hr))
{
// Calculate the length of the full file name string buffer by adding
// the length of the base directory buffer to the length of the part
// name buffer, and adding 1 to account for the terminating null
// character.
cchFileName += cchLength;
cchFileName += 1;
// Allocate memory for the full file name buffer of the length in
// cchFileName.
*fullFileName = (LPWSTR)CoTaskMemAlloc(cchFileName*sizeof(WCHAR));
if (!*fullFileName)
{
// Error encountered during allocation
// Set the return code to an error.
hr = E_OUTOFMEMORY;
}
}
szDest = *fullFileName;
if (SUCCEEDED(hr))
{
// Copy the base directory string to the full file name buffer;
// retrieve a pointer to the end of the base directory string in the
// full file name buffer; retrieve the number of unused characters in
// the full file name buffer; finally, set the flag to fill the
// remainder of the buffer with '0's if the copy succeeds, or fill the
// entire buffer with '0's if the copy fails.
hr = StringCchCopyEx(szDest, cchFileName, filePath, &szDest, &cchFileName, 0);
}
if (SUCCEEDED(hr))
{
// Recursively create the directory structure using base file path
// and the part name, which is converted to the file name.
for (size_t i = 0; i <= cchLength && SUCCEEDED(hr); i++, szDest++, partName++)
{
if (*partName == '/')
{
// The character is '/'; therefore, the szDest buffer
// contains a directory name.
// Temporarily null terminate string and create the directory.
*szDest = '\0';
BOOL createDirSucceeded = CreateDirectory(*fullFileName, NULL);
if (!createDirSucceeded)
{
// Creation of the directory failed; get the error returned
// by the CreateDirectory function.
hr = GetLastErrorAsHResult();
if (hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
{
// Ignore error if directory already exists.
hr = S_OK;
}
}
// Change the null character to a '\'.
*szDest = '\\';
}
else
{
// Copy the character at the current part name buffer index
// into the memory of current szDest buffer index.
*szDest = *partName;
}
}
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Opens a stream to the path and file name specified, and deserializes the
// file's content as the content of the part.
///////////////////////////////////////////////////////////////////////////////
HRESULT
WriteFileContentToPart(
IOpcFactory *opcFactory,
LPCWSTR filePath, // Directory where the file is located.
LPCWSTR fileName, // Name of file whose content will be deserialized.
IOpcPart *opcPart // Part into which the file content is deserialized.
)
{
HRESULT hr = S_OK;
LPWSTR fullFileName = NULL;
IStream * fileStream = NULL;
IStream * partStream = NULL;
// Get the full file name of the file.
hr = GetFullFileName(filePath, fileName, &fullFileName);
if (SUCCEEDED(hr))
{
// Create a read-only stream over the file.
hr = opcFactory->CreateStreamOnFile(
fullFileName,
OPC_STREAM_IO_READ,
NULL,
FILE_ATTRIBUTE_NORMAL,
&fileStream
);
}
if (SUCCEEDED(hr))
{
// Get the content stream of the part.
hr = opcPart->GetContentStream(&partStream);
}
if (SUCCEEDED(hr))
{
// Part content is overwritten; truncate the size of the stream to zero.
ULARGE_INTEGER zero = {0};
hr = partStream->SetSize(zero);
}
if (SUCCEEDED(hr))
{
// Copy the content from the file stream to the part content stream.
ULARGE_INTEGER ul = {(UINT) -1, (UINT) -1};
hr = fileStream->CopyTo(partStream, ul, NULL, NULL);
}
CoTaskMemFree(static_cast<LPVOID>(fullFileName));
if (fileStream)
{
fileStream->Release();
fileStream = NULL;
}
if (partStream)
{
partStream->Release();
partStream = NULL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Displays stream content in the console.
///////////////////////////////////////////////////////////////////////////////
HRESULT
DisplayStreamContent(
LPCWSTR title, // The title to be displayed above the stream contents.
IStream *stream // The stream whose contents are displayed.
)
{
HRESULT hr = S_OK;
byte buffer[MAX_BUFFER_SIZE] = {};
ULONG bytesRead = 1;
// Display title.
wprintf(L"++++++ %s ++++++\n", title);
// Read and display data from screen.
while (SUCCEEDED(hr) && bytesRead > 0)
{
// Pass in 'size - 1' so the buffer is always null-terminated
// when passed in to the wprintf method.
hr = stream->Read(buffer, sizeof(buffer) - 1, &bytesRead);
if (SUCCEEDED(hr) && bytesRead > 0)
{
// Display data.
wprintf(L"%S\n", buffer);
}
}
if (SUCCEEDED(hr))
{
// Display end delimiter.
wprintf(L"++++++ End %s ++++++\n\n", title);
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Reads and serializes part content to a specified file. Also creates the
// required directory structure that contains the file.
///////////////////////////////////////////////////////////////////////////////
HRESULT
WritePartContentToFile(
IOpcFactory *opcFactory,
LPCWSTR filePath, // Base directory path where the file is created.
IOpcPart *opcPart // Part whose content is serialized.
)
{
HRESULT hr = S_OK;
LPWSTR fullFileName = NULL;
IStream * fileStream = NULL;
IStream * partStream = NULL;
IOpcPartUri * opcPartUri = NULL;
BSTR partUriString = NULL;
// Get the part name of this part.
hr = opcPart->GetName(&opcPartUri);
if (SUCCEEDED(hr))
{
// Get the part name as string.
hr = opcPartUri->GetAbsoluteUri(&partUriString);
}
if (SUCCEEDED(hr))
{
// Create the full file name and the directory structure.
hr = CreateDirectoryFromPartName(filePath, partUriString, &fullFileName);
}
if (SUCCEEDED(hr))
{
// Create a write-only stream over the file where part content will be
// serialized.
hr = opcFactory->CreateStreamOnFile(
fullFileName,
OPC_STREAM_IO_WRITE,
NULL,
FILE_ATTRIBUTE_NORMAL,
&fileStream
);
}
if (SUCCEEDED(hr))
{
// Get the part content stream.
hr = opcPart->GetContentStream(&partStream);
}
if (SUCCEEDED(hr))
{
// Copy the part content stream to the file stream.
ULARGE_INTEGER const ul = {(UINT) -1, (UINT) -1};
hr = partStream->CopyTo(fileStream, ul, NULL, NULL);
}
CoTaskMemFree(static_cast<LPVOID>(fullFileName));
if (fileStream)
{
fileStream->Release();
fileStream = NULL;
}
if (partStream)
{
partStream->Release();
partStream = NULL;
}
if (opcPartUri)
{
opcPartUri->Release();
opcPartUri = NULL;
}
SysFreeString(partUriString);
return hr;
}
Related topics