/****************************************************************************** |* THIS CODE AND INFORMATION IS 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. |* |* Copyright 1995-2005 Nero AG. All Rights Reserved. |*----------------------------------------------------------------------------- |* PROJECT: NeroFiddles NeroAPI Example |* |* FILE: NeroFiddlesDlg.cpp |* |* PURPOSE: Implementation of a dialog for interaction with the user. ******************************************************************************/ // NeroFiddlesDlg.cpp : implementation file // #include "stdafx.h" #include "neroFiddles.h" #include "neroFiddlesDlg.h" #include "..\\..\\..\\..\\..\\..\\kernelcode\\Burndisc\\Burndisc\\DiscItem.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); // Dialog Data //{{AFX_DATA(CAboutDlg) enum { IDD = IDD_ABOUTBOX }; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CAboutDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: //{{AFX_MSG(CAboutDlg) //}}AFX_MSG DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { //{{AFX_DATA_INIT(CAboutDlg) //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CAboutDlg) //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) //{{AFX_MSG_MAP(CAboutDlg) // No message handlers //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CNeroFiddlesDlg dialog CNeroFiddlesDlg::CNeroFiddlesDlg(CWnd* pParent /*=NULL*/) : CDialog(CNeroFiddlesDlg::IDD, pParent) , m_pFile(NULL) , m_pniiFile(NULL) , m_pnwcWriteCD(NULL) , m_pndiDeviceInfos(NULL) { //{{AFX_DATA_INIT(CNeroFiddlesDlg) //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CNeroFiddlesDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CNeroFiddlesDlg) DDX_Control(pDX, IDC_CLOSE, m_chkbxClose); DDX_Control(pDX, IDC_IMPORT, m_chkbxImport); DDX_Control(pDX, IDC_AUDIO, m_chkbxAudio); DDX_Control(pDX, IDC_REMOVE, m_btnRemove); DDX_Control(pDX, IDC_FILELIST, m_lstFileList); DDX_Control(pDX, IDOK, m_OK); DDX_Control(pDX, IDCANCEL, m_Cancel); DDX_Control(pDX, IDC_ABORT, m_btnAbort); DDX_Control(pDX, IDC_PROGRESS1, m_pgsProgress); DDX_Control(pDX, IDC_MESSAGES, m_edtMessages); DDX_Control(pDX, IDC_DEVICES, m_cbxDevices); DDX_Control(pDX, IDC_BURN, m_btnBurn); DDX_Control(pDX, IDC_BROWSE, m_btnBrowse); //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CNeroFiddlesDlg, CDialog) //{{AFX_MSG_MAP(CNeroFiddlesDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BROWSE, OnBrowse) ON_BN_CLICKED(IDC_BURN, OnBurn) ON_BN_CLICKED(IDC_ABORT, OnAbort) ON_BN_CLICKED(IDC_REMOVE, OnRemove) ON_BN_CLICKED(IDC_AUDIO, OnAudio) ON_BN_CLICKED(IDC_IMPORT, OnImport) ON_LBN_SELCHANGE(IDC_FILELIST, OnSelchangeFilelist) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CNeroFiddlesDlg message handlers BOOL CNeroFiddlesDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here // Initialize the NeroAPI NeroAPIInit(); return TRUE; // return TRUE unless you set the focus to a control } void CNeroFiddlesDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CNeroFiddlesDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CNeroFiddlesDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void CNeroFiddlesDlg::OnBrowse() { // browse for the MP3 file for audio or any other file types for data that is supposed to be burned on CD // provide information about the file type that we want to open static char BASED_CODE szFilterAudio[] = "MP3 Files (*.mp3)|*.mp3|All Files (*.*)|*.*||"; static char BASED_CODE szFilterData[] = "All Files (*.*)|*.*||"; // create a CFileDialog object. // usage : CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, // DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, // CWnd* pParentWnd = NULL ); // // bOpenFileDialog = TRUE, create a File Open dialog // lpszDefExt = NULL, do not automatically append a file extension // dwFlags = OFN_FILEMUSTEXIST, only accepts file names for files that are present // szFilter = "MP3 Files (*.mp3)|*.mp3|All Files (*.*)|*.*||" // pParentWnd = this, our current Dialog window is the parent CFileDialog dlgOpen(TRUE, NULL, NULL, OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT, m_chkbxAudio.GetCheck() ? szFilterAudio : szFilterData, this); // do nothing if IDCANCEL is returned if (dlgOpen.DoModal() == IDOK) { // retrieve multiple file pathname and put them into list box, ensure one file only exist once POSITION pos = dlgOpen.GetStartPosition(); while (pos != NULL) { CString filename = dlgOpen.GetNextPathName(pos); if (m_lstFileList.FindString(0, filename) == LB_ERR) { m_lstFileList.AddString(filename); } } // check whether any devices have been found if (m_pndiDeviceInfos->nsdisNumDevInfos > 0) { // make the "Burn" button accessible m_btnBurn.EnableWindow(true); } } } void CNeroFiddlesDlg::OnRemove() { // TODO: Add your control notification handler code here // delete selected filenames, must do it in reverse order int selCount = m_lstFileList.GetSelCount(); if (selCount > 0) { int* selection = new int[selCount]; m_lstFileList.GetSelItems(selCount, selection); for (int i = selCount - 1; i >= 0; --i) { m_lstFileList.DeleteString(selection[i]); } delete[] selection; } m_btnRemove.EnableWindow(false); } void CNeroFiddlesDlg::OnSelchangeFilelist() { // TODO: Add your control notification handler code here // enable or disable button when selection changed int selCount = m_lstFileList.GetSelCount(); m_btnRemove.EnableWindow(selCount > 0); } void CNeroFiddlesDlg::OnBurn() { #if 1 int i = m_cbxDevices.GetCurSel(); NERO_SCSI_DEVICE_INFO* nsdiDevice = (NERO_SCSI_DEVICE_INFO*)m_cbxDevices.GetItemDataPtr(i); m_ndhDeviceHandle = NeroOpenDevice(nsdiDevice); if(FALSE) { NERO_IMPORT_DATA_TRACK_INFO nidtInfo; NERO_IMPORT_DATA_TRACK_RESULT nidtResult; // Prepare the struct. CString csUserMsg = ""; memset(&nidtInfo, 0, sizeof(nidtInfo)); nidtInfo.nidtiSize = sizeof(nidtInfo); void* pCDStamp = NULL; NERO_ISO_ITEM* pniiItem = NULL; NERO_CD_INFO* pnciInfo = NeroGetCDInfo(m_ndhDeviceHandle, 0); if(pnciInfo != NULL) { pniiItem = NeroImportDataTrack(m_ndhDeviceHandle, pnciInfo->ncdiNumTracks - 1, &pCDStamp, &nidtInfo, 0, &nidtResult, NULL); NeroFreeMem(pnciInfo); } // If there is a volume name after import, print it out. if(nidtInfo.nidtipVolumeName != NULL) { csUserMsg.Format("Imported volume name: %s", nidtInfo.nidtipVolumeName); AppendString(csUserMsg); NeroFreeMem (nidtInfo.nidtipVolumeName); } // If there was an error during import, let the user know about it. if((nidtResult != NIDTR_NO_ERROR) || (pniiItem == NULL)) { static LPCSTR errors[] = {"an unknown error","a generic error","a drive error","a read error","a filesystem error","an invalid track number"}; if (nidtResult > NIDTR_INVALID_TRACKNUMBER) nidtResult = NIDTR_NO_ERROR; csUserMsg.Format("There was %s while importing the track!", errors[nidtResult]); AppendString(csUserMsg); } else if(m_pniiFile != NULL) { if(MergeIsoTracks(&m_pniiFile, pniiItem)) // Merge the new track with the existing one. m_pnwcWriteCD->nwcdpCDStamp = pCDStamp; else { AppendString("There was an error while merging tracks!"); if(pCDStamp != NULL) NeroFreeCDStamp(pCDStamp), pCDStamp = NULL; } } } CDiscItem tagDiscItem; tagDiscItem.AddSiblingRootItem(_T("光盘根目录"),TRUE); #if 1 tagDiscItem.AddPath2Path(_T("光盘根目录"), _T("光盘2级目录-A")); tagDiscItem.AddPath2Path(_T("光盘根目录"), _T("光盘2级目录-B")); tagDiscItem.AddPath2Path(_T("光盘根目录"), _T("光盘2级目录-C")); tagDiscItem.AddPath2Path(_T("光盘根目录"), _T("光盘2级目录-D")); tagDiscItem.AddFile2Path(_T("光盘根目录"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录"),_T("C:\\Users\\IT\\Downloads\\QQ7.9Light.exe")); tagDiscItem.AddFile2Path(_T("光盘根目录"),_T("C:\\Users\\IT\\Downloads\\CDBurn_imapi.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-B"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-C"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-D"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-A"), _T("光盘3级目录-A")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-A"), _T("光盘3级目录-B")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-A"), _T("光盘3级目录-C")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-A"), _T("光盘3级目录-D")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B"), _T("光盘3级目录-A")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B"), _T("光盘3级目录-B")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B"), _T("光盘3级目录-C")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B"), _T("光盘3级目录-D")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-C"), _T("光盘3级目录-A")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-C"), _T("光盘3级目录-B")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-C"), _T("光盘3级目录-C")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-C"), _T("光盘3级目录-D")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D"), _T("光盘3级目录-A")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D"), _T("光盘3级目录-B")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D"), _T("光盘3级目录-C")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D"), _T("光盘3级目录-D")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-A"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-B"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-C"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-D"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B\\光盘3级目录-B"), _T("光盘4级目录-A")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B\\光盘3级目录-B"), _T("光盘4级目录-B")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B\\光盘3级目录-B"), _T("光盘4级目录-C")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-B\\光盘3级目录-B"), _T("光盘4级目录-D")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D\\光盘3级目录-B"), _T("光盘4级目录-A")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D\\光盘3级目录-B"), _T("光盘4级目录-B")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D\\光盘3级目录-B"), _T("光盘4级目录-C")); tagDiscItem.AddPath2Path(_T("光盘根目录\\光盘2级目录-D\\光盘3级目录-B"), _T("光盘4级目录-D")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-A\\光盘4级目录-A"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-A\\光盘4级目录-B"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-A\\光盘4级目录-C"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddFile2Path(_T("光盘根目录\\光盘2级目录-A\\光盘3级目录-A\\光盘4级目录-D"),_T("F:\\刻录驱动.rar")); tagDiscItem.AddSiblingPath(_T("光盘根目录"),_T("光盘根目录2")); tagDiscItem.AddSiblingPath(_T("光盘根目录"),_T("光盘根目录3")); tagDiscItem.AddSiblingPath(_T("光盘根目录"),_T("光盘根目录4")); tagDiscItem.AddSiblingPath(_T("光盘根目录"),_T("光盘根目录5")); tagDiscItem.AddSiblingPath(_T("光盘根目录"),_T("光盘根目录6")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录2"),_T("光盘根目录21")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录3"),_T("光盘根目录31")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录4"),_T("光盘根目录41")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录5"),_T("光盘根目录51")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录6"),_T("光盘根目录61")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录2"),_T("光盘根目录22")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录3"),_T("光盘根目录32")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录4"),_T("光盘根目录42")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录5"),_T("光盘根目录52")); tagDiscItem.AddSiblingPath(_T("光盘根目录\\光盘根目录6"),_T("光盘根目录62")); #endif m_pniiFile = tagDiscItem.GetDiscItem(); if(m_pnwcWriteCD != NULL) free(m_pnwcWriteCD), m_pnwcWriteCD = NULL; size_t stTheSize = sizeof(NERO_WRITE_CD); m_pnwcWriteCD = (NERO_WRITE_CD*)malloc(stTheSize); memset(m_pnwcWriteCD, 0, stTheSize); // no CD stamp, artist or title required m_pnwcWriteCD->nwcdpCDStamp = NULL; m_pnwcWriteCD->nwcdArtist = NULL; m_pnwcWriteCD->nwcdTitle = NULL; // no CD Extra information available m_pnwcWriteCD->nwcdCDExtra = FALSE; // do we have Audio tracks? m_pnwcWriteCD->nwcdNumTracks = 0; m_pnwcWriteCD->nwcdMediaType = MEDIA_DVD_ANY; m_pgsProgress.SetRange(0,100); // 创建刻录任务; m_pnwcWriteCD->nwcdIsoTrack = NeroCreateIsoTrackEx(m_pniiFile, "NeroFiddles", NCITEF_CREATE_ISO_FS|NCITEF_USE_JOLIET); // 开始刻录; int iRes = NeroBurn(m_ndhDeviceHandle, NERO_ISO_AUDIO_MEDIA, m_pnwcWriteCD, NBF_WRITE | NBF_CLOSE_SESSION , 10, &m_npProgress); // 获取刻录日志; //char* Log = NeroGetErrorLog(); if(m_pnwcWriteCD != NULL) { if(m_pnwcWriteCD->nwcdpCDStamp != NULL) NeroFreeCDStamp(m_pnwcWriteCD->nwcdpCDStamp), m_pnwcWriteCD->nwcdpCDStamp = NULL; free(m_pnwcWriteCD), m_pnwcWriteCD = NULL; } // 关闭设备; NeroCloseDevice(m_ndhDeviceHandle); tagDiscItem.RemoveRootItem(); #else // TODO: Add your control notification handler code here // perform the burn process // check whether a file has been selected if (m_lstFileList.GetCount() == 0) { // Tell the user what went wrong AppendString("You have to choose some files before you can start burning!"); } else { // Get the number of audio files. The NERO_WRITE_CD struct already provides space for 1 audio track. int iNumAudio = 1; if(m_chkbxAudio.GetCheck()) { for (int index = 0; index < m_lstFileList.GetCount(); ++index) { CString pathname; m_lstFileList.GetText(index, pathname); if(pathname.Right(4) == ".mp3") iNumAudio++; } } // usually this should always be uninitialized at this place but to be sure... if(m_pnwcWriteCD != NULL) free(m_pnwcWriteCD), m_pnwcWriteCD = NULL; // and allocate the memory for all initialization data size_t stTheSize = sizeof(NERO_WRITE_CD) + ((iNumAudio - 1) * sizeof(NERO_AUDIO_TRACK)); m_pnwcWriteCD = (NERO_WRITE_CD*)malloc(stTheSize); memset(m_pnwcWriteCD, 0, stTheSize); // create NERO_ISO_ITEM struct from file list or even append audio tracks NERO_ISO_ITEM* niiPrevItem = NULL; NERO_ISO_ITEM* pItem1 = new NERO_ISO_ITEM; NERO_ISO_ITEM* pItem2 = new NERO_ISO_ITEM; memset(pItem1, 0, sizeof(NERO_ISO_ITEM)); memset(pItem2, 0, sizeof(NERO_ISO_ITEM)); pItem1->longFileName = strdup(_T("目录7")); pItem1->isDirectory = TRUE; m_pniiFile = pItem1; pItem2->longFileName = _strdup(_T("目录8")); pItem2->isDirectory = TRUE; pItem1->nextItem = pItem2; int iTrackCnt = 0; for (int index = 0; index < m_lstFileList.GetCount(); ++index) { CString pathname; m_lstFileList.GetText(index, pathname); AppendString("Preparing data file:" + pathname); char path[MAX_PATH]; char* name; GetFullPathName(pathname, MAX_PATH, path, &name); if(!m_chkbxAudio.GetCheck()) { NERO_ISO_ITEM* pniiNewItem = NeroCreateIsoItem(); memset(pniiNewItem, 0, sizeof(NERO_ISO_ITEM)); if (m_pniiFile == 0) // mniiFile refer to the first item m_pniiFile = pniiNewItem; pniiNewItem->longFileName = _strdup(name); pniiNewItem->longSourceFilePath = _strdup(path); pniiNewItem->isDirectory = FALSE; pniiNewItem->isReference = FALSE; pniiNewItem->unicodeFileName = NULL; pniiNewItem->nextItem = NULL; if(niiPrevItem) niiPrevItem->nextItem = pniiNewItem; niiPrevItem = pniiNewItem; pItem1->subDirFirstItem = pniiNewItem; pItem2->subDirFirstItem = pniiNewItem; } else if((pathname.Right(4) == ".mp3") && (iTrackCnt < iNumAudio)) { // initialize the audio tracks memeber of NERO_WRITE_CD struct if we are about to write audio CD m_pnwcWriteCD->nwcdTracks[iTrackCnt].natPauseInBlksBeforeThisTrack = 150; // Always 2 sec. pause before m_pnwcWriteCD->nwcdTracks[iTrackCnt].natSourceDataExchg.ndeType = NERO_ET_FILE_MP3; // Has to be mp3 file NERO_DATA_EXCHANGE& ndeCurrent = m_pnwcWriteCD->nwcdTracks[iTrackCnt].natSourceDataExchg; ndeCurrent.ndeData.ndeLongFileName.reserved = 0; // As requested ndeCurrent.ndeData.ndeLongFileName.ptr = _strdup(path); // Last but not least the path iTrackCnt++; } } // no CD stamp, artist or title required m_pnwcWriteCD->nwcdpCDStamp = NULL; m_pnwcWriteCD->nwcdArtist = NULL; m_pnwcWriteCD->nwcdTitle = NULL; // no CD Extra information available m_pnwcWriteCD->nwcdCDExtra = FALSE; // do we have Audio tracks? m_pnwcWriteCD->nwcdNumTracks = iTrackCnt; // we want to write to a CD //m_pnwcWriteCD->nwcdMediaType = MEDIA_CD; m_pnwcWriteCD->nwcdMediaType = MEDIA_DVD_ANY; // get the currently selected device from the ComboBox int i = m_cbxDevices.GetCurSel(); // retrieve the NERO_SCSI_DEVICE_INFO pointer for the selected device // and assign it to a local variable NERO_SCSI_DEVICE_INFO* nsdiDevice = (NERO_SCSI_DEVICE_INFO*)m_cbxDevices.GetItemDataPtr(i); // try to open the selected device m_ndhDeviceHandle = NeroOpenDevice(nsdiDevice); // check whether a valid handle was returned if (!m_ndhDeviceHandle) { // no handle available; tell the user what happened AppendString("Device could not be opened: "+(CString)nsdiDevice->nsdiDeviceName); } else { // Now, import the last session if wished. If the function fails, it's probably // due to no CD in drive. // NeroImportDataTrack creates a NERO_ISO_ITEM tree from an already existing ISO // track in order to create a new session with reference to files from older sessions. // m_pCDStamp will be filled with a pointer to a CDStamp object // which will have to be freed later by the CBurnContext destructor. if(m_chkbxImport.GetCheck()) { NERO_IMPORT_DATA_TRACK_INFO nidtInfo; NERO_IMPORT_DATA_TRACK_RESULT nidtResult; // Prepare the struct. CString csUserMsg = ""; memset(&nidtInfo, 0, sizeof(nidtInfo)); nidtInfo.nidtiSize = sizeof(nidtInfo); void* pCDStamp = NULL; NERO_ISO_ITEM* pniiItem = NULL; NERO_CD_INFO* pnciInfo = NeroGetCDInfo(m_ndhDeviceHandle, 0); if(pnciInfo != NULL) { pniiItem = NeroImportDataTrack(m_ndhDeviceHandle, pnciInfo->ncdiNumTracks - 1, &pCDStamp, &nidtInfo, 0, &nidtResult, NULL); NeroFreeMem(pnciInfo); } // If there is a volume name after import, print it out. if(nidtInfo.nidtipVolumeName != NULL) { csUserMsg.Format("Imported volume name: %s", nidtInfo.nidtipVolumeName); AppendString(csUserMsg); NeroFreeMem (nidtInfo.nidtipVolumeName); } // If there was an error during import, let the user know about it. if((nidtResult != NIDTR_NO_ERROR) || (pniiItem == NULL)) { static LPCSTR errors[] = {"an unknown error","a generic error","a drive error","a read error","a filesystem error","an invalid track number"}; if (nidtResult > NIDTR_INVALID_TRACKNUMBER) nidtResult = NIDTR_NO_ERROR; csUserMsg.Format("There was %s while importing the track!", errors[nidtResult]); AppendString(csUserMsg); } else if(m_pniiFile != NULL) { if(MergeIsoTracks(&m_pniiFile, pniiItem)) // Merge the new track with the existing one. m_pnwcWriteCD->nwcdpCDStamp = pCDStamp; else { AppendString("There was an error while merging tracks!"); if(pCDStamp != NULL) NeroFreeCDStamp(pCDStamp), pCDStamp = NULL; } } } // we have a valid device handle // while burning the "Abort" button needs to be enabled // all the other buttons and controls have to be disabled m_btnAbort.EnableWindow(true); m_Cancel.EnableWindow(false); m_OK.EnableWindow(false); m_cbxDevices.EnableWindow(false); m_btnBrowse.EnableWindow(false); m_btnBurn.EnableWindow(false); m_btnRemove.EnableWindow(false); m_chkbxAudio.EnableWindow(false); m_chkbxImport.EnableWindow(false); m_chkbxClose.EnableWindow(false); // set the range for the progress control, we will display percent m_pgsProgress.SetRange(0,100); // create an ISO track, the audio tracks are initialized above // usage : NeroCreateIsoTrack(struct NERO_ISO_ITEM *root, const char *name, // BOOL useJoliet, BOOL useMode2); // // root = mniiFile, the NERO_ISO_ITEM we filled before // name = neroFiddles if(!m_chkbxAudio.GetCheck()) { m_pnwcWriteCD->nwcdIsoTrack = NeroCreateIsoTrackEx(m_pniiFile, "NeroFiddles", NCITEF_CREATE_ISO_FS|NCITEF_USE_JOLIET); } // start the burn process by calling NeroBurn // usage:NEROAPI_BURN_ERROR NADLL_ATTR NeroBurn( NERO_DEVICEHANDLE aDeviceHandle, // NERO_CD_FORMAT CDFormat, const void* pWriteCD, DWORD dwFlags, DWORD dwSpeedInX, // NERO_PROGRESS* pNeroProgress); // // aDeviceHandle = ndhDeviceHandle, the handle we got from NeroOpenDevice() // CDFormat = NERO_ISO_AUDIO_CD // pWriteCD = writeCD // dwFlags = NBF_WRITE, do not simulate - burn! NBF_CLOSE_SESSION means close only the session not the entire disc. // dwSpeedInX = 0, use maximum speed // pNeroProgress = npProgress, filled during NeroAPIInit() int iRes = NeroBurn(m_ndhDeviceHandle, /*NERO_ISO_AUDIO_CD*/NERO_ISO_AUDIO_MEDIA, m_pnwcWriteCD, NBF_WRITE | (!m_chkbxClose.GetCheck() ? NBF_CLOSE_SESSION : 0), 10, &m_npProgress); // free memory that was allocated for the track if(!m_chkbxAudio.GetCheck() && (m_pnwcWriteCD->nwcdIsoTrack != NULL)) NeroFreeIsoTrack(m_pnwcWriteCD->nwcdIsoTrack); // close the device NeroCloseDevice(m_ndhDeviceHandle); // burning is finished, disable "Abort" activate all other controls m_btnAbort.EnableWindow(false); m_Cancel.EnableWindow(true); m_OK.EnableWindow(true); m_cbxDevices.EnableWindow(true); m_btnBrowse.EnableWindow(true); m_btnBurn.EnableWindow(true); m_btnRemove.EnableWindow(m_lstFileList.GetSelCount() > 0); m_chkbxAudio.EnableWindow(!m_chkbxImport.GetCheck()); m_chkbxImport.EnableWindow(!m_chkbxAudio.GetCheck()); m_chkbxClose.EnableWindow(!m_chkbxAudio.GetCheck()); // clear the progress bar m_pgsProgress.SetPos(0); // make sure that aborted flag is not set if "Burn" button is pressed again m_bAborted = false; // retrieve the error log char* Log = NeroGetErrorLog(); // display the error log contents AppendString(Log); // free the log NeroFreeMem(Log); // free NERO_ISO_ITEM struct list DeleteIsoItemTree(m_pniiFile); m_pniiFile = NULL; // free the memory allocated for audio tracks file paths for(int i = 0; i < iTrackCnt; i++) { const char* pcPath = m_pnwcWriteCD->nwcdTracks[i].natSourceDataExchg.ndeData.ndeLongFileName.ptr; if(pcPath != NULL) free((void*)pcPath), pcPath = NULL; } // Free the NERO_WRITE_CD struct too if(m_pnwcWriteCD != NULL) { if(m_pnwcWriteCD->nwcdpCDStamp != NULL) NeroFreeCDStamp(m_pnwcWriteCD->nwcdpCDStamp), m_pnwcWriteCD->nwcdpCDStamp = NULL; free(m_pnwcWriteCD), m_pnwcWriteCD = NULL; } // tell the user how the burn process was finished switch(iRes) { case NEROAPI_BURN_OK: AppendString ("BurnCD() : burn successful"); break; case NEROAPI_BURN_UNKNOWN_CD_FORMAT: AppendString ("BurnCD() : unknown CD format"); break; case NEROAPI_BURN_INVALID_DRIVE: AppendString ("BurnCD() : invalid drive"); break; case NEROAPI_BURN_FAILED: AppendString ("BurnCD() : burn failed"); break; case NEROAPI_BURN_FUNCTION_NOT_ALLOWED: AppendString ("BurnCD() : function not allowed"); break; case NEROAPI_BURN_DRIVE_NOT_ALLOWED: AppendString ("BurnCD() : drive not allowed"); break; case NEROAPI_BURN_USER_ABORT: AppendString ("BurnCD() : user aborted"); break; case NEROAPI_BURN_BAD_MESSAGE_FILE: AppendString ("BurnCD() : bad message file"); break; default: AppendString ("BurnCD() : unknown error"); break; } } } #endif } BOOL NERO_CALLBACK_ATTR CNeroFiddlesDlg::IdleCallback(void *pUserData) { // idle callback is called frequently by NeroAPI // make sure that messages from other controls can be handled static MSG msg; while (!(((CNeroFiddlesDlg*)pUserData)->m_bAborted) && ::PeekMessage(&msg,NULL,NULL,NULL,PM_NOREMOVE)) { if (!AfxGetThread()->PumpMessage()) { break; } } // aborted-flag serves as function result return ((CNeroFiddlesDlg*)pUserData)->m_bAborted; } void CNeroFiddlesDlg::NeroAPIInit() { // initialization part, provide necessary information and check status m_bAborted = false; // try to open the NeroAPI DLL if (!NeroAPIGlueConnect (NULL)) { AppendString("Cannot open NeroAPI.DLL"); // it makes no sense to continue after loading the DLL failed return; } // the NeroAPI DLL could be openend, get version information AppendString("Retrieving version information."); WORD majhi, majlo, minhi, minlo; NeroGetAPIVersionEx(&majhi, &majlo, &minhi, &minlo, NULL); // format and display the version information CString strVersion; strVersion.Format("NeroAPI version %d.%d.%d.%d", majhi, majlo, minhi, minlo); AppendString(strVersion); // setup of structures that the NeroAPI needs AppendString("Filling NERO_SETTINGS structure"); // Information for registry access strcpy(m_pcNeroFilesPath, "NeroFiles"); strcpy(m_pcVendor, "Nero"); strcpy(m_pcSoftware, "Nero - NeroFiddles"); // use the US-English error message file strcpy(m_pcLanguageFile, "Nero.txt"); // Initialize NeroAPI settings m_nsSettings.nstNeroFilesPath = m_pcNeroFilesPath; m_nsSettings.nstVendor = m_pcVendor; // set pointers to various callback functions m_nsSettings.nstIdle.ncCallbackFunction = IdleCallback; // this pointer is required to access non-static variables from callback functions m_nsSettings.nstIdle.ncUserData = this; m_nsSettings.nstSoftware = m_pcSoftware; m_nsSettings.nstUserDialog.ncCallbackFunction = UserDialog; m_nsSettings.nstUserDialog.ncUserData = this; m_nsSettings.nstLanguageFile = m_pcLanguageFile; // npProgress will be used during the burn process m_npProgress.npAbortedCallback = AbortedCallback; m_npProgress.npAddLogLineCallback = AddLogLine; m_npProgress.npDisableAbortCallback = NULL; m_npProgress.npProgressCallback = ProgressCallback; m_npProgress.npSetPhaseCallback = SetPhaseCallback; m_npProgress.npUserData = this; m_npProgress.npSetMajorPhaseCallback = NULL; m_npProgress.npSubTaskProgressCallback= NULL; // no devices available yet m_pndiDeviceInfos = NULL; // initialize the NeroAPI with nsSettings and the // Serial Number that we got from the Registry NEROAPI_INIT_ERROR initErr; initErr = NeroInit (&m_nsSettings, NULL); // display the result of NeroInit() switch (initErr) { case NEROAPI_INIT_OK: AppendString("Initialization of the NeroAPI successful."); break; case NEROAPI_INIT_INVALID_ARGS: AppendString("The arguments are not valid."); break; case NEROAPI_INIT_INVALID_SERIAL_NUM: AppendString("The Serial Number is not valid."); break; default: AppendString("An error occured. The type of error cannot be determined."); break; } // get a list of available drives m_pndiDeviceInfos = NeroGetAvailableDrivesEx (MEDIA_CD, NULL); // check whether any devices have been found if (!m_pndiDeviceInfos) { // no device found, let the user know AppendString("NeroGetAvailableDrivesEx() returned no available devices."); } else { // devices found // check the number of available devices to be sure if (m_pndiDeviceInfos->nsdisNumDevInfos > 0) { // we have some devices, now fill the ComboBox AppendString("Found the following devices:"); for (DWORD dDeviceCounter = 0; dDeviceCounter < m_pndiDeviceInfos->nsdisNumDevInfos; dDeviceCounter++) { AppendString(m_pndiDeviceInfos->nsdisDevInfos[dDeviceCounter].nsdiDeviceName); // add the device name to the ComboBox and get the index number int i = m_cbxDevices.AddString(m_pndiDeviceInfos->nsdisDevInfos[dDeviceCounter].nsdiDeviceName); // use the index number to access the corresponding entry // connect the entry's ItemData pointer to a NERO_DEVICE_INFO structure m_cbxDevices.SetItemDataPtr(i, &m_pndiDeviceInfos->nsdisDevInfos[dDeviceCounter]); } // select the first ComboBox entry m_cbxDevices.SelectString(-1, m_pndiDeviceInfos->nsdisDevInfos[0].nsdiDeviceName); } else { AppendString("The number of available devices is 0."); } } } void CNeroFiddlesDlg::AppendString(CString str) { // a CString for temporary use CString strBuffer; // retrieve the content of the EditControl we use for messages m_edtMessages.GetWindowText (strBuffer); // add a new line if the EditControl is not empty if (!strBuffer.IsEmpty()) { strBuffer += "\r\n"; } // append the string the function got as a parameter strBuffer += str; // update the EditiControl with the new content m_edtMessages.SetWindowText (strBuffer); // Scroll the edit control to the end m_edtMessages.LineScroll (m_edtMessages.GetLineCount(), 0); } NeroUserDlgInOut NERO_CALLBACK_ATTR CNeroFiddlesDlg::UserDialog(void *pUserData, NeroUserDlgInOut type, void *data) { // handling of messages that require the user to perform an action // for reasons of brevity we only deal with the messages that // are absolutely mandatory for this application switch (type) { case DLG_AUTO_INSERT: return DLG_RETURN_CONTINUE; break; case DLG_DISCONNECT_RESTART: return DLG_RETURN_ON_RESTART; break; case DLG_DISCONNECT: return DLG_RETURN_CONTINUE; break; case DLG_AUTO_INSERT_RESTART: return DLG_RETURN_EXIT; break; case DLG_RESTART: return DLG_RETURN_EXIT; break; case DLG_SETTINGS_RESTART: return DLG_RETURN_CONTINUE; break; case DLG_OVERBURN: return DLG_RETURN_TRUE; break; case DLG_AUDIO_PROBLEMS: return DLG_RETURN_EXIT; break; case DLG_FILESEL_IMAGE: { // create filter for image files static char BASED_CODE szFilter[] = "Image Files (*.nrg)|*.nrg|All Files (*.*)|*.*||"; // create a CFileDialog object. // usage : CFileDialog( BOOL bOpenFileDialog, LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, // DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, // CWnd* pParentWnd = NULL ); // // bOpenFileDialog = TRUE, create a File Open dialog // lpszDefExt = NULL, do not automatically append a file extension // dwFlags = OFN_OVERWRITEPROMPT, makes no sense during file open, // just in case we decide to use File Save later // szFilter = "Image Files (*.nrg)|*.nrg|All Files (*.*)|*.*||" // pParentWnd = ((CNeroFiddlesDlg*)pUserData), our current Dialog window is the parent CFileDialog dlgOpen(TRUE, NULL, "test.nrg", OFN_OVERWRITEPROMPT, szFilter, ((CNeroFiddlesDlg*)pUserData)); // check how the dialog was ended if (dlgOpen.DoModal() == IDOK) { // user pressed "OK", copy the file name to the data parameter strcpy((char*)data,dlgOpen.GetPathName()); // proceed with the burn process return DLG_RETURN_TRUE; } else { // user canceled, do not proceed with the burn process return DLG_BURNIMAGE_CANCEL; } } break; case DLG_WAITCD: { NERO_WAITCD_TYPE waitcdType = (NERO_WAITCD_TYPE) (int)data; char *waitcdString = NeroGetLocalizedWaitCDTexts (waitcdType); ((CNeroFiddlesDlg*)pUserData)->AppendString(waitcdString); NeroFreeMem(waitcdString); return DLG_RETURN_EXIT; break; } default: break; } // default return value, in case we forgot to handle a request return DLG_RETURN_EXIT; } BOOL NERO_CALLBACK_ATTR CNeroFiddlesDlg::ProgressCallback(void *pUserData, DWORD dwProgressInPercent) { // the NeroAPI updates the current progress counter // set the progress bar to the percentage value that was passed to this function ((CNeroFiddlesDlg*)pUserData)->m_pgsProgress.SetPos(dwProgressInPercent); return true; } BOOL NERO_CALLBACK_ATTR CNeroFiddlesDlg::AbortedCallback(void *pUserData) { // do not ask the user if he really wants to abort // just return the aborted flag return ((CNeroFiddlesDlg*)pUserData)->m_bAborted; } void NERO_CALLBACK_ATTR CNeroFiddlesDlg::AddLogLine(void *pUserData, NERO_TEXT_TYPE type, const char *text) { // Add the text that was passed to this function to the message log CString csTemp(text); ((CNeroFiddlesDlg*)pUserData)->AppendString("Log line:" + csTemp); return; } void NERO_CALLBACK_ATTR CNeroFiddlesDlg::SetPhaseCallback(void *pUserData, const char *text) { // display the current phase the burn process is currently going through CString csTemp(text); ((CNeroFiddlesDlg*)pUserData)->AppendString("Phase: " + csTemp); return; } void CNeroFiddlesDlg::NeroAPIFree() { // free the resources that have been used // make sure there is something to free so we do not run into an exception if (m_pndiDeviceInfos) { NeroFreeMem(m_pndiDeviceInfos); } // nothing to check before calling these functions NeroClearErrors(); if(NeroDone()) { AfxMessageBox("Detected memory leaks in NeroFiddles"); } NeroAPIGlueDone(); return; } void CNeroFiddlesDlg::OnOK() { // TODO: Add extra validation here // user decides to quit by pressing "OK" NeroAPIFree(); CDialog::OnOK(); } void CNeroFiddlesDlg::OnCancel() { // TODO: Add extra cleanup here // user decides to quit by pressing "Cancel" // we handle this like the "OK" button NeroAPIFree(); CDialog::OnCancel(); } void CNeroFiddlesDlg::OnAbort() { // TODO: Add your control notification handler code here // nothing more required but setting the aborted flag m_bAborted = true; } void CNeroFiddlesDlg::OnAudio() { // TODO: Add your control notification handler code here // If we should burn audio disc we are not able to import previous sessions // and they are always closed m_chkbxImport.EnableWindow(!m_chkbxAudio.GetCheck()); m_chkbxClose.EnableWindow(!m_chkbxAudio.GetCheck()); m_chkbxClose.SetCheck(m_chkbxAudio.GetCheck()); } void CNeroFiddlesDlg::OnImport() { // TODO: Add your control notification handler code here // Imports a only possible on data discs m_chkbxAudio.EnableWindow(!m_chkbxImport.GetCheck()); } // A helper to get the correct filename in a NERO_ISO_ITEM. inline LPCSTR GetFilename(const NERO_ISO_ITEM* pItem) { return (pItem->longFileName != NULL)? pItem->longFileName : pItem->fileName; } // The following function performs a merge operation between two iso item trees. // The second tree is added onto the first one and the extra items are deleted. // As we do not allow to add directories in our file list we do not make recursive // calls of this method. BOOL CNeroFiddlesDlg::MergeIsoTracks(NERO_ISO_ITEM** ppniiTarget, NERO_ISO_ITEM* pniiToAdd) { BOOL bResult = ((ppniiTarget != NULL) && (*ppniiTarget != NULL) && (pniiToAdd != NULL)); // Two loops. Outer loops the first tree, the inner loops the second tree. for( ; bResult && (*ppniiTarget != NULL); ppniiTarget = &(*ppniiTarget)->nextItem) { for(NERO_ISO_ITEM** ppniiToAddLocal = &pniiToAdd; *ppniiToAddLocal != NULL; ) { // Compare entry names... if(0 == stricmp(GetFilename(*ppniiTarget), GetFilename(*ppniiToAddLocal))) { // If there is a file name conflict between iso items that belong to imported sessions // always replace the old files in terms of modification times. time_t timeTarget = mktime(&(*ppniiTarget)->entryTime); if(timeTarget == (time_t)-1) { HANDLE hFile = NULL; // handle to file FILETIME ftCreationTime; // creation time FILETIME ftLastAccessTime; // last access time FILETIME ftLastWriteTime; // last write time hFile = CreateFile((*ppniiTarget)->longSourceFilePath, // open the file to get handle GENERIC_READ, // open for reading FILE_SHARE_READ, // share for reading NULL, // no security OPEN_EXISTING, // existing file only FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template if(hFile == INVALID_HANDLE_VALUE) { CString csMsg; csMsg.Format("Could not open file: %s.", (*ppniiTarget)->longSourceFilePath); AppendString(csMsg); // show error } else if(GetFileTime(hFile, &ftCreationTime, &ftLastAccessTime, &ftLastWriteTime)) timeTarget = CTime(ftLastWriteTime).GetTime(); if(hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile), hFile = NULL; } time_t timeToAdd = mktime(&(*ppniiToAddLocal)->entryTime); // If we have to replace one item, we will now switch places of items in the first and second tree. // Since one of the items has to be deleted eventually, this operation will // essentially keep the item from the second tree and delete the item from the first tree. if(timeTarget < timeToAdd) { NERO_ISO_ITEM* pniiTmpItem = *ppniiToAddLocal; *ppniiToAddLocal = *ppniiTarget; *ppniiTarget = pniiTmpItem; pniiTmpItem = (*ppniiToAddLocal)->nextItem; (*ppniiToAddLocal)->nextItem = (*ppniiTarget)->nextItem; (*ppniiTarget)->nextItem = pniiTmpItem; } // Remove the item from the second tree. NERO_ISO_ITEM* pniiTmpItem = *ppniiToAddLocal; *ppniiToAddLocal = pniiTmpItem->nextItem; pniiTmpItem->nextItem = NULL; DeleteIsoItemTree(pniiTmpItem); } else // No match, advance to the next item. ppniiToAddLocal = &(*ppniiToAddLocal)->nextItem; } } // Attach whatever is left of the new tree to the main tree. *ppniiTarget = pniiToAdd; // Returning true means, everything is fine, continue. return bResult; } // This function deletes the iso tree recursively. void CNeroFiddlesDlg::DeleteIsoItemTree(NERO_ISO_ITEM* pniiItem) { // First free our own long filename strings, then free the whole tree. FreeOurOwnResources(pniiItem); NeroFreeIsoItemTree(pniiItem); } void CNeroFiddlesDlg::FreeOurOwnResources(NERO_ISO_ITEM* pniiItem) { // Step through the tree until the ISO item tree pointer becomes NULL while(pniiItem != NULL) { NERO_ISO_ITEM* pniiNextItem = pniiItem->nextItem; // We have encountered another ISO item tree; recurse another level. if(pniiItem->isDirectory) FreeOurOwnResources (pniiItem->subDirFirstItem); if(!pniiItem->isReference) { if(pniiItem->longFileName != NULL) free((void*)pniiItem->longFileName), pniiItem->longFileName = NULL; if(pniiItem->longSourceFilePath != NULL) free((void*)pniiItem->longSourceFilePath), pniiItem->longSourceFilePath = NULL; } pniiItem = pniiNextItem; } }