本文将就在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, 00
                        
&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。

或有什么问题可以Email到long.zou@gmail.com