本文将就在Windows XP/Windows 2003下如何使用ImapiService来实现CD/DVD刻录功能。
前提条件:
操作系统必须为Windows XP及Windows 2003以上。因为,在这两种平台下才提供ImapiService服务,缺省的,在Windows XP下,该服务是被设置为自动启动的,而在Windows 2003下,该服务为禁用的,也就需要手工来将其打开。这里就不写如何开启服务了。
开发平台,VS.NET 2003/VS.NET 2005并需要额外下载Windows Platform SDK,因为,VS.NET里的PlatformSDK是没有带入Imapi的支持的。安装了Windows PlatformSDK后,你便可以在C:\Program Files\Microsoft SDKs\Windows\v1.0下找到你所需要的imapi.h及imapierror.h文件,这时,你就可以在你的程序中引用它了。由于Imapi使用的是COM接口,所以你无须添加lib文件。
OK,准备结束。让我开始做这个刻录软件吧。
首先,我们需实例化IDiscMaster。CLSID_MSDiscMasterObj是它的CLSID。 这里我们使用了ATL的智能指针模板。
CComQIPtr<IDiscMaster> dm;
HRESULT m_hr = CoCreateInstance(CLSID_MSDiscMasterObj, &dm, CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER);
如果宏SUCCEEDED( m_hr )返回为TRUE的话,表明IDiscMaster初始化成功。这样,我们可以继续下面的操作。
初始化成功后,我们就可以调用Open方法来打开DiscMaster,记住,以后的任何一个步骤的前提条件都是要调用这个方法,否则会返回一个IMAPI_E_NOTOPENED错误。
在真正开始刻录之前,Imapi要求我们首先得告诉它使用哪个刻录机及什么样的格式来进行刻录,讲到这个格式,我们先弄明白,Windows所支持的格式有哪些。其实,这个格式大概的划分起来就两种,一种是众所周知的如CD的音频格式,另一种是我们经常使用的数据CD格式ISO9660及它的扩展Joliet。这里我们将需要使用到下面四种接口:
IDiscRecorder
IEnumDiscRecorders
IJolietDiscMaster
IRedbookDiscMaster
其中, IDiscRecorder是用来操作刻录机的COM接口。IEnumDiscRecorders是用来遍历所有的Recorder的接口,我们可以使用下面的方法来遍历所有的刻录机。
HRESULT hr;
CComQIPtr<IEnumDiscRecorders> enumRecorders;
CComQIPtr<IDiscRecorder> Recorders;
hr = dm->EnumDiscRecorders( &enumRecorders );
if( SUCCEEDED( hr ) ){
UINT nRet = 0;
while( SUCCEEDED( hr= enumRecorder->Next(1, &recorder.p, &nRet)) && nRet==1 ){
///You can set the ActiveDiscRecorder and Active format here.
dm->SetActiveDiscRecorder( recorder );
///说明将按数据CD的格式来刻录。
dm->SetActiveDiscMasterFormat( IID_IJolietDiscMaster, &recorder );
}
}
然后使用IJolietDiscMaster来将DiscMaster特例化,当我也可用IRedbookDiscMaster来将特例化。
CComQIPtr<IJolietDiscMaster> jdm(dm);
做完这些后,Imapi刻录机的状态就基本上准备完成了。接下的是我们要准备如何将想要的数据刻录到CD里。那在这里就有必要搞明白一个接口:
IStorage;
IStorage是微软提供的用于结构化存储的接口,在该接口中有子存储及子对象之分,分别可以使用方法CreateStorage及CreateStream来创建。而其中所创建的子Storage的话,则会当作目录被刻入到CD中,而Stream就会被当作文件写入到CD中。下面的程序将指定目录下的文件来组织成一个IStorage对象。
ULONGLONG PerformFileStream( const char *pFileName, IStream *stream ){
CFile file;
char *pBuffer = NULL;
bool isOpened = false;
UINT BufferSize = 40960;
ULONGLONG m_ReadedSize = 0L;
try{
if( file.Open( pFileName, CFile::modeRead ) ){
isOpened = true;
pBuffer = new char[BufferSize];
UINT readed = 0L;
if( pBuffer != NULL ){
ZeroMemory( pBuffer, BufferSize );
ULONG written = 0;
while( (readed = file.Read( pBuffer, BufferSize )) > 0 ){
stream->Write( pBuffer, readed, &written );
m_ReadedSize += readed;
}
}
}
}catch(CFileException* e){
e->ReportError();
}
catch(){
}
if( isOpened ){
file.Close();
}
if( pBuffer != NULL ){
delete[] pBuffer;
pBuffer = NULL;
}
return m_ReadedSize;
}
HRESULT PerformDirectory( const char *path, IStorage *storage ){
HRESULT hr = 0L;
long hFile = 0;
USES_CONVERSION;
_finddata_t fileinfo;
int ch = _chdir( path );
if (( hFile = _findfirst( "*.*", &fileinfo ) ) != -1 ){
do{
if (!(fileinfo.attrib & _A_SUBDIR))
{
char filename[_MAX_PATH];
strcpy( filename, path );
strcat( filename, fileinfo.name );
CComPtr<IStream> fileStream;
hr = storage->CreateStream( T2W( fileinfo.name ), STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, NULL, &fileStream );
ASSERT( SUCCEEDED( hr ) );
PerformFileStream( filename, fileStream );
}
else{
if ( strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0 )
{
char subdir[ _MAX_PATH ];
strcpy( subdir,path );
strcat( subdir,fileinfo.name);
strcat(subdir,"\\");
CComPtr<IStorage> sub;
hr = storage->CreateStorage( T2W( fileinfo.name ),
STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0,
&sub);
ASSERT( SUCCEEDED( hr ) );
hr = PerformDirectory( subdir, sub );
ASSERT( SUCCEEDED( hr ) );
}
}
}while (_findnext(hFile,&fileinfo) == 0);
_findclose(hFile);
}
return hr;
}
HRESULT CreateDirectoryStorage( const char *path, IStorage **stg )
{
HRESULT hr = 0L;
hr = StgCreateStorageEx( NULL,
STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE,
STGFMT_STORAGE,
// STGFMT_STORAGE => Structured Storage property sets
// STGFMT_FILE => NTFS property sets
0, NULL, NULL,
IID_IStorage,
reinterpret_cast<void**>(stg) );
if( FAILED(hr) ) throw L"Failed StgCreateStorageEx";
hr = PerformDirectory( path, (*stg) );
return hr;
}
这样我们就可以将这个创建好的IStorage对象写入到CD或DVD中了。
hr = jdm->AddData( storage, FALSE );
if( SUCCEEDED( hr ) ){
hr = dm->RecordDisc( FALSE, TRUE );
}
这里由于在调用了RecordDisc后方写入到CD,同时,这里将可能产生许多问题,如CD不能被刻录等问题都需要在这里进行处理。
VS.NET 2003/2005都提供了如何使用Imapi进行CD刻录的帮助,详细内容请参考相关的MSDN。
前提条件:
操作系统必须为Windows XP及Windows 2003以上。因为,在这两种平台下才提供ImapiService服务,缺省的,在Windows XP下,该服务是被设置为自动启动的,而在Windows 2003下,该服务为禁用的,也就需要手工来将其打开。这里就不写如何开启服务了。
开发平台,VS.NET 2003/VS.NET 2005并需要额外下载Windows Platform SDK,因为,VS.NET里的PlatformSDK是没有带入Imapi的支持的。安装了Windows PlatformSDK后,你便可以在C:\Program Files\Microsoft SDKs\Windows\v1.0下找到你所需要的imapi.h及imapierror.h文件,这时,你就可以在你的程序中引用它了。由于Imapi使用的是COM接口,所以你无须添加lib文件。
OK,准备结束。让我开始做这个刻录软件吧。
首先,我们需实例化IDiscMaster。CLSID_MSDiscMasterObj是它的CLSID。 这里我们使用了ATL的智能指针模板。
CComQIPtr<IDiscMaster> dm;
HRESULT m_hr = CoCreateInstance(CLSID_MSDiscMasterObj, &dm, CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER);
如果宏SUCCEEDED( m_hr )返回为TRUE的话,表明IDiscMaster初始化成功。这样,我们可以继续下面的操作。
初始化成功后,我们就可以调用Open方法来打开DiscMaster,记住,以后的任何一个步骤的前提条件都是要调用这个方法,否则会返回一个IMAPI_E_NOTOPENED错误。
在真正开始刻录之前,Imapi要求我们首先得告诉它使用哪个刻录机及什么样的格式来进行刻录,讲到这个格式,我们先弄明白,Windows所支持的格式有哪些。其实,这个格式大概的划分起来就两种,一种是众所周知的如CD的音频格式,另一种是我们经常使用的数据CD格式ISO9660及它的扩展Joliet。这里我们将需要使用到下面四种接口:
IDiscRecorder
IEnumDiscRecorders
IJolietDiscMaster
IRedbookDiscMaster
其中, IDiscRecorder是用来操作刻录机的COM接口。IEnumDiscRecorders是用来遍历所有的Recorder的接口,我们可以使用下面的方法来遍历所有的刻录机。
HRESULT hr;
CComQIPtr<IEnumDiscRecorders> enumRecorders;
CComQIPtr<IDiscRecorder> Recorders;
hr = dm->EnumDiscRecorders( &enumRecorders );
if( SUCCEEDED( hr ) ){
UINT nRet = 0;
while( SUCCEEDED( hr= enumRecorder->Next(1, &recorder.p, &nRet)) && nRet==1 ){
///You can set the ActiveDiscRecorder and Active format here.
dm->SetActiveDiscRecorder( recorder );
///说明将按数据CD的格式来刻录。
dm->SetActiveDiscMasterFormat( IID_IJolietDiscMaster, &recorder );
}
}
然后使用IJolietDiscMaster来将DiscMaster特例化,当我也可用IRedbookDiscMaster来将特例化。
CComQIPtr<IJolietDiscMaster> jdm(dm);
做完这些后,Imapi刻录机的状态就基本上准备完成了。接下的是我们要准备如何将想要的数据刻录到CD里。那在这里就有必要搞明白一个接口:
IStorage;
IStorage是微软提供的用于结构化存储的接口,在该接口中有子存储及子对象之分,分别可以使用方法CreateStorage及CreateStream来创建。而其中所创建的子Storage的话,则会当作目录被刻入到CD中,而Stream就会被当作文件写入到CD中。下面的程序将指定目录下的文件来组织成一个IStorage对象。
ULONGLONG PerformFileStream( const char *pFileName, IStream *stream ){
CFile file;
char *pBuffer = NULL;
bool isOpened = false;
UINT BufferSize = 40960;
ULONGLONG m_ReadedSize = 0L;
try{
if( file.Open( pFileName, CFile::modeRead ) ){
isOpened = true;
pBuffer = new char[BufferSize];
UINT readed = 0L;
if( pBuffer != NULL ){
ZeroMemory( pBuffer, BufferSize );
ULONG written = 0;
while( (readed = file.Read( pBuffer, BufferSize )) > 0 ){
stream->Write( pBuffer, readed, &written );
m_ReadedSize += readed;
}
}
}
}catch(CFileException* e){
e->ReportError();
}
catch(){
}
if( isOpened ){
file.Close();
}
if( pBuffer != NULL ){
delete[] pBuffer;
pBuffer = NULL;
}
return m_ReadedSize;
}
HRESULT PerformDirectory( const char *path, IStorage *storage ){
HRESULT hr = 0L;
long hFile = 0;
USES_CONVERSION;
_finddata_t fileinfo;
int ch = _chdir( path );
if (( hFile = _findfirst( "*.*", &fileinfo ) ) != -1 ){
do{
if (!(fileinfo.attrib & _A_SUBDIR))
{
char filename[_MAX_PATH];
strcpy( filename, path );
strcat( filename, fileinfo.name );
CComPtr<IStream> fileStream;
hr = storage->CreateStream( T2W( fileinfo.name ), STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, NULL, NULL, &fileStream );
ASSERT( SUCCEEDED( hr ) );
PerformFileStream( filename, fileStream );
}
else{
if ( strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0 )
{
char subdir[ _MAX_PATH ];
strcpy( subdir,path );
strcat( subdir,fileinfo.name);
strcat(subdir,"\\");
CComPtr<IStorage> sub;
hr = storage->CreateStorage( T2W( fileinfo.name ),
STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, 0,
&sub);
ASSERT( SUCCEEDED( hr ) );
hr = PerformDirectory( subdir, sub );
ASSERT( SUCCEEDED( hr ) );
}
}
}while (_findnext(hFile,&fileinfo) == 0);
_findclose(hFile);
}
return hr;
}
HRESULT CreateDirectoryStorage( const char *path, IStorage **stg )
{
HRESULT hr = 0L;
hr = StgCreateStorageEx( NULL,
STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE,
STGFMT_STORAGE,
// STGFMT_STORAGE => Structured Storage property sets
// STGFMT_FILE => NTFS property sets
0, NULL, NULL,
IID_IStorage,
reinterpret_cast<void**>(stg) );
if( FAILED(hr) ) throw L"Failed StgCreateStorageEx";
hr = PerformDirectory( path, (*stg) );
return hr;
}
这样我们就可以将这个创建好的IStorage对象写入到CD或DVD中了。
hr = jdm->AddData( storage, FALSE );
if( SUCCEEDED( hr ) ){
hr = dm->RecordDisc( FALSE, TRUE );
}
这里由于在调用了RecordDisc后方写入到CD,同时,这里将可能产生许多问题,如CD不能被刻录等问题都需要在这里进行处理。
VS.NET 2003/2005都提供了如何使用Imapi进行CD刻录的帮助,详细内容请参考相关的MSDN。