2025-11-28 00:35:46 +09:00

620 lines
20 KiB
C++

//<SnippetMusicBundle_cppProductionWholePage>
/*****************************************************************************
*
* File: MusicBundleProduction.cpp
*
* Description:
* This sample is a simple application that might be used as a starting-point
* for an application that uses the Packaging API. This sample demonstrates
* the production and consumption of a music bundle package, which is a custom
* package file-format.
*
* The music bundle format is an example of a custom package file format and
* was designed specifically for this sample. See the MusicBundle.h header file
* for the music bundle specification.
*
* For the sake of simplicity, production data used for the sample is hard
* coded. In a fully-functional application, production data would be retrieved
* from the user of the application.
*
* ------------------------------------
*
* 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"
#include "MusicBundle.h"
#include "urlmon.h"
//---------------------------------------
// For the sake of simplicity, the production data that appears below is hard
// coded. In a fully-functional application, this data would be retrieved from
// the user of the application.
// Track names.
static LPCWSTR g_trackNames[] = {
L"\\Tracks\\CrystalFree.wma",
L"\\Tracks\\Sire.wma",
L"\\Tracks\\SmallPines.wma",
L"\\Tracks\\Valparaiso.wma"
};
// Lyric file names. One-to-one mapping with track names.
static LPCWSTR g_lyricsNames[] = {
L"\\Lyrics\\CrystalFree.txt",
L"\\Lyrics\\Sire.txt",
L"\\Lyrics\\SmallPines.txt",
L"\\Lyrics\\Valparaiso.txt"
};
// Album art.
static const WCHAR g_albumArt[] = L"\\AlbumArt\\jacqui.jpg";
// Track list.
static const WCHAR g_trackList[] = L"\\TrackList.wpl";
// Link to external website for the album.
static const WCHAR g_albumWebsite[] = L"http://www.example.com/Media/Albums/Jacqui%20Kramer/Leap%20Forward";
//-------------------------------------
// Production helper methods.
///////////////////////////////////////////////////////////////////////////////
// Description:
// Creates an empty part, adds the part to the package's part set and writes
// data to the part as part content.
//
// Note: This method does not add Relationships parts to the music bundle.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddPartToBundle(
IOpcFactory *opcFactory,
LPCWSTR contentFileName, // File that contains the content to be stored in the part to add.
IOpcPartSet *packagePartSet, // Represents the set of parts (excluding Relationships parts)
// in a package.
LPCWSTR contentType, // The content type of the content to be stored in the part.
OPC_COMPRESSION_OPTIONS compressionOptions, // Level of compression to use on the part.
LPCWSTR inputDirectory, // Directory location of the file specified in contentFileName.
IOpcPartUri **createdPartUri,// Represents the part name. The caller must release the interface.
IOpcPart **createdPart = NULL// Optional. Represents the part to add to the package. The caller must
// release the interface.
)
{
HRESULT hr = S_OK;
IOpcPart * part = NULL;
if (createdPartUri == NULL)
{
// The createdPartUri parameter is required.
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
// Create the part name from the file name.
hr = opcFactory->CreatePartUri(contentFileName, createdPartUri);
}
if (SUCCEEDED(hr))
{
// Create the part as an empty part, and add it to the set of parts
// in the package.
hr = packagePartSet->CreatePart(
*createdPartUri,
contentType,
compressionOptions,
&part
);
}
if (SUCCEEDED(hr))
{
// Add content to the empty part.
hr = WriteFileContentToPart(
opcFactory,
inputDirectory,
contentFileName,
part
);
}
if (SUCCEEDED(hr) && createdPart != NULL)
{
// The createdPart out parameter was specified.
// Return the interface pointer to the part created and added to the
// set of parts in the package.
*createdPart = part;
part = NULL;
}
// Release resources
if (part)
{
part->Release();
part = NULL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Adds a Track part and a Lyrics part to the set.
// 1. Track part:
// + Adds a Track part to the music bundle
// + Creates a relationship where the source is the Track List part and the
// target is the Tark part by creating the relationship from the Track
// List part's relationship set.
// 2. Lyrics part:
// + Adds a Lryics part to the music bundle.
// + Creates a relationship where the source is the Track part that was
// added to the music bundle and the target is the Tark part by creating
// the relationship from the Track part's relationship set.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddTrackAndLyricsToBundle(
IOpcFactory *opcFactory,
IOpcPartSet *packagePartSet, // Represents the set of parts (excluding Relationships parts) in
// a package.
IOpcRelationshipSet *trackListRelationshipSet, // Represents the Relationships part containing
// relationships that have the Track List part
// as their target.
LPCWSTR inputDirectory, // Directory location of the files specified in trackName and
// lyricsName.
LPCWSTR trackName, // Name of file that contains the track data.
LPCWSTR lyricsName // Name of tile that contains lyrics data.
)
{
HRESULT hr = S_OK;
IOpcPartUri * trackPartUri = NULL;
IOpcPartUri * lyricsPartUri = NULL;
IOpcPart * trackPart = NULL;
IOpcRelationshipSet * trackRelationshipSet = NULL;
// Add a Track part that contains track data to the package.
hr = AddPartToBundle(
opcFactory,
trackName,
packagePartSet,
g_trackContentType,
OPC_COMPRESSION_NONE,
inputDirectory,
&trackPartUri,
&trackPart
);
if (SUCCEEDED(hr))
{
// Add a relationship that has the Track part as the target to the
// relationship set that represents the Relationships part containing
// relationships that have the Track List part as their source.
hr = trackListRelationshipSet->CreateRelationship(
NULL, // Use auto-generated relationship ID.
g_trackRelationshipType,
trackPartUri, // Relationship target's URI.
OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
NULL // No pointer needed since returned relationship not required.
);
}
if (SUCCEEDED(hr))
{
// Add a Lyrics part that contains lyrics for added track.
hr = AddPartToBundle(
opcFactory,
lyricsName,
packagePartSet,
g_lyricsContentType,
OPC_COMPRESSION_NORMAL,
inputDirectory,
&lyricsPartUri
);
}
if (SUCCEEDED(hr))
{
// Get relationship set for Track part. The relationship set
// represents the Relationship part that stores relationships that have
// the Track part as their source.
hr = trackPart->GetRelationshipSet(&trackRelationshipSet);
}
if (SUCCEEDED(hr))
{
// Add a relationship to the Track part's Relationships part,
// represented as a relationship object in the relationship object set.
hr = trackRelationshipSet->CreateRelationship(
NULL, // Use auto-generated relationship ID.
g_lyricsRelationshipType,
lyricsPartUri, // Relationship target's URI.
OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
NULL // No pointer needed since returned relationship not required.
);
}
// Release resources
if (trackPartUri)
{
trackPartUri->Release();
trackPartUri = NULL;
}
if (lyricsPartUri)
{
lyricsPartUri->Release();
lyricsPartUri = NULL;
}
if (trackPart)
{
trackPart->Release();
trackPart = NULL;
}
if (trackRelationshipSet)
{
trackRelationshipSet->Release();
trackRelationshipSet = NULL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Adds the Album Art part to the package and creates a package relationship to
// that has the Album Art part as its target.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddAlbumArtToBundle(
IOpcFactory *opcFactory,
IOpcPartSet *packagePartSet, // Represents the set of parts (excluding Relationships parts)
// in a package.
IOpcRelationshipSet *packageRelationshipSet, // Represents the Relationships part that stores
// package relationships.
LPCWSTR inputDirectory // Directory location of the files specified in trackName and
// lyricsName.
)
{
HRESULT hr = S_OK;
IOpcPartUri * albumArtPartUri = NULL;
// Add Album Art part.
hr = AddPartToBundle(
opcFactory,
g_albumArt,
packagePartSet,
g_albumArtContentType,
OPC_COMPRESSION_NONE,
inputDirectory,
&albumArtPartUri
);
if (SUCCEEDED(hr))
{
// Add a package relationship that has the Album Art part as its target.
hr = packageRelationshipSet->CreateRelationship(
NULL, // Use auto-generated relationship ID.
g_albumArtRelationshipType,
albumArtPartUri, // Relationship target's URI.
OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
NULL // No pointer needed since returned relationship not required.
);
}
// Release resources
if (albumArtPartUri)
{
albumArtPartUri->Release();
albumArtPartUri = NULL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Adds the Track List part to the package, and creates and adds a package
// relationship to the package relationship set that has the Track List part as
// its target, and then adds the track and lyrics to the music bundle.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddTrackListToBundle(
IOpcFactory *opcFactory,
IOpcPartSet *packagePartSet, // Represents the set of parts (excluding Relationships parts)
// in a package.
IOpcRelationshipSet *packageRelationshipSet, // The relationship set that stores package
// relationships.
LPCWSTR inputDirectory // Directory location of the files specified in trackName and
// lyricsName.
)
{
HRESULT hr = S_OK;
IOpcPartUri * trackListPartUri = NULL;
IOpcPart * trackListPart = NULL;
IOpcRelationshipSet * trackListRelationshipSet = NULL;
// Add Track List part.
hr = AddPartToBundle(
opcFactory,
g_trackList,
packagePartSet,
g_trackListContentType,
OPC_COMPRESSION_NORMAL,
inputDirectory,
&trackListPartUri,
&trackListPart
);
if (SUCCEEDED(hr))
{
// Add a package relationship that has the Track List part as its
// target.
hr = packageRelationshipSet->CreateRelationship(
NULL, // Use auto-generated relationship ID.
g_trackListRelationshipType,
trackListPartUri, // Relationship target's URI.
OPC_URI_TARGET_MODE_INTERNAL, // Relationship's target is internal.
NULL // No pointer needed since returned relationship not required.
);
}
if (SUCCEEDED(hr))
{
// Get the relationship set that represents the Relationships part that
// stores the relationships that have the Track List part as their
// source.
hr = trackListPart->GetRelationshipSet(&trackListRelationshipSet);
}
if(SUCCEEDED(hr))
{
// Add all track and lyric files to the music bundle as Track parts
// and Lyric parts, respectively.
for (int i = 0; i < countof(g_trackNames) && SUCCEEDED(hr); i++)
{
hr = AddTrackAndLyricsToBundle(
opcFactory,
packagePartSet,
trackListRelationshipSet,
inputDirectory,
g_trackNames[i],
g_lyricsNames[i]
);
}
}
// Release resources
if (trackListPartUri)
{
trackListPartUri->Release();
trackListPartUri = NULL;
}
if (trackListPart)
{
trackListPart->Release();
trackListPart = NULL;
}
if (trackListRelationshipSet)
{
trackListRelationshipSet->Release();
trackListRelationshipSet = NULL;
}
return hr;
}
///////////////////////////////////////////////////////////////////////////////
// Description:
// Creates a package relationship that targets the album website and adds the
// relationship to the package relationship set.
///////////////////////////////////////////////////////////////////////////////
HRESULT
AddAlbumWebsiteToBundle(
IOpcRelationshipSet *packageRelationshipSet // Relationship set that has all package relationships.
)
{
HRESULT hr = S_OK;
IUri * uri = NULL;
// Create the URI for the Album Website.
hr = CreateUri(
g_albumWebsite,
Uri_CREATE_CANONICALIZE,
0,
&uri
);
if (SUCCEEDED(hr))
{
// Add Album Website as a package relationship. Note that the target
// mode of the relationship is "External" because the website is
// a resource that exists outside of the package.
hr = packageRelationshipSet->CreateRelationship(
NULL, // Use auto-generated relationship ID.
g_albumWebsiteRelationshipType,
uri, // Relationship target's URI.
OPC_URI_TARGET_MODE_EXTERNAL, // Relationship's target is external.
NULL // No pointer needed since returned relationship not required.
);
}
// Release resources
if (uri)
{
uri->Release();
uri = NULL;
}
return hr;
}
//---------------------------------
// Function to create the new music bundle.
// Exposed through MusicBundle.h.
//
///////////////////////////////////////////////////////////////////////////////
// Description:
// Creates a package that is a music bundle, in compliance with both the OPC
// specification and the Music Bundle specification, which can be found in
// MusicBundle.h. Given the directory that contains all the files needed, this
// method creates all parts and relationships for the new music bundle and
// saves resultant package.
//
// Note: Relationships parts are not created explicitly. Relationship sets are
// serialized as Relationships parts when the package is saved.
///////////////////////////////////////////////////////////////////////////////
HRESULT
ProduceMusicBundle(
LPCWSTR inputDirectory, // Parent directory that contains files to add to the music bundle.
LPCWSTR outputPackageName // Name of the music bundle package to create.
)
{
HRESULT hr = S_OK;
IOpcFactory * opcFactory = NULL;
IOpcPackage * opcPackage = NULL;
IOpcPartSet * packagePartSet = NULL;
IOpcRelationshipSet * packageRelationshipSet = NULL;
IStream * fileStream = NULL;
// Create a new factory.
hr = CoCreateInstance(
__uuidof(OpcFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IOpcFactory),
(LPVOID*)&opcFactory
);
if (SUCCEEDED(hr))
{
// Create the new, empty music bundle.
hr = opcFactory->CreatePackage(&opcPackage);
}
if (SUCCEEDED(hr))
{
// Get the set of parts in the package. Parts (that are not
// Relationships parts) to be included in the music bundle to be
// created will be created in this set.
hr = opcPackage->GetPartSet(&packagePartSet);
}
if (SUCCEEDED(hr))
{
// Get the set of package relationships. All package relationships
// specific to the music bundle to be created will be created in this
// set.
hr = opcPackage->GetRelationshipSet(&packageRelationshipSet);
}
// Populate the music bundle.
if (SUCCEEDED(hr))
{
// Add the Album Art part to the package.
hr = AddAlbumArtToBundle(
opcFactory,
packagePartSet,
packageRelationshipSet,
inputDirectory
);
}
if (SUCCEEDED(hr))
{
// Add the Track List part, and Track and Lyrics parts to package.
hr = AddTrackListToBundle(
opcFactory,
packagePartSet,
packageRelationshipSet,
inputDirectory
);
}
if (SUCCEEDED(hr))
{
// Add a package relationship that targets the album website to the
// package.
hr = AddAlbumWebsiteToBundle(packageRelationshipSet);
}
// Save the music bundle.
if (SUCCEEDED(hr))
{
// Create a writable stream over the name of the package to be created.
hr = opcFactory->CreateStreamOnFile(
outputPackageName,
OPC_STREAM_IO_WRITE,
NULL,
FILE_ATTRIBUTE_NORMAL,
&fileStream
);
}
if (SUCCEEDED(hr))
{
// Serialize package content to the writable stream.
hr = opcFactory->WritePackageToStream(
opcPackage,
OPC_WRITE_DEFAULT,
fileStream
);
}
// Release resources
if (opcFactory)
{
opcFactory->Release();
opcFactory = NULL;
}
if (opcPackage)
{
opcPackage->Release();
opcPackage = NULL;
}
if (packagePartSet)
{
packagePartSet->Release();
packagePartSet = NULL;
}
if (packageRelationshipSet)
{
packageRelationshipSet->Release();
packageRelationshipSet = NULL;
}
if (fileStream)
{
fileStream->Release();
fileStream = NULL;
}
return hr;
}
//</SnippetMusicBundle_cppProductionWholePage>