Example: Burn files on ISO CD with the Nero API

vbimport

#1

I often read posts complaining that “burning some files to a simple data CD” is too complicated with the Nero API. In some cases, I think it might be helpful for the SDK users to get an overall grasp on how the Nero API works.

This example is extremely simple and in fact unusable in practice (at least it should be). However, it hopefully helps you to understand the Nero API.

The code was compiled and tested on MSVC 6 with all service releases. It does not use the MFC.

Note to Ahead developers: May you please review the code and tell us if I missed something in the example?

Please read the comments in the code.

#include "stdafx.h" // well, I'm using precompiled headers, you might not.

#include <NeroAPIGlue.h>
#include <assert.h>
#include <iostream>

/*
	Note:
	Link with the NeroAPIGlue.lib or NeroAPIGlueRT.lib, respectively.
	You may need to ignore the default libcmt library.
*/

/*
	This project demonstrates the most simple use of the NERO API to burn
	files in ISO mode on a CD. IT IS FOR DEMONSTRATION PURPOSE ONLY AND NOT
	MEANT TO BE USED AS REAL WORLD RELEASABLE CODE. Newbies to the Nero API
	may understand some thing more easily with code that is reduced to the lines
	absolutely necessary and littered with comments like this one.

	The design is - well - lets say "not so sophisticated". Particularly
	- I've attempted to put as much as possible into one single
	  function, which is definitely not efficient.
	- I've omitted all error handling, just asserting here and there that
	  the function call succeeded.
	- Consequently, proper cleanup is only performed if everything succeeded.
	- Many things are hard-wired into the code: the files that are burned,
	  the usage of the first found CD recorder, the CD title, track and burn
	  flags etc.
	- Many features have been deliberately omitted to improve code clarity
	  e.g. some callbacks like "idle", the progress etc.

	The files that will be burned to the disk are:
		C:\File2Burn1.txt
		C:\File2Burn2.txt
		C:\File2Burn3.txt

	I've separated the program into consecutively numbered sections that may
	be used as reference in discussions, improvements or extensions of the
	example.

	Moreover, this example has been put up in little time, so I can not
	guarantee that it is free of bugs or works as expected. You use it at
	your own risk.

	FOR GODS SAKE, NOBODY WILL HOPEFULLY EVER HAVE THE IDEA TO USE THIS CODE
	AS IT IS IN A REAL WORLD PROJECT.
*/


//////////////////////////////////////////////////////////////////////////
// Function Declarations

// UserDialog NERO callback function.
NeroUserDlgInOut NERO_CALLBACK_ATTR Callback_UserDialog(IN OUT void *pUserData, IN NeroUserDlgInOut DlgType, IN OUT void *pData);


//////////////////////////////////////////////////////////////////////////
// MAIN

int main(int argc, char* argv[])
{
	// Attempt to connect to NERO API.
	const BOOL cbRet_Connect = NeroAPIGlueConnect(NULL);
	assert(cbRet_Connect != FALSE);


	//----------------------------------------
	// (1) Initialize NERO Instance

	// Info.
	std::cout << "Initializing Nero" << std::endl;

	// Initialize the NERO settings structure.
	NERO_SETTINGS NeroSettings;
	memset(&NeroSettings, 0, sizeof(NeroSettings));

	// Directory name with trailing '\' of where to find the additional Nero DLL and text files.
	// Not important in our case, but we follow the suggestions of Ahead.
	NeroSettings.nstNeroFilesPath = "NeroFiles";

	// Vendor and software strings.
	NeroSettings.nstVendor   = "ahead";
	NeroSettings.nstSoftware = "Nero - Burning Rom";

	// Name of the Nero language file; relative to nstNeroFilesPath.
	// We chose the english language file, that is "Nero.txt".
	NeroSettings.nstLanguageFile = "Nero.txt";

	// NERO_IDLE_CALLBACK, may be NULL.
	NeroSettings.nstIdle.ncCallbackFunction = NULL;
	NeroSettings.nstIdle.ncUserData         = NULL;

	// NERO_USER_DIALOG, must not be NULL, see "NeroUserDialog.h" for details.
	// We put a callback function pointer in here.
	NeroSettings.nstUserDialog.ncCallbackFunction = Callback_UserDialog;
	NeroSettings.nstUserDialog.ncUserData         = NULL;

	// Overburning is unsupported.
	NeroSettings.nstEnableOverburn = FALSE;
	NeroSettings.nstOverburnSize   = 0;

	// Initialize the NERO API.
	NEROAPI_INIT_ERROR NeroInitRes = NeroInit(&NeroSettings, NULL);
	assert(NeroInitRes == NEROAPI_INIT_OK);


	//----------------------------------------
	// (2) Open First found CD Burner

	// Info.
	std::cout << "Retrieving Device Collection" << std::endl;

	// Get the device info collection.
	NERO_SCSI_DEVICE_INFOS * const cpDeviceInfos = NeroGetAvailableDrivesEx(MEDIA_CD, NULL);
	assert(cpDeviceInfos);
	assert(cpDeviceInfos->nsdisNumDevInfos >= 1); // assert at least 1 CD recorder was found.

	// Open the first found burning device.
	NERO_DEVICEHANDLE hDevice = NeroOpenDevice(&cpDeviceInfos->nsdisDevInfos[0]);
	assert(hDevice);


	//----------------------------------------
	// (3) Create Some ISO File Items

	// Important Note:
	// The ISO items created below as well as the referenced strings
	// must be valid until finished burning the CD! They are still
	// used by Nero after the ISO track has been created!

	// Info.
	std::cout << "Creating ISO items" << std::endl;

	// Create some ISO items to be used as files.
	// Note that the ISO item structure is zeroized and initialized by the Nero API.
	NERO_ISO_ITEM * pFile1 = NeroCreateIsoItem();
	NERO_ISO_ITEM * pFile2 = NeroCreateIsoItem();
	NERO_ISO_ITEM * pFile3 = NeroCreateIsoItem();

	// Create an ISO item to be used as directory.
	// Note that the ISO item structure is zeroized and initialized by the Nero API.
	NERO_ISO_ITEM * pDir1  = NeroCreateIsoItem();

	// Setup the 1st file item.
	assert(pFile1);
	pFile1->longSourceFilePath = "C:\\File2Burn1.txt";
	pFile1->longFileName = "File1.txt";
	pFile1->nextItem = pFile2;

	// Setup the 2nd file item.
	assert(pFile2);
	pFile2->longSourceFilePath = "C:\\File2Burn2.txt";
	pFile2->longFileName = "File2.txt";
	pFile2->nextItem = pDir1;

	// Setup the 3rd file item.
	assert(pDir1);
	pDir1->isDirectory = TRUE;
	pDir1->longFileName = "MySubdir";
	pDir1->subDirFirstItem = pFile3;

	// Setup the 3rd file item.
	assert(pFile3);
	pFile3->longSourceFilePath = "C:\\File2Burn3.txt";
	pFile3->longFileName = "File3inSubdir.txt";
	pFile3->nextItem = NULL;


	//----------------------------------------
	// (4) Create ISO Track

	// Info.
	std::cout << "Creating ISO track" << std::endl;

	// Create the ISO track.
	CNeroIsoTrack * pIsoTrack = NeroCreateIsoTrackEx(
			pFile1, // First file to burn.
			"MYCD", // CD Title.
			NCITEF_CREATE_ISO_FS|NCITEF_USE_JOLIET // ISO creation flags.
		);
	assert(pIsoTrack);


	//----------------------------------------
	// (5) Create WriteCD info

	// Info.
	std::cout << "Creating WriteCD info" << std::endl;

	NERO_WRITE_CD WriteCD;
	memset(&WriteCD, 0, sizeof(WriteCD));
	WriteCD.nwcdpCDStamp  = NULL;
	WriteCD.nwcdArtist    = NULL;
	WriteCD.nwcdTitle     = NULL;
	WriteCD.nwcdCDExtra   = FALSE;
	WriteCD.nwcdNumTracks = 0;         // No audio tracks.
	WriteCD.nwcdMediaType = MEDIA_CD;  // We're burning a CD.
	WriteCD.nwcdIsoTrack  = pIsoTrack; // Pointer to the created ISO track.
	WriteCD.nwcdTitle     = NULL;      // This title is ignored for data CDs.


	//----------------------------------------
	// (6) Burn the CD

	// Info.
	std::cout << "Burning disk" << std::endl;

	// Create a NeroProgress structure and initialize it.
	NERO_PROGRESS * pNeroProgress = NeroCreateProgress();

	// Burn the CD.
	const NEROAPI_BURN_ERROR ciBurnRes = NeroBurn(
			hDevice,           // Handle to the NERO device.
			NERO_ISO_AUDIO_CD, // We're burning ISO_Audio CD.
			&WriteCD,          // Pointer to the WriteCD structure.
			NBF_WRITE|NBF_BUF_UNDERRUN_PROT|
				NBF_DISABLE_EJECT|NBF_CLOSE_SESSION, // Burn flags.
			0,                 // Max. speed.
			pNeroProgress      // Nero Progress Callback structure; must not be NULL (bitter experience).
		);
	assert(ciBurnRes == NEROAPI_BURN_OK);

	// Free the progress.
	NeroFreeMem(pNeroProgress);


	//----------------------------------------
	// (7) Output Nero Log

	// Retrieve a pointer to the Nero Log text string.
	char * pszErrorLog = NeroGetErrorLog();
	assert(pszErrorLog);

	// Output the Log.
	std::cout << pszErrorLog << std::endl;

	// Free the Nero Log text memory.
	NeroFreeMem(pszErrorLog);


	//----------------------------------------
	// (8) Cleanup

	// Info.
	std::cout << "Cleanup" << std::endl;

	// Free the ISO track.
	NeroFreeIsoTrack(pIsoTrack);

	// Free all ISO items.
	NeroFreeIsoItemTree(pFile1);

	// Close the burning device.
	NeroCloseDevice(hDevice);

	// Free the device info collection memory.
	NeroFreeMem(cpDeviceInfos);

	// Cleanup instance.
	// Will assert here if Nero detected memory leaks.
	assert(!NeroDone());

	// Disconnect from the NERO API.
	NeroAPIGlueDone();


	return 0;
}


//////////////////////////////////////////////////////////////////////////
// Callback_UserDialog

NeroUserDlgInOut NERO_CALLBACK_ATTR Callback_UserDialog(IN OUT void *pUserData, IN NeroUserDlgInOut DlgType, IN OUT void *pData)
{
	// Switch for the dialog type.
	// Note that the Nero SDK supports much more types than we handle here!
	switch (DlgType)
	{
		case DLG_WAITCD:
			std::cout << "Waiting for disk...";
			break;

		case DLG_WAITCD_REMINDER:
			std::cout << "Still waiting for disk...";
			break;

		case DLG_WAITCD_DONE:
			std::cout << std::endl << "  Thanks!" << std::endl;
			break;
	}

	// Anyway, continue with what you're doing.
	return DLG_RETURN_CONTINUE;
}

Please do not ask for more sophisticated solutions in reply to this post!
I believe that it would be best if this post were reserved for code enhancements or comments on the code.


#2

To import a previously written track (multisession), you can use this code. It can be inserted in the upper example after (3).

	//----------------------------------------
	// (3.100) Import Previous Session

	// Info.
	std::cout << "Importing previous session" << std::endl;

	// Query Nero for CD-Info of the opened device.
	NERO_CD_INFO * pCDInfo = NeroGetCDInfo(hDevice, 0);
	assert(pCDInfo);

	// Output number of tracks on the CD.
	// Note that the CD-Info holds several additional information that might
	// be of interest.
	std::cout << "  Tracks: " << pCDInfo->ncdiNumTracks << std::endl;

	// If the CD contains at least one track.
	if (pCDInfo->ncdiNumTracks >= 1)
	{
		// Create a pointer variable that receives a pointer to the CD stamp.
		void * pCDStamp = NULL;

		// Create a structure that receives the import information and initialize it.
		NERO_IMPORT_DATA_TRACK_INFO ImportDataTrackInfo;
		memset(&ImportDataTrackInfo, 0, sizeof(ImportDataTrackInfo));
		ImportDataTrackInfo.nidtiSize = sizeof(ImportDataTrackInfo);

		// Create a variable that receives the import result.
		NERO_IMPORT_DATA_TRACK_RESULT ImportDataTrackResult;

		// Import the last written track, only.
		// Note that the track numbers are 0-based.
		// This function returns a NERO_ISO_ITEM that is the first of a tree
		// which represents the imported file system objects. It will be freed
		// with NeroFreeIsoItemTree (at the end of this example) if it is
		// correctly chained with the items we have already added to the ISO tree.
		NERO_ISO_ITEM * const cpImportedIsoItemTree = NeroImportDataTrack(
				hDevice,                    // The device handle.
				(pCDInfo->ncdiNumTracks-1), // Last track.
				&pCDStamp,                  // Receives the CD-Stamp.
				&ImportDataTrackInfo,       // Retrieves import information.
				NIITEF_IMPORT_ISO_ONLY,     // Flags; we import ISO tracks, only.
				&ImportDataTrackResult,     // Receives the import result.
				NULL);                      // Reserved.
		assert(ImportDataTrackResult == NIDTR_NO_ERROR);
		assert(cpImportedIsoItemTree);

		// Free the Nero CD-Stamp since we do not need it anymore in this example.
		NeroFreeCDStamp(pCDStamp);
		pCDStamp = NULL;

		// Free the volume name of the import information since we do not need
		// it anymore in this example.
		NeroFreeMem(ImportDataTrackInfo.nidtipVolumeName);
		ImportDataTrackInfo.nidtipVolumeName = NULL;

		// The last item of our previously created ISO tree (see above) currently
		// has no "nextItem" (points to NULL). We now let this last item point
		// to the root item returned by NeroImportDataTrack, thus joining the two
		// ISO trees together.
		//
		// Note that, since in this example pFile3 is in a subdirectory of the ISO
		// tree, the previous session will also be imported into this subdirectory.
		//
		// There is no need to free the item returned by NeroImportDataTrack
		// anymore.
		pFile3->nextItem = cpImportedIsoItemTree;
	}

	// Free the CD-Info memory.
	NeroFreeMem(pCDInfo);
	pCDInfo = NULL;

If you have further questions, please open a new thread and do not post here.
I believe that it would be best if this post were reserved for code enhancements or comments on the code.


#3

Hello Oliver M.

I appreciate your article (Example: Burn files on ISO CD with the Nero API ).

your sample Code is very good.
but I don’t know how to make audio CD.

If You Know hot to make Audio CD with the Nero API ,
Please Post this Forum,

Thanks
Have A Nice Day!