c++cwindowscd-burning

Windows CD Burning API


We need to programatically burn files to CD in a C\C++ Windows XP/Vista application we are developing using Borlands Turbo C++.

What is the simplest and best way to do this? We would prefer a native windows API (that doesnt rely on MFC) so as not to rely on any third party software/drivers if one is available.


Solution

  • We used the following:

    Store files in the directory returned by GetBurnPath, then write using Burn. GetCDRecordableInfo is used to check when the CD is ready.

    #include <stdio.h>
    #include <imapi.h>
    #include <windows.h>
    
    struct MEDIAINFO {
        BYTE nSessions;
        BYTE nLastTrack;
        ULONG nStartAddress;
        ULONG nNextWritable;
        ULONG nFreeBlocks;
    };
    //==============================================================================
    //  Description:    CD burning on Windows XP
    //==============================================================================
    #define CSIDL_CDBURN_AREA               0x003b
    SHSTDAPI_(BOOL) SHGetSpecialFolderPathA(HWND hwnd, LPSTR pszPath, int csidl, BOOL fCreate);
    SHSTDAPI_(BOOL) SHGetSpecialFolderPathW(HWND hwnd, LPWSTR pszPath, int csidl, BOOL fCreate);
    #ifdef UNICODE
    #define SHGetSpecialFolderPath  SHGetSpecialFolderPathW
    #else
    #define SHGetSpecialFolderPath  SHGetSpecialFolderPathA
    #endif
    //==============================================================================
    // Interface IDiscMaster
    const IID IID_IDiscMaster = {0x520CCA62,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
    const CLSID CLSID_MSDiscMasterObj = {0x520CCA63,0x51A5,0x11D3,{0x91,0x44,0x00,0x10,0x4B,0xA1,0x1C,0x5E}};
    
    typedef interface ICDBurn ICDBurn;
    // Interface ICDBurn
    const IID IID_ICDBurn =    {0x3d73a659,0xe5d0,0x4d42,{0xaf,0xc0,0x51,0x21,0xba,0x42,0x5c,0x8d}};
    const CLSID CLSID_CDBurn = {0xfbeb8a05,0xbeee,0x4442,{0x80,0x4e,0x40,0x9d,0x6c,0x45,0x15,0xe9}};
    
    MIDL_INTERFACE("3d73a659-e5d0-4d42-afc0-5121ba425c8d")
    ICDBurn : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetRecorderDriveLetter(
            /* [size_is][out] */ LPWSTR pszDrive,
            /* [in] */ UINT cch) = 0;
    
        virtual HRESULT STDMETHODCALLTYPE Burn(
            /* [in] */ HWND hwnd) = 0;
    
        virtual HRESULT STDMETHODCALLTYPE HasRecordableDrive(
            /* [out] */ BOOL *pfHasRecorder) = 0;
    };
    //==============================================================================
    //  Description:    Get burn pathname
    //  Parameters:     pathname - must be at least MAX_PATH in size
    //  Returns:        Non-zero for an error
    //  Notes:          CoInitialize(0) must be called once in application
    //==============================================================================
    int GetBurnPath(char *path)
    {
        ICDBurn* pICDBurn;
        int ret = 0;
    
        if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
            BOOL flag;
            if (pICDBurn->HasRecordableDrive(&flag) == S_OK) {
                if (SHGetSpecialFolderPath(0, path, CSIDL_CDBURN_AREA, 0)) {
                    strcat(path, "\\");
                }
                else {
                    ret = 1;
                }
            }
            else {
                ret = 2;
            }
            pICDBurn->Release();
        }
        else {
            ret = 3;
        }
        return ret;
    }
    //==============================================================================
    //  Description:    Get CD pathname
    //  Parameters:     pathname - must be at least 5 bytes in size
    //  Returns:        Non-zero for an error
    //  Notes:          CoInitialize(0) must be called once in application
    //==============================================================================
    int GetCDPath(char *path)
    {
        ICDBurn* pICDBurn;
        int ret = 0;
    
        if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
            BOOL flag;
            WCHAR drive[5];
            if (pICDBurn->GetRecorderDriveLetter(drive, 4) == S_OK) {
                sprintf(path, "%S", drive);
            }
            else {
                ret = 1;
            }
            pICDBurn->Release();
        }
        else {
            ret = 3;
        }
        return ret;
    }
    //==============================================================================
    //  Description:    Burn CD
    //  Parameters:     None
    //  Returns:        Non-zero for an error
    //  Notes:          CoInitialize(0) must be called once in application
    //==============================================================================
    int Burn(void)
    {
        ICDBurn* pICDBurn;
        int ret = 0;
    
        if (SUCCEEDED(CoCreateInstance(CLSID_CDBurn, NULL,CLSCTX_INPROC_SERVER,IID_ICDBurn,(LPVOID*)&pICDBurn))) {
            if (pICDBurn->Burn(NULL) != S_OK) {
                ret = 1;
            }
            pICDBurn->Release();
        }
        else {
            ret = 2;
        }
        return ret;
    }
    //==============================================================================
    bool GetCDRecordableInfo(long *FreeSpaceSize)
    {
        bool Result = false;
        IDiscMaster *idm = NULL;
        IDiscRecorder *idr = NULL;
        IEnumDiscRecorders *pEnumDiscRecorders = NULL;
        ULONG cnt;
        long type;
        long mtype;
        long mflags;
        MEDIAINFO mi;
    
        try {
            CoCreateInstance(CLSID_MSDiscMasterObj, 0, CLSCTX_ALL, IID_IDiscMaster, (void**)&idm);
            idm->Open();
            idm->EnumDiscRecorders(&pEnumDiscRecorders);
            pEnumDiscRecorders->Next(1, &idr, &cnt);
            pEnumDiscRecorders->Release();
    
            idr->OpenExclusive();
            idr->GetRecorderType(&type);
            idr->QueryMediaType(&mtype, &mflags);
            idr->QueryMediaInfo(&mi.nSessions, &mi.nLastTrack, &mi.nStartAddress, &mi.nNextWritable, &mi.nFreeBlocks);
            idr->Release();
    
            idm->Close();
            idm->Release();
            Result = true;
        }
        catch (...) {
            Result = false;
        }
    
        if (Result == true) {
            Result = false;
            if (mtype == 0) {
                //  No Media inserted
                Result = false;
            }
            else {
                if ((mflags & 0x04) == 0x04) {
                    // Writable Media
                    Result = true;
                }
                else {
                    Result = false;
                }
    
                if (Result == true) {
                    *FreeSpaceSize = (mi.nFreeBlocks * 2048);
                }
                else {
                    *FreeSpaceSize = 0;
                }
            }
        }
    
        return Result;
    }