Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

6117 lines
204 KiB

  1. #include "shellprv.h"
  2. #include "ids.h"
  3. #include "views.h"
  4. #include "stgutil.h"
  5. #include "imapi.h"
  6. #include "propsht.h"
  7. #include "mtpt.h"
  8. #include "shcombox.h"
  9. #include "datautil.h"
  10. #include "fstreex.h"
  11. #include "balmsg.h"
  12. #include <imapierror.h>
  13. #include <imapi/imapiregistry.h>
  14. #include "cowsite.h"
  15. #include <cfgmgr32.h>
  16. #include "cdmedia.h"
  17. #include <lmcons.h>
  18. #include "prop.h"
  19. #include "clsobj.h"
  20. #include "filetbl.h"
  21. #include "cdburn.h"
  22. #include "setupapi.h"
  23. #include "isproc.h"
  24. #include "ole2dup.h"
  25. #include "copy.h"
  26. #pragma hdrstop
  27. #define REGSTR_PATH_CDBURNING REGSTR_PATH_EXPLORER TEXT("\\CD Burning")
  28. #define REGSTR_PATH_DRIVES REGSTR_PATH_CDBURNING TEXT("\\Drives")
  29. #define REGSTR_PATH_HANDLERS REGSTR_PATH_CDBURNING TEXT("\\Extensions")
  30. #define REGSTR_PATH_PERMEDIA REGSTR_PATH_CDBURNING TEXT("\\Current Media")
  31. #define REGSTR_PATH_EXCLUDE REGSTR_PATH_CDBURNING TEXT("\\ExcludedFS")
  32. #define REGSTR_PATH_AUDIOEXTS REGSTR_PATH_CDBURNING TEXT("\\AudioBurnHandlers")
  33. #define REGVALUE_FILEEXTS TEXT("SupportedFileTypes")
  34. #define REGSTR_PATH_IMAPI TEXT(IMAPI_PRODUCT_REGPATH) TEXT(IMAPI_REGKEY_STASHFILE)
  35. #define REGVALUE_CURRENTDRIVE TEXT("CD Recorder Drive")
  36. #define REGVALUE_AUTOEJECT TEXT("Auto Eject")
  37. #define REGVALUE_AUTOCLOSE TEXT("Auto Close")
  38. #define REGVALUE_FIRSTHANDLER TEXT("FirstHandler")
  39. #define REGVALUE_CLSID TEXT("CLSID")
  40. #define REGVALUE_VERB TEXT("verb")
  41. #define REGVALUE_CACHEDINDEX TEXT("DriveIndex")
  42. #define REGVALUE_DRIVETYPE TEXT("Drive Type")
  43. #define REGVALUE_CURRENTSPEED TEXT("CurrentCDWriteSpeed")
  44. #define REGVALUE_MAXSPEED TEXT("MaxCDWriteSpeed")
  45. #define REGVALUE_TOTALBYTES TEXT("TotalBytes")
  46. #define REGVALUE_FREEBYTES TEXT("FreeBytes")
  47. #define REGVALUE_MEDIATYPE TEXT("Media Type")
  48. #define REGVALUE_UDF TEXT("UDF")
  49. #define REGVALUE_DISCLABEL TEXT("Disc Label")
  50. #define REGVALUE_SET TEXT("Set")
  51. #define REGVALUE_ERASETIME TEXT("Erase Time")
  52. #define REGVALUE_STAGERATE TEXT("Stage Rate")
  53. #define REGVALUE_BURNRATE TEXT("Burn Rate")
  54. #define REGVALUE_CLOSEFACTOR TEXT("Close Factor")
  55. #define WRITESPEED_FASTEST 0xFFFFFFFF
  56. #define STASH_FILENAME TEXT("CD Burning Stash File.bin")
  57. #define JOLIET_MAX_LABEL 16
  58. #define PROPSTR_EJECT TEXT("Eject")
  59. #define PROPSTR_ERASE TEXT("Erase")
  60. #define PROPSTR_HR TEXT("HR")
  61. #define PROPSTR_DISCLABEL REGVALUE_DISCLABEL
  62. #define PROPSTR_AUTOCLOSE REGVALUE_AUTOCLOSE
  63. #define PROPSTR_DISCFULLTEXT TEXT("DiscFullText")
  64. #define PROPSTR_CURRENTEXT TEXT("CurrentExt")
  65. #define PROPSTR_FAILSILENTLY TEXT("FailSilently")
  66. #define PROPSTR_STATUSTEXT TEXT("StatusText")
  67. #define INDEX_DLG_BURNWIZ_MAX 30
  68. #define PROGRESS_INCREMENTS 1000
  69. enum {
  70. DRIVE_USEEXISTING = 0,
  71. DRIVE_CDR = RECORDER_CDR,
  72. DRIVE_CDRW = RECORDER_CDRW,
  73. DRIVE_NOTSUPPORTED
  74. };
  75. #define SUPPORTED(x) ((x) && !((x) == DRIVE_NOTSUPPORTED))
  76. typedef struct
  77. {
  78. INT idPage;
  79. INT idHeading;
  80. INT idSubHeading;
  81. DWORD dwFlags;
  82. DLGPROC dlgproc;
  83. } WIZPAGE;
  84. typedef struct
  85. {
  86. DWORD dwSecStaging, dwTickStagingStart, dwTickStagingEnd;
  87. DWORD dwSecBurn, dwTickBurnStart, dwTickBurnEnd;
  88. DWORD dwSecClose, dwTickCloseStart, dwTickCloseEnd;
  89. DWORD dwSecErase, dwTickEraseStart, dwTickEraseEnd;
  90. DWORD dwSecRemaining, dwSecTotal;
  91. } TIMESTATS;
  92. class CCDBurn;
  93. typedef struct
  94. {
  95. CCDBurn *pcdb;
  96. IStream *pstmDataObj;
  97. BOOL fMove;
  98. } CDDROPPARAMS;
  99. // CLSIDs used for merged namespace for CD mastering
  100. /* 00da2f99-f2a6-40c2-b770-a920f8e44abc */
  101. const CLSID CLSID_StagingFolder = {0x00da2f99, 0xf2a6, 0x40c2, {0xb7, 0x70, 0xa9, 0x20, 0xf8, 0xe4, 0x4a, 0xbc}};
  102. // CDBurn - object which performs the CDBurning and displaying the progress etc.
  103. class CCDBurn : public CObjectWithSite,
  104. public IShellExtInit,
  105. public IContextMenu,
  106. public IShellPropSheetExt,
  107. public IDiscMasterProgressEvents,
  108. public IDropTarget,
  109. public IPersistFile,
  110. public IOleCommandTarget,
  111. public ICDBurn,
  112. public ICDBurnPriv,
  113. public IPersistPropertyBag,
  114. public IDriveFolderExt,
  115. public INamespaceWalkCB,
  116. public IWizardSite,
  117. public IServiceProvider,
  118. public ITransferAdviseSink,
  119. public IQueryCancelAutoPlay
  120. {
  121. public:
  122. // IUnknown methods
  123. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  124. STDMETHOD_(ULONG, AddRef)();
  125. STDMETHOD_(ULONG, Release)();
  126. // IPersist methods
  127. STDMETHOD(GetClassID)(CLSID *pClassID)
  128. { *pClassID = CLSID_CDBurn; return S_OK; };
  129. // IPersistFile methods
  130. STDMETHOD(IsDirty)(void)
  131. { return S_FALSE; };
  132. STDMETHOD(Load)(LPCOLESTR pszFileName, DWORD dwMode)
  133. { return S_OK; };
  134. STDMETHOD(Save)(LPCOLESTR pszFileName, BOOL fRemember)
  135. { return S_OK; };
  136. STDMETHOD(SaveCompleted)(LPCOLESTR pszFileName)
  137. { return S_OK; };
  138. STDMETHOD(GetCurFile)(LPOLESTR *ppszFileName)
  139. { *ppszFileName = NULL; return S_OK; };
  140. // IDropTarget methods
  141. STDMETHOD(DragEnter)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  142. STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  143. STDMETHOD(DragLeave)(void);
  144. STDMETHOD(Drop)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  145. // IShellExtInit methods
  146. STDMETHOD(Initialize)(LPCITEMIDLIST pidlFolder, LPDATAOBJECT lpdobj, HKEY hkeyProgID);
  147. // IContextMenu methods
  148. STDMETHOD(QueryContextMenu)(HMENU hMenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags);
  149. STDMETHOD(GetCommandString)(UINT_PTR idCommand, UINT uFlags, LPUINT lpReserved, LPSTR pszName, UINT uMaxNameLen);
  150. STDMETHOD(InvokeCommand)(LPCMINVOKECOMMANDINFO lpcmi);
  151. // IShellPropSheetExt methods
  152. STDMETHOD(AddPages)(LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam);
  153. STDMETHOD(ReplacePage)(UINT uPageID, LPFNADDPROPSHEETPAGE pReplacePageFunc, LPARAM lParam)
  154. { return S_OK; };
  155. // IDiscMasterProgressEvents methods
  156. STDMETHOD(QueryCancel)(boolean *pbCancel);
  157. STDMETHOD(NotifyPnPActivity)();
  158. STDMETHOD(NotifyAddProgress)(long nCompletedSteps, long nTotalSteps);
  159. STDMETHOD(NotifyBlockProgress)(long nCompleted, long nTotal);
  160. STDMETHOD(NotifyTrackProgress)(long nCurrentTrack, long nTotalTracks);
  161. STDMETHOD(NotifyPreparingBurn)(long nEstimatedSeconds);
  162. STDMETHOD(NotifyClosingDisc)(long nEstimatedSeconds);
  163. STDMETHOD(NotifyBurnComplete)(HRESULT status);
  164. STDMETHOD(NotifyEraseComplete)(HRESULT status);
  165. // IOleCommandTarget methods
  166. STDMETHOD(QueryStatus)(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText);
  167. STDMETHOD(Exec)(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut);
  168. // ICDBurn methods
  169. STDMETHOD(GetRecorderDriveLetter)(LPWSTR pszDrive, UINT cch);
  170. STDMETHOD(Burn)(HWND hwnd);
  171. STDMETHOD(HasRecordableDrive)(BOOL *pfHasRecorder);
  172. // ICDBurnPriv methods
  173. STDMETHOD(GetMediaCapabilities)(DWORD *pdwCaps, BOOL *pfUDF);
  174. STDMETHOD(GetContentState)(BOOL *pfStagingHasFiles, BOOL *pfDiscHasFiles);
  175. STDMETHOD(IsWizardUp)();
  176. // IPersistPropertyBag methods
  177. STDMETHOD(InitNew)();
  178. STDMETHOD(Load)(IPropertyBag *ppb, IErrorLog *pErr);
  179. STDMETHOD(Save)(IPropertyBag *ppb, BOOL fClearDirty, BOOL fSaveAll)
  180. { return E_NOTIMPL; }
  181. // IDriveFolderExt methods
  182. STDMETHOD(DriveMatches)(int iDrive);
  183. STDMETHOD(Bind)(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppv);
  184. STDMETHOD(GetSpace)(ULONGLONG *pcbTotal, ULONGLONG *pcbFree);
  185. // INamespaceWalkCB methods
  186. STDMETHOD(FoundItem)(IShellFolder *psf, LPCITEMIDLIST pidl);
  187. STDMETHOD(EnterFolder)(IShellFolder *psf, LPCITEMIDLIST pidl)
  188. { return S_OK; }
  189. STDMETHOD(LeaveFolder)(IShellFolder *psf, LPCITEMIDLIST pidl)
  190. { return S_OK; }
  191. STDMETHOD(InitializeProgressDialog)(LPWSTR *ppszTitle, LPWSTR *ppszCancel)
  192. { *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; }
  193. // IWizardSite methods
  194. STDMETHOD(GetNextPage)(HPROPSHEETPAGE *phPage);
  195. STDMETHOD(GetPreviousPage)(HPROPSHEETPAGE *phPage);
  196. STDMETHOD(GetCancelledPage)(HPROPSHEETPAGE *phPage)
  197. { return E_NOTIMPL; }
  198. // IServiceProvider methods
  199. STDMETHOD(QueryService)(REFGUID guidService, REFIID riid, void **ppv);
  200. // IQueryCancelAutoPlay methods
  201. STDMETHOD(AllowAutoPlay)(LPCWSTR pszPath, DWORD dwContentType, LPCWSTR pszLabel, DWORD dwSerialNumber);
  202. // ITransferAdviseSink methods
  203. STDMETHOD(PreOperation)(const STGOP op, IShellItem *psiItem, IShellItem *psiDest)
  204. { return S_OK; }
  205. STDMETHOD(ConfirmOperation)(IShellItem *psiSource, IShellItem *psiDest, STGTRANSCONFIRMATION stc, LPCUSTOMCONFIRMATION pcc);
  206. STDMETHOD(OperationProgress)(const STGOP op, IShellItem *psiItem, IShellItem *psiDest, ULONGLONG ullTotal, ULONGLONG ullComplete)
  207. { return S_OK; }
  208. STDMETHOD(PostOperation)(const STGOP op, IShellItem *psiItem, IShellItem *psiDest, HRESULT hrResult)
  209. { return S_OK; }
  210. STDMETHOD(QueryContinue)()
  211. { return S_OK; }
  212. // exposed for the static tables
  213. static INT_PTR s_WelcomeDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  214. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_WelcomeDlgProc(hwnd, uMsg, wParam, lParam); }
  215. static INT_PTR s_EjectDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  216. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_EjectDlgProc(hwnd, uMsg, wParam, lParam); }
  217. static INT_PTR s_ProgressDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  218. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_ProgressDlgProc(hwnd, uMsg, wParam, lParam); }
  219. static INT_PTR s_DoneDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  220. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_DoneDlgProc(hwnd, uMsg, wParam, lParam); }
  221. static INT_PTR s_WaitForMediaDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  222. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_WaitForMediaDlgProc(hwnd, uMsg, wParam, lParam); }
  223. static INT_PTR s_StartEraseDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  224. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_StartEraseDlgProc(hwnd, uMsg, wParam, lParam); }
  225. static INT_PTR s_DiskFullDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  226. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_DiskFullDlgProc(hwnd, uMsg, wParam, lParam); }
  227. static INT_PTR s_EarlyExitDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  228. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_EarlyExitDlgProc(hwnd, uMsg, wParam, lParam); }
  229. static INT_PTR s_HDFullDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  230. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_HDFullDlgProc(hwnd, uMsg, wParam, lParam); }
  231. static INT_PTR s_NoFilesDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  232. { CCDBurn *pcdb = s_GetCDBurn(hwnd, uMsg, lParam); return pcdb->_NoFilesDlgProc(hwnd, uMsg, wParam, lParam); }
  233. private:
  234. CCDBurn();
  235. ~CCDBurn();
  236. LONG _cRef;
  237. TCHAR _szVolumeName[MAX_PATH]; // device path of the drive that we were invoked on
  238. IDataObject *_pdo; // IDataObject (from IShellExtInit::Initialize)
  239. LPITEMIDLIST _pidl; // pidl we're sited on (only for droptarget, through IShellExtInit::Initialize)
  240. BOOL _fCancelled;
  241. BOOL _fRecording;
  242. BOOL _fIsRecordingDrive; // if this is the current recording drive (on init)
  243. BOOL _fPropSheetDirty;
  244. IDropTarget *_pdt; // IDropTarget object that we wrap
  245. DWORD _dwDropEffect; // drop effect chosen in dragenter
  246. IPropertyBag *_ppb; // propertybag to track state in wizard
  247. HWND _hwndWizardPage; // hwnd of the wizard page (used in progress)
  248. HWND _hwndBrowser; // browser hwnd, used to parent error dialogs
  249. ULONGLONG _cbStagedSize; // total size of staged files
  250. DWORD _dwCurSpeed; // current burning speed
  251. DWORD _dwTimeSet, _dwLastTime; // state variables for showing estimated time remaining
  252. TIMESTATS _ts;
  253. HDPA _hdpaExts; // pointers to the extensibility objects
  254. HPROPSHEETPAGE _rgWizPages[INDEX_DLG_BURNWIZ_MAX];
  255. HANDLE _hMutexBurning; // this tells us if we're burning or not.
  256. static HWND s_hwndWiz; // we can call SetForegroundWindow on this to bring up the wizard if its already up
  257. static BOOL s_fDriveInUse;
  258. DWORD _dwROTRegister; // DWORD to track our moniker stuff for autoplay cancellation
  259. // namespace
  260. static HRESULT _GetPidlForDriveIndex(int iDrive, LPITEMIDLIST *ppidl);
  261. static HRESULT _GetPidlForVolumeName(LPCTSTR pszVolume, LPITEMIDLIST *ppidl);
  262. static HRESULT _GetFolderPidl(LPITEMIDLIST *ppidl);
  263. static HRESULT _GetBurnStagingPath(LPTSTR pszPath, UINT cchBuf);
  264. static HRESULT _GetPlainCDPidl(LPITEMIDLIST *ppidl);
  265. static BOOL _HasFiles(LPCITEMIDLIST pidl);
  266. static BOOL _StagingAreaHasFiles();
  267. static BOOL _DiscHasFiles();
  268. static HRESULT _GetStagingFolder(LPCITEMIDLIST pidlDrive, REFIID riid, void **ppv);
  269. // drop / transfer engine
  270. HRESULT _EnsureDropTarget();
  271. static void _FreeDropParams(CDDROPPARAMS *pcddp);
  272. static DWORD WINAPI _DropThread(void *pv);
  273. HRESULT _GetDropPidl(LPITEMIDLIST *ppidl);
  274. HRESULT _StorageDrop(IDataObject *pdtobj, BOOL fMove);
  275. BOOL _IsStagingAreaSource(IDataObject *pdtobj, LPCITEMIDLIST pidlDrop);
  276. HRESULT _StagingPidlFromMerged(LPCITEMIDLIST pidlDrop, LPITEMIDLIST *ppidlDest);
  277. static HRESULT _LockCurrentDrive(BOOL fLock, BOOL fForce = FALSE);
  278. // initialization helpers
  279. static DWORD WINAPI _ExecThread(void *pv);
  280. // registry and cached info management
  281. static HRESULT _GetCurrentBurnVolumeName(LPTSTR pszVolumeName, UINT cchBuf);
  282. static HRESULT _SetCurrentBurnVolumeName(LPCTSTR pszVolumeName, BOOL fDelete);
  283. static BOOL _BurningIsEnabled();
  284. static HRESULT _GetStashFile(LPTSTR pszFile, UINT cchBuf);
  285. static HRESULT _GetCurrentStashDrive(LPTSTR pszDrive, UINT cchBuf);
  286. static HRESULT _SetCurrentStashDrive(LPCTSTR pszDrive);
  287. static HRESULT _DumpDiscInfo();
  288. static HRESULT _GetDiscInfoUsingIMAPI(IJolietDiscMaster *pjdm, IDiscRecorder *pdr, ULONGLONG *pcbFree);
  289. static HRESULT _GetDiscInfoUsingFilesystem(ULONGLONG *pcbTotal, ULONGLONG *pcbFree, BOOL *pfUDF);
  290. static HRESULT _StoreDiscInfo();
  291. static HRESULT _GetDiscRecorderInfo(IDiscRecorder *pdr, DWORD *pdwCurrentWriteSpeed, DWORD *pdwMaxWriteSpeed, DWORD *pdwDriveType);
  292. HRESULT _SetRecorderProps(IDiscRecorder *pdr, DWORD dwWriteSpeed);
  293. HRESULT _SetJolietProps(IJolietDiscMaster *pjdm);
  294. static HRESULT _GetCachedDriveInfo(LPCTSTR pszVolumeName, DWORD *pdwDriveType, DWORD *pdwCurWrite, DWORD *pdwMaxWrite);
  295. static HRESULT _SetCachedDriveInfo(LPCTSTR pszVolumeName, DWORD dwDriveType, DWORD dwCurWrite, DWORD dwMaxWrite);
  296. HRESULT _GetEjectSetting(BOOL *pfEject);
  297. HRESULT _SetEjectSetting(BOOL fEject);
  298. static HRESULT _GetRecorderPath(IDiscRecorder *pdr, LPTSTR pszPath, UINT cchBuf);
  299. static void _PruneRemovedDevices();
  300. // drive helpers
  301. static HRESULT _GetMediaCapabilities(DWORD *pdwCaps, BOOL *pfUDF);
  302. static HRESULT _GetVolumeNameForDriveIndex(int iDrive, LPTSTR pszVolumeName, UINT cchBuf);
  303. static HRESULT _GetDriveIndexForVolumeName(LPCTSTR pszVolumeName, int *piDrive);
  304. static HRESULT _GetCurrentDriveIndex(int *piDrive);
  305. static HRESULT _GetVolumeNameForDevicePath(LPCTSTR pszDevice, LPTSTR pszVolumeName, UINT cchBuf);
  306. static BOOL _DevicePathMatchesVolumeName(LPCTSTR pszDevice, LPCTSTR pszVolumeName);
  307. static DWORD _ExecSyncIoctl(HANDLE hDriver, DWORD dwIoctl, void *pbuf, DWORD cbBuf);
  308. static BOOL _CouldPossiblySupport(LPCWSTR pszVolume);
  309. HRESULT _GetVolumeNameFromDataObject(BOOL fCheckIsConfiguredDrive, LPTSTR pszVolumeName, UINT cchBuf);
  310. HRESULT _CheckTotal();
  311. HRESULT _Validate();
  312. static BOOL _HasMedia();
  313. // main IMAPI helpers
  314. HRESULT _GetDriveInfo(LPCTSTR pszVolumeName, DWORD *pdwDriveType, DWORD *pdwCurWrite, DWORD *pdwMaxWrite);
  315. HRESULT _GetDiscMasters(IDiscMaster **ppdm, IJolietDiscMaster **ppjdm);
  316. HRESULT _GetDiscRecorderForDrive(IDiscMaster *pdm, LPCTSTR pszVolumeName, IDiscRecorder **ppdr);
  317. HRESULT _FindAndSetRecorder(LPCTSTR pszVolumeName, IDiscMaster *pdm, BOOL fSetActive, IDiscRecorder **ppdr);
  318. HRESULT _FindAndSetDefaultRecorder(IDiscMaster *pdm, BOOL fSetActive, IDiscRecorder **ppdr);
  319. HRESULT _AddData(IJolietDiscMaster *pjdm);
  320. static BOOL _IsBurningNow();
  321. BOOL _EnterExclusiveBurning();
  322. void _LeaveExclusiveBurning();
  323. static void _HandleBookkeeping();
  324. // property sheet stuff
  325. void _MarkDirty(HWND hDlg);
  326. static BOOL_PTR CALLBACK s_DlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam);
  327. void _EnableRecordingDlgArea(HWND hwnd, BOOL fEnable);
  328. void _RecordingPrshtInit(HWND hDlg);
  329. BOOL _HandleApply(HWND hDlg);
  330. // UI
  331. HRESULT _Balloon();
  332. static BOOL CALLBACK _EnumProc(HWND hwnd, LPCITEMIDLIST pidl, LPARAM lParam);
  333. static BOOL _BurningFolderOpen();
  334. static DWORD CALLBACK _NotifyThreadProc(void *pv);
  335. void _CheckStagingArea();
  336. // wizard
  337. void _SetStatus(UINT uID);
  338. HRESULT _CreateDefaultPropBag(REFIID riid, void **ppv);
  339. static CCDBurn* s_GetCDBurn(HWND hwnd, UINT uMsg, LPARAM lParam);
  340. void _SetupFirstPage(HWND hwnd, BOOL fSubclass);
  341. static LRESULT CALLBACK _WizSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRefData);
  342. HRESULT _GetBurnHR();
  343. void _ShowRoxio();
  344. INT_PTR _WelcomeDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  345. INT_PTR _EjectDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  346. INT_PTR _ProgressDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  347. INT_PTR _DoneDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  348. INT_PTR _WaitForMediaDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  349. INT_PTR _StartEraseDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  350. INT_PTR _DiskFullDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  351. INT_PTR _EarlyExitDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  352. INT_PTR _HDFullDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  353. INT_PTR _NoFilesDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  354. void _HDFullSetText(HWND hwnd);
  355. void _InitProgressPage(HWND hwnd);
  356. void _SetEstimatedTime(DWORD dwSeconds);
  357. void _ConstructTimeString(DWORD dwEstTime, LPTSTR psz, UINT cch);
  358. void _DisplayEstimatedTime(HWND hwnd);
  359. void _InitTimeStats(BOOL fErase);
  360. void _SaveTimeStats(BOOL fErase);
  361. void _SetUpStartPage(HWND hwnd);
  362. void _LeaveStartPage(HWND hwnd);
  363. void _DisplayMediaErrorOnNext(HWND hwnd, UINT idMsg, UINT idMsgInsert);
  364. void _SetNextPage(HWND hwnd, int iIndex);
  365. HRESULT _PostOperation();
  366. HRESULT _ShowWizard();
  367. HRESULT _ShowWizardOnSeparateThread();
  368. static DWORD WINAPI _WizardThreadWrapper(void *pv);
  369. HRESULT _WizardThreadProc();
  370. void _RegisterAutoplayCanceller();
  371. void _UnregisterAutoplayCanceller();
  372. // verbs
  373. HRESULT _GetVerb(UINT_PTR idCmd, LPSTR pszName, UINT cchMax, BOOL bUnicode);
  374. static INT_PTR CALLBACK _ConfirmDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  375. HRESULT _CleanUp(LPCMINVOKECOMMANDINFO lpcmi, BOOL fRecycle);
  376. HRESULT _PrepWiz(LPCMINVOKECOMMANDINFO lpcmi, BOOL fErase, BOOL fFailSilently);
  377. static DWORD WINAPI _BurnThread(void *pv);
  378. static DWORD WINAPI _EraseThread(void *pv);
  379. // extensibility
  380. void _PruneExts();
  381. static HRESULT _TryCLSID(REFCLSID clsid, DWORD dwExtType, REFIID riid, void **ppv);
  382. static HRESULT _TryKey(LPTSTR pszKey, DWORD dwExtType, REFIID riid, void **ppv);
  383. static HRESULT _TestDropEffect(IDropTarget *pdt, IDataObject *pdo, REFIID riid, void **ppv);
  384. static HRESULT _TryCLSIDWithDropEffect(REFCLSID clsid, DWORD dwExtType, IDataObject *pdo, REFIID riid, void **ppv);
  385. static HRESULT _TryKeyWithDropEffect(LPTSTR pszKey, DWORD dwExtType, IDataObject *pdo, REFIID riid, void **ppv);
  386. void _AddExtensionToDPA(IWizardExtension *pwe, HPROPSHEETPAGE *rgPages, UINT cNumPages, UINT *pcPagesAdded);
  387. HRESULT _FillExtensionDPA(HPROPSHEETPAGE *rgPages, UINT cNumPages, UINT *pcPagesAdded);
  388. HRESULT _GetExtPage(int nExt, BOOL fNext, HPROPSHEETPAGE *phpage);
  389. HRESULT _GetExtPageFromPropBag(BOOL fNext, HPROPSHEETPAGE *phpage);
  390. void _SetExtPageFromPropBag(HWND hwnd, BOOL fNext);
  391. // "exports"
  392. friend HRESULT CCDBurn_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv);
  393. friend HRESULT CDBurn_OnEject(HWND hwnd, INT iDrive);
  394. friend HRESULT CDBurn_OnDeviceAdded(DWORD dwDriveMask, BOOL fFullRefresh, BOOL fPickNewDrive);
  395. friend HRESULT CDBurn_OnDeviceRemoved(DWORD dwDriveMask);
  396. friend HRESULT CDBurn_GetCDInfo(LPCTSTR pszVolume, DWORD *pdwDriveCapabilities, DWORD *pdwMediaCapabilities);
  397. friend HRESULT CDBurn_OnMediaChange(BOOL fInsert, LPCWSTR pszDrive);
  398. friend HRESULT CDBurn_GetExtensionObject(DWORD dwExtType, IDataObject *pdo, REFIID riid, void **ppv);
  399. friend HRESULT CheckStagingArea();
  400. };
  401. HWND CCDBurn::s_hwndWiz = NULL;
  402. BOOL CCDBurn::s_fDriveInUse = FALSE;
  403. const static DWORD aPrshtHelpIDs[] =
  404. {
  405. IDC_RECORD_ENABLE, IDH_CDMEDIA_ENABLERECORDING,
  406. IDC_RECORD_TEXTIMAGE, IDH_CDMEDIA_STOREDISCIMAGE,
  407. IDC_RECORD_IMAGELOC, IDH_CDMEDIA_STOREDISCIMAGE,
  408. IDC_RECORD_TEXTWRITE, IDH_CDMEDIA_WRITESPEED,
  409. IDC_RECORD_WRITESPEED, IDH_CDMEDIA_WRITESPEED,
  410. IDC_RECORD_EJECT, IDH_CDMEDIA_EJECT,
  411. 0, 0
  412. };
  413. CCDBurn::CCDBurn() :
  414. _cRef(1)
  415. {
  416. DllAddRef();
  417. }
  418. CCDBurn::~CCDBurn()
  419. {
  420. if (_pdo)
  421. {
  422. _pdo->Release();
  423. }
  424. if (_pdt)
  425. {
  426. _pdt->Release();
  427. }
  428. ILFree(_pidl);
  429. if (_hMutexBurning)
  430. {
  431. CloseHandle(_hMutexBurning);
  432. }
  433. DllRelease();
  434. }
  435. // IUnknown
  436. STDMETHODIMP_(ULONG) CCDBurn::AddRef()
  437. {
  438. return InterlockedIncrement(&_cRef);
  439. }
  440. STDMETHODIMP_(ULONG) CCDBurn::Release()
  441. {
  442. if (InterlockedDecrement(&_cRef))
  443. return _cRef;
  444. delete this;
  445. return 0;
  446. }
  447. HRESULT CCDBurn::QueryInterface(REFIID riid, void **ppv)
  448. {
  449. static const QITAB qit[] =
  450. {
  451. QITABENT(CCDBurn, IObjectWithSite),
  452. QITABENT(CCDBurn, IContextMenu),
  453. QITABENT(CCDBurn, IShellExtInit),
  454. QITABENT(CCDBurn, IShellPropSheetExt),
  455. QITABENT(CCDBurn, IDiscMasterProgressEvents),
  456. QITABENT(CCDBurn, IDropTarget),
  457. QITABENT(CCDBurn, IPersistFile),
  458. QITABENT(CCDBurn, IOleCommandTarget),
  459. QITABENT(CCDBurn, ICDBurn),
  460. QITABENT(CCDBurn, ICDBurnPriv),
  461. QITABENT(CCDBurn, IPersistPropertyBag),
  462. QITABENT(CCDBurn, IDriveFolderExt),
  463. QITABENT(CCDBurn, INamespaceWalkCB),
  464. QITABENT(CCDBurn, IWizardSite),
  465. QITABENT(CCDBurn, IServiceProvider),
  466. QITABENT(CCDBurn, IQueryCancelAutoPlay),
  467. QITABENT(CCDBurn, ITransferAdviseSink),
  468. { 0 },
  469. };
  470. return QISearch(this, qit, riid, ppv);
  471. }
  472. STDAPI CCDBurn_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  473. {
  474. if (punkOuter)
  475. return CLASS_E_NOAGGREGATION;
  476. if (SHRestricted(REST_NOCDBURNING))
  477. return E_FAIL;
  478. CCDBurn *pcdb = new CCDBurn();
  479. if (!pcdb)
  480. return E_OUTOFMEMORY;
  481. HRESULT hr = pcdb->QueryInterface(riid, ppv);
  482. pcdb->Release();
  483. return hr;
  484. }
  485. // IShellExtInit
  486. STDMETHODIMP CCDBurn::Initialize(LPCITEMIDLIST pidlFolder, IDataObject* pdo, HKEY hkeyProgID)
  487. {
  488. if (!pdo && !pidlFolder)
  489. return E_INVALIDARG;
  490. IUnknown_Set((IUnknown **)&_pdo, (IUnknown *)pdo);
  491. Pidl_Set(&_pidl, pidlFolder);
  492. return (_pidl || _pdo) ? S_OK : E_FAIL;
  493. }
  494. HRESULT CCDBurn::_GetVolumeNameFromDataObject(BOOL fCheckIsConfiguredDrive, LPTSTR pszVolumeName, UINT cchBuf)
  495. {
  496. HRESULT hr = E_FAIL;
  497. if (!SHRestricted(REST_NOCDBURNING) && _pdo)
  498. {
  499. STGMEDIUM medium;
  500. LPIDA pida = DataObj_GetHIDA(_pdo, &medium);
  501. if (pida)
  502. {
  503. // we only display the page if there is a single item selected
  504. if (pida->cidl == 1)
  505. {
  506. // get the IDLIST an try to determine the drive we are showing this for
  507. LPITEMIDLIST pidl = IDA_ILClone(pida, 0);
  508. if (pidl)
  509. {
  510. TCHAR szPath[MAX_PATH];
  511. SHGetPathFromIDList(pidl, szPath);
  512. // only go farther if the drive is a CD ROM
  513. if ((GetDriveType(szPath) == DRIVE_CDROM) &&
  514. (GetVolumeNameForVolumeMountPoint(szPath, pszVolumeName, cchBuf)))
  515. {
  516. hr = S_OK;
  517. if (fCheckIsConfiguredDrive)
  518. {
  519. TCHAR szCurrent[MAX_PATH];
  520. hr = _GetCurrentBurnVolumeName(szCurrent, ARRAYSIZE(szCurrent));
  521. if (SUCCEEDED(hr))
  522. {
  523. hr = (lstrcmpi(szCurrent, pszVolumeName) == 0) ? S_OK : E_FAIL;
  524. }
  525. }
  526. }
  527. ILFree(pidl);
  528. }
  529. }
  530. HIDA_ReleaseStgMedium(pida, &medium);
  531. }
  532. }
  533. return hr;
  534. }
  535. // Property sheet code (for configuring the burnable drive)
  536. HRESULT CCDBurn::_GetCurrentBurnVolumeName(LPTSTR pszVolumeName, UINT cchBuf)
  537. {
  538. HRESULT hr = E_FAIL; // failed == no drive.
  539. if (!SHRestricted(REST_NOCDBURNING))
  540. {
  541. ULONG cb = cchBuf * sizeof(*pszVolumeName);
  542. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CURRENTDRIVE, NULL, pszVolumeName, &cb))
  543. {
  544. // we encode an empty string as a special failure case as well.
  545. hr = (lstrlen(pszVolumeName) > 0) ? S_OK : E_UNEXPECTED;
  546. }
  547. }
  548. return hr;
  549. }
  550. BOOL CCDBurn::_BurningIsEnabled()
  551. {
  552. TCHAR szDummy[MAX_PATH];
  553. return SUCCEEDED(_GetCurrentBurnVolumeName(szDummy, ARRAYSIZE(szDummy)));
  554. }
  555. HRESULT CCDBurn::_SetCurrentBurnVolumeName(LPCTSTR pszVolumeName, BOOL fDelete)
  556. {
  557. // setcurrentburnvolumename is called when the burning drive is getting switched,
  558. // whether because were turning off burning or moving it to a different drive.
  559. // whenever this changes, that means drivefolder's Parse() and Enum() will start returning
  560. // different stuff for the affected drive letters.
  561. // so we issue SHCNE_DRIVEADD and SHCNE_DRIVEREMOVED for the changes.
  562. LPITEMIDLIST pidlBeforeOld = NULL, pidlBeforeNew = NULL, pidlAfterOld = NULL, pidlAfterNew = NULL;
  563. DWORD dwDriveOld = 0, cb = sizeof(dwDriveOld);
  564. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CACHEDINDEX, NULL, &dwDriveOld, &cb))
  565. {
  566. _GetPidlForDriveIndex(dwDriveOld, &pidlBeforeOld);
  567. }
  568. _GetPidlForVolumeName(pszVolumeName, &pidlAfterOld);
  569. SHDeleteValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CACHEDINDEX);
  570. DWORD dwRet;
  571. if (!fDelete)
  572. {
  573. cb = (lstrlen(pszVolumeName) + 1) * sizeof(TCHAR);
  574. dwRet = SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CURRENTDRIVE, REG_SZ, pszVolumeName, cb);
  575. int iDrive;
  576. if ((ERROR_SUCCESS == dwRet) && SUCCEEDED(_GetDriveIndexForVolumeName(pszVolumeName, &iDrive)))
  577. {
  578. dwRet = SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CACHEDINDEX, REG_DWORD, &iDrive, sizeof(iDrive));
  579. }
  580. }
  581. else
  582. {
  583. dwRet = SHDeleteValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CURRENTDRIVE);
  584. }
  585. HRESULT hr = (ERROR_SUCCESS == dwRet) ? S_OK : E_FAIL;
  586. if (SUCCEEDED(hr))
  587. {
  588. if (dwDriveOld)
  589. {
  590. _GetPidlForDriveIndex(dwDriveOld, &pidlBeforeNew);
  591. }
  592. _GetPidlForVolumeName(pszVolumeName, &pidlAfterNew);
  593. if (pidlBeforeOld && pidlBeforeNew && !ILIsEqual(pidlBeforeOld, pidlBeforeNew))
  594. {
  595. SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_IDLIST, pidlBeforeOld, NULL);
  596. SHChangeNotify(SHCNE_DRIVEADD, SHCNF_IDLIST, pidlBeforeNew, NULL);
  597. }
  598. if (pidlAfterOld && pidlAfterNew && !ILIsEqual(pidlAfterOld, pidlAfterNew))
  599. {
  600. SHChangeNotify(SHCNE_DRIVEREMOVED, SHCNF_IDLIST, pidlAfterOld, NULL);
  601. SHChangeNotify(SHCNE_DRIVEADD, SHCNF_IDLIST, pidlAfterNew, NULL);
  602. }
  603. }
  604. ILFree(pidlBeforeOld);
  605. ILFree(pidlBeforeNew);
  606. ILFree(pidlAfterOld);
  607. ILFree(pidlAfterNew);
  608. return hr;
  609. }
  610. HRESULT CCDBurn::_GetBurnStagingPath(LPTSTR pszPath, UINT cchBuf)
  611. {
  612. ASSERTMSG(cchBuf >= MAX_PATH, "caller needs to pass bigger buffer");
  613. return SHGetFolderPath(NULL, CSIDL_CDBURN_AREA | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, pszPath);
  614. }
  615. HRESULT CCDBurn::_GetStashFile(LPTSTR pszFile, UINT cchBuf)
  616. {
  617. TCHAR szPath[MAX_PATH];
  618. ULONG cb = sizeof(szPath);
  619. DWORD dwRet = SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_IMAPI, TEXT(IMAPI_REGVAL_NEWPATH), NULL, szPath, &cb);
  620. if ((dwRet != ERROR_SUCCESS) || (lstrlen(szPath) == 0))
  621. {
  622. cb = sizeof(szPath);
  623. dwRet = SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_IMAPI, TEXT(IMAPI_REGVAL_PATH), NULL, szPath, &cb);
  624. }
  625. HRESULT hr = E_FAIL;
  626. if (dwRet == ERROR_SUCCESS)
  627. {
  628. lstrcpyn(pszFile, szPath, cchBuf);
  629. hr = S_OK;
  630. }
  631. return hr;
  632. }
  633. HRESULT CCDBurn::_GetCurrentStashDrive(LPTSTR pszDrive, UINT cchBuf)
  634. {
  635. TCHAR szPath[MAX_PATH];
  636. HRESULT hr = _GetStashFile(szPath, ARRAYSIZE(szPath));
  637. if (SUCCEEDED(hr) && PathStripToRoot(szPath))
  638. {
  639. lstrcpyn(pszDrive, szPath, cchBuf);
  640. hr = S_OK;
  641. }
  642. return hr;
  643. }
  644. HRESULT CCDBurn::_SetCurrentStashDrive(LPCTSTR pszDrive)
  645. {
  646. TCHAR szStashFile[MAX_PATH];
  647. // we're using a fixed filename at the root of the hard drive.
  648. lstrcpyn(szStashFile, pszDrive, ARRAYSIZE(szStashFile));
  649. PathAppend(szStashFile, STASH_FILENAME);
  650. UINT cbStashFile = (lstrlen(szStashFile) + 1) * sizeof(TCHAR);
  651. DWORD dwRet = SHSetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_IMAPI, TEXT(IMAPI_REGVAL_NEWPATH), REG_SZ, szStashFile, cbStashFile);
  652. return (dwRet == ERROR_SUCCESS) ? S_OK : E_FAIL;
  653. }
  654. HRESULT CCDBurn::_GetEjectSetting(BOOL *pfEject)
  655. {
  656. DWORD dwEject, cb = sizeof(dwEject);
  657. DWORD dwRet = SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_AUTOEJECT, NULL, &dwEject, &cb);
  658. HRESULT hr = E_FAIL;
  659. if (dwRet == ERROR_SUCCESS)
  660. {
  661. *pfEject = dwEject;
  662. hr = S_OK;
  663. }
  664. return hr;
  665. }
  666. HRESULT CCDBurn::_SetEjectSetting(BOOL fEject)
  667. {
  668. DWORD dwEject = fEject;
  669. DWORD dwRet = SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_AUTOEJECT, REG_DWORD, &dwEject, sizeof(dwEject));
  670. return (dwRet == ERROR_SUCCESS) ? S_OK : E_FAIL;
  671. }
  672. HRESULT CCDBurn::_GetCachedDriveInfo(LPCTSTR pszVolumeName, DWORD *pdwDriveType, DWORD *pdwCurrentWriteSpeed, DWORD *pdwMaxWriteSpeed)
  673. {
  674. HRESULT hr = S_OK;
  675. TCHAR szRegPath[MAX_PATH];
  676. lstrcpyn(szRegPath, REGSTR_PATH_DRIVES, ARRAYSIZE(szRegPath));
  677. StrCatBuff(szRegPath, TEXT("\\"), ARRAYSIZE(szRegPath));
  678. StrCatBuff(szRegPath, PathFindFileName(pszVolumeName), ARRAYSIZE(szRegPath));
  679. if (pdwDriveType)
  680. {
  681. DWORD dwTemp;
  682. ULONG cb = sizeof(dwTemp);
  683. DWORD dwRet = SHGetValue(HKEY_CURRENT_USER, szRegPath, REGVALUE_DRIVETYPE, NULL, &dwTemp, &cb);
  684. if (dwRet == ERROR_SUCCESS)
  685. {
  686. *pdwDriveType = dwTemp;
  687. }
  688. else
  689. {
  690. // this is the one value that should always be present if the key is there,
  691. // so fail if it's not there yet.
  692. hr = E_FAIL;
  693. }
  694. }
  695. if (pdwCurrentWriteSpeed)
  696. {
  697. DWORD dwTemp;
  698. ULONG cb = sizeof(dwTemp);
  699. DWORD dwRet = SHGetValue(HKEY_CURRENT_USER, szRegPath, REGVALUE_CURRENTSPEED, NULL, &dwTemp, &cb);
  700. if (dwRet == ERROR_SUCCESS)
  701. {
  702. *pdwCurrentWriteSpeed = dwTemp;
  703. }
  704. }
  705. if (pdwMaxWriteSpeed)
  706. {
  707. DWORD dwTemp;
  708. ULONG cb = sizeof(dwTemp);
  709. DWORD dwRet = SHGetValue(HKEY_CURRENT_USER, szRegPath, REGVALUE_MAXSPEED, NULL, &dwTemp, &cb);
  710. if (dwRet == ERROR_SUCCESS)
  711. {
  712. *pdwMaxWriteSpeed = dwTemp;
  713. }
  714. }
  715. return hr;
  716. }
  717. HRESULT CCDBurn::_SetCachedDriveInfo(LPCTSTR pszVolumeName, DWORD dwDriveType, DWORD dwWriteSpeed, DWORD dwMaxWriteSpeed)
  718. {
  719. TCHAR szRegPath[MAX_PATH];
  720. lstrcpyn(szRegPath, REGSTR_PATH_DRIVES, ARRAYSIZE(szRegPath));
  721. StrCatBuff(szRegPath, TEXT("\\"), ARRAYSIZE(szRegPath));
  722. StrCatBuff(szRegPath, PathFindFileName(pszVolumeName), ARRAYSIZE(szRegPath));
  723. if (dwDriveType > 0)
  724. {
  725. SHSetValue(HKEY_CURRENT_USER, szRegPath, REGVALUE_DRIVETYPE, REG_DWORD, &dwDriveType, sizeof(dwDriveType));
  726. }
  727. if (dwWriteSpeed > 0)
  728. {
  729. SHSetValue(HKEY_CURRENT_USER, szRegPath, REGVALUE_CURRENTSPEED, REG_DWORD, &dwWriteSpeed, sizeof(dwWriteSpeed));
  730. }
  731. if (dwMaxWriteSpeed > 0)
  732. {
  733. SHSetValue(HKEY_CURRENT_USER, szRegPath, REGVALUE_MAXSPEED, REG_DWORD, &dwMaxWriteSpeed, sizeof(dwMaxWriteSpeed));
  734. }
  735. return S_OK;
  736. }
  737. HRESULT CCDBurn::_GetDiscRecorderInfo(IDiscRecorder *pdr, DWORD *pdwCurrentWriteSpeed, DWORD *pdwMaxWriteSpeed, DWORD *pdwDriveType)
  738. {
  739. IPropertyStorage *pps;
  740. HRESULT hr = pdr->GetRecorderProperties(&pps);
  741. if (SUCCEEDED(hr))
  742. {
  743. PROPSPEC rgpspec[2];
  744. rgpspec[0].ulKind = PRSPEC_LPWSTR;
  745. rgpspec[0].lpwstr = L"WriteSpeed";
  746. rgpspec[1].ulKind = PRSPEC_LPWSTR;
  747. rgpspec[1].lpwstr = L"MaxWriteSpeed";
  748. PROPVARIANT rgvar[2];
  749. hr = pps->ReadMultiple(2, rgpspec, rgvar);
  750. if (SUCCEEDED(hr))
  751. {
  752. if ((V_VT(&rgvar[0]) == VT_I4) &&
  753. (V_VT(&rgvar[1]) == VT_I4))
  754. {
  755. if (pdwCurrentWriteSpeed)
  756. *pdwCurrentWriteSpeed = rgvar[0].lVal;
  757. if (pdwMaxWriteSpeed)
  758. *pdwMaxWriteSpeed = rgvar[1].lVal;
  759. }
  760. else
  761. {
  762. hr = E_FAIL;
  763. }
  764. }
  765. pps->Release();
  766. }
  767. if (SUCCEEDED(hr) && pdwDriveType)
  768. {
  769. long lType;
  770. hr = pdr->GetRecorderType(&lType);
  771. if (SUCCEEDED(hr))
  772. {
  773. *pdwDriveType = lType;
  774. }
  775. }
  776. return hr;
  777. }
  778. HRESULT CCDBurn::_SetRecorderProps(IDiscRecorder *pdr, DWORD dwWriteSpeed)
  779. {
  780. IPropertyStorage *pps;
  781. HRESULT hr = pdr->GetRecorderProperties(&pps);
  782. if (SUCCEEDED(hr))
  783. {
  784. PROPSPEC rgpspec[1];
  785. PROPVARIANT rgvar[1];
  786. rgpspec[0].ulKind = PRSPEC_LPWSTR;
  787. rgpspec[0].lpwstr = L"WriteSpeed";
  788. rgvar[0].vt = VT_I4;
  789. rgvar[0].lVal = dwWriteSpeed;
  790. hr = pps->WriteMultiple(1, rgpspec, rgvar, PID_FIRST_USABLE);
  791. if (SUCCEEDED(hr))
  792. {
  793. // commit propertystorage to the discrecorder
  794. hr = pdr->SetRecorderProperties(pps);
  795. }
  796. pps->Release();
  797. }
  798. return hr;
  799. }
  800. HRESULT CCDBurn::_SetJolietProps(IJolietDiscMaster *pjdm)
  801. {
  802. IPropertyStorage *pps;
  803. HRESULT hr = pjdm->GetJolietProperties(&pps);
  804. if (SUCCEEDED(hr))
  805. {
  806. WCHAR szLabel[JOLIET_MAX_LABEL + 1];
  807. szLabel[0] = 0;
  808. SHPropertyBag_ReadStr(_ppb, PROPSTR_DISCLABEL, szLabel, ARRAYSIZE(szLabel));
  809. PROPSPEC rgpspec[1];
  810. PROPVARIANT rgvar[1];
  811. rgpspec[0].ulKind = PRSPEC_LPWSTR;
  812. rgpspec[0].lpwstr = L"VolumeName";
  813. rgvar[0].vt = VT_BSTR;
  814. rgvar[0].bstrVal = SysAllocString(szLabel);
  815. hr = pps->WriteMultiple(1, rgpspec, rgvar, PID_FIRST_USABLE);
  816. if (SUCCEEDED(hr))
  817. {
  818. // commit propertystorage to the joliet disc master
  819. hr = pjdm->SetJolietProperties(pps);
  820. }
  821. PropVariantClear(&rgvar[0]);
  822. pps->Release();
  823. }
  824. return hr;
  825. }
  826. HRESULT CCDBurn::_GetDriveInfo(LPCTSTR pszVolumeName, DWORD *pdwDriveType, DWORD *pdwCurWrite, DWORD *pdwMaxWrite)
  827. {
  828. HRESULT hr = _GetCachedDriveInfo(pszVolumeName, pdwDriveType, pdwCurWrite, pdwMaxWrite);
  829. if (FAILED(hr))
  830. {
  831. IDiscMaster *pdm;
  832. IJolietDiscMaster *pjdm;
  833. hr = _GetDiscMasters(&pdm, &pjdm);
  834. if (SUCCEEDED(hr))
  835. {
  836. IDiscRecorder *pdr;
  837. hr = _GetDiscRecorderForDrive(pdm, pszVolumeName, &pdr);
  838. if (SUCCEEDED(hr))
  839. {
  840. if (S_OK == hr)
  841. {
  842. DWORD dwMaxWrite, dwDriveType;
  843. hr = _GetDiscRecorderInfo(pdr, NULL, &dwMaxWrite, &dwDriveType);
  844. if (SUCCEEDED(hr))
  845. {
  846. // default to fastest speed
  847. _SetCachedDriveInfo(pszVolumeName, dwDriveType, WRITESPEED_FASTEST, dwMaxWrite);
  848. if (pdwMaxWrite)
  849. {
  850. *pdwMaxWrite = dwMaxWrite;
  851. }
  852. if (pdwCurWrite)
  853. {
  854. *pdwCurWrite = dwMaxWrite;
  855. }
  856. if (pdwDriveType)
  857. {
  858. *pdwDriveType = dwDriveType;
  859. }
  860. }
  861. pdr->Release();
  862. }
  863. else
  864. {
  865. if (pdwDriveType)
  866. {
  867. *pdwDriveType = DRIVE_NOTSUPPORTED;
  868. }
  869. _SetCachedDriveInfo(pszVolumeName, DRIVE_NOTSUPPORTED, 0, 0);
  870. }
  871. }
  872. pdm->Release();
  873. pjdm->Release();
  874. }
  875. }
  876. return hr;
  877. }
  878. STDMETHODIMP CCDBurn::AddPages(LPFNADDPROPSHEETPAGE pAddPageProc, LPARAM lParam)
  879. {
  880. // get the destination burn drive from the IDataObject we recieved during initialization
  881. HRESULT hr = _GetVolumeNameFromDataObject(FALSE, _szVolumeName, ARRAYSIZE(_szVolumeName));
  882. if (SUCCEEDED(hr))
  883. {
  884. DWORD dwDriveType;
  885. // only add page if we're supported by IMAPI.
  886. if (SUCCEEDED(_GetDriveInfo(_szVolumeName, &dwDriveType, NULL, NULL)) && SUPPORTED(dwDriveType))
  887. {
  888. // is this drive configured to be the recording drive?
  889. _fIsRecordingDrive = FALSE;
  890. TCHAR szCurrent[MAX_PATH];
  891. if (SUCCEEDED(_GetCurrentBurnVolumeName(szCurrent, ARRAYSIZE(szCurrent))))
  892. {
  893. _fIsRecordingDrive = (lstrcmpi(szCurrent, _szVolumeName) == 0);
  894. }
  895. PROPSHEETPAGE psp = { 0 };
  896. psp.dwSize = sizeof(psp); // extra data
  897. psp.dwFlags = PSP_DEFAULT;
  898. psp.hInstance = HINST_THISDLL;
  899. psp.pszTemplate = MAKEINTRESOURCE(DLG_DRV_RECORDINGTAB);
  900. psp.pfnDlgProc = s_DlgProc;
  901. psp.lParam = (LPARAM)this; // pass out a reference to our object
  902. AddRef();
  903. // create the page, and add it using the cb function they gave us
  904. hr = E_OUTOFMEMORY;
  905. HPROPSHEETPAGE hPage = CreatePropertySheetPage(&psp);
  906. if (hPage)
  907. {
  908. if (!pAddPageProc(hPage, lParam))
  909. {
  910. DestroyPropertySheetPage(hPage);
  911. }
  912. else
  913. {
  914. hr = S_OK; // success
  915. }
  916. }
  917. if (FAILED(hr))
  918. {
  919. Release();
  920. }
  921. }
  922. }
  923. return hr;
  924. }
  925. void CCDBurn::_EnableRecordingDlgArea(HWND hwnd, BOOL fEnable)
  926. {
  927. EnableWindow(GetDlgItem(hwnd, IDC_RECORD_IMAGELOC), IsUserAnAdmin() ? fEnable : FALSE);
  928. EnableWindow(GetDlgItem(hwnd, IDC_RECORD_WRITESPEED), fEnable);
  929. EnableWindow(GetDlgItem(hwnd, IDC_RECORD_TEXTIMAGE), fEnable);
  930. EnableWindow(GetDlgItem(hwnd, IDC_RECORD_TEXTWRITE), fEnable);
  931. EnableWindow(GetDlgItem(hwnd, IDC_RECORD_EJECT), fEnable);
  932. }
  933. void CCDBurn::_RecordingPrshtInit(HWND hDlg)
  934. {
  935. DECLAREWAITCURSOR;
  936. SetWaitCursor();
  937. HWND hwndWriteSpeed = GetDlgItem(hDlg, IDC_RECORD_WRITESPEED);
  938. HWND hwndImageLoc = GetDlgItem(hDlg, IDC_RECORD_IMAGELOC);
  939. TCHAR szTemp[MAX_PATH];
  940. // get the icon for the drive
  941. HICON hIcon = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(IDI_DRIVECD));
  942. ReplaceDlgIcon(hDlg, IDC_RECORD_ICON, hIcon);
  943. // set the "we are the recording drive" flag
  944. CheckDlgButton(hDlg, IDC_RECORD_ENABLE, _fIsRecordingDrive);
  945. _EnableRecordingDlgArea(hDlg, _fIsRecordingDrive);
  946. BOOL fAutoEject = TRUE;
  947. _GetEjectSetting(&fAutoEject);
  948. CheckDlgButton(hDlg, IDC_RECORD_EJECT, fAutoEject);
  949. // populate the controls for the write speed
  950. LoadString(HINST_THISDLL, IDS_BURN_WRITESPEED_NX, szTemp, ARRAYSIZE(szTemp));
  951. // if the registry info is missing
  952. // then default to 1x.
  953. DWORD dwMaxSpeed = 1;
  954. DWORD dwCurSpeed = 1;
  955. _GetDriveInfo(_szVolumeName, NULL, &dwCurSpeed, &dwMaxSpeed);
  956. int iCurSel = 0, iSelIndex = 0;
  957. TCHAR szSpeed[20];
  958. LoadString(HINST_THISDLL, IDS_BURN_WRITESPEED_FASTEST, szSpeed, ARRAYSIZE(szSpeed));
  959. ComboBox_AddString(hwndWriteSpeed, szSpeed);
  960. ComboBox_SetItemData(hwndWriteSpeed, iSelIndex, WRITESPEED_FASTEST);
  961. iSelIndex++;
  962. // we want the following:
  963. // 8x drive: 8, 4, 2, 1
  964. // 10x drive: 10, 8, 4, 2, 1
  965. for (DWORD dwSpeed = dwMaxSpeed; dwSpeed >= 1; iSelIndex++)
  966. {
  967. if (dwSpeed == dwCurSpeed)
  968. {
  969. iCurSel = iSelIndex;
  970. }
  971. _sntprintf(szSpeed, ARRAYSIZE(szSpeed), szTemp, dwSpeed);
  972. ComboBox_AddString(hwndWriteSpeed, szSpeed);
  973. ComboBox_SetItemData(hwndWriteSpeed, iSelIndex, dwSpeed);
  974. // so if we just added the max speed, set dwSpeed to the lowest
  975. // power of two greater than the max speed.
  976. // then divide by two.
  977. // thus 10->8, 8->4, etc.
  978. if (dwSpeed == dwMaxSpeed)
  979. {
  980. for (dwSpeed = 1; dwSpeed < dwMaxSpeed; dwSpeed *= 2);
  981. }
  982. dwSpeed /= 2;
  983. }
  984. ComboBox_SetCurSel(hwndWriteSpeed, iCurSel);
  985. // populate the staging drive information, and try and set the drive
  986. SendMessage(hwndImageLoc, CBEM_SETIMAGELIST, 0, (LPARAM)GetSystemImageListSmallIcons());
  987. PopulateLocalDrivesCombo(hwndImageLoc, NULL, (LPARAM)hwndImageLoc);
  988. BOOL fSetAlready = FALSE;
  989. if (SUCCEEDED(_GetCurrentStashDrive(szTemp, ARRAYSIZE(szTemp))))
  990. {
  991. for (int i = 0; (i < ComboBox_GetCount(hwndImageLoc)) && !fSetAlready; i++)
  992. {
  993. LPTSTR pszData = (LPTSTR)ComboBox_GetItemData(hwndImageLoc, i);
  994. if (lstrcmpi(szTemp, pszData) == 0)
  995. {
  996. ComboBox_SetCurSel(hwndImageLoc, i);
  997. fSetAlready = TRUE;
  998. }
  999. }
  1000. }
  1001. if (!fSetAlready)
  1002. ComboBox_SetCurSel(hwndImageLoc, 0);
  1003. // if we're not an admin, the user wont be able to change the stash location due to kernel security
  1004. // issues. thus disable that part.
  1005. if (!IsUserAnAdmin())
  1006. {
  1007. EnableWindow(hwndImageLoc, FALSE);
  1008. WCHAR szText[200];
  1009. LoadString(HINST_THISDLL, IDS_BURN_USERBLOCK, szText, ARRAYSIZE(szText));
  1010. SetWindowText(GetDlgItem(hDlg, IDC_RECORD_TEXTIMAGE), szText);
  1011. }
  1012. _fPropSheetDirty = FALSE;
  1013. ResetWaitCursor();
  1014. }
  1015. BOOL CCDBurn::_HandleApply(HWND hDlg)
  1016. {
  1017. BOOL fGoAhead = TRUE;
  1018. if (_fPropSheetDirty)
  1019. {
  1020. if (IsDlgButtonChecked(hDlg, IDC_RECORD_ENABLE) == BST_CHECKED)
  1021. {
  1022. _fIsRecordingDrive = TRUE;
  1023. // get the stash drive setup
  1024. HWND hwndStash = GetDlgItem(hDlg, IDC_RECORD_IMAGELOC);
  1025. int iItem = ComboBox_GetCurSel(hwndStash);
  1026. if (iItem != (int)CB_ERR)
  1027. {
  1028. LPTSTR pszStashDrive = (LPTSTR)ComboBox_GetItemData(hwndStash, iItem);
  1029. // set the stash drive
  1030. _SetCurrentStashDrive(pszStashDrive);
  1031. // mark the drive we are going to burn to
  1032. _SetCurrentBurnVolumeName(_szVolumeName, FALSE);
  1033. // set speed
  1034. HWND hwndSpeed = GetDlgItem(hDlg, IDC_RECORD_WRITESPEED);
  1035. iItem = ComboBox_GetCurSel(hwndSpeed);
  1036. if (iItem != (int)CB_ERR)
  1037. {
  1038. DWORD dwSpeed = (DWORD)ComboBox_GetItemData(hwndSpeed, iItem);
  1039. _SetCachedDriveInfo(_szVolumeName, DRIVE_USEEXISTING, dwSpeed, 0);
  1040. }
  1041. // set autoeject
  1042. BOOL fAutoEject = (IsDlgButtonChecked(hDlg, IDC_RECORD_EJECT) == BST_CHECKED);
  1043. _SetEjectSetting(fAutoEject);
  1044. // redo free space
  1045. _HandleBookkeeping();
  1046. }
  1047. _fPropSheetDirty = FALSE;
  1048. }
  1049. else if (_fIsRecordingDrive)
  1050. {
  1051. if (!_StagingAreaHasFiles() ||
  1052. (IDYES == ShellMessageBox(HINST_THISDLL, hDlg, MAKEINTRESOURCE(IDS_BURN_CONFIRM_DISABLE), MAKEINTRESOURCE(IDS_BURN),
  1053. MB_YESNO | MB_ICONQUESTION)))
  1054. {
  1055. // clear out any trash, we're disabling the cd burning now.
  1056. _SetCurrentBurnVolumeName(TEXT(""), FALSE); // there is no recording drive now
  1057. TCHAR szStash[MAX_PATH];
  1058. if (SUCCEEDED(_GetStashFile(szStash, ARRAYSIZE(szStash))))
  1059. {
  1060. DeleteFile(szStash);
  1061. }
  1062. _fPropSheetDirty = FALSE;
  1063. }
  1064. else
  1065. {
  1066. fGoAhead = FALSE;
  1067. }
  1068. }
  1069. }
  1070. return fGoAhead;
  1071. }
  1072. void CCDBurn::_MarkDirty(HWND hDlg)
  1073. {
  1074. PropSheet_Changed(GetParent(hDlg), hDlg);
  1075. _fPropSheetDirty = TRUE;
  1076. }
  1077. BOOL_PTR CCDBurn::s_DlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
  1078. {
  1079. if (uMessage == WM_INITDIALOG)
  1080. {
  1081. PROPSHEETPAGE *psp = (PROPSHEETPAGE*)lParam;
  1082. CCDBurn *pcdb = (CCDBurn*)psp->lParam;
  1083. SetWindowLongPtr(hDlg, GWLP_USERDATA, psp->lParam);
  1084. pcdb->_RecordingPrshtInit(hDlg);
  1085. }
  1086. else
  1087. {
  1088. CCDBurn *pcdb = (CCDBurn*)GetWindowLongPtr(hDlg, GWLP_USERDATA);
  1089. switch (uMessage)
  1090. {
  1091. case WM_DESTROY:
  1092. ReplaceDlgIcon(hDlg, IDC_RECORD_ICON, NULL);
  1093. SetWindowLongPtr(hDlg, GWLP_USERDATA, 0x0);
  1094. pcdb->Release();
  1095. break;
  1096. case WM_HELP:
  1097. WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, CDBURN_HELPFILE, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aPrshtHelpIDs);
  1098. break;
  1099. case WM_CONTEXTMENU:
  1100. WinHelp((HWND)wParam, CDBURN_HELPFILE, HELP_CONTEXTMENU, (ULONG_PTR)(void *)aPrshtHelpIDs);
  1101. break;
  1102. case WM_COMMAND:
  1103. switch (GET_WM_COMMAND_ID(wParam, lParam))
  1104. {
  1105. case IDC_RECORD_ENABLE:
  1106. {
  1107. BOOL fRecordingEnabled = (IsDlgButtonChecked(hDlg, IDC_RECORD_ENABLE) == BST_CHECKED);
  1108. pcdb->_EnableRecordingDlgArea(hDlg, fRecordingEnabled);
  1109. pcdb->_MarkDirty(hDlg);
  1110. }
  1111. break;
  1112. case IDC_RECORD_IMAGELOC:
  1113. case IDC_RECORD_WRITESPEED:
  1114. if (GET_WM_COMMAND_CMD(wParam, lParam) == LBN_SELCHANGE)
  1115. {
  1116. pcdb->_MarkDirty(hDlg);
  1117. }
  1118. break;
  1119. case IDC_RECORD_EJECT:
  1120. pcdb->_MarkDirty(hDlg);
  1121. break;
  1122. default:
  1123. return TRUE;
  1124. }
  1125. break;
  1126. case WM_NOTIFY:
  1127. switch (((NMHDR *)lParam)->code)
  1128. {
  1129. case PSN_SETACTIVE:
  1130. break;
  1131. case PSN_APPLY:
  1132. if (!pcdb->_HandleApply(hDlg))
  1133. {
  1134. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE);
  1135. }
  1136. else
  1137. {
  1138. PropSheet_UnChanged(GetParent(hDlg), hDlg);
  1139. }
  1140. return TRUE;
  1141. default:
  1142. return FALSE;
  1143. }
  1144. break;
  1145. default:
  1146. return FALSE;
  1147. }
  1148. }
  1149. return TRUE;
  1150. }
  1151. // IContextMenu
  1152. STDMETHODIMP CCDBurn::QueryContextMenu(HMENU hmenu, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
  1153. {
  1154. INT idMax = idCmdFirst;
  1155. // only display this if we are invoked on the currently configured burn drive
  1156. TCHAR szDummy[MAX_PATH];
  1157. if (SUCCEEDED(_GetVolumeNameFromDataObject(TRUE, szDummy, ARRAYSIZE(szDummy))))
  1158. {
  1159. // load and merge the context menu with the current menu
  1160. HMENU hmMerge = SHLoadPopupMenu(HINST_THISDLL, POPUP_BURN_POPUPMERGE);
  1161. if (hmMerge)
  1162. {
  1163. idMax = Shell_MergeMenus(hmenu, hmMerge, indexMenu, idCmdFirst, idCmdLast, 0);
  1164. BOOL fInStaging, fOnMedia;
  1165. if (SUCCEEDED(GetContentState(&fInStaging, &fOnMedia)))
  1166. {
  1167. DWORD dwCaps = 0;
  1168. BOOL fUDF = FALSE;
  1169. _GetMediaCapabilities(&dwCaps, &fUDF); // this will fail if there is no media in the drive.
  1170. // delete write to cd if it's UDF
  1171. if (fUDF)
  1172. DeleteMenu(hmenu, idCmdFirst + FSIDM_BURN, MF_BYCOMMAND);
  1173. // delete clear staging area if no files to burn or it's UDF
  1174. if (!fInStaging || fUDF)
  1175. DeleteMenu(hmenu, idCmdFirst + FSIDM_CLEANUP, MF_BYCOMMAND);
  1176. // delete erase if it's not UDF and no cd-rw or no files on disc
  1177. if (!(dwCaps & HWDMC_CDREWRITABLE) || (!fOnMedia && !fUDF))
  1178. DeleteMenu(hmenu, idCmdFirst + FSIDM_ERASE, MF_BYCOMMAND);
  1179. }
  1180. DestroyMenu(hmMerge);
  1181. }
  1182. }
  1183. return ResultFromShort(idMax - idCmdFirst);
  1184. }
  1185. const ICIVERBTOIDMAP c_CDBurnMap[] =
  1186. {
  1187. { L"burn", "burn", FSIDM_BURN, FSIDM_BURN, },
  1188. { L"cleanup", "cleanup", FSIDM_CLEANUP, FSIDM_CLEANUP, },
  1189. { L"erase", "erase", FSIDM_ERASE, FSIDM_ERASE, },
  1190. };
  1191. STDMETHODIMP CCDBurn::GetCommandString(UINT_PTR idCmd, UINT uFlags, LPUINT lpReserved, LPSTR pszName, UINT cchMax)
  1192. {
  1193. HRESULT hr;
  1194. switch(uFlags)
  1195. {
  1196. case GCS_VERBA:
  1197. case GCS_VERBW:
  1198. hr = SHMapCmdIDToVerb(idCmd, c_CDBurnMap, ARRAYSIZE(c_CDBurnMap), pszName, cchMax, uFlags == GCS_VERBW);
  1199. break;
  1200. default:
  1201. hr = E_NOTIMPL;
  1202. break;
  1203. }
  1204. return hr;
  1205. }
  1206. STDMETHODIMP CCDBurn::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi)
  1207. {
  1208. UINT uID;
  1209. HRESULT hr = SHMapICIVerbToCmdID(lpcmi, c_CDBurnMap, ARRAYSIZE(c_CDBurnMap), &uID);
  1210. if (SUCCEEDED(hr))
  1211. {
  1212. switch (uID)
  1213. {
  1214. case FSIDM_BURN:
  1215. case FSIDM_ERASE:
  1216. hr = InitNew();
  1217. if (SUCCEEDED(hr))
  1218. {
  1219. hr = _PrepWiz(lpcmi, (uID == FSIDM_ERASE), FALSE);
  1220. }
  1221. break;
  1222. case FSIDM_CLEANUP:
  1223. hr = _CleanUp(lpcmi, TRUE);
  1224. break;
  1225. default:
  1226. hr = E_FAIL;
  1227. break;
  1228. }
  1229. }
  1230. return hr;
  1231. }
  1232. // IMAPI Advise Sink
  1233. STDMETHODIMP CCDBurn::QueryCancel(boolean *pbCancel)
  1234. {
  1235. *pbCancel = (boolean)_fCancelled;
  1236. return S_OK;
  1237. }
  1238. STDMETHODIMP CCDBurn::NotifyPnPActivity()
  1239. {
  1240. return E_NOTIMPL;
  1241. }
  1242. STDMETHODIMP CCDBurn::NotifyAddProgress(long nCompletedSteps, long nTotalSteps)
  1243. {
  1244. if (nTotalSteps != 0)
  1245. {
  1246. _SetEstimatedTime((DWORD)((float) _ts.dwSecStaging * (nTotalSteps - nCompletedSteps) / nTotalSteps + _ts.dwSecBurn + _ts.dwSecClose));
  1247. }
  1248. return S_OK;
  1249. }
  1250. STDMETHODIMP CCDBurn::NotifyBlockProgress(long nCompleted, long nTotal)
  1251. {
  1252. // Unfortunately we need to set the "burning" text here,
  1253. // because we get this notification immediately after the NotifyPreparingBurn.
  1254. if (!_fRecording)
  1255. {
  1256. _SetStatus(IDS_BURN_RECORDING);
  1257. _ts.dwTickBurnStart = GetTickCount();
  1258. _fRecording = TRUE;
  1259. }
  1260. if (nTotal != 0)
  1261. {
  1262. _SetEstimatedTime((DWORD)((float) _ts.dwSecBurn * (nTotal - nCompleted) / nTotal + _ts.dwSecClose));
  1263. }
  1264. return S_OK;
  1265. }
  1266. STDMETHODIMP CCDBurn::NotifyTrackProgress(long nCurrentTrack, long nTotalTracks)
  1267. {
  1268. // audio cd only
  1269. return E_NOTIMPL;
  1270. }
  1271. STDMETHODIMP CCDBurn::NotifyPreparingBurn(long nEstimatedSeconds)
  1272. {
  1273. _SetStatus(IDS_BURN_PREPARINGBURN);
  1274. return S_OK;
  1275. }
  1276. STDMETHODIMP CCDBurn::NotifyClosingDisc(long nEstimatedSeconds)
  1277. {
  1278. _ts.dwTickBurnEnd = _ts.dwTickCloseStart = GetTickCount();
  1279. _SetEstimatedTime(_ts.dwSecClose);
  1280. _SetStatus(IDS_BURN_CLOSINGDISC);
  1281. return S_OK;
  1282. }
  1283. STDMETHODIMP CCDBurn::NotifyBurnComplete(HRESULT status)
  1284. {
  1285. _ts.dwTickCloseEnd = GetTickCount();
  1286. _SetEstimatedTime(0);
  1287. _SetStatus(IDS_BURN_COMPLETE);
  1288. return S_OK;
  1289. }
  1290. STDMETHODIMP CCDBurn::NotifyEraseComplete(HRESULT status)
  1291. {
  1292. _ts.dwTickEraseEnd = GetTickCount();
  1293. _SetEstimatedTime(0);
  1294. _SetStatus(IDS_BURN_COMPLETE);
  1295. return S_OK;
  1296. }
  1297. HRESULT CCDBurn::_LockCurrentDrive(BOOL fLock, BOOL fForce)
  1298. {
  1299. HRESULT hr = S_OK;
  1300. // fForce defaults to FALSE
  1301. if (fForce || (fLock != s_fDriveInUse))
  1302. {
  1303. TCHAR szVolume[MAX_PATH];
  1304. hr = _GetCurrentBurnVolumeName(szVolume, ARRAYSIZE(szVolume));
  1305. if (SUCCEEDED(hr))
  1306. {
  1307. // strip trailing backslash (always there)
  1308. ASSERT(szVolume[lstrlen(szVolume) - 1] == TEXT('\\'));
  1309. szVolume[lstrlen(szVolume) - 1] = 0;
  1310. hr = E_FAIL;
  1311. HANDLE hDevice = CreateFile(szVolume, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
  1312. if (hDevice != INVALID_HANDLE_VALUE)
  1313. {
  1314. PREVENT_MEDIA_REMOVAL pmr = {0};
  1315. pmr.PreventMediaRemoval = BOOLIFY(fLock);
  1316. DWORD dwDummy;
  1317. if (DeviceIoControl(hDevice, IOCTL_STORAGE_MEDIA_REMOVAL, &pmr, sizeof(pmr), NULL, 0, &dwDummy, NULL))
  1318. {
  1319. // we're sealed up tight now, set the state var.
  1320. s_fDriveInUse = fLock;
  1321. hr = S_OK;
  1322. }
  1323. CloseHandle(hDevice);
  1324. }
  1325. }
  1326. }
  1327. return hr;
  1328. }
  1329. BOOL CCDBurn::_HasFiles(LPCITEMIDLIST pidl)
  1330. {
  1331. BOOL fHasFiles = FALSE;
  1332. IBindCtx *pbc;
  1333. HRESULT hr = SHCreateSkipBindCtx(NULL, &pbc);
  1334. if (SUCCEEDED(hr))
  1335. {
  1336. IStorage *pstg;
  1337. hr = SHBindToObjectEx(NULL, pidl, pbc, IID_PPV_ARG(IStorage, &pstg));
  1338. if (SUCCEEDED(hr))
  1339. {
  1340. IEnumSTATSTG *penum;
  1341. hr = pstg->EnumElements(0, NULL, 0, &penum);
  1342. if (SUCCEEDED(hr))
  1343. {
  1344. STATSTG stat;
  1345. hr = penum->Next(1, &stat, NULL);
  1346. if (hr == S_OK)
  1347. {
  1348. fHasFiles = TRUE;
  1349. CoTaskMemFree(stat.pwcsName);
  1350. }
  1351. penum->Release();
  1352. }
  1353. pstg->Release();
  1354. }
  1355. pbc->Release();
  1356. }
  1357. return fHasFiles;
  1358. }
  1359. BOOL CCDBurn::_StagingAreaHasFiles()
  1360. {
  1361. BOOL fHasFiles = FALSE;
  1362. LPITEMIDLIST pidl;
  1363. if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA, NULL, 0, &pidl)))
  1364. {
  1365. fHasFiles = _HasFiles(pidl);
  1366. ILFree(pidl);
  1367. }
  1368. return fHasFiles;
  1369. }
  1370. BOOL CCDBurn::_DiscHasFiles()
  1371. {
  1372. BOOL fHasFiles = FALSE;
  1373. LPITEMIDLIST pidl;
  1374. if (SUCCEEDED(_GetPlainCDPidl(&pidl)))
  1375. {
  1376. fHasFiles = _HasFiles(pidl);
  1377. ILFree(pidl);
  1378. }
  1379. return fHasFiles;
  1380. }
  1381. HRESULT CCDBurn::GetContentState(BOOL *pfStagingHasFiles, BOOL *pfDiscHasFiles)
  1382. {
  1383. if (pfStagingHasFiles)
  1384. *pfStagingHasFiles = _StagingAreaHasFiles();
  1385. if (pfDiscHasFiles)
  1386. *pfDiscHasFiles = _DiscHasFiles();
  1387. return S_OK;
  1388. }
  1389. HRESULT CCDBurn::IsWizardUp()
  1390. {
  1391. HRESULT hr;
  1392. CCDBurn *pcdb = new CCDBurn();
  1393. if (pcdb)
  1394. {
  1395. // check if the wizard is currently running.
  1396. // the reason why we have to do both the s_hwndWiz and the exclusive mutex check is that
  1397. // there could be other explorer.exe's that are running. the s_hwndWiz is a shortcut for
  1398. // the typical case where we're the only one.
  1399. if (!pcdb->s_hwndWiz && pcdb->_EnterExclusiveBurning())
  1400. {
  1401. // if we could get the exclusive mutex, we werent up yet, so release it.
  1402. pcdb->_LeaveExclusiveBurning();
  1403. hr = S_FALSE;
  1404. }
  1405. else
  1406. {
  1407. // the window is up or we couldn't get the mutex so we're definitely burning now.
  1408. hr = S_OK;
  1409. }
  1410. pcdb->Release();
  1411. }
  1412. else
  1413. {
  1414. hr = E_OUTOFMEMORY;
  1415. }
  1416. return hr;
  1417. }
  1418. HRESULT CCDBurn::_GetRecorderPath(IDiscRecorder *pdr, LPTSTR pszPath, UINT cchBuf)
  1419. {
  1420. BSTR bstr;
  1421. HRESULT hr = pdr->GetPath(&bstr);
  1422. if (SUCCEEDED(hr))
  1423. {
  1424. lstrcpyn(pszPath, bstr, cchBuf);
  1425. SysFreeString(bstr);
  1426. }
  1427. return hr;
  1428. }
  1429. HRESULT CCDBurn::_GetVolumeNameForDriveIndex(int iDrive, LPTSTR pszVolumeName, UINT cchBuf)
  1430. {
  1431. HRESULT hr = E_FAIL;
  1432. TCHAR szDriveLetter[4];
  1433. if (PathBuildRoot(szDriveLetter, iDrive) &&
  1434. GetVolumeNameForVolumeMountPoint(szDriveLetter, pszVolumeName, cchBuf))
  1435. {
  1436. hr = S_OK;
  1437. }
  1438. return hr;
  1439. }
  1440. HRESULT CCDBurn::_GetDriveIndexForVolumeName(LPCTSTR pszVolumeName, int *piDrive)
  1441. {
  1442. HRESULT hr = E_FAIL;
  1443. DWORD dwLen;
  1444. BOOL fRet = GetVolumePathNamesForVolumeName(pszVolumeName, NULL, 0, &dwLen);
  1445. if (fRet || (GetLastError() == ERROR_MORE_DATA))
  1446. {
  1447. LPWSTR pszBuf = new WCHAR[dwLen];
  1448. if (pszBuf)
  1449. {
  1450. hr = E_FAIL;
  1451. if (GetVolumePathNamesForVolumeName(pszVolumeName, pszBuf, dwLen, NULL))
  1452. {
  1453. for (PWSTR pszPath = pszBuf; *pszPath; pszPath += lstrlenW(pszPath) + 1)
  1454. {
  1455. // if it's mounted at more than one root, take the first one.
  1456. if (PathIsRoot(pszPath))
  1457. {
  1458. *piDrive = PathGetDriveNumber(pszBuf);
  1459. hr = S_OK;
  1460. break;
  1461. }
  1462. }
  1463. }
  1464. delete [] pszBuf;
  1465. }
  1466. else
  1467. {
  1468. hr = E_OUTOFMEMORY;
  1469. }
  1470. }
  1471. return hr;
  1472. }
  1473. HRESULT CCDBurn::_GetCurrentDriveIndex(int *piDrive)
  1474. {
  1475. TCHAR szCurrent[MAX_PATH];
  1476. HRESULT hr = _GetCurrentBurnVolumeName(szCurrent, ARRAYSIZE(szCurrent));
  1477. if (SUCCEEDED(hr))
  1478. {
  1479. hr = _GetDriveIndexForVolumeName(szCurrent, piDrive);
  1480. }
  1481. return hr;
  1482. }
  1483. HRESULT CCDBurn::_GetVolumeNameForDevicePath(LPCTSTR pszDevice, LPTSTR pszVolumeName, UINT cchBuf)
  1484. {
  1485. TCHAR szMountPoint[MAX_PATH];
  1486. lstrcpyn(szMountPoint, TEXT("\\\\?\\GLOBALROOT"), ARRAYSIZE(szMountPoint));
  1487. StrCatBuff(szMountPoint, pszDevice, ARRAYSIZE(szMountPoint));
  1488. StrCatBuff(szMountPoint, TEXT("\\"), ARRAYSIZE(szMountPoint));
  1489. return GetVolumeNameForVolumeMountPoint(szMountPoint, pszVolumeName, cchBuf) ? S_OK : E_FAIL;
  1490. }
  1491. BOOL CCDBurn::_DevicePathMatchesVolumeName(LPCTSTR pszDevice, LPCTSTR pszVolumeName)
  1492. {
  1493. BOOL fRet = FALSE;
  1494. TCHAR szVolumeNameTest[MAX_PATH];
  1495. if (SUCCEEDED(_GetVolumeNameForDevicePath(pszDevice, szVolumeNameTest, ARRAYSIZE(szVolumeNameTest))))
  1496. {
  1497. fRet = (lstrcmpi(pszVolumeName, szVolumeNameTest) == 0);
  1498. }
  1499. return fRet;
  1500. }
  1501. // can return S_FALSE and not fill up ppdr
  1502. HRESULT CCDBurn::_GetDiscRecorderForDrive(IDiscMaster *pdm, LPCTSTR pszVolumeName, IDiscRecorder **ppdr)
  1503. {
  1504. IEnumDiscRecorders *penumdr;
  1505. HRESULT hr = pdm->EnumDiscRecorders(&penumdr);
  1506. if (SUCCEEDED(hr))
  1507. {
  1508. BOOL fDone = FALSE;
  1509. do
  1510. {
  1511. ULONG celt;
  1512. IDiscRecorder *pdr;
  1513. hr = penumdr->Next(1, &pdr, &celt);
  1514. if (hr == S_OK)
  1515. {
  1516. TCHAR szPath[MAX_PATH];
  1517. hr = _GetRecorderPath(pdr, szPath, ARRAYSIZE(szPath));
  1518. if (SUCCEEDED(hr))
  1519. {
  1520. if (_DevicePathMatchesVolumeName(szPath, pszVolumeName))
  1521. {
  1522. if (ppdr)
  1523. {
  1524. pdr->AddRef();
  1525. *ppdr = pdr;
  1526. }
  1527. fDone = TRUE;
  1528. }
  1529. }
  1530. pdr->Release();
  1531. }
  1532. else
  1533. {
  1534. fDone = TRUE;
  1535. }
  1536. } while (!fDone);
  1537. penumdr->Release();
  1538. }
  1539. return hr;
  1540. }
  1541. HRESULT CCDBurn::_FindAndSetRecorder(LPCTSTR pszVolumeName, IDiscMaster *pdm, BOOL fSetActive, IDiscRecorder **ppdr)
  1542. {
  1543. IDiscRecorder *pdr;
  1544. HRESULT hr = _GetDiscRecorderForDrive(pdm, pszVolumeName, &pdr);
  1545. if (S_OK == hr)
  1546. {
  1547. DWORD dwCurSpeed = 1, dwMaxSpeed = 1;
  1548. _GetCachedDriveInfo(pszVolumeName, NULL, &dwCurSpeed, &dwMaxSpeed);
  1549. if (fSetActive)
  1550. {
  1551. hr = pdm->SetActiveDiscRecorder(pdr);
  1552. if (SUCCEEDED(hr))
  1553. {
  1554. hr = _SetRecorderProps(pdr, dwCurSpeed);
  1555. }
  1556. }
  1557. // _dwCurSpeed is used only for timing, so if it's 0xFFFFFFFF we need to scale
  1558. // it back down to what we think is the max speed.
  1559. // this may be incorrect for certain drives that have an actual speed different
  1560. // from what they report but it's the best we can do (and it'll be compensated for
  1561. // in the next burn).
  1562. _dwCurSpeed = (WRITESPEED_FASTEST == dwCurSpeed) ? dwMaxSpeed : dwCurSpeed;
  1563. if (SUCCEEDED(hr))
  1564. {
  1565. *ppdr = pdr;
  1566. pdr->AddRef();
  1567. }
  1568. pdr->Release();
  1569. }
  1570. else
  1571. {
  1572. // munge S_FALSE into E_FAIL (i.e. we got through the whole enumerator without
  1573. // finding a match, so that's bad)
  1574. if (hr == S_FALSE)
  1575. {
  1576. hr = E_FAIL;
  1577. }
  1578. }
  1579. return hr;
  1580. }
  1581. HRESULT CCDBurn::_FindAndSetDefaultRecorder(IDiscMaster *pdm, BOOL fSetActive, IDiscRecorder **ppdr)
  1582. {
  1583. TCHAR szCurrent[MAX_PATH];
  1584. HRESULT hr = _GetCurrentBurnVolumeName(szCurrent, ARRAYSIZE(szCurrent));
  1585. if (SUCCEEDED(hr))
  1586. {
  1587. hr = _FindAndSetRecorder(szCurrent, pdm, fSetActive, ppdr);
  1588. }
  1589. return hr;
  1590. }
  1591. INT_PTR CALLBACK CCDBurn::_ConfirmDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1592. {
  1593. switch (uMsg)
  1594. {
  1595. case WM_INITDIALOG:
  1596. {
  1597. LPITEMIDLIST pidl;
  1598. if (SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_BITBUCKET, &pidl)))
  1599. {
  1600. SHFILEINFO fi;
  1601. if (SHGetFileInfo((LPCTSTR)pidl, 0, &fi, sizeof(fi), SHGFI_PIDL | SHGFI_ICON | SHGFI_LARGEICON))
  1602. {
  1603. ReplaceDlgIcon(hwndDlg, IDD_ICON, fi.hIcon);
  1604. }
  1605. ILFree(pidl);
  1606. }
  1607. }
  1608. break;
  1609. case WM_DESTROY:
  1610. ReplaceDlgIcon(hwndDlg, IDD_ICON, NULL);
  1611. break;
  1612. case WM_COMMAND:
  1613. switch (GET_WM_COMMAND_ID(wParam, lParam))
  1614. {
  1615. case IDNO:
  1616. EndDialog(hwndDlg, IDNO);
  1617. break;
  1618. case IDYES:
  1619. EndDialog(hwndDlg, IDYES);
  1620. break;
  1621. }
  1622. break;
  1623. default:
  1624. return FALSE;
  1625. }
  1626. return TRUE;
  1627. }
  1628. HRESULT CCDBurn::_CleanUp(LPCMINVOKECOMMANDINFO lpcmi, BOOL fRecycle)
  1629. {
  1630. // delete the files in the staging area, by deleting the entire staging area
  1631. TCHAR szStaging[MAX_PATH + 1]; // a little room for double-null
  1632. HRESULT hr = _GetBurnStagingPath(szStaging, MAX_PATH);
  1633. if (SUCCEEDED(hr) && PathFileExists(szStaging))
  1634. {
  1635. // cant use shfileop confirmation because its too hacky.
  1636. if ((lpcmi->fMask & CMIC_MASK_FLAG_NO_UI) ||
  1637. (IDYES == DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_DELETE_STAGING), lpcmi->hwnd, _ConfirmDialogProc, NULL)))
  1638. {
  1639. PathAppend(szStaging, c_szStarDotStar);
  1640. // double-null
  1641. szStaging[lstrlen(szStaging) + 1] = 0;
  1642. SHFILEOPSTRUCT fo = {0};
  1643. fo.wFunc = FO_DELETE;
  1644. fo.pFrom = szStaging;
  1645. fo.fFlags = FOF_NOCONFIRMATION;
  1646. if (fRecycle)
  1647. {
  1648. fo.fFlags |= FOF_ALLOWUNDO;
  1649. }
  1650. fo.hwnd = lpcmi->hwnd;
  1651. hr = (SHFileOperation(&fo) == ERROR_SUCCESS) ? S_OK : E_FAIL;
  1652. }
  1653. }
  1654. return hr;
  1655. }
  1656. HRESULT CCDBurn::_CreateDefaultPropBag(REFIID riid, void **ppv)
  1657. {
  1658. IPropertyBag *ppb;
  1659. HRESULT hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &ppb));
  1660. if (SUCCEEDED(hr))
  1661. {
  1662. // default to think we didn't hit eject
  1663. SHPropertyBag_WriteBOOL(ppb, PROPSTR_EJECT, FALSE);
  1664. // do volume name
  1665. TCHAR szDiscLabel[JOLIET_MAX_LABEL + 1];
  1666. ULONG cb = sizeof(szDiscLabel);
  1667. if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_DISCLABEL, NULL, szDiscLabel, &cb))
  1668. {
  1669. // make a default string like "Feb 02 2001"
  1670. TCHAR szFormat[JOLIET_MAX_LABEL + 1];
  1671. LoadString(HINST_THISDLL, IDS_BURN_FORMAT_DISCLABEL, szFormat, ARRAYSIZE(szFormat));
  1672. if (!GetDateFormat(LOCALE_USER_DEFAULT, 0, NULL, szFormat, szDiscLabel, ARRAYSIZE(szDiscLabel)))
  1673. {
  1674. szDiscLabel[0] = 0;
  1675. }
  1676. }
  1677. SHPropertyBag_WriteStr(ppb, PROPSTR_DISCLABEL, szDiscLabel);
  1678. // do autoclose
  1679. DWORD dwClose;
  1680. cb = sizeof(dwClose);
  1681. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_AUTOCLOSE, NULL, &dwClose, &cb) && dwClose)
  1682. {
  1683. SHPropertyBag_WriteBOOL(ppb, PROPSTR_AUTOCLOSE, TRUE);
  1684. }
  1685. hr = ppb->QueryInterface(riid, ppv);
  1686. ppb->Release();
  1687. }
  1688. return hr;
  1689. }
  1690. HRESULT CCDBurn::_PrepWiz(LPCMINVOKECOMMANDINFO lpcmi, BOOL fErase, BOOL fFailSilently)
  1691. {
  1692. HRESULT hr = E_FAIL;
  1693. if (s_hwndWiz)
  1694. {
  1695. // if we know the shell is burning and we have an hwnd to the wizard, pop it up.
  1696. SetForegroundWindow(s_hwndWiz);
  1697. hr = S_OK;
  1698. }
  1699. else
  1700. {
  1701. SHPropertyBag_WriteBOOL(_ppb, PROPSTR_ERASE, fErase);
  1702. SHPropertyBag_WriteBOOL(_ppb, PROPSTR_FAILSILENTLY, fFailSilently);
  1703. hr = _ShowWizardOnSeparateThread();
  1704. }
  1705. return hr;
  1706. }
  1707. void CCDBurn::_SetStatus(UINT uID)
  1708. {
  1709. TCHAR szBuf[MAX_PATH];
  1710. LoadString(HINST_THISDLL, uID, szBuf, ARRAYSIZE(szBuf));
  1711. SetDlgItemText(_hwndWizardPage, IDC_BURNWIZ_STATUSTEXT, szBuf);
  1712. }
  1713. HRESULT CCDBurn::_GetDiscMasters(IDiscMaster **ppdm, IJolietDiscMaster **ppjdm)
  1714. {
  1715. *ppdm = NULL;
  1716. *ppjdm = NULL;
  1717. HRESULT hr = CoCreateInstance(CLSID_MSDiscMasterObj, NULL, CLSCTX_ALL, IID_PPV_ARG(IDiscMaster, ppdm));
  1718. if (SUCCEEDED(hr))
  1719. {
  1720. // open() takes some time, so set up the progress dialog
  1721. _SetStatus(IDS_BURN_INITIALIZESTASH);
  1722. hr = (*ppdm)->Open();
  1723. if (SUCCEEDED(hr))
  1724. {
  1725. hr = (*ppdm)->SetActiveDiscMasterFormat(IID_PPV_ARG(IJolietDiscMaster, ppjdm));
  1726. }
  1727. // clean up if we couldn't get the joliet disc master.
  1728. if (FAILED(hr))
  1729. {
  1730. ATOMICRELEASE(*ppdm);
  1731. }
  1732. }
  1733. ASSERT(SUCCEEDED(hr) ? ((*ppdm != NULL) && (*ppjdm != NULL)) : ((*ppdm == NULL) && (*ppjdm == NULL)));
  1734. return hr;
  1735. }
  1736. HRESULT CCDBurn::_AddData(IJolietDiscMaster *pjdm)
  1737. {
  1738. _SetStatus(IDS_BURN_ADDDATA);
  1739. LPITEMIDLIST pidl;
  1740. HRESULT hr = SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA, NULL, 0, &pidl);
  1741. if (SUCCEEDED(hr))
  1742. {
  1743. IStorage *pstg;
  1744. hr = SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IStorage, &pstg));
  1745. if (SUCCEEDED(hr))
  1746. {
  1747. _ts.dwTickStagingStart = GetTickCount();
  1748. hr = pjdm->AddData(pstg, 1);
  1749. _ts.dwTickStagingEnd = GetTickCount();
  1750. pstg->Release();
  1751. }
  1752. ILFree(pidl);
  1753. }
  1754. return hr;
  1755. }
  1756. BOOL CCDBurn::_IsBurningNow()
  1757. {
  1758. BOOL fBurning = TRUE;
  1759. CCDBurn *pcdb = new CCDBurn();
  1760. if (pcdb)
  1761. {
  1762. // if the shell is burning, we know it for sure. otherwise we have to check imapi to see if any other
  1763. // process is using imapi to burn.
  1764. if (!pcdb->s_hwndWiz && pcdb->_EnterExclusiveBurning())
  1765. {
  1766. pcdb->_LeaveExclusiveBurning();
  1767. // if we can get the mutex, we're not burning.
  1768. fBurning = FALSE;
  1769. IDiscMaster *pdm;
  1770. IJolietDiscMaster *pjdm;
  1771. if (SUCCEEDED(pcdb->_GetDiscMasters(&pdm, &pjdm)))
  1772. {
  1773. IDiscRecorder *pdr;
  1774. if (SUCCEEDED(pcdb->_FindAndSetDefaultRecorder(pdm, FALSE, &pdr)))
  1775. {
  1776. ULONG ulState;
  1777. if (SUCCEEDED(pdr->GetRecorderState(&ulState)))
  1778. {
  1779. fBurning = (ulState & (RECORDER_BURNING | RECORDER_OPENED));
  1780. }
  1781. pdr->Release();
  1782. }
  1783. pdm->Release();
  1784. pjdm->Release();
  1785. }
  1786. }
  1787. pcdb->Release();
  1788. }
  1789. return fBurning;
  1790. }
  1791. // handy stuff from lowdisk
  1792. BOOL CCDBurn::_EnterExclusiveBurning()
  1793. {
  1794. if (NULL == _hMutexBurning)
  1795. {
  1796. // since imapi runs in the system context and only one client can use it at a time,
  1797. // make the mutex in the global namespace.
  1798. _hMutexBurning = CreateMutex(CreateAllAccessSecurityAttributes(NULL, NULL, NULL), FALSE, L"Global\\CDBurnExclusive");
  1799. if (!_hMutexBurning)
  1800. {
  1801. // we'll get here if we're running as a limited user.
  1802. // we don't have access to the global namespace so create it in the current session.
  1803. // this will look normal within the session but across users it'll be more bogus since we'll let the
  1804. // user start the burn from the wizard but we'll fail later. that's good enough.
  1805. _hMutexBurning = CreateMutex(CreateAllAccessSecurityAttributes(NULL, NULL, NULL), FALSE, L"CDBurnExclusive");
  1806. }
  1807. }
  1808. return _hMutexBurning && (WAIT_OBJECT_0 == WaitForSingleObject(_hMutexBurning, 0)); // zero timeout
  1809. }
  1810. void CCDBurn::_LeaveExclusiveBurning()
  1811. {
  1812. ASSERT(_hMutexBurning);
  1813. ReleaseMutex(_hMutexBurning);
  1814. }
  1815. HRESULT CCDBurn::_Balloon()
  1816. {
  1817. WCHAR szTitle[50];
  1818. LoadString(HINST_THISDLL, IDS_BURN_NOTIFY_TITLE, szTitle, ARRAYSIZE(szTitle));
  1819. WCHAR szMsg[100];
  1820. LoadString(HINST_THISDLL, IDS_BURN_NOTIFY, szMsg, ARRAYSIZE(szMsg));
  1821. HICON hIcon = LoadIcon(g_hinst, MAKEINTRESOURCE(IDI_DRIVECD));
  1822. IUserNotification *pun;
  1823. HRESULT hr = CoCreateInstance(CLSID_UserNotification, NULL, CLSCTX_ALL, IID_PPV_ARG(IUserNotification, &pun));
  1824. if (SUCCEEDED(hr))
  1825. {
  1826. pun->SetBalloonRetry(30 * 1000, -1, 0);
  1827. pun->SetIconInfo(hIcon, szTitle);
  1828. pun->SetBalloonInfo(szTitle, szMsg, NIIF_INFO);
  1829. hr = pun->Show(NULL, 0);
  1830. pun->Release();
  1831. }
  1832. if (hIcon)
  1833. {
  1834. DestroyIcon(hIcon);
  1835. }
  1836. return hr;
  1837. }
  1838. typedef struct
  1839. {
  1840. LPCITEMIDLIST pidlFolder;
  1841. BOOL fRet;
  1842. } CDBURNENUMSTRUCT;
  1843. BOOL CALLBACK CCDBurn::_EnumProc(HWND hwnd, LPCITEMIDLIST pidl, LPARAM lParam)
  1844. {
  1845. CDBURNENUMSTRUCT *pes = (CDBURNENUMSTRUCT*)lParam;
  1846. if (ILIsParent(pes->pidlFolder, pidl, FALSE))
  1847. {
  1848. pes->fRet = TRUE;
  1849. return FALSE; // stop enumerating, we know we got it.
  1850. }
  1851. return TRUE;
  1852. }
  1853. BOOL CCDBurn::_BurningFolderOpen()
  1854. {
  1855. BOOL fRet = FALSE;
  1856. LPITEMIDLIST pidlFolder;
  1857. if (SUCCEEDED(_GetFolderPidl(&pidlFolder)))
  1858. {
  1859. CDBURNENUMSTRUCT es = { 0 };
  1860. es.pidlFolder = pidlFolder;
  1861. EnumShellWindows(_EnumProc, (LPARAM)&es);
  1862. fRet = es.fRet;
  1863. ILFree(pidlFolder);
  1864. }
  1865. return fRet;
  1866. }
  1867. DWORD CALLBACK CCDBurn::_NotifyThreadProc(void *pv)
  1868. {
  1869. CCDBurn *pcdb = (CCDBurn *)pv;
  1870. HANDLE hMutexNotify = CreateMutex(CreateAllAccessSecurityAttributes(NULL, NULL, NULL), FALSE, L"CDBurnNotify");
  1871. if (hMutexNotify)
  1872. {
  1873. if (WAIT_OBJECT_0 == WaitForSingleObject(hMutexNotify, 0))
  1874. {
  1875. if (!_BurningFolderOpen() && !_IsBurningNow())
  1876. {
  1877. if (S_OK == pcdb->_Balloon())
  1878. {
  1879. LPITEMIDLIST pidl;
  1880. if (SUCCEEDED(_GetFolderPidl(&pidl)))
  1881. {
  1882. // Use shellexecuteex to open a view folder
  1883. SHELLEXECUTEINFO sei = { 0 };
  1884. sei.cbSize = sizeof(sei);
  1885. sei.lpIDList = pidl;
  1886. sei.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_IDLIST;
  1887. sei.nShow = SW_SHOWNORMAL;
  1888. sei.lpVerb = c_szOpen;
  1889. ShellExecuteEx(&sei);
  1890. ILFree(pidl);
  1891. }
  1892. }
  1893. }
  1894. ReleaseMutex(hMutexNotify);
  1895. }
  1896. CloseHandle(hMutexNotify);
  1897. }
  1898. pcdb->Release();
  1899. return 0;
  1900. }
  1901. void CCDBurn::_CheckStagingArea()
  1902. {
  1903. // only do stuff if we're enabled.
  1904. int iCurrent;
  1905. if (SUCCEEDED(_GetCurrentDriveIndex(&iCurrent)) && _StagingAreaHasFiles())
  1906. {
  1907. AddRef();
  1908. if (!SHCreateThread(_NotifyThreadProc, this, CTF_COINIT, NULL))
  1909. {
  1910. Release();
  1911. }
  1912. }
  1913. }
  1914. STDAPI CheckStagingArea()
  1915. {
  1916. CCDBurn *pcdb = new CCDBurn();
  1917. if (pcdb)
  1918. {
  1919. pcdb->_CheckStagingArea();
  1920. pcdb->Release();
  1921. }
  1922. return S_OK;
  1923. }
  1924. HRESULT _CreateDataObject(IDataObject **ppdo)
  1925. {
  1926. LPITEMIDLIST pidl;
  1927. HRESULT hr = SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA, NULL, 0, &pidl);
  1928. if (SUCCEEDED(hr))
  1929. {
  1930. hr = SHGetUIObjectOf(pidl, NULL, IID_PPV_ARG(IDataObject, ppdo));
  1931. ILFree(pidl);
  1932. }
  1933. return hr;
  1934. }
  1935. // this will eventually put up confirmation UI. for now just tally size.
  1936. HRESULT CCDBurn::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl)
  1937. {
  1938. IShellFolder2 *psf2;
  1939. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  1940. {
  1941. ULONGLONG cbItemSize;
  1942. if (SUCCEEDED(GetLongProperty(psf2, pidl, &SCID_SIZE, &cbItemSize)))
  1943. {
  1944. _cbStagedSize += cbItemSize;
  1945. }
  1946. psf2->Release();
  1947. }
  1948. return S_OK;
  1949. }
  1950. HRESULT CCDBurn::_CheckTotal()
  1951. {
  1952. ULONGLONG cbTotal, cbFree;
  1953. HRESULT hr = GetSpace(&cbTotal, &cbFree);
  1954. if (SUCCEEDED(hr))
  1955. {
  1956. if (_cbStagedSize > cbFree)
  1957. {
  1958. hr = HRESULT_FROM_WIN32(ERROR_DISK_FULL);
  1959. TCHAR szStaged[40], szFree[40], szOver[40];
  1960. StrFormatByteSize64(_cbStagedSize, szStaged, ARRAYSIZE(szStaged));
  1961. StrFormatByteSize64(cbFree, szFree, ARRAYSIZE(szFree));
  1962. StrFormatByteSize64(_cbStagedSize - cbFree, szOver, ARRAYSIZE(szOver));
  1963. LPTSTR pszMessage = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_BURN_DISCFULLTEXT), szFree, szStaged, szOver);
  1964. if (pszMessage)
  1965. {
  1966. SHPropertyBag_WriteStr(_ppb, PROPSTR_DISCFULLTEXT, pszMessage);
  1967. LocalFree(pszMessage);
  1968. }
  1969. }
  1970. }
  1971. return hr;
  1972. }
  1973. HRESULT CCDBurn::_Validate()
  1974. {
  1975. INamespaceWalk *pnsw;
  1976. HRESULT hr = CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw));
  1977. if (SUCCEEDED(hr))
  1978. {
  1979. LPITEMIDLIST pidlStaging;
  1980. hr = SHGetFolderLocation(NULL, CSIDL_CDBURN_AREA, NULL, 0, &pidlStaging);
  1981. if (SUCCEEDED(hr))
  1982. {
  1983. IShellFolder *psf;
  1984. hr = SHBindToObjectEx(NULL, pidlStaging, NULL, IID_PPV_ARG(IShellFolder, &psf));
  1985. if (SUCCEEDED(hr))
  1986. {
  1987. _cbStagedSize = 0;
  1988. // walk 15 levels deep, TODO figure out what Joliet's limitations are.
  1989. hr = pnsw->Walk(psf, NSWF_DONT_TRAVERSE_LINKS, 15, this);
  1990. if (SUCCEEDED(hr))
  1991. {
  1992. hr = _CheckTotal();
  1993. }
  1994. psf->Release();
  1995. }
  1996. ILFree(pidlStaging);
  1997. }
  1998. pnsw->Release();
  1999. }
  2000. return hr;
  2001. }
  2002. DWORD WINAPI CCDBurn::_BurnThread(void *pv)
  2003. {
  2004. CCDBurn *pcdb = (CCDBurn *) pv;
  2005. HRESULT hr = pcdb->_Validate();
  2006. if (SUCCEEDED(hr))
  2007. {
  2008. IDiscMaster *pdm;
  2009. IJolietDiscMaster *pjdm;
  2010. hr = pcdb->_GetDiscMasters(&pdm, &pjdm);
  2011. if (SUCCEEDED(hr))
  2012. {
  2013. hr = pcdb->_SetJolietProps(pjdm);
  2014. if (SUCCEEDED(hr))
  2015. {
  2016. UINT_PTR lCookie;
  2017. hr = pdm->ProgressAdvise(pcdb, &lCookie);
  2018. if (SUCCEEDED(hr))
  2019. {
  2020. IDiscRecorder *pdr;
  2021. hr = pcdb->_FindAndSetDefaultRecorder(pdm, TRUE, &pdr);
  2022. if (SUCCEEDED(hr))
  2023. {
  2024. pcdb->_InitTimeStats(FALSE);
  2025. // TODO ISSUE BUGBUG: import previous session here!
  2026. hr = pcdb->_AddData(pjdm);
  2027. // TODO ISSUE BUG: AddData doesn't call QueryCancel, we do it manually for now
  2028. boolean bCancelled;
  2029. pcdb->QueryCancel(&bCancelled);
  2030. if (SUCCEEDED(hr) && !bCancelled)
  2031. {
  2032. BOOL fAutoEject = TRUE;
  2033. pcdb->_GetEjectSetting(&fAutoEject);
  2034. // false is to make a real recording instead of a simulated one
  2035. hr = pdm->RecordDisc(FALSE, (boolean)fAutoEject);
  2036. if (SUCCEEDED(hr))
  2037. {
  2038. // bug: recorddisc doesnt call back into NotifyBurnComplete so this has to be done here
  2039. pcdb->_ts.dwTickCloseEnd = GetTickCount();
  2040. pcdb->_SaveTimeStats(FALSE);
  2041. }
  2042. }
  2043. pdr->Release();
  2044. }
  2045. pdm->ProgressUnadvise(lCookie);
  2046. }
  2047. }
  2048. pdm->Release();
  2049. pjdm->Release();
  2050. }
  2051. }
  2052. _LockCurrentDrive(FALSE);
  2053. if (!pcdb->_fCancelled)
  2054. {
  2055. SHPropertyBag_WriteDWORD(pcdb->_ppb, PROPSTR_HR, hr);
  2056. PropSheet_SetWizButtons(GetParent(pcdb->_hwndWizardPage), PSWIZB_NEXT);
  2057. PropSheet_PressButton(GetParent(pcdb->_hwndWizardPage), PSBTN_NEXT);
  2058. }
  2059. pcdb->Release();
  2060. return 0;
  2061. }
  2062. DWORD WINAPI CCDBurn::_EraseThread(void *pv)
  2063. {
  2064. CCDBurn *pcdb = (CCDBurn *) pv;
  2065. IDiscMaster *pdm;
  2066. IJolietDiscMaster *pjdm;
  2067. HRESULT hr = pcdb->_GetDiscMasters(&pdm, &pjdm);
  2068. if (SUCCEEDED(hr))
  2069. {
  2070. IDiscRecorder *pdr;
  2071. hr = pcdb->_FindAndSetDefaultRecorder(pdm, FALSE, &pdr);
  2072. if (SUCCEEDED(hr))
  2073. {
  2074. pcdb->_SetStatus(IDS_BURN_ERASEDISC);
  2075. hr = pdr->OpenExclusive();
  2076. if (SUCCEEDED(hr))
  2077. {
  2078. pcdb->_InitTimeStats(TRUE);
  2079. pcdb->_ts.dwTickEraseStart = GetTickCount();
  2080. hr = pdr->Erase(FALSE); // do quick erase
  2081. if (SUCCEEDED(hr))
  2082. {
  2083. hr = pdr->Close();
  2084. // bug: this is here only because Erase doesn't call NotifyEraseComplete
  2085. pcdb->_ts.dwTickEraseEnd = GetTickCount();
  2086. pcdb->_SaveTimeStats(TRUE);
  2087. }
  2088. }
  2089. pdr->Release();
  2090. }
  2091. pdm->Release();
  2092. pjdm->Release();
  2093. }
  2094. _LockCurrentDrive(FALSE);
  2095. if (!pcdb->_fCancelled)
  2096. {
  2097. SHPropertyBag_WriteDWORD(pcdb->_ppb, PROPSTR_HR, hr);
  2098. PropSheet_SetWizButtons(GetParent(pcdb->_hwndWizardPage), PSWIZB_NEXT);
  2099. PropSheet_PressButton(GetParent(pcdb->_hwndWizardPage), PSBTN_NEXT);
  2100. }
  2101. pcdb->Release();
  2102. return 0;
  2103. }
  2104. HRESULT CCDBurn::_GetPidlForDriveIndex(int iDrive, LPITEMIDLIST *ppidl)
  2105. {
  2106. HRESULT hr = E_FAIL;
  2107. TCHAR szPath[4];
  2108. if (PathBuildRoot(szPath, iDrive))
  2109. {
  2110. hr = SHILCreateFromPath(szPath, ppidl, NULL);
  2111. }
  2112. return hr;
  2113. }
  2114. HRESULT CCDBurn::_GetPidlForVolumeName(LPCTSTR pszVolume, LPITEMIDLIST *ppidl)
  2115. {
  2116. int iDrive;
  2117. HRESULT hr = _GetDriveIndexForVolumeName(pszVolume, &iDrive);
  2118. if (SUCCEEDED(hr))
  2119. {
  2120. WCHAR szRoot[4];
  2121. hr = PathBuildRoot(szRoot, iDrive) ? S_OK : E_FAIL;
  2122. if (SUCCEEDED(hr))
  2123. {
  2124. hr = SHILCreateFromPath(szRoot, ppidl, NULL);
  2125. }
  2126. }
  2127. return hr;
  2128. }
  2129. HRESULT CCDBurn::_GetFolderPidl(LPITEMIDLIST *ppidl)
  2130. {
  2131. TCHAR szCurrent[MAX_PATH];
  2132. HRESULT hr = _GetCurrentBurnVolumeName(szCurrent, ARRAYSIZE(szCurrent));
  2133. if (SUCCEEDED(hr))
  2134. {
  2135. hr = _GetPidlForVolumeName(szCurrent, ppidl);
  2136. }
  2137. return hr;
  2138. }
  2139. void CCDBurn::_HandleBookkeeping()
  2140. {
  2141. _DumpDiscInfo();
  2142. _StoreDiscInfo();
  2143. }
  2144. // IDropTarget handling
  2145. // This code forwards to drop target folder for the currently configured burn drive.
  2146. // merged folder hands off to ccdburn because otherwise it'd be doing too much
  2147. // cd burning specific stuff (media detection on drop, etc.)
  2148. HRESULT CCDBurn::_EnsureDropTarget()
  2149. {
  2150. HRESULT hr = S_OK;
  2151. if (!_pdt)
  2152. {
  2153. if (_BurningIsEnabled())
  2154. {
  2155. TCHAR szStaging[MAX_PATH];
  2156. hr = _GetBurnStagingPath(szStaging, ARRAYSIZE(szStaging));
  2157. if (SUCCEEDED(hr))
  2158. {
  2159. LPITEMIDLIST pidl;
  2160. hr = SHILCreateFromPath(szStaging, &pidl, NULL);
  2161. if (SUCCEEDED(hr))
  2162. {
  2163. IShellFolder *psf;
  2164. hr = SHBindToObjectEx(NULL, pidl, NULL, IID_PPV_ARG(IShellFolder, &psf));
  2165. if (SUCCEEDED(hr))
  2166. {
  2167. hr = psf->CreateViewObject(NULL, IID_PPV_ARG(IDropTarget, &_pdt));
  2168. psf->Release();
  2169. }
  2170. ILFree(pidl);
  2171. }
  2172. }
  2173. }
  2174. else
  2175. {
  2176. hr = E_FAIL;
  2177. }
  2178. }
  2179. return hr;
  2180. }
  2181. HRESULT CCDBurn::_GetDropPidl(LPITEMIDLIST *ppidl)
  2182. {
  2183. HRESULT hr;
  2184. if (!_pidl)
  2185. {
  2186. // in the sendto case we aren't called through Initialize, so get default root pidl.
  2187. hr = _GetFolderPidl(ppidl);
  2188. }
  2189. else
  2190. {
  2191. hr = SHILClone(_pidl, ppidl);
  2192. }
  2193. return hr;
  2194. }
  2195. HRESULT CCDBurn::_StagingPidlFromMerged(LPCITEMIDLIST pidlDrop, LPITEMIDLIST *ppidlDest)
  2196. {
  2197. IAugmentedShellFolder *pasf;
  2198. HRESULT hr = SHBindToObjectEx(NULL, pidlDrop, NULL, IID_PPV_ARG(IAugmentedShellFolder, &pasf));
  2199. if (SUCCEEDED(hr))
  2200. {
  2201. hr = E_FAIL;
  2202. DWORD dwNSId;
  2203. GUID guid;
  2204. IShellFolder *psf;
  2205. BOOL fDone = FALSE;
  2206. for (DWORD dwIndex = 0;
  2207. !fDone && SUCCEEDED(pasf->EnumNameSpace(dwIndex, &dwNSId)) && SUCCEEDED(pasf->QueryNameSpace(dwNSId, &guid, &psf));
  2208. dwIndex++)
  2209. {
  2210. if (IsEqualGUID(guid, CLSID_StagingFolder))
  2211. {
  2212. IPersistFolder3 *ppf3;
  2213. hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf3));
  2214. if (SUCCEEDED(hr))
  2215. {
  2216. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  2217. hr = ppf3->GetFolderTargetInfo(&pfti);
  2218. if (SUCCEEDED(hr))
  2219. {
  2220. *ppidlDest = pfti.pidlTargetFolder;
  2221. }
  2222. ppf3->Release();
  2223. }
  2224. fDone = TRUE;
  2225. }
  2226. psf->Release();
  2227. }
  2228. pasf->Release();
  2229. }
  2230. return hr;
  2231. }
  2232. BOOL CCDBurn::_IsStagingAreaSource(IDataObject *pdtobj, LPCITEMIDLIST pidlDrop)
  2233. {
  2234. BOOL fParent = FALSE; //source is parent of destination
  2235. BOOL fSame = FALSE; //source and destination are the same
  2236. // in UDF case we skip staging area so no need to do this work
  2237. BOOL fUDF = FALSE;
  2238. _GetMediaCapabilities(NULL, &fUDF);
  2239. if (!fUDF)
  2240. {
  2241. LPITEMIDLIST pidlDest;
  2242. if (SUCCEEDED(_StagingPidlFromMerged(pidlDrop, &pidlDest)))
  2243. {
  2244. STGMEDIUM medium;
  2245. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  2246. if (pida)
  2247. {
  2248. LPCITEMIDLIST pidlSource = IDA_GetIDListPtr(pida, -1);
  2249. if (pidlSource)
  2250. {
  2251. if (!ILIsEmpty(pidlSource))
  2252. {
  2253. fSame = ILIsEqual(pidlSource, pidlDest);
  2254. if (!fSame && ILIsParent(pidlSource, pidlDest, FALSE))
  2255. {
  2256. LPCITEMIDLIST pidl;
  2257. for (UINT i = 0; !fSame && !fParent && (pidl = IDA_GetIDListPtr(pida, i)); i++)
  2258. {
  2259. LPITEMIDLIST pidlFull = ILCombine(pidlSource, pidl);
  2260. if (pidlFull)
  2261. {
  2262. fSame = ILIsEqual(pidlFull, pidlDest);
  2263. if (!fSame)
  2264. fParent = ILIsParent(pidlFull, pidlDest, FALSE);
  2265. ILFree(pidlFull);
  2266. }
  2267. }
  2268. }
  2269. }
  2270. else //find folder has full pidls and empty one for a source
  2271. {
  2272. LPCITEMIDLIST pidl;
  2273. for (UINT i = 0; !fSame && !fParent && (pidl = IDA_GetIDListPtr(pida, i)); i++)
  2274. {
  2275. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  2276. if (pidlParent)
  2277. {
  2278. fSame = ILIsEqual(pidlParent, pidlDest) || ILIsEqual(pidl, pidlDest);
  2279. if (!fSame)
  2280. fParent = ILIsParent(pidl, pidlDest, FALSE);
  2281. ILFree(pidlParent);
  2282. }
  2283. }
  2284. }
  2285. }
  2286. HIDA_ReleaseStgMedium(pida, &medium);
  2287. }
  2288. ILFree(pidlDest);
  2289. }
  2290. }
  2291. if (fSame || fParent)
  2292. {
  2293. UINT idMessage = fSame ? IDS_REASONS_DESTSAMETREE : IDS_REASONS_DESTSUBTREE;
  2294. ShellMessageBox(g_hinst, _hwndBrowser, MAKEINTRESOURCE(idMessage), MAKEINTRESOURCE(IDS_BURN), MB_ICONEXCLAMATION | MB_OK);
  2295. }
  2296. return (fSame || fParent);
  2297. }
  2298. HRESULT CCDBurn::_StorageDrop(IDataObject *pdtobj, BOOL fMove)
  2299. {
  2300. LPITEMIDLIST pidlDrop;
  2301. HRESULT hr = _GetDropPidl(&pidlDrop);
  2302. if (SUCCEEDED(hr))
  2303. {
  2304. if (!_IsStagingAreaSource(pdtobj, pidlDrop))
  2305. {
  2306. IShellItem *psiDest;
  2307. hr = SHCreateShellItem(NULL, NULL, pidlDrop, &psiDest);
  2308. if (SUCCEEDED(hr))
  2309. {
  2310. hr = TransferDataObject(pdtobj, psiDest, fMove ? STGOP_MOVE : STGOP_COPY_PREFERHARDLINK, 0, this);
  2311. psiDest->Release();
  2312. }
  2313. }
  2314. else
  2315. {
  2316. hr = E_FAIL;
  2317. }
  2318. ILFree(pidlDrop);
  2319. }
  2320. return hr;
  2321. }
  2322. HRESULT CCDBurn::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  2323. {
  2324. // lets get the drop object for the folder that is configured for the
  2325. // burn drive. to do this we need to get the staging folder.
  2326. // this also determines if we show up in sendto or not.
  2327. HRESULT hr = _EnsureDropTarget();
  2328. if (SUCCEEDED(hr) && _pdt)
  2329. {
  2330. hr = _pdt->DragEnter(pdtobj, grfKeyState, pt, pdwEffect);
  2331. }
  2332. else
  2333. {
  2334. // there was no drop target, therefore there is no burn drive, therefore we shouldn't
  2335. // offer the ability to drag drop onto our object.
  2336. *pdwEffect = DROPEFFECT_NONE;
  2337. }
  2338. _dwDropEffect = *pdwEffect;
  2339. return hr;
  2340. }
  2341. HRESULT CCDBurn::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  2342. {
  2343. HRESULT hr = E_FAIL;
  2344. if (_pdt)
  2345. {
  2346. hr = _pdt->DragOver(grfKeyState, pt, pdwEffect);
  2347. _dwDropEffect = *pdwEffect;
  2348. }
  2349. return hr;
  2350. }
  2351. HRESULT CCDBurn::DragLeave(void)
  2352. {
  2353. if (!_pdt)
  2354. return E_FAIL;
  2355. return _pdt->DragLeave();
  2356. }
  2357. void CCDBurn::_FreeDropParams(CDDROPPARAMS *pcddp)
  2358. {
  2359. pcddp->pcdb->Release();
  2360. ATOMICRELEASE(pcddp->pstmDataObj);
  2361. delete pcddp;
  2362. }
  2363. DWORD WINAPI CCDBurn::_DropThread(void *pv)
  2364. {
  2365. CDDROPPARAMS *pcddp = (CDDROPPARAMS*)pv;
  2366. IDataObject *pdtobj;
  2367. if (SUCCEEDED(CoGetInterfaceAndReleaseStream(pcddp->pstmDataObj, IID_PPV_ARG(IDataObject, &pdtobj))))
  2368. {
  2369. pcddp->pcdb->_StorageDrop(pdtobj, pcddp->fMove);
  2370. pdtobj->Release();
  2371. }
  2372. pcddp->pstmDataObj = NULL; // released by CoGetInterfaceAndReleaseStream
  2373. _FreeDropParams(pcddp);
  2374. return 0;
  2375. }
  2376. HRESULT CCDBurn::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  2377. {
  2378. HRESULT hr = E_OUTOFMEMORY;
  2379. CDDROPPARAMS *pcddp = new CDDROPPARAMS;
  2380. if (pcddp)
  2381. {
  2382. // get the hwnd from the site while we're on this thread so that we
  2383. // don't get SetSite(NULL) before we check it later.
  2384. IOleWindow *pow;
  2385. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pow))))
  2386. {
  2387. pow->GetWindow(&_hwndBrowser);
  2388. pow->Release();
  2389. }
  2390. pcddp->pcdb = this;
  2391. AddRef();
  2392. CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pdtobj, &pcddp->pstmDataObj);
  2393. pcddp->fMove = (_dwDropEffect == DROPEFFECT_MOVE);
  2394. if (pcddp->fMove)
  2395. {
  2396. _dwDropEffect = DROPEFFECT_NONE; // other thread will take care of move, so caller should take no action
  2397. }
  2398. *pdwEffect = _dwDropEffect;
  2399. if (SHCreateThread(_DropThread, pcddp, CTF_COINIT, NULL))
  2400. {
  2401. hr = S_OK;
  2402. }
  2403. else
  2404. {
  2405. _FreeDropParams(pcddp);
  2406. hr = E_OUTOFMEMORY;
  2407. }
  2408. }
  2409. DragLeave();
  2410. return hr;
  2411. }
  2412. // Helper function to get an IShellFolder object for the staging folder
  2413. HRESULT CCDBurn::_GetStagingFolder(LPCITEMIDLIST pidlDrive, REFIID riid, void **ppv)
  2414. {
  2415. TCHAR szPath[MAX_PATH];
  2416. HRESULT hr = CCDBurn::_GetBurnStagingPath(szPath, ARRAYSIZE(szPath));
  2417. if (SUCCEEDED(hr))
  2418. {
  2419. // fake up a directory object simple IDLIST
  2420. WIN32_FIND_DATA fd = {0};
  2421. fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  2422. LPITEMIDLIST pidlStg;
  2423. hr = SHSimpleIDListFromFindData(szPath, &fd, &pidlStg);
  2424. if (SUCCEEDED(hr))
  2425. {
  2426. // now initialize the folder with it
  2427. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  2428. pfti.pidlTargetFolder = (LPITEMIDLIST)pidlStg;
  2429. SHTCharToUnicode(szPath, pfti.szTargetParsingName, ARRAYSIZE(pfti.szTargetParsingName));
  2430. pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY; // maybe add system?
  2431. pfti.csidl = -1;
  2432. hr = CFSFolder_CreateFolder(NULL, NULL, pidlDrive, &pfti, riid, ppv);
  2433. ILFree(pidlStg);
  2434. }
  2435. }
  2436. return hr;
  2437. }
  2438. // ripped from IMAPI.
  2439. DWORD CCDBurn::_ExecSyncIoctl(HANDLE hDriver, DWORD dwIoctl, void *pbuf, DWORD cbBuf)
  2440. {
  2441. DWORD dwResult = NO_ERROR;
  2442. OVERLAPPED ol = { 0 };
  2443. ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  2444. if (ol.hEvent)
  2445. {
  2446. DWORD dwBytes;
  2447. BOOL bStatus = DeviceIoControl(hDriver, dwIoctl, pbuf, cbBuf, pbuf, cbBuf, &dwBytes, &ol);
  2448. if (!bStatus)
  2449. {
  2450. dwResult = GetLastError();
  2451. if (ERROR_IO_PENDING == dwResult)
  2452. {
  2453. bStatus = GetOverlappedResult(hDriver, &ol, &dwBytes, TRUE);
  2454. dwResult = bStatus ? NO_ERROR : GetLastError();
  2455. }
  2456. }
  2457. CloseHandle(ol.hEvent);
  2458. }
  2459. else
  2460. {
  2461. dwResult = ERROR_OUTOFMEMORY;
  2462. }
  2463. return dwResult;
  2464. }
  2465. // {1186654D-47B8-48b9-BEB9-7DF113AE3C67}
  2466. static const GUID IMAPIDeviceInterfaceGUID = { 0x1186654d, 0x47b8, 0x48b9, { 0xbe, 0xb9, 0x7d, 0xf1, 0x13, 0xae, 0x3c, 0x67 } };
  2467. // this is a first approximation to see if the drive is supported or not to avoid loading imapi at boot
  2468. // whenever possible. if this passes, then we still have to use imapi to make sure, but if this returns
  2469. // false, theres no way imapi will support it.
  2470. // note that this mimics the logic in imapi's CMSEnumDiscRecordersObj::Next enumerator function.
  2471. BOOL CCDBurn::_CouldPossiblySupport(LPCWSTR pszVolume)
  2472. {
  2473. // if the user isnt an admin, then these checks wont work.
  2474. // just assume that we could support it, and let imapi do the privileged stuff since its a service.
  2475. // perfwise this isnt such a big hit since the admin home user is the main scenario.
  2476. if (!IsUserAnAdmin())
  2477. return TRUE;
  2478. BOOL fSupported = FALSE;
  2479. HDEVINFO hDevInfo = SetupDiGetClassDevs(&IMAPIDeviceInterfaceGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
  2480. if (INVALID_HANDLE_VALUE != hDevInfo)
  2481. {
  2482. SP_DEVICE_INTERFACE_DATA did = { 0 };
  2483. did.cbSize = sizeof(did);
  2484. for (int i = 0; !fSupported && SetupDiEnumDeviceInterfaces(hDevInfo, 0, &IMAPIDeviceInterfaceGUID, i, &did); i++)
  2485. {
  2486. ULONG cbRequired = 0;
  2487. SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, NULL, 0, &cbRequired, NULL);
  2488. if (cbRequired > 0)
  2489. {
  2490. cbRequired += sizeof(WCHAR);
  2491. PSP_DEVICE_INTERFACE_DETAIL_DATA pdidd = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LPTR, cbRequired);
  2492. if (pdidd)
  2493. {
  2494. pdidd->cbSize = sizeof(*pdidd);
  2495. SP_DEVINFO_DATA dd = { 0 };
  2496. dd.cbSize = sizeof(dd);
  2497. if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &did, pdidd, cbRequired, NULL, &dd))
  2498. {
  2499. WCHAR szLower[100];
  2500. BOOL fHasFilter = FALSE;
  2501. if (SetupDiGetDeviceRegistryProperty(hDevInfo, &dd, SPDRP_LOWERFILTERS, NULL, (BYTE*)szLower, sizeof(szLower), NULL))
  2502. {
  2503. PCWSTR psz = szLower;
  2504. while (*psz && !fHasFilter)
  2505. {
  2506. if (StrCmpI(psz, L"imapi") == 0)
  2507. {
  2508. fHasFilter = TRUE;
  2509. }
  2510. psz += lstrlenW(psz) + 1;
  2511. }
  2512. }
  2513. if (fHasFilter)
  2514. {
  2515. HANDLE hDriver = CreateFile(pdidd->DevicePath, GENERIC_READ | GENERIC_WRITE,
  2516. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  2517. FILE_FLAG_OVERLAPPED, NULL);
  2518. if (INVALID_HANDLE_VALUE != hDriver)
  2519. {
  2520. STORAGE_DEVICE_NUMBER sdn = { 0 };
  2521. if (NO_ERROR == _ExecSyncIoctl(hDriver, IOCTL_STORAGE_GET_DEVICE_NUMBER, &sdn, sizeof(sdn)))
  2522. {
  2523. WCHAR szDevicePath[50];
  2524. wnsprintf(szDevicePath, ARRAYSIZE(szDevicePath), L"\\Device\\CdRom%d", sdn.DeviceNumber);
  2525. fSupported = _DevicePathMatchesVolumeName(szDevicePath, pszVolume);
  2526. }
  2527. CloseHandle(hDriver);
  2528. }
  2529. }
  2530. }
  2531. LocalFree(pdidd);
  2532. }
  2533. }
  2534. }
  2535. SetupDiDestroyDeviceInfoList(hDevInfo);
  2536. }
  2537. return fSupported;
  2538. }
  2539. HRESULT CDBurn_OnDeviceAdded(DWORD dwDriveMask, BOOL fFullRefresh, BOOL fPickNewDrive)
  2540. {
  2541. HRESULT hr = S_OK;
  2542. // only do stuff if the user doesn't have a drive configured already
  2543. TCHAR szDummy[MAX_PATH];
  2544. HRESULT hrCheck = CCDBurn::_GetCurrentBurnVolumeName(szDummy, ARRAYSIZE(szDummy));
  2545. // if hr == E_UNEXPECTED, then there's already a empty string in the registry for the current
  2546. // burn drive. that means the user disabled the functionality so don't put it back.
  2547. if (fFullRefresh || (FAILED(hrCheck) && (hrCheck != E_UNEXPECTED)))
  2548. {
  2549. hr = E_OUTOFMEMORY;
  2550. CCDBurn *pcdb = new CCDBurn();
  2551. if (pcdb)
  2552. {
  2553. // keep track of the fastest drive in the system to default to.
  2554. DWORD dwBestWrite = 0;
  2555. TCHAR szNewBurnVolume[MAX_PATH];
  2556. // only run through this if the device that just got added is a cd.
  2557. // this drive letter stuff is weak but it's what comes from base's notification.
  2558. BOOL fCheck = FALSE;
  2559. for (int i = 0; i < 26; i++)
  2560. {
  2561. TCHAR szDriveLetter[4];
  2562. if (PathBuildRoot(szDriveLetter, i) &&
  2563. (GetDriveType(szDriveLetter) == DRIVE_CDROM))
  2564. {
  2565. TCHAR szVolumeName[MAX_PATH];
  2566. if (SUCCEEDED(CCDBurn::_GetVolumeNameForDriveIndex(i, szVolumeName, ARRAYSIZE(szVolumeName))))
  2567. {
  2568. DWORD dwMaxWrite, dwDriveType;
  2569. // if we already have info on the drive, we don't need to worry about it.
  2570. // if we dont have info, check it.
  2571. // if we're doing a full refresh then always recheck if it wasnt supported before --
  2572. // this could happen if a driver update across a reboot caused a drive to get supported.
  2573. if (FAILED(CCDBurn::_GetCachedDriveInfo(szVolumeName, &dwDriveType, NULL, &dwMaxWrite)) ||
  2574. (fFullRefresh && !SUPPORTED(dwDriveType)))
  2575. {
  2576. if (CCDBurn::_CouldPossiblySupport(szVolumeName))
  2577. {
  2578. // all new cd-rom drives need to be checked with IMAPI.
  2579. fCheck = TRUE;
  2580. }
  2581. // default to unsupported. this is so we don't have to hit IMAPI on
  2582. // the property sheet later.
  2583. CCDBurn::_SetCachedDriveInfo(szVolumeName, DRIVE_NOTSUPPORTED, 0, 0);
  2584. }
  2585. else
  2586. {
  2587. if (SUPPORTED(dwDriveType) && (dwMaxWrite > dwBestWrite))
  2588. {
  2589. // we'll always take the fastest one
  2590. dwBestWrite = dwMaxWrite;
  2591. lstrcpyn(szNewBurnVolume, szVolumeName, ARRAYSIZE(szNewBurnVolume));
  2592. }
  2593. }
  2594. }
  2595. }
  2596. }
  2597. // okay we have to enumerate through IMAPI.
  2598. if (fCheck)
  2599. {
  2600. IDiscMaster *pdm;
  2601. IJolietDiscMaster *pjdm;
  2602. hr = pcdb->_GetDiscMasters(&pdm, &pjdm);
  2603. if (SUCCEEDED(hr))
  2604. {
  2605. IEnumDiscRecorders *penumdr;
  2606. hr = pdm->EnumDiscRecorders(&penumdr);
  2607. if (SUCCEEDED(hr))
  2608. {
  2609. ULONG celt;
  2610. IDiscRecorder *pdr;
  2611. while (S_OK == penumdr->Next(1, &pdr, &celt))
  2612. {
  2613. TCHAR szDevicePath[MAX_PATH];
  2614. if (SUCCEEDED(pcdb->_GetRecorderPath(pdr, szDevicePath, ARRAYSIZE(szDevicePath))))
  2615. {
  2616. DWORD dwMaxWrite, dwDriveType;
  2617. if (SUCCEEDED(pcdb->_GetDiscRecorderInfo(pdr, NULL, &dwMaxWrite, &dwDriveType)))
  2618. {
  2619. TCHAR szVolumeName[MAX_PATH];
  2620. if (SUCCEEDED(pcdb->_GetVolumeNameForDevicePath(szDevicePath, szVolumeName, ARRAYSIZE(szVolumeName))))
  2621. {
  2622. if (dwMaxWrite > dwBestWrite)
  2623. {
  2624. // we'll always take the fastest one
  2625. dwBestWrite = dwMaxWrite;
  2626. lstrcpyn(szNewBurnVolume, szVolumeName, ARRAYSIZE(szNewBurnVolume));
  2627. }
  2628. // default to fastest speed for the drive
  2629. pcdb->_SetCachedDriveInfo(szVolumeName, dwDriveType, WRITESPEED_FASTEST, dwMaxWrite);
  2630. }
  2631. }
  2632. }
  2633. pdr->Release();
  2634. }
  2635. penumdr->Release();
  2636. }
  2637. pdm->Release();
  2638. pjdm->Release();
  2639. }
  2640. }
  2641. if (fPickNewDrive && (dwBestWrite > 0))
  2642. {
  2643. pcdb->_SetCurrentBurnVolumeName(szNewBurnVolume, FALSE);
  2644. }
  2645. pcdb->Release();
  2646. }
  2647. }
  2648. return hr;
  2649. }
  2650. void CCDBurn::_PruneRemovedDevices()
  2651. {
  2652. HKEY hk;
  2653. if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_DRIVES, &hk))
  2654. {
  2655. // since we're deleting as we're moving along, walk backwards.
  2656. DWORD dwLast;
  2657. if (ERROR_SUCCESS == RegQueryInfoKey(hk, NULL, NULL, NULL, &dwLast, NULL, NULL, NULL, NULL, NULL, NULL, NULL))
  2658. {
  2659. TCHAR szVolKey[MAX_PATH];
  2660. for (int i = dwLast - 1; (i >= 0) && ERROR_SUCCESS == RegEnumKey(hk, i, szVolKey, ARRAYSIZE(szVolKey)); i--)
  2661. {
  2662. TCHAR szCachedVol[MAX_PATH];
  2663. lstrcpyn(szCachedVol, L"\\\\?\\", ARRAYSIZE(szCachedVol));
  2664. StrCatBuff(szCachedVol, szVolKey, ARRAYSIZE(szCachedVol));
  2665. StrCatBuff(szCachedVol, L"\\", ARRAYSIZE(szCachedVol));
  2666. int iDrive;
  2667. if (FAILED(CCDBurn::_GetDriveIndexForVolumeName(szCachedVol, &iDrive)))
  2668. {
  2669. // this drive is dead, remove its cached info
  2670. TCHAR szRegPath[MAX_PATH];
  2671. lstrcpyn(szRegPath, REGSTR_PATH_DRIVES, ARRAYSIZE(szRegPath));
  2672. StrCatBuff(szRegPath, L"\\", ARRAYSIZE(szRegPath));
  2673. StrCatBuff(szRegPath, szVolKey, ARRAYSIZE(szRegPath));
  2674. SHDeleteKey(HKEY_CURRENT_USER, szRegPath);
  2675. }
  2676. }
  2677. }
  2678. RegCloseKey(hk);
  2679. }
  2680. }
  2681. HRESULT CDBurn_OnDeviceRemoved(DWORD dwDriveMask)
  2682. {
  2683. HRESULT hr = S_OK;
  2684. // see if the selected drive just got removed.
  2685. TCHAR szVolumeName[MAX_PATH];
  2686. HRESULT hrCheck = CCDBurn::_GetCurrentBurnVolumeName(szVolumeName, ARRAYSIZE(szVolumeName));
  2687. CCDBurn::_PruneRemovedDevices();
  2688. BOOL fFound = FALSE;
  2689. HKEY hk;
  2690. if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_DRIVES, &hk))
  2691. {
  2692. TCHAR szVolKey[MAX_PATH];
  2693. for (int i = 0; ERROR_SUCCESS == RegEnumKey(hk, i, szVolKey, ARRAYSIZE(szVolKey)); i++)
  2694. {
  2695. TCHAR szCachedVol[MAX_PATH];
  2696. lstrcpyn(szCachedVol, L"\\\\?\\", ARRAYSIZE(szCachedVol));
  2697. StrCatBuff(szCachedVol, szVolKey, ARRAYSIZE(szCachedVol));
  2698. StrCatBuff(szCachedVol, L"\\", ARRAYSIZE(szCachedVol));
  2699. int iDrive;
  2700. if (SUCCEEDED(CCDBurn::_GetDriveIndexForVolumeName(szCachedVol, &iDrive)) &&
  2701. SUCCEEDED(hrCheck) && (lstrcmpi(szCachedVol, szVolumeName) == 0))
  2702. {
  2703. // we found the drive that's currently enabled.
  2704. // this means it's still currently mounted.
  2705. // now check if either we dont have a cached index, or if the cached index is still the
  2706. // same as it is now. then we really didn't change.
  2707. DWORD dwIndex, cb = sizeof(dwIndex);
  2708. if ((ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CACHEDINDEX, NULL, &dwIndex, &cb)) ||
  2709. (dwIndex == iDrive))
  2710. {
  2711. fFound = TRUE;
  2712. }
  2713. }
  2714. }
  2715. RegCloseKey(hk);
  2716. }
  2717. if (SUCCEEDED(hrCheck) && !fFound)
  2718. {
  2719. // zonk the currently selected drive, we'll pick a new one from the rest.
  2720. // this is all because the drive letter scheme doesn't let us get back to the volume name
  2721. // since the drive letter is gone at this point.
  2722. CCDBurn::_SetCurrentBurnVolumeName(TEXT(""), TRUE);
  2723. hr = CDBurn_OnDeviceAdded(0, FALSE, TRUE);
  2724. }
  2725. return hr;
  2726. }
  2727. HRESULT CDBurn_OnDeviceChange(BOOL fAdd, LPCWSTR pszDrive)
  2728. {
  2729. int iDriveIndex = PathGetDriveNumber(pszDrive);
  2730. if (fAdd)
  2731. CDBurn_OnDeviceAdded(1 << iDriveIndex, FALSE, TRUE);
  2732. else
  2733. CDBurn_OnDeviceRemoved(1 << iDriveIndex);
  2734. return S_OK;
  2735. }
  2736. BOOL CCDBurn::_HasMedia()
  2737. {
  2738. // unfortunately we have to assume there's media unless we find otherwise.
  2739. BOOL fHasMedia = TRUE;
  2740. CCDBurn *pcdb = new CCDBurn();
  2741. if (pcdb)
  2742. {
  2743. IDiscMaster *pdm;
  2744. IJolietDiscMaster *pjdm;
  2745. if (SUCCEEDED(pcdb->_GetDiscMasters(&pdm, &pjdm)))
  2746. {
  2747. IDiscRecorder *pdr;
  2748. if (SUCCEEDED(pcdb->_FindAndSetDefaultRecorder(pdm, FALSE, &pdr)))
  2749. {
  2750. if (SUCCEEDED(pdr->OpenExclusive()))
  2751. {
  2752. long lMediaType, lMediaFlags;
  2753. if (SUCCEEDED(pdr->QueryMediaType(&lMediaType, &lMediaFlags)))
  2754. {
  2755. if (!lMediaType && !lMediaFlags)
  2756. {
  2757. fHasMedia = FALSE;
  2758. }
  2759. }
  2760. pdr->Close();
  2761. }
  2762. pdr->Release();
  2763. }
  2764. pdm->Release();
  2765. pjdm->Release();
  2766. }
  2767. pcdb->Release();
  2768. }
  2769. return fHasMedia;
  2770. }
  2771. // this is called by mountpoint code BEFORE sending off the SHCNE_MEDIA events
  2772. // checking stuff on the media has to be done synchronously because it uses
  2773. // IMAPI and IMAPI can only have one caller at a time.
  2774. // autorun and things responding to SHCNE_MEDIA may also use IMAPI so we need to
  2775. // get this out of the way ASAP when media is inserted.
  2776. HRESULT CDBurn_OnMediaChange(BOOL fInsert, LPCWSTR pszDrive)
  2777. {
  2778. int iCurrent;
  2779. HRESULT hr = CCDBurn::_GetCurrentDriveIndex(&iCurrent);
  2780. if (SUCCEEDED(hr) && (PathGetDriveNumber(pszDrive) == iCurrent))
  2781. {
  2782. if (fInsert)
  2783. {
  2784. CCDBurn::_HandleBookkeeping();
  2785. }
  2786. else if (!CCDBurn::_HasMedia())
  2787. {
  2788. CDBurn_OnEject(NULL, iCurrent);
  2789. }
  2790. }
  2791. return hr;
  2792. }
  2793. HRESULT CDBurn_OnEject(HWND hwnd, INT iDrive)
  2794. {
  2795. int iCurrent;
  2796. HRESULT hr = CCDBurn::_GetCurrentDriveIndex(&iCurrent);
  2797. if (SUCCEEDED(hr))
  2798. {
  2799. CCDBurn *pcdb = new CCDBurn();
  2800. if (pcdb)
  2801. {
  2802. // we need to know whether to put up the dialog or not based on if the last
  2803. // media was writable. we cant check the media now since in the non-MMC2 case
  2804. // the media is already ejected at this point, so we hit up our cache.
  2805. DWORD dwMediaCap;
  2806. DWORD cb = sizeof(dwMediaCap);
  2807. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_MEDIATYPE, NULL, &dwMediaCap, &cb) &&
  2808. (dwMediaCap & HWDMC_CDRECORDABLE))
  2809. {
  2810. pcdb->_DumpDiscInfo();
  2811. if ((iDrive == iCurrent) && CCDBurn::_StagingAreaHasFiles())
  2812. {
  2813. IPropertyBag *ppb;
  2814. hr = pcdb->_CreateDefaultPropBag(IID_PPV_ARG(IPropertyBag, &ppb));
  2815. if (SUCCEEDED(hr))
  2816. {
  2817. SHPropertyBag_WriteBOOL(ppb, PROPSTR_EJECT, TRUE);
  2818. hr = pcdb->Load(ppb, NULL);
  2819. if (SUCCEEDED(hr))
  2820. {
  2821. CMINVOKECOMMANDINFO cmi = { 0 };
  2822. hr = pcdb->_PrepWiz(&cmi, FALSE, TRUE);
  2823. }
  2824. ppb->Release();
  2825. }
  2826. }
  2827. }
  2828. else
  2829. {
  2830. pcdb->_DumpDiscInfo();
  2831. hr = S_OK;
  2832. }
  2833. pcdb->Release();
  2834. }
  2835. else
  2836. {
  2837. hr = E_OUTOFMEMORY;
  2838. }
  2839. }
  2840. return hr;
  2841. }
  2842. // IOleCommandTarget implementation
  2843. HRESULT CCDBurn::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText)
  2844. {
  2845. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  2846. if (*pguidCmdGroup == CGID_ShellServiceObject)
  2847. {
  2848. // We like Shell Service Object notifications...
  2849. hr = S_OK;
  2850. }
  2851. return hr;
  2852. }
  2853. DWORD WINAPI CCDBurn::_ExecThread(void *pv)
  2854. {
  2855. CCDBurn *pcdb = (CCDBurn *)pv;
  2856. // just in case explorer AV'd while holding the lock, unlock the next time we come up.
  2857. // there's no way to guard against this.
  2858. pcdb->_LockCurrentDrive(FALSE, TRUE);
  2859. // hardware may have been added while we were powered down, and it won't generate
  2860. // a notification event so we have to assume that anything could have changed
  2861. // (inf registration for existing drives too, usually ones that require reboot to update).
  2862. pcdb->_PruneRemovedDevices();
  2863. // save off the currently selected drive and its write speed
  2864. TCHAR szCurrent[MAX_PATH];
  2865. DWORD dwCurWrite;
  2866. HRESULT hrName = pcdb->_GetCurrentBurnVolumeName(szCurrent, ARRAYSIZE(szCurrent));
  2867. if (SUCCEEDED(hrName))
  2868. {
  2869. DWORD dwType;
  2870. hrName = pcdb->_GetCachedDriveInfo(szCurrent, &dwType, &dwCurWrite, NULL);
  2871. // if the drive has become unsupported, we need to drop our currently selected
  2872. // drive.
  2873. if (SUCCEEDED(hrName) && !SUPPORTED(dwType))
  2874. {
  2875. hrName = E_FAIL;
  2876. }
  2877. }
  2878. BOOL fSet = SUCCEEDED(hrName);
  2879. // if it's E_UNEXPECTED then the user has disabled the burning feature completely.
  2880. BOOL fDisabled = (hrName == E_UNEXPECTED);
  2881. // readd devices, syncro
  2882. CDBurn_OnDeviceAdded(0xFFFFFFFF, TRUE, !fSet);
  2883. DWORD dwDriveType;
  2884. if (fDisabled)
  2885. {
  2886. // if we're disabled, stay disabled
  2887. pcdb->_SetCurrentBurnVolumeName(L"", FALSE);
  2888. }
  2889. else if (fSet && SUCCEEDED(pcdb->_GetCachedDriveInfo(szCurrent, &dwDriveType, NULL, NULL)) && SUPPORTED(dwDriveType))
  2890. {
  2891. // if the device we were using before is still supported, use the previous write speed
  2892. pcdb->_SetCachedDriveInfo(szCurrent, DRIVE_USEEXISTING, dwCurWrite, 0);
  2893. }
  2894. if (pcdb->_BurningIsEnabled())
  2895. {
  2896. pcdb->_HandleBookkeeping();
  2897. int iDrive;
  2898. if (SUCCEEDED(pcdb->_GetCurrentDriveIndex(&iDrive)))
  2899. {
  2900. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CACHEDINDEX, REG_DWORD, &iDrive, sizeof(iDrive));
  2901. }
  2902. else
  2903. {
  2904. SHDeleteValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CACHEDINDEX);
  2905. }
  2906. }
  2907. pcdb->Release();
  2908. return 0;
  2909. }
  2910. HRESULT CCDBurn::Exec(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
  2911. {
  2912. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  2913. if (*pguidCmdGroup == CGID_ShellServiceObject)
  2914. {
  2915. hr = S_OK; // Any ol' notification is ok with us
  2916. // Handle Shell Service Object notifications here.
  2917. switch (nCmdID)
  2918. {
  2919. case SSOCMDID_OPEN:
  2920. // we're on the tray's thread as a service object so lets create our own thread.
  2921. // this is fine since our thread is bound by IOCTLs to the CD-R etc.
  2922. AddRef();
  2923. if (SHCreateThread(_ExecThread, this, CTF_COINIT, NULL))
  2924. break;
  2925. Release();
  2926. break;
  2927. default:
  2928. break;
  2929. }
  2930. }
  2931. return hr;
  2932. }
  2933. HRESULT CCDBurn::GetRecorderDriveLetter(LPWSTR pszDrive, UINT cch)
  2934. {
  2935. if (cch < 4)
  2936. return E_INVALIDARG;
  2937. int iDrive;
  2938. HRESULT hr = _GetCurrentDriveIndex(&iDrive);
  2939. if (SUCCEEDED(hr))
  2940. {
  2941. // pathbuildroot assumes cch >= 4
  2942. hr = PathBuildRoot(pszDrive, iDrive) ? S_OK : E_FAIL;
  2943. }
  2944. return hr;
  2945. }
  2946. // helper for lazy callers within shell32
  2947. STDAPI CDBurn_GetRecorderDriveLetter(LPWSTR pszDrive, UINT cch)
  2948. {
  2949. ICDBurn *pcdb;
  2950. HRESULT hr = CoCreateInstance(CLSID_CDBurn, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ICDBurn, &pcdb));
  2951. if (SUCCEEDED(hr))
  2952. {
  2953. hr = pcdb->GetRecorderDriveLetter(pszDrive, cch);
  2954. pcdb->Release();
  2955. }
  2956. return hr;
  2957. }
  2958. HRESULT CCDBurn::Burn(HWND hwnd)
  2959. {
  2960. HRESULT hr = InitNew();
  2961. if (SUCCEEDED(hr))
  2962. {
  2963. // do this synchronously, callers will handle putting it on a separate thread.
  2964. // this is easier than making a callback to tell them when its done.
  2965. hr = _WizardThreadProc();
  2966. }
  2967. return hr;
  2968. }
  2969. HRESULT CCDBurn::HasRecordableDrive(BOOL *pfHasRecorder)
  2970. {
  2971. *pfHasRecorder = FALSE;
  2972. HKEY hk;
  2973. if (ERROR_SUCCESS == RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_DRIVES, &hk))
  2974. {
  2975. TCHAR szVolKey[MAX_PATH];
  2976. for (int i = 0; !*pfHasRecorder && (ERROR_SUCCESS == RegEnumKey(hk, i, szVolKey, ARRAYSIZE(szVolKey))); i++)
  2977. {
  2978. TCHAR szRegPath[MAX_PATH];
  2979. lstrcpyn(szRegPath, REGSTR_PATH_DRIVES, ARRAYSIZE(szRegPath));
  2980. StrCatBuff(szRegPath, L"\\", ARRAYSIZE(szRegPath));
  2981. StrCatBuff(szRegPath, szVolKey, ARRAYSIZE(szRegPath));
  2982. DWORD dwType;
  2983. ULONG cb = sizeof(dwType);
  2984. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, szRegPath, REGVALUE_DRIVETYPE, NULL, &dwType, &cb))
  2985. {
  2986. *pfHasRecorder = SUPPORTED(dwType);
  2987. }
  2988. }
  2989. RegCloseKey(hk);
  2990. }
  2991. return S_OK;
  2992. }
  2993. HRESULT CCDBurn::_DumpDiscInfo()
  2994. {
  2995. SHDeleteKey(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA);
  2996. return S_OK;
  2997. }
  2998. HRESULT CCDBurn::_GetDiscInfoUsingIMAPI(IJolietDiscMaster *pjdm, IDiscRecorder *pdr, ULONGLONG *pcbFree)
  2999. {
  3000. HRESULT hr = E_FAIL;
  3001. BYTE bSessions, bLastTrack;
  3002. LONG nBlockBytes;
  3003. ULONG ulStartAddress, ulNextWritable, ulFreeBlocks;
  3004. if (SUCCEEDED(pjdm->GetDataBlockSize(&nBlockBytes)) &&
  3005. SUCCEEDED(pdr->QueryMediaInfo(&bSessions, &bLastTrack, &ulStartAddress, &ulNextWritable, &ulFreeBlocks)))
  3006. {
  3007. hr = S_OK;
  3008. *pcbFree = (ULONGLONG) ulFreeBlocks * nBlockBytes;
  3009. }
  3010. return hr;
  3011. }
  3012. HRESULT CCDBurn::_GetDiscInfoUsingFilesystem(ULONGLONG *pcbTotal, ULONGLONG *pcbFree, BOOL *pfUDF)
  3013. {
  3014. int iCurrent;
  3015. HRESULT hr = _GetCurrentDriveIndex(&iCurrent);
  3016. if (SUCCEEDED(hr))
  3017. {
  3018. *pcbTotal = 0;
  3019. *pcbFree = 0;
  3020. ULARGE_INTEGER ulTotal, ulFree;
  3021. TCHAR szRoot[4];
  3022. if (PathBuildRoot(szRoot, iCurrent) && SHGetDiskFreeSpace(szRoot, &ulFree, &ulTotal, NULL))
  3023. {
  3024. *pcbTotal = ulTotal.QuadPart;
  3025. *pcbFree = ulFree.QuadPart;
  3026. }
  3027. *pfUDF = FALSE;
  3028. TCHAR szFilesystem[30];
  3029. if (GetVolumeInformation(szRoot, NULL, 0, NULL, NULL, NULL, szFilesystem, ARRAYSIZE(szFilesystem)))
  3030. {
  3031. HUSKEY huskeyExclude;
  3032. if (ERROR_SUCCESS == SHRegOpenUSKey(REGSTR_PATH_EXCLUDE, KEY_READ, NULL, &huskeyExclude, FALSE))
  3033. {
  3034. DWORD dwIndex = 0;
  3035. TCHAR szKey[30];
  3036. DWORD cchKey = ARRAYSIZE(szKey);
  3037. while (!*pfUDF && (ERROR_SUCCESS == SHRegEnumUSValue(huskeyExclude, dwIndex, szKey, &cchKey, NULL, NULL, NULL, SHREGENUM_DEFAULT)))
  3038. {
  3039. *pfUDF = (lstrcmpi(szFilesystem, szKey) == 0);
  3040. dwIndex++;
  3041. cchKey = ARRAYSIZE(szKey);
  3042. }
  3043. SHRegCloseUSKey(huskeyExclude);
  3044. }
  3045. }
  3046. }
  3047. return hr;
  3048. }
  3049. HRESULT CDBurn_GetCDInfo(LPCTSTR pszVolume, DWORD *pdwDriveCapabilities, DWORD *pdwMediaCapabilities)
  3050. {
  3051. *pdwDriveCapabilities = 0;
  3052. *pdwMediaCapabilities = 0;
  3053. DWORD dwDriveType;
  3054. HRESULT hr = CCDBurn::_GetCachedDriveInfo(pszVolume, &dwDriveType, NULL, NULL);
  3055. if (SUCCEEDED(hr))
  3056. {
  3057. switch (dwDriveType)
  3058. {
  3059. case RECORDER_CDR:
  3060. *pdwDriveCapabilities = HWDDC_CDROM | HWDDC_CDRECORDABLE;
  3061. break;
  3062. case RECORDER_CDRW:
  3063. *pdwDriveCapabilities = HWDDC_CDROM | HWDDC_CDRECORDABLE | HWDDC_CDREWRITABLE;
  3064. break;
  3065. default:
  3066. *pdwDriveCapabilities = HWDDC_CDROM;
  3067. break;
  3068. }
  3069. DWORD dwMediaCap;
  3070. DWORD cb = sizeof(dwMediaCap);
  3071. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_MEDIATYPE, NULL, &dwMediaCap, &cb))
  3072. {
  3073. *pdwMediaCapabilities = dwMediaCap;
  3074. }
  3075. }
  3076. return hr;
  3077. }
  3078. void CDBurn_GetUDFState(BOOL *pfUDF)
  3079. {
  3080. *pfUDF = FALSE;
  3081. DWORD dw, cb = sizeof(dw);
  3082. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_UDF, NULL, &dw, &cb))
  3083. {
  3084. *pfUDF = dw;
  3085. }
  3086. }
  3087. HRESULT CCDBurn::_GetMediaCapabilities(DWORD *pdwCap, BOOL *pfUDF)
  3088. {
  3089. HRESULT hr = S_OK;
  3090. if (pfUDF)
  3091. {
  3092. CDBurn_GetUDFState(pfUDF);
  3093. }
  3094. if (pdwCap)
  3095. {
  3096. *pdwCap = 0;
  3097. TCHAR szVolumeName[MAX_PATH];
  3098. hr = CCDBurn::_GetCurrentBurnVolumeName(szVolumeName, ARRAYSIZE(szVolumeName));
  3099. if (SUCCEEDED(hr))
  3100. {
  3101. DWORD dwDriveCap;
  3102. CDBurn_GetCDInfo(szVolumeName, &dwDriveCap, pdwCap);
  3103. }
  3104. }
  3105. return hr;
  3106. }
  3107. HRESULT CCDBurn::GetMediaCapabilities(DWORD *pdwCap, BOOL *pfUDF)
  3108. {
  3109. return _GetMediaCapabilities(pdwCap, pfUDF);
  3110. }
  3111. HRESULT CCDBurn::_StoreDiscInfo()
  3112. {
  3113. HRESULT hr = E_FAIL;
  3114. BOOL fDone = FALSE;
  3115. int iRetryCount = 0;
  3116. // retry up to a max of 5 times.
  3117. // we can get into a state where we failed to pick up the disc information, if IMAPI
  3118. // is around and in use by somebody else. in that case just wait and loop around until we can
  3119. // get it.
  3120. while (!fDone && (iRetryCount < 5))
  3121. {
  3122. iRetryCount++;
  3123. // these are the values we're going to pick up.
  3124. ULONGLONG cbTotal, cbFree;
  3125. DWORD dwMediaCaps;
  3126. BOOL fUDF;
  3127. hr = E_OUTOFMEMORY;
  3128. CCDBurn *pcdb = new CCDBurn();
  3129. if (pcdb)
  3130. {
  3131. IDiscMaster *pdm;
  3132. IJolietDiscMaster *pjdm;
  3133. hr = pcdb->_GetDiscMasters(&pdm, &pjdm);
  3134. if (SUCCEEDED(hr))
  3135. {
  3136. IDiscRecorder *pdr;
  3137. // dont call SetActiveDiscRecorder unless absolutely necessary -- it returns random errors
  3138. // if there's UDF media.
  3139. hr = pcdb->_FindAndSetDefaultRecorder(pdm, FALSE, &pdr);
  3140. if (SUCCEEDED(hr))
  3141. {
  3142. hr = pdr->OpenExclusive();
  3143. if (SUCCEEDED(hr))
  3144. {
  3145. // don't use the mountpoint layer to determine capabilities.
  3146. dwMediaCaps = HWDMC_CDROM;
  3147. long lMediaType, lMediaFlags;
  3148. hr = pdr->QueryMediaType(&lMediaType, &lMediaFlags);
  3149. if (SUCCEEDED(hr) && !lMediaType && !lMediaFlags)
  3150. {
  3151. hr = E_FAIL;
  3152. }
  3153. if (SUCCEEDED(hr))
  3154. {
  3155. if (lMediaFlags & MEDIA_WRITABLE)
  3156. {
  3157. dwMediaCaps |= HWDMC_CDRECORDABLE;
  3158. }
  3159. if (lMediaFlags & MEDIA_RW)
  3160. {
  3161. dwMediaCaps |= HWDMC_CDREWRITABLE;
  3162. }
  3163. hr = _GetDiscInfoUsingFilesystem(&cbTotal, &cbFree, &fUDF);
  3164. // also adjust the fUDF bit to include unusable media.
  3165. if (lMediaFlags & MEDIA_FORMAT_UNUSABLE_BY_IMAPI)
  3166. {
  3167. fUDF = TRUE;
  3168. }
  3169. if (SUCCEEDED(hr) && (dwMediaCaps & (HWDMC_CDRECORDABLE | HWDMC_CDREWRITABLE)) && !fUDF)
  3170. {
  3171. ULONGLONG cbFreeIMAPI;
  3172. hr = _GetDiscInfoUsingIMAPI(pjdm, pdr, &cbFreeIMAPI);
  3173. if (SUCCEEDED(hr))
  3174. {
  3175. cbTotal += cbFreeIMAPI;
  3176. cbFree += cbFreeIMAPI;
  3177. }
  3178. }
  3179. }
  3180. pdr->Close();
  3181. }
  3182. // pick up the write speed, it might have changed. don't worry about
  3183. // failure since this is a bonus perk anyway.
  3184. DWORD dwMaxWrite;
  3185. TCHAR szDevicePath[MAX_PATH], szVolumeName[MAX_PATH];
  3186. if (SUCCEEDED(_GetDiscRecorderInfo(pdr, NULL, &dwMaxWrite, NULL)) &&
  3187. SUCCEEDED(_GetRecorderPath(pdr, szDevicePath, ARRAYSIZE(szDevicePath))) &&
  3188. SUCCEEDED(_GetVolumeNameForDevicePath(szDevicePath, szVolumeName, ARRAYSIZE(szVolumeName))))
  3189. {
  3190. // update max speed
  3191. _SetCachedDriveInfo(szVolumeName, DRIVE_USEEXISTING, 0, dwMaxWrite);
  3192. }
  3193. pdr->Release();
  3194. }
  3195. pdm->Release();
  3196. pjdm->Release();
  3197. }
  3198. pcdb->Release();
  3199. }
  3200. if (SUCCEEDED(hr))
  3201. {
  3202. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_TOTALBYTES, REG_BINARY, &cbTotal, sizeof(cbTotal));
  3203. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_FREEBYTES, REG_BINARY, &cbFree, sizeof(cbFree));
  3204. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_MEDIATYPE, REG_DWORD, &dwMediaCaps, sizeof(dwMediaCaps));
  3205. DWORD dw = fUDF;
  3206. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_UDF, REG_DWORD, &dw, sizeof(dw));
  3207. int iCurrent;
  3208. if (SUCCEEDED(_GetCurrentDriveIndex(&iCurrent)))
  3209. {
  3210. TCHAR szRoot[4];
  3211. TCHAR szVolName[MAX_PATH];
  3212. if (PathBuildRoot(szRoot, iCurrent) &&
  3213. GetVolumeInformation(szRoot, szVolName, ARRAYSIZE(szVolName), NULL, NULL, NULL, NULL, 0))
  3214. {
  3215. UINT cb = (lstrlen(szVolName) + 1) * sizeof(TCHAR);
  3216. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_DISCLABEL, REG_SZ, szVolName, cb);
  3217. }
  3218. }
  3219. DWORD dwSet = 1;
  3220. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_SET, REG_DWORD, &dwSet, sizeof(dwSet));
  3221. LPITEMIDLIST pidl;
  3222. if (SUCCEEDED(_GetFolderPidl(&pidl)))
  3223. {
  3224. SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_IDLIST, pidl, NULL);
  3225. ILFree(pidl);
  3226. }
  3227. fDone = TRUE;
  3228. }
  3229. else if ((IMAPI_E_STASHINUSE == hr) || // IMAPI is being used by somebody else.
  3230. (IMAPI_E_DEVICE_NOTACCESSIBLE == hr)) // CD has not spun up.
  3231. {
  3232. // wait and try again.
  3233. SHProcessMessagesUntilEvent(NULL, NULL, 5 * 1000); // 5 seconds
  3234. }
  3235. else
  3236. {
  3237. // there was some other error, bail out anyway.
  3238. fDone = TRUE;
  3239. }
  3240. }
  3241. return hr;
  3242. }
  3243. HRESULT CCDBurn::GetSpace(ULONGLONG *pcbTotal, ULONGLONG *pcbFree)
  3244. {
  3245. HRESULT hr = E_FAIL;
  3246. ULONGLONG cbTotal, cbFree;
  3247. DWORD cbReg1 = sizeof(cbTotal), cbReg2 = sizeof(cbFree);
  3248. if ((ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_TOTALBYTES, NULL, &cbTotal, &cbReg1)) &&
  3249. (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_FREEBYTES, NULL, &cbFree, &cbReg2)))
  3250. {
  3251. if (pcbTotal)
  3252. *pcbTotal = cbTotal;
  3253. if (pcbFree)
  3254. *pcbFree = cbFree;
  3255. hr = S_OK;
  3256. }
  3257. return hr;
  3258. }
  3259. HRESULT CCDBurn::InitNew()
  3260. {
  3261. IPropertyBag *ppb;
  3262. // load up the default property bag for peruser perfolder
  3263. // may have problems down the line with thumbs.db being alluser.
  3264. HRESULT hr = _CreateDefaultPropBag(IID_PPV_ARG(IPropertyBag, &ppb));
  3265. if (SUCCEEDED(hr))
  3266. {
  3267. IUnknown_Set((IUnknown**)&_ppb, ppb);
  3268. ppb->Release();
  3269. }
  3270. return hr;
  3271. }
  3272. HRESULT CCDBurn::Load(IPropertyBag *ppb, IErrorLog *pErr)
  3273. {
  3274. IUnknown_Set((IUnknown**)&_ppb, ppb);
  3275. return S_OK;
  3276. }
  3277. STDAPI SHCreateQueryCancelAutoPlayMoniker(IMoniker** ppmoniker); // mtptarun2.cpp
  3278. void CCDBurn::_RegisterAutoplayCanceller()
  3279. {
  3280. IMoniker *pmoniker;
  3281. if (SUCCEEDED(SHCreateQueryCancelAutoPlayMoniker(&pmoniker)))
  3282. {
  3283. IRunningObjectTable *prot;
  3284. if (SUCCEEDED(GetRunningObjectTable(0, &prot)))
  3285. {
  3286. IUnknown *punkThis;
  3287. if (SUCCEEDED(QueryInterface(IID_PPV_ARG(IUnknown, &punkThis))))
  3288. {
  3289. prot->Register(ROTFLAGS_REGISTRATIONKEEPSALIVE, punkThis, pmoniker, &_dwROTRegister);
  3290. punkThis->Release();
  3291. }
  3292. prot->Release();
  3293. }
  3294. pmoniker->Release();
  3295. }
  3296. }
  3297. void CCDBurn::_UnregisterAutoplayCanceller()
  3298. {
  3299. if (_dwROTRegister)
  3300. {
  3301. IRunningObjectTable *prot;
  3302. if (SUCCEEDED(GetRunningObjectTable(0, &prot)))
  3303. {
  3304. prot->Revoke(_dwROTRegister);
  3305. _dwROTRegister = 0;
  3306. prot->Release();
  3307. }
  3308. }
  3309. }
  3310. HRESULT CCDBurn::_WizardThreadProc()
  3311. {
  3312. HRESULT hr = E_FAIL;
  3313. BOOL fWizardShown = FALSE;
  3314. // the burningnow check does an additional call into IMAPI -- this may be slow so do it while
  3315. // we're on the background thread instead of the duiview one.
  3316. if (!_IsBurningNow())
  3317. {
  3318. if (_EnterExclusiveBurning())
  3319. {
  3320. _RegisterAutoplayCanceller();
  3321. hr = _ShowWizard();
  3322. if (SUCCEEDED(_GetBurnHR()) && SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_AUTOCLOSE, FALSE))
  3323. {
  3324. // if we auto-closed the wizard, leave this thread going for a few more seconds
  3325. // to cancel any spurious autoplays that come in.
  3326. SHProcessMessagesUntilEvent(NULL, NULL, 3 * 1000);
  3327. }
  3328. _UnregisterAutoplayCanceller();
  3329. fWizardShown = TRUE;
  3330. _LeaveExclusiveBurning();
  3331. }
  3332. }
  3333. if (!fWizardShown && !SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_FAILSILENTLY, FALSE))
  3334. {
  3335. // put up UI so we dont silently fail.
  3336. ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE(IDS_BURN_CANTBURN), MAKEINTRESOURCE(IDS_BURN),
  3337. MB_OK | MB_ICONEXCLAMATION);
  3338. }
  3339. return hr;
  3340. }
  3341. DWORD WINAPI CCDBurn::_WizardThreadWrapper(void *pv)
  3342. {
  3343. CCDBurn *pcdb = (CCDBurn*)pv;
  3344. pcdb->_WizardThreadProc();
  3345. pcdb->Release();
  3346. return 0;
  3347. }
  3348. HRESULT CCDBurn::_ShowWizardOnSeparateThread()
  3349. {
  3350. HRESULT hr = S_OK;
  3351. AddRef();
  3352. if (!SHCreateThread(_WizardThreadWrapper, this, CTF_COINIT, NULL))
  3353. {
  3354. Release();
  3355. hr = E_FAIL;
  3356. }
  3357. return hr;
  3358. }
  3359. // match this with below
  3360. #define INDEX_DLG_BURNWIZ_WELCOME 0
  3361. #define INDEX_DLG_BURNWIZ_EJECT 1
  3362. #define INDEX_DLG_BURNWIZ_BURN_PROGRESS 2
  3363. #define INDEX_DLG_BURNWIZ_BURN_SUCCESS 3
  3364. #define INDEX_DLG_BURNWIZ_BURN_FAILURE 4
  3365. #define INDEX_DLG_BURNWIZ_WAITFORMEDIA 5
  3366. #define INDEX_DLG_BURNWIZ_STARTERASE 6
  3367. #define INDEX_DLG_BURNWIZ_ERASE_PROGRESS 7
  3368. #define INDEX_DLG_BURNWIZ_ERASE_SUCCESS 8
  3369. #define INDEX_DLG_BURNWIZ_ERASE_FAILURE 9
  3370. #define INDEX_DLG_BURNWIZ_DISCFULL 10
  3371. #define INDEX_DLG_BURNWIZ_EARLYEXIT 11
  3372. #define INDEX_DLG_BURNWIZ_HDFULL 12
  3373. #define INDEX_DLG_BURNWIZ_NOFILES 13
  3374. const WIZPAGE c_wpPages[] =
  3375. {
  3376. {DLG_BURNWIZ_WELCOME, 0, 0, PSP_HIDEHEADER, CCDBurn::s_WelcomeDlgProc},
  3377. {DLG_BURNWIZ_EJECT, 0, 0, PSP_HIDEHEADER, CCDBurn::s_EjectDlgProc},
  3378. {DLG_BURNWIZ_PROGRESS, IDS_BURNWIZ_PROGRESS_BURN_HEAD, IDS_BURNWIZ_PROGRESS_BURN_SUB, 0, CCDBurn::s_ProgressDlgProc},
  3379. {DLG_BURNWIZ_BURN_SUCCESS, 0, 0, PSP_HIDEHEADER, CCDBurn::s_DoneDlgProc},
  3380. {DLG_BURNWIZ_BURN_FAILURE, 0, 0, PSP_HIDEHEADER, CCDBurn::s_DoneDlgProc},
  3381. {DLG_BURNWIZ_WAITFORMEDIA, IDS_BURNWIZ_WAIT_HEAD, IDS_BURNWIZ_WAIT_SUB, 0, CCDBurn::s_WaitForMediaDlgProc},
  3382. {DLG_BURNWIZ_STARTERASE, 0, 0, PSP_HIDEHEADER, CCDBurn::s_StartEraseDlgProc},
  3383. {DLG_BURNWIZ_PROGRESS, IDS_BURNWIZ_PROGRESS_ERASE_HEAD, IDS_BURNWIZ_PROGRESS_ERASE_SUB, 0, CCDBurn::s_ProgressDlgProc},
  3384. {DLG_BURNWIZ_ERASE_SUCCESS, 0, 0, PSP_HIDEHEADER, CCDBurn::s_DoneDlgProc},
  3385. {DLG_BURNWIZ_ERASE_FAILURE, 0, 0, PSP_HIDEHEADER, CCDBurn::s_DoneDlgProc},
  3386. {DLG_BURNWIZ_DISCFULL, 0, 0, PSP_HIDEHEADER, CCDBurn::s_DiskFullDlgProc},
  3387. {DLG_BURNWIZ_PROGRESS, 0, 0, PSP_HIDEHEADER, CCDBurn::s_EarlyExitDlgProc},
  3388. {DLG_BURNWIZ_HDFULL, 0, 0, PSP_HIDEHEADER, CCDBurn::s_HDFullDlgProc},
  3389. {DLG_BURNWIZ_NOFILES, 0, 0, PSP_HIDEHEADER, CCDBurn::s_NoFilesDlgProc},
  3390. };
  3391. HPROPSHEETPAGE _CreatePropPageFromInfo(const WIZPAGE *pwp, LPARAM lParam)
  3392. {
  3393. PROPSHEETPAGE psp = { 0 };
  3394. psp.dwSize = sizeof(psp);
  3395. psp.hInstance = HINST_THISDLL;
  3396. psp.lParam = lParam;
  3397. psp.dwFlags = PSP_USETITLE | PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE | pwp->dwFlags;
  3398. psp.pszTemplate = MAKEINTRESOURCE(pwp->idPage);
  3399. psp.pfnDlgProc = pwp->dlgproc;
  3400. psp.pszTitle = MAKEINTRESOURCE(IDS_BURN_WIZTITLE);
  3401. psp.pszHeaderTitle = MAKEINTRESOURCE(pwp->idHeading);
  3402. psp.pszHeaderSubTitle = MAKEINTRESOURCE(pwp->idSubHeading);
  3403. return CreatePropertySheetPage(&psp);
  3404. }
  3405. int ReleaseCallback(void *pv, void *)
  3406. {
  3407. IWizardExtension *pwe = (IWizardExtension*)pv;
  3408. IUnknown_SetSite(pwe, NULL);
  3409. pwe->Release();
  3410. return 1;
  3411. }
  3412. HRESULT CCDBurn::_ShowWizard()
  3413. {
  3414. LinkWindow_RegisterClass();
  3415. _fCancelled = FALSE;
  3416. // create the page array
  3417. int nStartPage;
  3418. if (SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_EJECT, FALSE))
  3419. {
  3420. // we want the eject page
  3421. nStartPage = INDEX_DLG_BURNWIZ_EJECT;
  3422. }
  3423. else
  3424. {
  3425. // normal entry point
  3426. if (SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_ERASE, FALSE))
  3427. {
  3428. nStartPage = INDEX_DLG_BURNWIZ_STARTERASE;
  3429. }
  3430. else
  3431. {
  3432. nStartPage = _StagingAreaHasFiles() ? INDEX_DLG_BURNWIZ_WELCOME : INDEX_DLG_BURNWIZ_NOFILES;
  3433. }
  3434. }
  3435. // wtf? if i dont include a propsheet without PSP_HIDEHEADER the sizing gets messed up.
  3436. for (int i = 0; i < ARRAYSIZE(c_wpPages); i++)
  3437. {
  3438. _rgWizPages[i] = _CreatePropPageFromInfo(&c_wpPages[i], (LPARAM)this);
  3439. }
  3440. UINT cTotalExtPages;
  3441. HRESULT hr = _FillExtensionDPA(_rgWizPages + ARRAYSIZE(c_wpPages), ARRAYSIZE(_rgWizPages) - ARRAYSIZE(c_wpPages), &cTotalExtPages);
  3442. if (SUCCEEDED(hr))
  3443. {
  3444. PROPSHEETHEADER psh = { 0 };
  3445. psh.dwSize = sizeof(psh);
  3446. psh.hInstance = HINST_THISDLL;
  3447. psh.dwFlags = PSH_WIZARD | PSH_WIZARD97 | PSH_WATERMARK | PSH_STRETCHWATERMARK | PSH_HEADER | PSH_USEICONID;
  3448. psh.pszbmHeader = MAKEINTRESOURCE(IDB_BURNWIZ_HEADER);
  3449. psh.pszbmWatermark = MAKEINTRESOURCE(IDB_BURNWIZ_WATERMARK);
  3450. psh.pszIcon = MAKEINTRESOURCE(IDI_DRIVECD);
  3451. psh.phpage = _rgWizPages;
  3452. psh.nPages = ARRAYSIZE(c_wpPages) + cTotalExtPages;
  3453. psh.nStartPage = nStartPage;
  3454. PropertySheet(&psh);
  3455. HRESULT hrOp = E_FAIL;
  3456. DWORD dwHR;
  3457. if (SUCCEEDED(SHPropertyBag_ReadDWORD(_ppb, PROPSTR_HR, &dwHR)))
  3458. {
  3459. hrOp = dwHR;
  3460. }
  3461. hr = SUCCEEDED(hrOp) ? S_OK : S_FALSE;
  3462. }
  3463. if (_hdpaExts)
  3464. {
  3465. DPA_DestroyCallback(_hdpaExts, ReleaseCallback, 0);
  3466. }
  3467. s_hwndWiz = NULL;
  3468. // the drive could have been locked after the wait for media page.
  3469. _LockCurrentDrive(FALSE);
  3470. return hr;
  3471. }
  3472. CCDBurn* CCDBurn::s_GetCDBurn(HWND hwnd, UINT uMsg, LPARAM lParam)
  3473. {
  3474. if (uMsg == WM_INITDIALOG)
  3475. {
  3476. PROPSHEETPAGE *ppsp = (PROPSHEETPAGE*)lParam;
  3477. SetWindowLongPtr(hwnd, GWLP_USERDATA, ppsp->lParam);
  3478. return (CCDBurn*)ppsp->lParam;
  3479. }
  3480. return (CCDBurn*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  3481. }
  3482. HFONT GetIntroFont(HWND hwnd)
  3483. {
  3484. static HFONT _hfontIntro = NULL;
  3485. if (!_hfontIntro)
  3486. {
  3487. TCHAR szBuffer[64];
  3488. NONCLIENTMETRICS ncm = { 0 };
  3489. LOGFONT lf;
  3490. ncm.cbSize = sizeof(ncm);
  3491. SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0);
  3492. lf = ncm.lfMessageFont;
  3493. LoadString(g_hinst, IDS_BURNWIZ_TITLEFONTNAME, lf.lfFaceName, ARRAYSIZE(lf.lfFaceName));
  3494. lf.lfWeight = FW_BOLD;
  3495. LoadString(g_hinst, IDS_BURNWIZ_TITLEFONTSIZE, szBuffer, ARRAYSIZE(szBuffer));
  3496. lf.lfHeight = 0 - (GetDeviceCaps(NULL, LOGPIXELSY) * StrToInt(szBuffer) / 72);
  3497. _hfontIntro = CreateFontIndirect(&lf);
  3498. }
  3499. return _hfontIntro;
  3500. }
  3501. void CCDBurn::_SetNextPage(HWND hwnd, int iIndex)
  3502. {
  3503. PropSheet_SetCurSel(GetParent(hwnd), _rgWizPages[iIndex], -1);
  3504. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
  3505. }
  3506. void CCDBurn::_SetUpStartPage(HWND hwnd)
  3507. {
  3508. TCHAR szDiscLabel[JOLIET_MAX_LABEL + 1];
  3509. szDiscLabel[0] = 0;
  3510. SHPropertyBag_ReadStr(_ppb, PROPSTR_DISCLABEL, szDiscLabel, ARRAYSIZE(szDiscLabel));
  3511. SetDlgItemText(hwnd, IDC_BURNWIZ_DISCLABEL, szDiscLabel);
  3512. Edit_LimitText(GetDlgItem(hwnd, IDC_BURNWIZ_DISCLABEL), JOLIET_MAX_LABEL);
  3513. SHLimitInputEditChars(GetDlgItem(hwnd, IDC_BURNWIZ_DISCLABEL), NULL, INVALID_JOLIETNAME_CHARS);
  3514. BOOL fClose = SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_AUTOCLOSE, FALSE);
  3515. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_AUTOCLOSEWIZ), fClose ? BST_CHECKED : BST_UNCHECKED);
  3516. }
  3517. void CCDBurn::_LeaveStartPage(HWND hwnd)
  3518. {
  3519. BOOL fClose = (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_AUTOCLOSEWIZ) == BST_CHECKED);
  3520. SHPropertyBag_WriteBOOL(_ppb, PROPSTR_AUTOCLOSE, fClose);
  3521. DWORD dwClose = fClose;
  3522. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_AUTOCLOSE, REG_DWORD, &dwClose, sizeof(dwClose));
  3523. }
  3524. #define CDBURNWM_SHOWSHUTDOWNMESSAGE WM_APP
  3525. LRESULT CALLBACK CCDBurn::_WizSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uID, DWORD_PTR dwRefData)
  3526. {
  3527. LRESULT lres;
  3528. switch (uMsg)
  3529. {
  3530. case WM_NCDESTROY:
  3531. // Clean up subclass
  3532. RemoveWindowSubclass(hwnd, _WizSubclassProc, 0);
  3533. lres = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  3534. break;
  3535. case WM_QUERYENDSESSION:
  3536. lres = TRUE;
  3537. if (s_fDriveInUse)
  3538. {
  3539. // post so we return to user quickly
  3540. PostMessage(hwnd, CDBURNWM_SHOWSHUTDOWNMESSAGE, 0, 0);
  3541. // always bail if the drive is in use.
  3542. lres = FALSE;
  3543. }
  3544. break;
  3545. case CDBURNWM_SHOWSHUTDOWNMESSAGE:
  3546. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_BURN_CANTSHUTDOWN), MAKEINTRESOURCE(IDS_BURN),
  3547. MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
  3548. lres = TRUE;
  3549. break;
  3550. default:
  3551. lres = DefSubclassProc(hwnd, uMsg, wParam, lParam);
  3552. break;
  3553. }
  3554. return lres;
  3555. }
  3556. void CCDBurn::_SetupFirstPage(HWND hwnd, BOOL fSubclass)
  3557. {
  3558. s_hwndWiz = GetParent(hwnd);
  3559. SendDlgItemMessage(hwnd, IDC_BURNWIZ_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0);
  3560. if (fSubclass)
  3561. {
  3562. SetWindowSubclass(s_hwndWiz, _WizSubclassProc, 0, 0);
  3563. }
  3564. }
  3565. INT_PTR CCDBurn::_WelcomeDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3566. {
  3567. BOOL fRet = FALSE;
  3568. switch (uMsg)
  3569. {
  3570. case WM_INITDIALOG:
  3571. _SetupFirstPage(hwnd, TRUE);
  3572. fRet = TRUE;
  3573. break;
  3574. case WM_NOTIFY:
  3575. {
  3576. LPNMHDR pnmh = (LPNMHDR)lParam;
  3577. switch (pnmh->code)
  3578. {
  3579. case PSN_SETACTIVE:
  3580. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_NEXT);
  3581. _SetUpStartPage(hwnd);
  3582. fRet = TRUE;
  3583. break;
  3584. case PSN_WIZNEXT:
  3585. TCHAR szDiscLabel[JOLIET_MAX_LABEL + 1];
  3586. szDiscLabel[0] = 0;
  3587. // GetDlgItemText helpfully returns 0 for error and 0 for the empty string, so dont use
  3588. // its return value.
  3589. GetDlgItemText(hwnd, IDC_BURNWIZ_DISCLABEL, szDiscLabel, ARRAYSIZE(szDiscLabel));
  3590. SHPropertyBag_WriteStr(_ppb, PROPSTR_DISCLABEL, szDiscLabel);
  3591. _SetNextPage(hwnd, INDEX_DLG_BURNWIZ_WAITFORMEDIA);
  3592. fRet = TRUE;
  3593. break;
  3594. case PSN_KILLACTIVE:
  3595. _LeaveStartPage(hwnd);
  3596. break;
  3597. }
  3598. break;
  3599. }
  3600. }
  3601. return fRet;
  3602. }
  3603. INT_PTR CCDBurn::_StartEraseDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3604. {
  3605. BOOL fRet = FALSE;
  3606. switch (uMsg)
  3607. {
  3608. case WM_INITDIALOG:
  3609. _SetupFirstPage(hwnd, TRUE);
  3610. fRet = TRUE;
  3611. break;
  3612. case WM_NOTIFY:
  3613. {
  3614. LPNMHDR pnmh = (LPNMHDR)lParam;
  3615. switch (pnmh->code)
  3616. {
  3617. case PSN_SETACTIVE:
  3618. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_NEXT);
  3619. _SetUpStartPage(hwnd);
  3620. fRet = TRUE;
  3621. break;
  3622. case PSN_WIZNEXT:
  3623. // the erase progress page will almost immediately lock the drive -- this call is for
  3624. // consistency and to set the state variable used in shutdown prevention.
  3625. _LockCurrentDrive(TRUE);
  3626. _SetNextPage(hwnd, INDEX_DLG_BURNWIZ_ERASE_PROGRESS);
  3627. fRet = TRUE;
  3628. break;
  3629. case PSN_KILLACTIVE:
  3630. _LeaveStartPage(hwnd);
  3631. break;
  3632. }
  3633. break;
  3634. }
  3635. }
  3636. return fRet;
  3637. }
  3638. INT_PTR CCDBurn::_EjectDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3639. {
  3640. BOOL fRet = FALSE;
  3641. switch (uMsg)
  3642. {
  3643. case WM_INITDIALOG:
  3644. _SetupFirstPage(hwnd, TRUE);
  3645. fRet = TRUE;
  3646. break;
  3647. case WM_COMMAND:
  3648. switch (GET_WM_COMMAND_ID(wParam, lParam))
  3649. {
  3650. case IDC_BURNWIZ_BURNDATA:
  3651. case IDC_BURNWIZ_CLEAR:
  3652. case IDC_BURNWIZ_EJECT:
  3653. if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNDATA) == BST_CHECKED)
  3654. {
  3655. PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
  3656. }
  3657. else
  3658. {
  3659. PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
  3660. }
  3661. break;
  3662. }
  3663. break;
  3664. case WM_NOTIFY:
  3665. {
  3666. LPNMHDR pnmh = (LPNMHDR)lParam;
  3667. switch (pnmh->code)
  3668. {
  3669. case PSN_SETACTIVE:
  3670. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_NEXT);
  3671. _SetUpStartPage(hwnd);
  3672. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNDATA), BST_CHECKED);
  3673. fRet = TRUE;
  3674. break;
  3675. case PSN_WIZNEXT:
  3676. {
  3677. TCHAR szDiscLabel[JOLIET_MAX_LABEL + 1];
  3678. if (GetDlgItemText(hwnd, IDC_BURNWIZ_DISCLABEL, szDiscLabel, ARRAYSIZE(szDiscLabel)))
  3679. {
  3680. SHPropertyBag_WriteStr(_ppb, PROPSTR_DISCLABEL, szDiscLabel);
  3681. }
  3682. _SetNextPage(hwnd, INDEX_DLG_BURNWIZ_WAITFORMEDIA);
  3683. fRet = TRUE;
  3684. break;
  3685. }
  3686. case PSN_WIZFINISH:
  3687. if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_CLEAR) == BST_CHECKED)
  3688. {
  3689. CMINVOKECOMMANDINFO cmi = {0};
  3690. cmi.fMask = CMIC_MASK_FLAG_NO_UI;
  3691. _CleanUp(&cmi, TRUE);
  3692. }
  3693. fRet = TRUE;
  3694. break;
  3695. case PSN_KILLACTIVE:
  3696. _LeaveStartPage(hwnd);
  3697. break;
  3698. }
  3699. break;
  3700. }
  3701. }
  3702. return fRet;
  3703. }
  3704. void CCDBurn::_InitTimeStats(BOOL fErase)
  3705. {
  3706. ZeroMemory(&_ts, sizeof(_ts));
  3707. DWORD cb;
  3708. if (fErase)
  3709. {
  3710. DWORD dwEraseTime;
  3711. cb = sizeof(dwEraseTime);
  3712. if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_ERASETIME, NULL, &dwEraseTime, &cb))
  3713. {
  3714. // default to 3 minutes or so.
  3715. dwEraseTime = 180;
  3716. }
  3717. _ts.dwSecErase = dwEraseTime / _dwCurSpeed;
  3718. _ts.dwSecTotal = _ts.dwSecErase;
  3719. }
  3720. else
  3721. {
  3722. _fRecording = FALSE;
  3723. ULONGLONG ullStageRate;
  3724. cb = sizeof(ullStageRate);
  3725. if ((ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_STAGERATE, NULL, &ullStageRate, &cb)) ||
  3726. (0 == ullStageRate))
  3727. {
  3728. // default to staging approx. 2MB/s.
  3729. ullStageRate = 2000000;
  3730. }
  3731. _ts.dwSecStaging = (DWORD) (_cbStagedSize / ullStageRate);
  3732. ULONGLONG ullBurnRate;
  3733. cb = sizeof(ullBurnRate);
  3734. if ((ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_BURNRATE, NULL, &ullBurnRate, &cb)) ||
  3735. (0 == ullBurnRate))
  3736. {
  3737. // default, single speed.
  3738. ullBurnRate = 150000;
  3739. }
  3740. _ts.dwSecBurn = (DWORD) (_cbStagedSize / (ullBurnRate * _dwCurSpeed));
  3741. DWORD dwCloseFactor;
  3742. cb = sizeof(dwCloseFactor);
  3743. if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CLOSEFACTOR, NULL, &dwCloseFactor, &cb))
  3744. {
  3745. // default to ~2 min, single speed.
  3746. dwCloseFactor = 120;
  3747. }
  3748. _ts.dwSecClose = dwCloseFactor / _dwCurSpeed;
  3749. _ts.dwSecTotal = _ts.dwSecStaging + _ts.dwSecBurn + _ts.dwSecClose;
  3750. }
  3751. if (0 == _ts.dwSecTotal)
  3752. _ts.dwSecTotal = 1;
  3753. _dwLastTime = 0;
  3754. _SetEstimatedTime(_ts.dwSecTotal);
  3755. }
  3756. void CCDBurn::_SaveTimeStats(BOOL fErase)
  3757. {
  3758. if (fErase)
  3759. {
  3760. // if we didnt fill in one of our timing fields, IMAPI called us wrong and we throw out all our data.
  3761. if (_ts.dwTickEraseStart && _ts.dwTickEraseEnd)
  3762. {
  3763. DWORD dwEraseTime = (_ts.dwTickEraseEnd - _ts.dwTickEraseStart) / 1000 * _dwCurSpeed;
  3764. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_ERASETIME, REG_DWORD, &dwEraseTime, sizeof(dwEraseTime));
  3765. }
  3766. }
  3767. else
  3768. {
  3769. // if we didnt fill in one of our timing fields, IMAPI called us wrong and we throw out all our data.
  3770. if (_ts.dwTickStagingStart && _ts.dwTickStagingEnd &&
  3771. _ts.dwTickBurnStart && _ts.dwTickBurnEnd &&
  3772. _ts.dwTickCloseStart && _ts.dwTickCloseEnd)
  3773. {
  3774. // only tally results from burns of more than 1MB (arbitrary, but good enough).
  3775. // 1MB is about 8s on a 1x drive and 1s on an 8x drive.
  3776. // the main danger is people burning a few text files that total 1k, then the burn phase takes 1 second
  3777. // (rather, the time between our IMAPI notifications is 1s) and we get stuck with some measly transfer rate
  3778. // that doesnt make any sense and makes the next burn's estimated time up in the gazillions of minutes.
  3779. if (_cbStagedSize > 1000000)
  3780. {
  3781. ULONGLONG ullStageRate = _cbStagedSize * 1000 / (_ts.dwTickStagingEnd - _ts.dwTickStagingStart);
  3782. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_STAGERATE, REG_BINARY, &ullStageRate, sizeof(ullStageRate));
  3783. ULONGLONG ullBurnRate = _cbStagedSize * 1000 / (_ts.dwTickBurnEnd - _ts.dwTickBurnStart) / _dwCurSpeed;
  3784. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_BURNRATE, REG_BINARY, &ullBurnRate, sizeof(ullBurnRate));
  3785. }
  3786. DWORD dwCloseFactor = (_ts.dwTickCloseEnd - _ts.dwTickCloseStart) / 1000 * _dwCurSpeed;
  3787. SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CLOSEFACTOR, REG_DWORD, &dwCloseFactor, sizeof(dwCloseFactor));
  3788. }
  3789. }
  3790. }
  3791. void CCDBurn::_ConstructTimeString(DWORD dwEstTime, LPTSTR psz, UINT cch)
  3792. {
  3793. TCHAR szBuf[100];
  3794. if (dwEstTime >= 60)
  3795. {
  3796. LoadString(HINST_THISDLL, IDS_TIMEEST_MINUTES2, szBuf, ARRAYSIZE(szBuf));
  3797. wnsprintf(psz, cch, szBuf, dwEstTime / 60 + 1);
  3798. }
  3799. else
  3800. {
  3801. LoadString(HINST_THISDLL, IDS_TIMEEST_SECONDS2, szBuf, ARRAYSIZE(szBuf));
  3802. wnsprintf(psz, cch, szBuf, (dwEstTime / 5 + 1) * 5); // round off to 5sec increments
  3803. }
  3804. }
  3805. void CCDBurn::_SetEstimatedTime(DWORD dwSeconds)
  3806. {
  3807. _ts.dwSecRemaining = dwSeconds;
  3808. _dwTimeSet = GetTickCount();
  3809. }
  3810. void CCDBurn::_DisplayEstimatedTime(HWND hwnd)
  3811. {
  3812. if (_ts.dwSecRemaining)
  3813. {
  3814. // we know when we last made an estimate, so show time based on that.
  3815. // however, our estimate could have easily been wrong, so skew it so it never quite reaches 0,
  3816. // and instead sit at a few seconds remaining (hopefully for not too long).
  3817. DWORD dwElapsedTime = (GetTickCount() - _dwTimeSet) / 1000;
  3818. DWORD dwEstTime = 0;
  3819. if (_ts.dwSecRemaining > dwElapsedTime + 5)
  3820. {
  3821. dwEstTime = _ts.dwSecRemaining - dwElapsedTime;
  3822. }
  3823. if (!_dwLastTime || (dwEstTime < _dwLastTime))
  3824. {
  3825. TCHAR szTime[100];
  3826. _ConstructTimeString(dwEstTime, szTime, ARRAYSIZE(szTime));
  3827. SetDlgItemText(hwnd, IDC_BURNWIZ_ESTTIME, szTime);
  3828. SendMessage(GetDlgItem(_hwndWizardPage, IDC_BURNWIZ_PROGRESS), PBM_SETPOS, (WPARAM) (PROGRESS_INCREMENTS * (_ts.dwSecTotal - dwEstTime) / _ts.dwSecTotal), 0);
  3829. _dwLastTime = dwEstTime;
  3830. }
  3831. }
  3832. }
  3833. void CCDBurn::_InitProgressPage(HWND hwnd)
  3834. {
  3835. SendMessage(GetDlgItem(hwnd, IDC_BURNWIZ_PROGRESS), PBM_SETRANGE, (WPARAM)0, MAKELPARAM(0, PROGRESS_INCREMENTS));
  3836. SendMessage(GetDlgItem(hwnd, IDC_BURNWIZ_PROGRESS), PBM_SETPOS, (WPARAM)0, 0);
  3837. SetDlgItemText(hwnd, IDC_BURNWIZ_ESTTIME, L"");
  3838. SetDlgItemText(hwnd, IDC_BURNWIZ_STATUSTEXT, L"");
  3839. }
  3840. #define IDT_SHOWTIME 1
  3841. INT_PTR CCDBurn::_ProgressDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3842. {
  3843. BOOL fRet = FALSE;
  3844. switch (uMsg)
  3845. {
  3846. case WM_NOTIFY:
  3847. {
  3848. LPNMHDR pnmh = (LPNMHDR)lParam;
  3849. switch (pnmh->code)
  3850. {
  3851. case PSN_SETACTIVE:
  3852. _InitProgressPage(hwnd);
  3853. _hwndWizardPage = hwnd;
  3854. PropSheet_SetWizButtons(pnmh->hwndFrom, 0);
  3855. // start up a timer to periodically refresh the time remaining
  3856. _ts.dwSecRemaining = 0;
  3857. SetTimer(hwnd, IDT_SHOWTIME, 1000, NULL);
  3858. AddRef();
  3859. if (!SHCreateThread(SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_ERASE, FALSE) ? _EraseThread : _BurnThread,
  3860. this, CTF_COINIT, NULL))
  3861. {
  3862. Release();
  3863. }
  3864. fRet = TRUE;
  3865. break;
  3866. case PSN_QUERYCANCEL:
  3867. if (IDYES == ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_BURN_CONFIRM_CANCEL), MAKEINTRESOURCE(IDS_BURN),
  3868. MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2))
  3869. {
  3870. _fCancelled = TRUE;
  3871. }
  3872. else
  3873. {
  3874. // TRUE means don't cancel
  3875. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, TRUE);
  3876. fRet = TRUE;
  3877. }
  3878. break;
  3879. case PSN_WIZNEXT:
  3880. _PostOperation();
  3881. fRet = TRUE;
  3882. break;
  3883. case PSN_KILLACTIVE:
  3884. KillTimer(hwnd, IDT_SHOWTIME);
  3885. break;
  3886. }
  3887. break;
  3888. }
  3889. case WM_TIMER:
  3890. switch (wParam)
  3891. {
  3892. case IDT_SHOWTIME:
  3893. _DisplayEstimatedTime(hwnd);
  3894. break;
  3895. }
  3896. break;
  3897. }
  3898. return fRet;
  3899. }
  3900. void CCDBurn::_DisplayMediaErrorOnNext(HWND hwnd, UINT idMsg, UINT idMsgInsert)
  3901. {
  3902. WCHAR sz[100];
  3903. LoadString(HINST_THISDLL, idMsg, sz, ARRAYSIZE(sz));
  3904. SetDlgItemText(hwnd, IDC_BURNWIZ_STATUSTEXT, sz);
  3905. int iIndex;
  3906. if (SUCCEEDED(_GetCurrentDriveIndex(&iIndex)) && PathBuildRoot(sz, iIndex))
  3907. {
  3908. LPWSTR pszInsertDisc = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(idMsgInsert), sz);
  3909. if (pszInsertDisc)
  3910. {
  3911. SetDlgItemText(hwnd, IDC_BURNWIZ_PLEASEINSERT, pszInsertDisc);
  3912. LocalFree(pszInsertDisc);
  3913. }
  3914. }
  3915. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, -1);
  3916. }
  3917. // to make the wizard happy, we load all of the extensions' pages at the beginning.
  3918. // however, not all will be used, since some behavior is dependent on the media that's
  3919. // inserted -- which can only be known AFTER _WaitForMediaDlgProc.
  3920. void CCDBurn::_PruneExts()
  3921. {
  3922. IDataObject *pdo;
  3923. if (SUCCEEDED(_CreateDataObject(&pdo)))
  3924. {
  3925. int i = DPA_GetPtrCount(_hdpaExts);
  3926. // count down and remove from the end of the DPA.
  3927. while (i--)
  3928. {
  3929. BOOL fKeep = FALSE;
  3930. IWizardExtension *pwe = (IWizardExtension*)DPA_GetPtr(_hdpaExts, i);
  3931. ASSERT(pwe);
  3932. IDropTarget *pdt;
  3933. if (SUCCEEDED(pwe->QueryInterface(IID_PPV_ARG(IDropTarget, &pdt))))
  3934. {
  3935. DWORD dwEffect;
  3936. POINTL pt = { 0 };
  3937. if (SUCCEEDED(pdt->DragEnter(pdo, 0, pt, &dwEffect)))
  3938. {
  3939. if (DROPEFFECT_NONE != dwEffect)
  3940. {
  3941. fKeep = TRUE;
  3942. }
  3943. pdt->DragLeave();
  3944. }
  3945. pdt->Release();
  3946. }
  3947. if (!fKeep)
  3948. {
  3949. DPA_DeletePtr(_hdpaExts, i);
  3950. IUnknown_SetSite(pwe, NULL);
  3951. pwe->Release();
  3952. }
  3953. }
  3954. pdo->Release();
  3955. }
  3956. }
  3957. // timer for our retries
  3958. #define IDT_CLICKNEXT 1
  3959. INT_PTR CCDBurn::_WaitForMediaDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3960. {
  3961. BOOL fRet = FALSE;
  3962. switch (uMsg)
  3963. {
  3964. case WM_NOTIFY:
  3965. {
  3966. LPNMHDR pnmh = (LPNMHDR)lParam;
  3967. switch (pnmh->code)
  3968. {
  3969. case PSN_SETACTIVE:
  3970. {
  3971. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
  3972. // kick off a trial to see if media is here already
  3973. PropSheet_PressButton(GetParent(hwnd), PSBTN_NEXT);
  3974. // start up a timer too -- we could also register the window for notification
  3975. // but this is a cheap operation and its less code
  3976. SetTimer(hwnd, IDT_CLICKNEXT, 250, NULL);
  3977. fRet = TRUE;
  3978. }
  3979. break;
  3980. case PSN_WIZBACK:
  3981. _SetNextPage(hwnd, SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_EJECT, FALSE) ? INDEX_DLG_BURNWIZ_EJECT : INDEX_DLG_BURNWIZ_WELCOME);
  3982. fRet = TRUE;
  3983. break;
  3984. case PSN_WIZNEXT:
  3985. {
  3986. // used to check if our media-insert thread is done with using IMAPI.
  3987. DWORD dwDummy, cb = sizeof(dwDummy);
  3988. if (ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_PERMEDIA, REGVALUE_SET, NULL, &dwDummy, &cb))
  3989. {
  3990. DWORD dwCaps;
  3991. BOOL fUDF;
  3992. if (SUCCEEDED(_GetMediaCapabilities(&dwCaps, &fUDF)) && (dwCaps & HWDMC_CDRECORDABLE) && !fUDF)
  3993. {
  3994. _LockCurrentDrive(TRUE);
  3995. _PruneExts();
  3996. SHPropertyBag_WriteDWORD(_ppb, PROPSTR_CURRENTEXT, 1);
  3997. _SetExtPageFromPropBag(hwnd, TRUE);
  3998. }
  3999. else
  4000. {
  4001. _DisplayMediaErrorOnNext(hwnd, IDS_BURN_FAILURE_MEDIUM_INVALIDTYPE, IDS_BURN_INSERTDISCFULL);
  4002. }
  4003. }
  4004. else
  4005. {
  4006. _DisplayMediaErrorOnNext(hwnd, IDS_BURN_FAILURE_MEDIUM_NOTPRESENT, IDS_BURN_INSERTDISC);
  4007. }
  4008. fRet = TRUE;
  4009. break;
  4010. }
  4011. case PSN_KILLACTIVE:
  4012. KillTimer(hwnd, IDT_CLICKNEXT);
  4013. break;
  4014. }
  4015. break;
  4016. }
  4017. case WM_TIMER:
  4018. switch (wParam)
  4019. {
  4020. case IDT_CLICKNEXT:
  4021. PropSheet_PressButton(GetParent(hwnd), PSBTN_NEXT);
  4022. break;
  4023. }
  4024. break;
  4025. }
  4026. return fRet;
  4027. }
  4028. HRESULT CCDBurn::_PostOperation()
  4029. {
  4030. HRESULT hrOp = E_FAIL;
  4031. DWORD dwHR;
  4032. if (SUCCEEDED(SHPropertyBag_ReadDWORD(_ppb, PROPSTR_HR, &dwHR)))
  4033. {
  4034. hrOp = dwHR;
  4035. }
  4036. if (SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_ERASE, FALSE))
  4037. {
  4038. _SetNextPage(_hwndWizardPage, SUCCEEDED(hrOp) ? INDEX_DLG_BURNWIZ_ERASE_SUCCESS : INDEX_DLG_BURNWIZ_ERASE_FAILURE);
  4039. }
  4040. else
  4041. {
  4042. switch (hrOp)
  4043. {
  4044. case IMAPI_E_DISCFULL:
  4045. {
  4046. TCHAR szDiscFull[200];
  4047. LoadString(HINST_THISDLL, IDS_BURN_DISCFULL, szDiscFull, ARRAYSIZE(szDiscFull));
  4048. SHPropertyBag_WriteStr(_ppb, PROPSTR_DISCFULLTEXT, szDiscFull);
  4049. _SetNextPage(_hwndWizardPage, INDEX_DLG_BURNWIZ_DISCFULL);
  4050. break;
  4051. }
  4052. case IMAPI_E_NOTENOUGHDISKFORSTASH:
  4053. _SetNextPage(_hwndWizardPage, INDEX_DLG_BURNWIZ_HDFULL);
  4054. break;
  4055. case HRESULT_FROM_WIN32(ERROR_DISK_FULL):
  4056. // diskfulltext has already been set by _CheckTotal.
  4057. _SetNextPage(_hwndWizardPage, INDEX_DLG_BURNWIZ_DISCFULL);
  4058. break;
  4059. case IMAPI_E_LOSS_OF_STREAMING:
  4060. {
  4061. // slow down the speed by one step
  4062. TCHAR szVolume[MAX_PATH];
  4063. if (SUCCEEDED(_GetCurrentBurnVolumeName(szVolume, ARRAYSIZE(szVolume))))
  4064. {
  4065. DWORD dwCurSpeed, dwMaxSpeed;
  4066. if (SUCCEEDED(_GetCachedDriveInfo(szVolume, NULL, &dwCurSpeed, &dwMaxSpeed)))
  4067. {
  4068. if (WRITESPEED_FASTEST == dwCurSpeed)
  4069. {
  4070. // if we're set at the fastest speed, tone down to dwMaxSpeed
  4071. dwCurSpeed = dwMaxSpeed;
  4072. }
  4073. DWORD dwNewSpeed = 1;
  4074. // find the highest power of 2 smaller than dwSpeed
  4075. while (dwNewSpeed * 2 < dwCurSpeed)
  4076. {
  4077. dwNewSpeed *= 2;
  4078. }
  4079. _SetCachedDriveInfo(szVolume, DRIVE_USEEXISTING, dwNewSpeed, 0);
  4080. }
  4081. }
  4082. _SetNextPage(_hwndWizardPage, INDEX_DLG_BURNWIZ_BURN_FAILURE);
  4083. break;
  4084. }
  4085. case IMAPI_E_CANNOT_WRITE_TO_MEDIA:
  4086. {
  4087. WCHAR szText[300];
  4088. LoadString(HINST_THISDLL, IDS_BURN_CANTWRITETOMEDIA, szText, ARRAYSIZE(szText));
  4089. SHPropertyBag_WriteStr(_ppb, PROPSTR_STATUSTEXT, szText);
  4090. _SetNextPage(_hwndWizardPage, INDEX_DLG_BURNWIZ_BURN_FAILURE);
  4091. }
  4092. default:
  4093. _SetNextPage(_hwndWizardPage, SUCCEEDED(hrOp) ? INDEX_DLG_BURNWIZ_BURN_SUCCESS : INDEX_DLG_BURNWIZ_BURN_FAILURE);
  4094. break;
  4095. }
  4096. }
  4097. return S_OK;
  4098. }
  4099. HRESULT CCDBurn::_GetBurnHR()
  4100. {
  4101. HRESULT hrBurn = E_FAIL;
  4102. DWORD dwHR;
  4103. if (SUCCEEDED(SHPropertyBag_ReadDWORD(_ppb, PROPSTR_HR, &dwHR)))
  4104. {
  4105. hrBurn = dwHR;
  4106. }
  4107. return hrBurn;
  4108. }
  4109. void CCDBurn::_ShowRoxio()
  4110. {
  4111. SHELLEXECUTEINFO sei = {0};
  4112. sei.cbSize = sizeof(sei);
  4113. sei.lpFile = L"http://go.microsoft.com/fwlink/?LinkId=932"; // http://www.roxio.com/
  4114. ShellExecuteEx(&sei);
  4115. }
  4116. INT_PTR CCDBurn::_DoneDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4117. {
  4118. BOOL fRet = FALSE;
  4119. switch (uMsg)
  4120. {
  4121. case WM_INITDIALOG:
  4122. SendDlgItemMessage(hwnd, IDC_BURNWIZ_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0);
  4123. fRet = TRUE;
  4124. break;
  4125. case WM_COMMAND:
  4126. switch (GET_WM_COMMAND_ID(wParam, lParam))
  4127. {
  4128. case IDC_BURNWIZ_BURNAGAIN:
  4129. case IDC_BURNWIZ_CLEAR:
  4130. case IDC_BURNWIZ_EXIT:
  4131. if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNAGAIN) == BST_CHECKED)
  4132. {
  4133. PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
  4134. }
  4135. else
  4136. {
  4137. PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
  4138. }
  4139. break;
  4140. }
  4141. break;
  4142. case WM_NOTIFY:
  4143. {
  4144. LPNMHDR pnmh = (LPNMHDR)lParam;
  4145. switch (pnmh->code)
  4146. {
  4147. case PSN_SETACTIVE:
  4148. EnableWindow(GetDlgItem(GetParent(hwnd), IDCANCEL), FALSE);
  4149. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNAGAIN), BST_UNCHECKED);
  4150. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_FINISH);
  4151. WCHAR szStatus[300];
  4152. if (SUCCEEDED(SHPropertyBag_ReadStr(_ppb, PROPSTR_STATUSTEXT, szStatus, ARRAYSIZE(szStatus))))
  4153. {
  4154. SetDlgItemText(hwnd, IDC_BURNWIZ_STATUSTEXT, szStatus);
  4155. }
  4156. if (SUCCEEDED(_GetBurnHR()))
  4157. {
  4158. if (SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_AUTOCLOSE, FALSE))
  4159. {
  4160. PropSheet_PressButton(GetParent(hwnd), PSBTN_FINISH);
  4161. }
  4162. }
  4163. else
  4164. {
  4165. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNAGAIN), BST_UNCHECKED);
  4166. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_CLEAR), BST_UNCHECKED);
  4167. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_EXIT), BST_CHECKED);
  4168. if (IMAPI_E_LOSS_OF_STREAMING != _GetBurnHR())
  4169. {
  4170. ShowWindow(GetDlgItem(hwnd, IDC_BURNWIZ_LOWERED), FALSE);
  4171. }
  4172. }
  4173. fRet = TRUE;
  4174. break;
  4175. case PSN_WIZNEXT:
  4176. {
  4177. // dump stored info since we're ejecting
  4178. _DumpDiscInfo();
  4179. // eject media
  4180. int iCurrent;
  4181. if (SUCCEEDED(CCDBurn::_GetCurrentDriveIndex(&iCurrent)))
  4182. {
  4183. CMountPoint *pmtpt = CMountPoint::GetMountPoint(iCurrent);
  4184. if (pmtpt)
  4185. {
  4186. pmtpt->Eject(hwnd);
  4187. pmtpt->Release();
  4188. }
  4189. }
  4190. // go back to the beginning
  4191. EnableWindow(GetDlgItem(GetParent(hwnd), IDCANCEL), TRUE);
  4192. _SetNextPage(hwnd, INDEX_DLG_BURNWIZ_WAITFORMEDIA);
  4193. fRet = TRUE;
  4194. break;
  4195. }
  4196. case PSN_WIZFINISH:
  4197. if (!SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_ERASE, FALSE))
  4198. {
  4199. if (SUCCEEDED(_GetBurnHR()) ||
  4200. (pnmh->code == PSN_WIZFINISH) && (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_CLEAR) == BST_CHECKED))
  4201. {
  4202. // clean up staging area, the files are already on the cd.
  4203. CMINVOKECOMMANDINFO cmi = {0};
  4204. cmi.fMask = CMIC_MASK_FLAG_NO_UI;
  4205. _CleanUp(&cmi, FAILED(_GetBurnHR()));
  4206. }
  4207. }
  4208. fRet = TRUE;
  4209. break;
  4210. case NM_CLICK:
  4211. case NM_RETURN:
  4212. switch (pnmh->idFrom)
  4213. {
  4214. case IDC_BURNWIZ_LOWERED:
  4215. {
  4216. SHELLEXECUTEINFO sei = {0};
  4217. sei.cbSize = sizeof(sei);
  4218. sei.lpFile = L"hcp://services/subsite?node=TopLevelBucket_4/Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Ccdmedia.chm%3A%3A/cdmedia_fail2_moreinfo_buffer_underrun.htm&select=TopLevelBucket_4/Hardware/CDs_and_other_storage_devices";
  4219. ShellExecuteEx(&sei);
  4220. fRet = TRUE;
  4221. }
  4222. break;
  4223. case IDC_BURNWIZ_ATTRIB:
  4224. _ShowRoxio();
  4225. fRet = TRUE;
  4226. break;
  4227. }
  4228. break;
  4229. }
  4230. break;
  4231. }
  4232. }
  4233. return fRet;
  4234. }
  4235. INT_PTR CCDBurn::_DiskFullDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4236. {
  4237. BOOL fRet = FALSE;
  4238. switch (uMsg)
  4239. {
  4240. case WM_INITDIALOG:
  4241. SendDlgItemMessage(hwnd, IDC_BURNWIZ_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0);
  4242. fRet = TRUE;
  4243. break;
  4244. case WM_COMMAND:
  4245. switch (GET_WM_COMMAND_ID(wParam, lParam))
  4246. {
  4247. case IDC_BURNWIZ_BURNAGAIN:
  4248. case IDC_BURNWIZ_EXIT:
  4249. if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNAGAIN) == BST_CHECKED)
  4250. {
  4251. PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
  4252. }
  4253. else
  4254. {
  4255. PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
  4256. }
  4257. break;
  4258. }
  4259. break;
  4260. case WM_NOTIFY:
  4261. {
  4262. LPNMHDR pnmh = (LPNMHDR)lParam;
  4263. switch (pnmh->code)
  4264. {
  4265. case PSN_SETACTIVE:
  4266. {
  4267. EnableWindow(GetDlgItem(GetParent(hwnd), IDCANCEL), FALSE);
  4268. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_FINISH);
  4269. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNAGAIN), BST_UNCHECKED);
  4270. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_EXIT), BST_CHECKED);
  4271. TCHAR szText[200];
  4272. if (SUCCEEDED(SHPropertyBag_ReadStr(_ppb, PROPSTR_DISCFULLTEXT, szText, ARRAYSIZE(szText))))
  4273. {
  4274. SetDlgItemText(hwnd, IDC_BURNWIZ_STATUSTEXT, szText);
  4275. }
  4276. fRet = TRUE;
  4277. break;
  4278. }
  4279. case PSN_WIZNEXT:
  4280. EnableWindow(GetDlgItem(GetParent(hwnd), IDCANCEL), TRUE);
  4281. _SetNextPage(hwnd, INDEX_DLG_BURNWIZ_WAITFORMEDIA);
  4282. fRet = TRUE;
  4283. break;
  4284. case NM_CLICK:
  4285. case NM_RETURN:
  4286. if (IDC_BURNWIZ_ATTRIB == pnmh->idFrom)
  4287. {
  4288. _ShowRoxio();
  4289. fRet = TRUE;
  4290. }
  4291. break;
  4292. }
  4293. break;
  4294. }
  4295. }
  4296. return fRet;
  4297. }
  4298. // a stub page just to bail on the wizard when an extension says "dont run any more".
  4299. INT_PTR CCDBurn::_EarlyExitDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4300. {
  4301. BOOL fRet = FALSE;
  4302. switch (uMsg)
  4303. {
  4304. case WM_INITDIALOG:
  4305. PropSheet_PressButton(GetParent(hwnd), PSBTN_CANCEL);
  4306. fRet = TRUE;
  4307. break;
  4308. }
  4309. return fRet;
  4310. }
  4311. void CCDBurn::_HDFullSetText(HWND hwnd)
  4312. {
  4313. TCHAR szStashDrive[4];
  4314. if (SUCCEEDED(_GetCurrentStashDrive(szStashDrive, ARRAYSIZE(szStashDrive))))
  4315. {
  4316. LPTSTR pszMessage1 = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_BURN_HDFULL1), szStashDrive);
  4317. if (pszMessage1)
  4318. {
  4319. SetDlgItemText(hwnd, IDC_BURNWIZ_STATUSTEXT, pszMessage1);
  4320. LocalFree(pszMessage1);
  4321. }
  4322. // 50 MB overhead fudge factor. this is the best estimate we have.
  4323. ULONGLONG cbStash = _cbStagedSize + 50 * 1024 * 1024;
  4324. ULARGE_INTEGER ulFree;
  4325. if (SHGetDiskFreeSpaceEx(szStashDrive, &ulFree, NULL, NULL) &&
  4326. (ulFree.QuadPart < cbStash))
  4327. {
  4328. TCHAR szNeed[40], szToDelete[40];
  4329. StrFormatByteSize64(cbStash, szNeed, ARRAYSIZE(szNeed));
  4330. StrFormatByteSize64(cbStash - ulFree.QuadPart, szToDelete, ARRAYSIZE(szToDelete));
  4331. LPTSTR pszMessage2 = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_BURN_HDFULL2), szNeed, szStashDrive, szToDelete);
  4332. if (pszMessage2)
  4333. {
  4334. SetDlgItemText(hwnd, IDC_BURNWIZ_STATUSTEXT2, pszMessage2);
  4335. LocalFree(pszMessage2);
  4336. }
  4337. }
  4338. }
  4339. }
  4340. INT_PTR CCDBurn::_HDFullDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4341. {
  4342. BOOL fRet = FALSE;
  4343. switch (uMsg)
  4344. {
  4345. case WM_INITDIALOG:
  4346. SendDlgItemMessage(hwnd, IDC_BURNWIZ_TITLE, WM_SETFONT, (WPARAM)GetIntroFont(hwnd), 0);
  4347. fRet = TRUE;
  4348. break;
  4349. case WM_NOTIFY:
  4350. {
  4351. LPNMHDR pnmh = (LPNMHDR)lParam;
  4352. switch (pnmh->code)
  4353. {
  4354. case PSN_SETACTIVE:
  4355. {
  4356. EnableWindow(GetDlgItem(GetParent(hwnd), IDCANCEL), FALSE);
  4357. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_FINISH);
  4358. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_CLEAR), BST_UNCHECKED);
  4359. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_EXIT), BST_CHECKED);
  4360. _HDFullSetText(hwnd);
  4361. fRet = TRUE;
  4362. break;
  4363. }
  4364. case PSN_WIZFINISH:
  4365. if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_CLEAR) == BST_CHECKED)
  4366. {
  4367. TCHAR szStash[4];
  4368. if (SUCCEEDED(_GetCurrentStashDrive(szStash, ARRAYSIZE(szStash))))
  4369. {
  4370. LaunchDiskCleanup(NULL, DRIVEID(szStash), DISKCLEANUP_NOFLAG);
  4371. }
  4372. }
  4373. fRet = TRUE;
  4374. break;
  4375. case NM_CLICK:
  4376. case NM_RETURN:
  4377. switch (pnmh->idFrom)
  4378. {
  4379. case IDC_BURNWIZ_STATUSTEXT:
  4380. {
  4381. SHELLEXECUTEINFO sei = {0};
  4382. sei.cbSize = sizeof(sei);
  4383. sei.lpFile = L"hcp://services/subsite?node=TopLevelBucket_4/Hardware&topic=MS-ITS%3A%25HELP_LOCATION%25%5Ccdmedia.chm%3A%3A/cdmedia_fail3_moreinfo_disk_full.htm&select=TopLevelBucket_4/Hardware/CDs_and_other_storage_devices";
  4384. ShellExecuteEx(&sei);
  4385. fRet = TRUE;
  4386. }
  4387. break;
  4388. case IDC_BURNWIZ_ATTRIB:
  4389. _ShowRoxio();
  4390. fRet = TRUE;
  4391. break;
  4392. }
  4393. break;
  4394. }
  4395. break;
  4396. }
  4397. }
  4398. return fRet;
  4399. }
  4400. INT_PTR CCDBurn::_NoFilesDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4401. {
  4402. BOOL fRet = FALSE;
  4403. switch (uMsg)
  4404. {
  4405. case WM_INITDIALOG:
  4406. _SetupFirstPage(hwnd, FALSE);
  4407. fRet = TRUE;
  4408. break;
  4409. case WM_NOTIFY:
  4410. {
  4411. LPNMHDR pnmh = (LPNMHDR)lParam;
  4412. switch (pnmh->code)
  4413. {
  4414. case PSN_SETACTIVE:
  4415. {
  4416. EnableWindow(GetDlgItem(GetParent(hwnd), IDCANCEL), FALSE);
  4417. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_FINISH);
  4418. fRet = TRUE;
  4419. break;
  4420. }
  4421. case PSN_WIZFINISH:
  4422. fRet = TRUE;
  4423. break;
  4424. }
  4425. break;
  4426. }
  4427. }
  4428. return fRet;
  4429. }
  4430. HRESULT CCDBurn::DriveMatches(int iDrive)
  4431. {
  4432. HRESULT hr = E_FAIL;
  4433. // check if the drive index matches what we stored off last time.
  4434. // this lets us keep parsing the drive correctly if its been recently unmounted.
  4435. DWORD dwDrive, cb = sizeof(dwDrive);
  4436. if ((ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_CDBURNING, REGVALUE_CACHEDINDEX, NULL, &dwDrive, &cb)) &&
  4437. (iDrive == dwDrive))
  4438. {
  4439. hr = S_OK;
  4440. }
  4441. return hr;
  4442. }
  4443. HRESULT CCDBurn::_GetPlainCDPidl(LPITEMIDLIST *ppidl)
  4444. {
  4445. *ppidl = NULL;
  4446. HRESULT hr = E_FAIL;
  4447. WCHAR szDrive[4];
  4448. int iCurrent;
  4449. if (SUCCEEDED(_GetCurrentDriveIndex(&iCurrent)) &&
  4450. PathBuildRoot(szDrive, iCurrent))
  4451. {
  4452. hr = ILCreateFromPathEx(szDrive, NULL, ILCFP_FLAG_SKIPJUNCTIONS, ppidl, NULL);
  4453. }
  4454. return hr;
  4455. }
  4456. HRESULT CCDBurn::Bind(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppv)
  4457. {
  4458. IShellFolder *psfStg;
  4459. HRESULT hr = _GetStagingFolder(pidl, IID_PPV_ARG(IShellFolder, &psfStg));
  4460. if (SUCCEEDED(hr))
  4461. {
  4462. IAugmentedShellFolder *pasf;
  4463. hr = SHCoCreateInstance(NULL, &CLSID_CDBurnFolder, NULL, IID_PPV_ARG(IAugmentedShellFolder, &pasf));
  4464. if (SUCCEEDED(hr))
  4465. {
  4466. // initialize with its point in the shell namespace
  4467. // NOTE: worry about IPersistFolder3?
  4468. IPersistFolder *ppf;
  4469. hr = pasf->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf));
  4470. if (SUCCEEDED(hr))
  4471. {
  4472. hr = ppf->Initialize(pidl);
  4473. ppf->Release();
  4474. }
  4475. if (SUCCEEDED(hr))
  4476. {
  4477. IBindCtx *pbcNoForce;
  4478. if (pbc)
  4479. {
  4480. // tack on to existing bind context if available
  4481. pbcNoForce = pbc;
  4482. pbc->AddRef();
  4483. }
  4484. else
  4485. {
  4486. // otherwise make a new one
  4487. hr = CreateBindCtx(0, &pbcNoForce);
  4488. }
  4489. if (SUCCEEDED(hr))
  4490. {
  4491. // this is the drive shellfolder for cd burning --
  4492. // in this case we want to ensure that new files specified by comdlg are created
  4493. // in the staging area, so we tell the drive shellfolder that it can't
  4494. // succeed on parsedisplayname if the file doesnt exist.
  4495. hr = pbcNoForce->RegisterObjectParam(STR_DONT_FORCE_CREATE, psfStg); // just need a non-null object to register
  4496. if (SUCCEEDED(hr))
  4497. {
  4498. LPITEMIDLIST pidlCD;
  4499. hr = _GetPlainCDPidl(&pidlCD);
  4500. if (SUCCEEDED(hr))
  4501. {
  4502. IShellFolder *psf;
  4503. hr = CFSFolder_CreateFolder(NULL, pbcNoForce, pidlCD, NULL, IID_PPV_ARG(IShellFolder, &psf));
  4504. if (SUCCEEDED(hr))
  4505. {
  4506. hr = pasf->AddNameSpace(&CLSID_CDBurn, psf, NULL, ASFF_COMMON);
  4507. psf->Release();
  4508. }
  4509. ILFree(pidlCD);
  4510. }
  4511. // clean up after ourselves
  4512. pbcNoForce->RevokeObjectParam(STR_DONT_FORCE_CREATE);
  4513. }
  4514. pbcNoForce->Release();
  4515. }
  4516. }
  4517. // lets add the namespace that represents the storage
  4518. if (SUCCEEDED(hr))
  4519. {
  4520. hr = pasf->AddNameSpace(&CLSID_StagingFolder, psfStg, NULL, ASFF_DEFNAMESPACE_ALL);
  4521. }
  4522. // tell the namespace its CLSID so we can get the correct views of the
  4523. // web view etc.
  4524. if (SUCCEEDED(hr))
  4525. {
  4526. IPropertyBag *ppb;
  4527. hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &ppb));
  4528. if (SUCCEEDED(hr))
  4529. {
  4530. // store the class ID for the CD mastering folder
  4531. SHPropertyBag_WriteGUID(ppb, L"MergedFolder\\CLSID", &CLSID_CDBurn);
  4532. // store the default effect for this folder.
  4533. SHPropertyBag_WriteInt(ppb, L"MergedFolder\\DropEffect", DROPEFFECT_COPY); // sets the default
  4534. // say it's in a shellview.
  4535. SHPropertyBag_WriteBOOL(ppb, L"MergedFolder\\ShellView", TRUE);
  4536. SHLoadFromPropertyBag(pasf, ppb);
  4537. ppb->Release();
  4538. }
  4539. }
  4540. if (SUCCEEDED(hr))
  4541. hr = pasf->QueryInterface(riid, ppv);
  4542. pasf->Release();
  4543. }
  4544. psfStg->Release();
  4545. }
  4546. return hr;
  4547. }
  4548. // IWizardSite
  4549. // note: these two methods change state -- the alternative is to have the
  4550. // extensions themselves manage which extension goes next. we have to track
  4551. // which extension we're in with state because we dont get ownership of the
  4552. // wizard back until the extensions are done.
  4553. STDMETHODIMP CCDBurn::GetNextPage(HPROPSHEETPAGE *phPage)
  4554. {
  4555. DWORD dwExtNum;
  4556. if (SUCCEEDED(SHPropertyBag_ReadDWORD(_ppb, PROPSTR_CURRENTEXT, &dwExtNum)))
  4557. {
  4558. SHPropertyBag_WriteDWORD(_ppb, PROPSTR_CURRENTEXT, dwExtNum + 1);
  4559. }
  4560. return _GetExtPageFromPropBag(FALSE, phPage);
  4561. }
  4562. STDMETHODIMP CCDBurn::GetPreviousPage(HPROPSHEETPAGE *phPage)
  4563. {
  4564. DWORD dwExtNum;
  4565. if (SUCCEEDED(SHPropertyBag_ReadDWORD(_ppb, PROPSTR_CURRENTEXT, &dwExtNum)))
  4566. {
  4567. SHPropertyBag_WriteDWORD(_ppb, PROPSTR_CURRENTEXT, dwExtNum - 1);
  4568. }
  4569. return _GetExtPageFromPropBag(FALSE, phPage);
  4570. }
  4571. HRESULT CCDBurn::_GetExtPage(int nExt, BOOL fNext, HPROPSHEETPAGE *phpage)
  4572. {
  4573. HRESULT hr = E_FAIL;
  4574. // nExt is 1-based
  4575. IWizardExtension *pwe = (IWizardExtension*)DPA_GetPtr(_hdpaExts, nExt - 1);
  4576. if (pwe)
  4577. {
  4578. if (fNext)
  4579. {
  4580. hr = pwe->GetFirstPage(phpage);
  4581. }
  4582. else
  4583. {
  4584. hr = pwe->GetLastPage(phpage);
  4585. }
  4586. }
  4587. return hr;
  4588. }
  4589. HRESULT CCDBurn::_GetExtPageFromPropBag(BOOL fNext, HPROPSHEETPAGE *phpage)
  4590. {
  4591. HRESULT hr = S_OK;
  4592. *phpage = NULL;
  4593. // an extension could have completed, in which case check its return state to see if
  4594. // we should be running more of them.
  4595. DWORD dwState;
  4596. if (SUCCEEDED(SHPropertyBag_ReadDWORD(_ppb, PROPSTR_EXTENSIONCOMPLETIONSTATE, &dwState)))
  4597. {
  4598. if (dwState & CDBE_RET_STOPWIZARD)
  4599. {
  4600. // leave the wizard.
  4601. *phpage = _rgWizPages[INDEX_DLG_BURNWIZ_EARLYEXIT];
  4602. }
  4603. else if (dwState & CDBE_RET_DONTRUNOTHEREXTS)
  4604. {
  4605. // we're done with extensions, go to the burn.
  4606. *phpage = _rgWizPages[INDEX_DLG_BURNWIZ_BURN_PROGRESS];
  4607. }
  4608. }
  4609. if (!*phpage)
  4610. {
  4611. DWORD dwExt;
  4612. hr = SHPropertyBag_ReadDWORD(_ppb, PROPSTR_CURRENTEXT, &dwExt);
  4613. if (SUCCEEDED(hr))
  4614. {
  4615. if ((int)dwExt > DPA_GetPtrCount(_hdpaExts))
  4616. {
  4617. // we're done with extensions, go to the burn.
  4618. *phpage = _rgWizPages[INDEX_DLG_BURNWIZ_BURN_PROGRESS];
  4619. }
  4620. else if (dwExt == 0)
  4621. {
  4622. // we went 'back' through all extensions, put us at the start page.
  4623. int nIndex = SHPropertyBag_ReadBOOLDefRet(_ppb, PROPSTR_ERASE, FALSE) ? INDEX_DLG_BURNWIZ_STARTERASE : INDEX_DLG_BURNWIZ_WELCOME;
  4624. *phpage = _rgWizPages[nIndex];
  4625. }
  4626. else
  4627. {
  4628. hr = _GetExtPage(dwExt, fNext, phpage);
  4629. }
  4630. }
  4631. }
  4632. return hr;
  4633. }
  4634. void CCDBurn::_SetExtPageFromPropBag(HWND hwnd, BOOL fNext)
  4635. {
  4636. HPROPSHEETPAGE hpage;
  4637. if (SUCCEEDED(_GetExtPageFromPropBag(fNext, &hpage)))
  4638. {
  4639. PropSheet_SetCurSel(GetParent(hwnd), hpage, -1);
  4640. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
  4641. }
  4642. }
  4643. HRESULT CCDBurn::_TryCLSID(REFCLSID clsid, DWORD dwExtType, REFIID riid, void **ppv)
  4644. {
  4645. *ppv = NULL;
  4646. ICDBurnExt *pcdbe;
  4647. // Use SHExtCoCreateInstance to go through approval checking and app compat checking
  4648. HRESULT hr = SHExtCoCreateInstance2(NULL, &clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(ICDBurnExt, &pcdbe));
  4649. if (SUCCEEDED(hr))
  4650. {
  4651. DWORD dw;
  4652. hr = pcdbe->GetSupportedActionTypes(&dw);
  4653. if (SUCCEEDED(hr))
  4654. {
  4655. if (dw & dwExtType)
  4656. {
  4657. hr = pcdbe->QueryInterface(riid, ppv);
  4658. }
  4659. else
  4660. {
  4661. hr = E_NOTIMPL;
  4662. }
  4663. }
  4664. pcdbe->Release();
  4665. }
  4666. ASSERT(SUCCEEDED(hr) ? (*ppv != NULL) : (*ppv == NULL));
  4667. return hr;
  4668. }
  4669. HRESULT CCDBurn::_TryKey(LPTSTR pszKey, DWORD dwExtType, REFIID riid, void **ppv)
  4670. {
  4671. HRESULT hr = E_FAIL;
  4672. *ppv = NULL;
  4673. TCHAR szFullKey[MAX_PATH];
  4674. lstrcpyn(szFullKey, REGSTR_PATH_HANDLERS, ARRAYSIZE(szFullKey));
  4675. PathAppend(szFullKey, pszKey);
  4676. TCHAR szCLSID[MAX_GUID_STRING_LEN];
  4677. DWORD cbCLSID = sizeof(szCLSID);
  4678. CLSID clsid;
  4679. if ((ERROR_SUCCESS == SHRegGetUSValue(szFullKey, REGVALUE_CLSID, NULL, szCLSID, &cbCLSID, FALSE, NULL, 0)) &&
  4680. GUIDFromString(szCLSID, &clsid))
  4681. {
  4682. hr = _TryCLSID(clsid, dwExtType, riid, ppv);
  4683. }
  4684. ASSERT(SUCCEEDED(hr) ? (*ppv != NULL) : (*ppv == NULL));
  4685. return hr;
  4686. }
  4687. HRESULT CCDBurn::_TestDropEffect(IDropTarget *pdt, IDataObject *pdo, REFIID riid, void **ppv)
  4688. {
  4689. *ppv = NULL;
  4690. DWORD dwEffect;
  4691. POINTL pt = {0};
  4692. HRESULT hr = pdt->DragEnter(pdo, 0, pt, &dwEffect);
  4693. if (SUCCEEDED(hr))
  4694. {
  4695. if (dwEffect != DROPEFFECT_NONE)
  4696. {
  4697. hr = pdt->QueryInterface(riid, ppv);
  4698. }
  4699. else
  4700. {
  4701. hr = E_FAIL;
  4702. }
  4703. pdt->DragLeave();
  4704. }
  4705. ASSERT(SUCCEEDED(hr) ? (*ppv != NULL) : (*ppv == NULL));
  4706. return hr;
  4707. }
  4708. HRESULT CCDBurn::_TryKeyWithDropEffect(LPTSTR pszKey, DWORD dwExtType, IDataObject *pdo, REFIID riid, void **ppv)
  4709. {
  4710. *ppv = NULL;
  4711. IDropTarget *pdt;
  4712. HRESULT hr = _TryKey(pszKey, dwExtType, IID_PPV_ARG(IDropTarget, &pdt));
  4713. if (SUCCEEDED(hr))
  4714. {
  4715. hr = _TestDropEffect(pdt, pdo, riid, ppv);
  4716. pdt->Release();
  4717. }
  4718. ASSERT(SUCCEEDED(hr) ? (*ppv != NULL) : (*ppv == NULL));
  4719. return hr;
  4720. }
  4721. HRESULT CCDBurn::_TryCLSIDWithDropEffect(REFCLSID clsid, DWORD dwExtType, IDataObject *pdo, REFIID riid, void **ppv)
  4722. {
  4723. *ppv = NULL;
  4724. IDropTarget *pdt;
  4725. HRESULT hr = _TryCLSID(clsid, dwExtType, IID_PPV_ARG(IDropTarget, &pdt));
  4726. if (SUCCEEDED(hr))
  4727. {
  4728. hr = _TestDropEffect(pdt, pdo, riid, ppv);
  4729. pdt->Release();
  4730. }
  4731. ASSERT(SUCCEEDED(hr) ? (*ppv != NULL) : (*ppv == NULL));
  4732. return hr;
  4733. }
  4734. void CCDBurn::_AddExtensionToDPA(IWizardExtension *pwe, HPROPSHEETPAGE *rgPages, UINT cNumPages, UINT *pcPagesAdded)
  4735. {
  4736. IUnknown_SetSite(pwe, SAFECAST(this, IWizardSite *));
  4737. BOOL fAdded = FALSE;
  4738. UINT cExtPages;
  4739. if (SUCCEEDED(pwe->AddPages(rgPages + *pcPagesAdded, cNumPages - *pcPagesAdded, &cExtPages)) && cExtPages)
  4740. {
  4741. if (-1 != DPA_AppendPtr(_hdpaExts, pwe))
  4742. {
  4743. fAdded = TRUE;
  4744. *pcPagesAdded += cExtPages;
  4745. pwe->AddRef();
  4746. }
  4747. else
  4748. {
  4749. for (UINT i = 0; i < cExtPages; i++)
  4750. {
  4751. DestroyPropertySheetPage(rgPages[*pcPagesAdded + i]);
  4752. }
  4753. }
  4754. }
  4755. if (!fAdded)
  4756. {
  4757. // if it's in the dpa, it gets the setsite(NULL) later.
  4758. IUnknown_SetSite(pwe, NULL);
  4759. }
  4760. }
  4761. // does the enumRegFlags == SHREGENUM_BOTH case.
  4762. DWORD MySHRegEnumUSKey(HUSKEY hUSKey, DWORD dwIndex, LPWSTR pszName, DWORD *pcchName)
  4763. {
  4764. // start with HKCU if possible.
  4765. DWORD cKeys;
  4766. DWORD dwRet = SHRegQueryInfoUSKey(hUSKey, &cKeys, NULL, NULL, NULL, SHREGENUM_HKCU);
  4767. if (ERROR_SUCCESS == dwRet)
  4768. {
  4769. // HKCU is present.
  4770. if (dwIndex < cKeys)
  4771. {
  4772. // enum from HKCU first
  4773. dwRet = SHRegEnumUSKey(hUSKey, dwIndex, pszName, pcchName, SHREGENUM_HKCU);
  4774. }
  4775. else
  4776. {
  4777. // and next, HKLM
  4778. dwRet = SHRegEnumUSKey(hUSKey, dwIndex - cKeys, pszName, pcchName, SHREGENUM_HKLM);
  4779. }
  4780. }
  4781. else
  4782. {
  4783. // go only with HKLM.
  4784. dwRet = SHRegEnumUSKey(hUSKey, dwIndex, pszName, pcchName, SHREGENUM_HKLM);
  4785. }
  4786. return dwRet;
  4787. }
  4788. HRESULT CCDBurn::_FillExtensionDPA(HPROPSHEETPAGE *rgPages, UINT cNumPages, UINT *pcPagesAdded)
  4789. {
  4790. _hdpaExts = DPA_Create(4);
  4791. HRESULT hr = _hdpaExts ? S_OK : E_OUTOFMEMORY;
  4792. if (SUCCEEDED(hr))
  4793. {
  4794. *pcPagesAdded = 0;
  4795. // first up is the burn audio cd extension
  4796. IWizardExtension *pwe;
  4797. if (SUCCEEDED(_TryCLSID(CLSID_BurnAudioCDExtension, CDBE_TYPE_ALL, IID_PPV_ARG(IWizardExtension, &pwe))))
  4798. {
  4799. _AddExtensionToDPA(pwe, rgPages, cNumPages, pcPagesAdded);
  4800. pwe->Release();
  4801. }
  4802. HUSKEY huskeyHandlers;
  4803. if (ERROR_SUCCESS == SHRegOpenUSKey(REGSTR_PATH_HANDLERS, KEY_READ, NULL, &huskeyHandlers, FALSE))
  4804. {
  4805. DWORD dwIndex = 0;
  4806. TCHAR szKey[50];
  4807. DWORD cchKey = ARRAYSIZE(szKey);
  4808. while (ERROR_SUCCESS == SHRegEnumUSKey(huskeyHandlers, dwIndex, szKey, &cchKey, SHREGENUM_DEFAULT))
  4809. {
  4810. if (SUCCEEDED(_TryKey(szKey, CDBE_TYPE_ALL, IID_PPV_ARG(IWizardExtension, &pwe))))
  4811. {
  4812. _AddExtensionToDPA(pwe, rgPages, cNumPages, pcPagesAdded);
  4813. pwe->Release();
  4814. }
  4815. dwIndex++;
  4816. cchKey = ARRAYSIZE(szKey);
  4817. }
  4818. SHRegCloseUSKey(huskeyHandlers);
  4819. }
  4820. }
  4821. return hr;
  4822. }
  4823. HRESULT CDBurn_GetExtensionObject(DWORD dwExtType, IDataObject *pdo, REFIID riid, void **ppv)
  4824. {
  4825. *ppv = NULL;
  4826. TCHAR szDefault[50];
  4827. DWORD cb = sizeof(szDefault);
  4828. HRESULT hr = (ERROR_SUCCESS == SHRegGetUSValue(REGSTR_PATH_HANDLERS, REGVALUE_FIRSTHANDLER,
  4829. NULL, szDefault, &cb, FALSE, NULL, 0)) ? S_OK : E_FAIL;
  4830. if (SUCCEEDED(hr))
  4831. {
  4832. hr = CCDBurn::_TryKeyWithDropEffect(szDefault, dwExtType, pdo, riid, ppv);
  4833. }
  4834. else
  4835. {
  4836. szDefault[0] = 0;
  4837. }
  4838. // if we're not overridden by the FIRSTHANDLER value, try the burn audio cd extension.
  4839. if (FAILED(hr))
  4840. {
  4841. hr = CCDBurn::_TryCLSIDWithDropEffect(CLSID_BurnAudioCDExtension, dwExtType, pdo, riid, ppv);
  4842. }
  4843. if (FAILED(hr))
  4844. {
  4845. HUSKEY huskeyHandlers;
  4846. if (ERROR_SUCCESS == SHRegOpenUSKey(REGSTR_PATH_HANDLERS, KEY_READ, NULL, &huskeyHandlers, FALSE))
  4847. {
  4848. DWORD dwIndex = 0;
  4849. TCHAR szKey[50];
  4850. DWORD cchKey = ARRAYSIZE(szKey);
  4851. while (FAILED(hr) && (ERROR_SUCCESS == MySHRegEnumUSKey(huskeyHandlers, dwIndex, szKey, &cchKey)))
  4852. {
  4853. if (StrCmpI(szDefault, szKey) != 0)
  4854. {
  4855. hr = CCDBurn::_TryKeyWithDropEffect(szKey, dwExtType, pdo, riid, ppv);
  4856. }
  4857. dwIndex++;
  4858. cchKey = ARRAYSIZE(szKey);
  4859. }
  4860. SHRegCloseUSKey(huskeyHandlers);
  4861. }
  4862. }
  4863. return hr;
  4864. }
  4865. STDMETHODIMP CCDBurn::QueryService(REFGUID guidService, REFIID riid, void **ppv)
  4866. {
  4867. *ppv = NULL;
  4868. HRESULT hr = E_FAIL;
  4869. if (IsEqualGUID(guidService, SID_CDWizardHost))
  4870. {
  4871. if (IsEqualIID(riid, IID_IPropertyBag) && _ppb)
  4872. {
  4873. hr = _ppb->QueryInterface(riid, ppv);
  4874. }
  4875. }
  4876. return hr;
  4877. }
  4878. STDMETHODIMP CCDBurn::AllowAutoPlay(LPCWSTR pszPath, DWORD dwContentType, LPCWSTR pszLabel, DWORD dwSerialNumber)
  4879. {
  4880. HRESULT hr = S_OK; // default to allow autoplay
  4881. int iDrive;
  4882. if (SUCCEEDED(_GetCurrentDriveIndex(&iDrive)) &&
  4883. (iDrive == DRIVEID(pszPath)))
  4884. {
  4885. // while we're registered in the running object table the wizard is always running, so dont autoplay.
  4886. hr = S_FALSE;
  4887. }
  4888. return hr;
  4889. }
  4890. HRESULT CCDBurn::ConfirmOperation(IShellItem *psiSource, IShellItem *psiDest, STGTRANSCONFIRMATION stc, LPCUSTOMCONFIRMATION pcc)
  4891. {
  4892. HRESULT hr = STRESPONSE_CONTINUE; // use default postop handling.
  4893. // null psiitem means this is postop for the entire operation.
  4894. if (psiSource && IsEqualGUID(STCONFIRM_ACCESS_DENIED, stc))
  4895. {
  4896. DWORD dwCaps;
  4897. BOOL fUDF;
  4898. if (SUCCEEDED(_GetMediaCapabilities(&dwCaps, &fUDF)) && fUDF)
  4899. {
  4900. int id = (dwCaps & HWDMC_CDREWRITABLE) ? IDS_BURN_CANTWRITEMEDIACDRW : IDS_BURN_CANTWRITEMEDIACDR;
  4901. ShellMessageBox(HINST_THISDLL, NULL, MAKEINTRESOURCE(id),
  4902. MAKEINTRESOURCE(IDS_BURN), MB_OK | MB_ICONSTOP);
  4903. hr = E_FAIL; // stop the operation
  4904. }
  4905. }
  4906. return hr;
  4907. }
  4908. typedef struct
  4909. {
  4910. WCHAR szExts[MAX_PATH]; // PathMatchSpec list ("*.wma;*.mp3")
  4911. } FILE_EXTS;
  4912. // handles the audio cd burner extensions
  4913. class CBurnAudioCDExtension : public CObjectWithSite,
  4914. public ICDBurnExt,
  4915. public IDropTarget,
  4916. public IWizardExtension,
  4917. public INamespaceWalkCB
  4918. {
  4919. public:
  4920. // IUnknown methods
  4921. STDMETHOD(QueryInterface)(REFIID riid, void **ppvObj);
  4922. STDMETHOD_(ULONG, AddRef)();
  4923. STDMETHOD_(ULONG, Release)();
  4924. // ICDBurnExt methods
  4925. STDMETHOD(GetSupportedActionTypes)(DWORD *pdwActions);
  4926. // IDropTarget methods
  4927. STDMETHOD(DragEnter)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  4928. STDMETHOD(DragOver)(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  4929. { return E_NOTIMPL; }
  4930. STDMETHOD(DragLeave)(void)
  4931. { return E_NOTIMPL; }
  4932. STDMETHOD(Drop)(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
  4933. // IWizardExtension
  4934. STDMETHOD(AddPages)(HPROPSHEETPAGE *aPages, UINT cPages, UINT *pnPages);
  4935. STDMETHOD(GetFirstPage)(HPROPSHEETPAGE *phPage);
  4936. STDMETHOD(GetLastPage)(HPROPSHEETPAGE *phPage);
  4937. // INamespaceWalkCB
  4938. STDMETHOD(FoundItem)(IShellFolder *psf, LPCITEMIDLIST pidl);
  4939. STDMETHOD(EnterFolder)(IShellFolder *psf, LPCITEMIDLIST pidl)
  4940. { return S_OK; }
  4941. STDMETHOD(LeaveFolder)(IShellFolder *psf, LPCITEMIDLIST pidl)
  4942. { return S_OK; }
  4943. STDMETHOD(InitializeProgressDialog)(LPWSTR *ppszTitle, LPWSTR *ppszCancel)
  4944. { *ppszTitle = NULL; *ppszCancel = NULL; return E_NOTIMPL; }
  4945. static INT_PTR s_MusicDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  4946. { CBurnAudioCDExtension *pwe = s_GetBurnAudioCDExtension(hwnd, uMsg, lParam); return pwe->_MusicDlgProc(hwnd, uMsg, wParam, lParam); }
  4947. private:
  4948. CBurnAudioCDExtension();
  4949. ~CBurnAudioCDExtension();
  4950. LONG _cRef;
  4951. DWORD _cFiles, _cAudioFiles, _cNonAudioFiles; // state vars for walk callback
  4952. BOOL _fBreakOnNonAudioFiles;
  4953. HPROPSHEETPAGE _hpage;
  4954. BOOL _fSelectMusic;
  4955. HDSA _hdsaExtensions; // bunch of FILE_EXTS. we keep them individually for each extension and don't strcat them all
  4956. // together so if one registry entry is hopelessly bad it wont hurt everyone else.
  4957. // hook stuff
  4958. void _AddFileExtsForCLSID(REFCLSID clsid);
  4959. void _AddFileExtsForKey(PCWSTR pszKey);
  4960. void _AddAllExts();
  4961. BOOL _HasAudioExtension(LPCTSTR pszName);
  4962. BOOL _DataObjectHasAllAudioFiles(IDataObject *pdo);
  4963. DWORD _CountOfAudioFilesForHandler(IDataObject *pdo, REFCLSID clsid);
  4964. CLSID _GetDefaultCLSID();
  4965. HRESULT _GetVerbForCLSID(REFCLSID clsid, PWSTR psz, UINT cch);
  4966. BOOL _CanCreate(REFCLSID clsid);
  4967. HRESULT _DropOnHandler(REFCLSID clsid, IDataObject *pdo);
  4968. HRESULT _PickHandler(IDataObject *pdo, CLSID *pclsid);
  4969. // wizard page
  4970. static CBurnAudioCDExtension* s_GetBurnAudioCDExtension(HWND hwnd, UINT uMsg, LPARAM lParam);
  4971. INT_PTR _MusicDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  4972. void _SetCompletionState();
  4973. // "exports"
  4974. friend HRESULT CBurnAudioCDExtension_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv);
  4975. };
  4976. CBurnAudioCDExtension::CBurnAudioCDExtension() :
  4977. _cRef(1)
  4978. {
  4979. _fSelectMusic = TRUE;
  4980. }
  4981. CBurnAudioCDExtension::~CBurnAudioCDExtension()
  4982. {
  4983. ASSERT(!_punkSite);
  4984. DSA_Destroy(_hdsaExtensions);
  4985. }
  4986. // IUnknown
  4987. STDMETHODIMP_(ULONG) CBurnAudioCDExtension::AddRef()
  4988. {
  4989. return InterlockedIncrement(&_cRef);
  4990. }
  4991. STDMETHODIMP_(ULONG) CBurnAudioCDExtension::Release()
  4992. {
  4993. if (InterlockedDecrement(&_cRef))
  4994. return _cRef;
  4995. delete this;
  4996. return 0;
  4997. }
  4998. HRESULT CBurnAudioCDExtension::QueryInterface(REFIID riid, void **ppv)
  4999. {
  5000. static const QITAB qit[] =
  5001. {
  5002. QITABENT(CBurnAudioCDExtension, ICDBurnExt),
  5003. QITABENT(CBurnAudioCDExtension, IDropTarget),
  5004. QITABENT(CBurnAudioCDExtension, IWizardExtension),
  5005. QITABENT(CBurnAudioCDExtension, IObjectWithSite),
  5006. QITABENT(CBurnAudioCDExtension, INamespaceWalkCB),
  5007. { 0 },
  5008. };
  5009. return QISearch(this, qit, riid, ppv);
  5010. }
  5011. STDAPI CBurnAudioCDExtension_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppv)
  5012. {
  5013. if (punkOuter)
  5014. return CLASS_E_NOAGGREGATION;
  5015. CBurnAudioCDExtension *pbe = new CBurnAudioCDExtension();
  5016. if (!pbe)
  5017. return E_OUTOFMEMORY;
  5018. HRESULT hr = pbe->QueryInterface(riid, ppv);
  5019. pbe->Release();
  5020. return hr;
  5021. }
  5022. CBurnAudioCDExtension* CBurnAudioCDExtension::s_GetBurnAudioCDExtension(HWND hwnd, UINT uMsg, LPARAM lParam)
  5023. {
  5024. if (uMsg == WM_INITDIALOG)
  5025. {
  5026. PROPSHEETPAGE *ppsp = (PROPSHEETPAGE*)lParam;
  5027. SetWindowLongPtr(hwnd, GWLP_USERDATA, ppsp->lParam);
  5028. return (CBurnAudioCDExtension*)ppsp->lParam;
  5029. }
  5030. return (CBurnAudioCDExtension*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
  5031. }
  5032. HRESULT CBurnAudioCDExtension::GetSupportedActionTypes(DWORD *pdwActions)
  5033. {
  5034. *pdwActions = CDBE_TYPE_MUSIC;
  5035. return S_OK;
  5036. }
  5037. void CBurnAudioCDExtension::_AddFileExtsForKey(PCWSTR pszKey)
  5038. {
  5039. ASSERT(_hdsaExtensions);
  5040. FILE_EXTS fileexts;
  5041. DWORD cb = sizeof(fileexts.szExts);
  5042. if (ERROR_SUCCESS == SHRegGetUSValue(pszKey, REGVALUE_FILEEXTS, NULL, fileexts.szExts, &cb, FALSE, NULL, 0))
  5043. {
  5044. DSA_AppendItem(_hdsaExtensions, &fileexts);
  5045. }
  5046. }
  5047. void CBurnAudioCDExtension::_AddFileExtsForCLSID(REFCLSID clsid)
  5048. {
  5049. ASSERT(_hdsaExtensions);
  5050. if (_CanCreate(clsid))
  5051. {
  5052. WCHAR szCLSID[GUIDSTR_MAX];
  5053. SHStringFromGUID(clsid, szCLSID, ARRAYSIZE(szCLSID));
  5054. WCHAR szExtKey[MAX_PATH];
  5055. StrCpyN(szExtKey, REGSTR_PATH_AUDIOEXTS, ARRAYSIZE(szExtKey));
  5056. PathAppend(szExtKey, szCLSID);
  5057. _AddFileExtsForKey(szExtKey);
  5058. }
  5059. }
  5060. void CBurnAudioCDExtension::_AddAllExts()
  5061. {
  5062. HUSKEY huskeyExts;
  5063. if (ERROR_SUCCESS == SHRegOpenUSKey(REGSTR_PATH_AUDIOEXTS, KEY_READ, NULL, &huskeyExts, FALSE))
  5064. {
  5065. DWORD dwIndex = 0;
  5066. WCHAR szCLSID[GUIDSTR_MAX];
  5067. DWORD cchCLSID = ARRAYSIZE(szCLSID);
  5068. while (ERROR_SUCCESS == MySHRegEnumUSKey(huskeyExts, dwIndex, szCLSID, &cchCLSID))
  5069. {
  5070. CLSID clsid;
  5071. if (GUIDFromString(szCLSID, &clsid))
  5072. {
  5073. _AddFileExtsForCLSID(clsid);
  5074. }
  5075. cchCLSID = ARRAYSIZE(szCLSID);
  5076. dwIndex++;
  5077. }
  5078. SHRegCloseUSKey(huskeyExts);
  5079. }
  5080. }
  5081. BOOL CBurnAudioCDExtension::_HasAudioExtension(LPCTSTR pszName)
  5082. {
  5083. // this list of extensions is based on what files the extension can BURN, not just play.
  5084. // (so we cant do a check for generic audio type.)
  5085. BOOL fRet = FALSE;
  5086. if (_hdsaExtensions)
  5087. {
  5088. for (int i = 0; !fRet && (i < DSA_GetItemCount(_hdsaExtensions)); i++)
  5089. {
  5090. FILE_EXTS *pfileexts = (FILE_EXTS *)DSA_GetItemPtr(_hdsaExtensions, i);
  5091. fRet = PathMatchSpec(pszName, pfileexts->szExts);
  5092. }
  5093. }
  5094. return fRet;
  5095. }
  5096. HRESULT CBurnAudioCDExtension::FoundItem(IShellFolder *psf, LPCITEMIDLIST pidl)
  5097. {
  5098. // early-out if we are checking that ALL files are audio files and we've found one that's not.
  5099. if (_fBreakOnNonAudioFiles && (_cNonAudioFiles > 0))
  5100. return E_FAIL;
  5101. // break if we've seen enough files.
  5102. if (_cFiles > 50)
  5103. return E_FAIL;
  5104. _cFiles++;
  5105. TCHAR szName[MAX_PATH];
  5106. HRESULT hr = DisplayNameOf(psf, pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szName, ARRAYSIZE(szName));
  5107. if (SUCCEEDED(hr))
  5108. {
  5109. if (_HasAudioExtension(szName))
  5110. {
  5111. _cAudioFiles++;
  5112. }
  5113. else
  5114. {
  5115. _cNonAudioFiles++;
  5116. }
  5117. }
  5118. return hr;
  5119. }
  5120. BOOL CBurnAudioCDExtension::_DataObjectHasAllAudioFiles(IDataObject *pdo)
  5121. {
  5122. _cFiles = _cAudioFiles = _cNonAudioFiles = 0;
  5123. _fBreakOnNonAudioFiles = TRUE;
  5124. if (_hdsaExtensions)
  5125. DSA_Destroy(_hdsaExtensions);
  5126. _hdsaExtensions = DSA_Create(sizeof(FILE_EXTS), 4);
  5127. if (_hdsaExtensions)
  5128. {
  5129. _AddAllExts();
  5130. INamespaceWalk *pnsw;
  5131. if (SUCCEEDED(CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw))))
  5132. {
  5133. pnsw->Walk(pdo, NSWF_DONT_ACCUMULATE_RESULT, 4, this);
  5134. pnsw->Release();
  5135. }
  5136. }
  5137. return ((_cFiles > 0) && (_cNonAudioFiles == 0));
  5138. }
  5139. DWORD CBurnAudioCDExtension::_CountOfAudioFilesForHandler(IDataObject *pdo, REFCLSID clsid)
  5140. {
  5141. _cFiles = _cAudioFiles = _cNonAudioFiles = 0;
  5142. if (pdo)
  5143. {
  5144. _fBreakOnNonAudioFiles = FALSE;
  5145. if (_hdsaExtensions)
  5146. DSA_Destroy(_hdsaExtensions);
  5147. _hdsaExtensions = DSA_Create(sizeof(FILE_EXTS), 4);
  5148. if (_hdsaExtensions)
  5149. {
  5150. _AddFileExtsForCLSID(clsid);
  5151. INamespaceWalk *pnsw;
  5152. if (SUCCEEDED(CoCreateInstance(CLSID_NamespaceWalker, NULL, CLSCTX_INPROC, IID_PPV_ARG(INamespaceWalk, &pnsw))))
  5153. {
  5154. pnsw->Walk(pdo, NSWF_DONT_ACCUMULATE_RESULT, 4, this);
  5155. pnsw->Release();
  5156. }
  5157. }
  5158. }
  5159. else
  5160. {
  5161. // if we're not given a data object, we're being polled just to see if we can create
  5162. if (_CanCreate(clsid))
  5163. {
  5164. _cAudioFiles = 1;
  5165. }
  5166. }
  5167. return _cAudioFiles;
  5168. }
  5169. BOOL CBurnAudioCDExtension::_CanCreate(REFCLSID clsid)
  5170. {
  5171. // Use SHExtCoCreateInstance to go through approval checking and app compat checking
  5172. IUnknown *punk;
  5173. HRESULT hr = SHExtCoCreateInstance2(NULL, &clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IUnknown, &punk));
  5174. if (SUCCEEDED(hr))
  5175. {
  5176. punk->Release();
  5177. }
  5178. return SUCCEEDED(hr);
  5179. }
  5180. // if pdo is NULL, only test which extensions can be cocreated.
  5181. HRESULT CBurnAudioCDExtension::_PickHandler(IDataObject *pdo, CLSID *pclsid)
  5182. {
  5183. HRESULT hr;
  5184. *pclsid = _GetDefaultCLSID();
  5185. if (_CountOfAudioFilesForHandler(pdo, *pclsid) > 0)
  5186. {
  5187. // if the default handler supports any of the files, let it take over and we're done.
  5188. hr = S_OK;
  5189. }
  5190. else
  5191. {
  5192. hr = E_FAIL;
  5193. CLSID clsidBestSoFar;
  5194. DWORD dwCountBestSoFar = 0;
  5195. HUSKEY huskeyExts;
  5196. if (ERROR_SUCCESS == SHRegOpenUSKey(REGSTR_PATH_AUDIOEXTS, KEY_READ, NULL, &huskeyExts, FALSE))
  5197. {
  5198. DWORD dwIndex = 0;
  5199. WCHAR szCLSID[GUIDSTR_MAX];
  5200. DWORD cchKey = ARRAYSIZE(szCLSID);
  5201. while (ERROR_SUCCESS == MySHRegEnumUSKey(huskeyExts, dwIndex, szCLSID, &cchKey))
  5202. {
  5203. CLSID clsidExt;
  5204. if (GUIDFromString(szCLSID, &clsidExt))
  5205. {
  5206. DWORD dwCountExt = _CountOfAudioFilesForHandler(pdo, clsidExt);
  5207. if (dwCountExt > dwCountBestSoFar)
  5208. {
  5209. hr = S_OK;
  5210. dwCountBestSoFar = dwCountExt;
  5211. clsidBestSoFar = clsidExt;
  5212. }
  5213. }
  5214. cchKey = ARRAYSIZE(szCLSID);
  5215. dwIndex++;
  5216. }
  5217. SHRegCloseUSKey(huskeyExts);
  5218. }
  5219. if (SUCCEEDED(hr))
  5220. {
  5221. *pclsid = clsidBestSoFar;
  5222. }
  5223. }
  5224. return hr;
  5225. }
  5226. HRESULT CBurnAudioCDExtension::DragEnter(IDataObject *pdo, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  5227. {
  5228. *pdwEffect = DROPEFFECT_NONE;
  5229. // on DTC even the default handler may or may not be installed, so do an early check if we can create the object.
  5230. // cache it since this DragEnter is hit determining if the "burn audio cd" task show up.
  5231. static int s_fBurnHandlerAvailable = -1;
  5232. if (s_fBurnHandlerAvailable == -1)
  5233. {
  5234. CLSID clsid;
  5235. if (SUCCEEDED(_PickHandler(NULL, &clsid)))
  5236. {
  5237. s_fBurnHandlerAvailable = 1;
  5238. }
  5239. else
  5240. {
  5241. s_fBurnHandlerAvailable = 0;
  5242. }
  5243. }
  5244. ICDBurnPriv *pcdbp;
  5245. if (s_fBurnHandlerAvailable && SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_CDBurn, NULL, IID_PPV_ARG(ICDBurnPriv, &pcdbp))))
  5246. {
  5247. // mini-hack: we want the web view task "copy to audio cd" to always show up no matter what
  5248. // the current state of the media is, however we only want to show the wizard page if it's blank.
  5249. // so we check against if the wizard is active or not now.
  5250. // this has the added effect of disabling the "copy to audio cd" task if we're burning in
  5251. // the wizard -- which is probably what we want so its okay.
  5252. if (_DataObjectHasAllAudioFiles(pdo))
  5253. {
  5254. BOOL fOnMedia;
  5255. // if we're not running the wizard, go for it.
  5256. // if we are running the wizard, make sure there are no files on the media.
  5257. if ((S_OK != pcdbp->IsWizardUp()) || (SUCCEEDED(pcdbp->GetContentState(NULL, &fOnMedia)) && !fOnMedia))
  5258. {
  5259. *pdwEffect = DROPEFFECT_COPY;
  5260. }
  5261. }
  5262. pcdbp->Release();
  5263. }
  5264. return S_OK;
  5265. }
  5266. CLSID CBurnAudioCDExtension::_GetDefaultCLSID()
  5267. {
  5268. WCHAR szCLSID[GUIDSTR_MAX];
  5269. DWORD cb = sizeof(szCLSID);
  5270. CLSID clsid;
  5271. if ((ERROR_SUCCESS != SHRegGetUSValue(REGSTR_PATH_AUDIOEXTS, L"", NULL, szCLSID, &cb, FALSE, NULL, 0)) ||
  5272. (!GUIDFromString(szCLSID, &clsid)))
  5273. {
  5274. clsid = CLSID_NULL; // default to this, if registry entry is not there or erroneous
  5275. }
  5276. return clsid;
  5277. }
  5278. HRESULT CBurnAudioCDExtension::_GetVerbForCLSID(REFCLSID clsid, PWSTR psz, UINT cch)
  5279. {
  5280. WCHAR szCLSID[GUIDSTR_MAX];
  5281. SHStringFromGUID(clsid, szCLSID, ARRAYSIZE(szCLSID));
  5282. WCHAR szExtensionRegPath[MAX_PATH];
  5283. lstrcpyn(szExtensionRegPath, REGSTR_PATH_AUDIOEXTS, ARRAYSIZE(szExtensionRegPath));
  5284. PathAppend(szExtensionRegPath, szCLSID);
  5285. DWORD cbVerb = cch * sizeof(*psz);
  5286. return (ERROR_SUCCESS == SHRegGetUSValue(szExtensionRegPath, REGVALUE_VERB, NULL, psz, &cbVerb, FALSE, NULL, 0)) ? S_OK : E_FAIL;
  5287. }
  5288. HRESULT CBurnAudioCDExtension::_DropOnHandler(REFCLSID clsid, IDataObject *pdo)
  5289. {
  5290. WCHAR wzVerb[20];
  5291. HRESULT hr = _GetVerbForCLSID(clsid, wzVerb, ARRAYSIZE(wzVerb));
  5292. if (SUCCEEDED(hr))
  5293. {
  5294. IShellExtInit *psei;
  5295. hr = SHExtCoCreateInstance2(NULL, &clsid, NULL, CLSCTX_ALL, IID_PPV_ARG(IShellExtInit, &psei));
  5296. if (SUCCEEDED(hr))
  5297. {
  5298. hr = psei->Initialize(NULL, pdo, NULL);
  5299. if (SUCCEEDED(hr))
  5300. {
  5301. IContextMenu *pcm;
  5302. hr = psei->QueryInterface(IID_PPV_ARG(IContextMenu, &pcm));
  5303. if (SUCCEEDED(hr))
  5304. {
  5305. HMENU hmenu = CreatePopupMenu();
  5306. if (hmenu)
  5307. {
  5308. hr = pcm->QueryContextMenu(hmenu, 0, 0x1, 0x7fff, 0);
  5309. if (SUCCEEDED(hr))
  5310. {
  5311. CMINVOKECOMMANDINFOEX ici = { 0 };
  5312. ici.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
  5313. ici.fMask = CMIC_MASK_UNICODE;
  5314. ici.lpVerbW = wzVerb;
  5315. CHAR szVerbAnsi[20];
  5316. SHTCharToAnsi(wzVerb, szVerbAnsi, ARRAYSIZE(szVerbAnsi));
  5317. ici.lpVerb = szVerbAnsi;
  5318. ici.nShow = SW_NORMAL;
  5319. hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)(&ici));
  5320. }
  5321. DestroyMenu(hmenu);
  5322. }
  5323. else
  5324. {
  5325. hr = E_OUTOFMEMORY;
  5326. }
  5327. pcm->Release();
  5328. }
  5329. }
  5330. psei->Release();
  5331. }
  5332. }
  5333. return hr;
  5334. }
  5335. HRESULT CBurnAudioCDExtension::Drop(IDataObject *pdo, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
  5336. {
  5337. CLSID clsid;
  5338. HRESULT hr = _PickHandler(pdo, &clsid);
  5339. if (SUCCEEDED(hr))
  5340. {
  5341. _DropOnHandler(clsid, pdo);
  5342. }
  5343. return hr;
  5344. }
  5345. HRESULT CBurnAudioCDExtension::GetLastPage(HPROPSHEETPAGE *phPage)
  5346. {
  5347. *phPage = _hpage;
  5348. return S_OK;
  5349. }
  5350. HRESULT CBurnAudioCDExtension::GetFirstPage(HPROPSHEETPAGE *phPage)
  5351. {
  5352. *phPage = _hpage;
  5353. return S_OK;
  5354. }
  5355. HRESULT CBurnAudioCDExtension::AddPages(HPROPSHEETPAGE *aPages, UINT cPages, UINT *pnPages)
  5356. {
  5357. *pnPages = 0;
  5358. WIZPAGE c_wp =
  5359. {DLG_BURNWIZ_MUSIC, IDS_BURNWIZ_MUSIC_HEAD, IDS_BURNWIZ_MUSIC_SUB, 0, CBurnAudioCDExtension::s_MusicDlgProc};
  5360. _hpage = _CreatePropPageFromInfo(&c_wp, (LPARAM)this);
  5361. if (cPages > 0)
  5362. {
  5363. aPages[0] = _hpage;
  5364. *pnPages = 1;
  5365. }
  5366. return S_OK;
  5367. }
  5368. // pushes the return state back to the main wizard
  5369. void CBurnAudioCDExtension::_SetCompletionState()
  5370. {
  5371. IPropertyBag *ppb;
  5372. if (SUCCEEDED(IUnknown_QueryService(_punkSite, SID_CDWizardHost, IID_PPV_ARG(IPropertyBag, &ppb))))
  5373. {
  5374. SHPropertyBag_WriteDWORD(ppb, PROPSTR_EXTENSIONCOMPLETIONSTATE, CDBE_RET_STOPWIZARD);
  5375. ppb->Release();
  5376. }
  5377. }
  5378. INT_PTR CBurnAudioCDExtension::_MusicDlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  5379. {
  5380. BOOL fRet = FALSE;
  5381. switch (uMsg)
  5382. {
  5383. case WM_NOTIFY:
  5384. {
  5385. LPNMHDR pnmh = (LPNMHDR)lParam;
  5386. switch (pnmh->code)
  5387. {
  5388. case PSN_SETACTIVE:
  5389. PropSheet_SetWizButtons(pnmh->hwndFrom, PSWIZB_BACK | PSWIZB_NEXT);
  5390. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNAUDIO), _fSelectMusic ? BST_CHECKED : BST_UNCHECKED);
  5391. Button_SetCheck(GetDlgItem(hwnd, IDC_BURNWIZ_BURNDATA), _fSelectMusic ? BST_UNCHECKED : BST_CHECKED);
  5392. fRet = TRUE;
  5393. break;
  5394. case PSN_WIZBACK:
  5395. if (_punkSite)
  5396. {
  5397. IWizardSite *pws;
  5398. if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IWizardSite, &pws))))
  5399. {
  5400. HPROPSHEETPAGE hpage;
  5401. if (SUCCEEDED(pws->GetPreviousPage(&hpage)))
  5402. {
  5403. PropSheet_SetCurSel(GetParent(hwnd), hpage, -1);
  5404. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
  5405. }
  5406. pws->Release();
  5407. }
  5408. }
  5409. fRet = TRUE;
  5410. break;
  5411. case PSN_WIZNEXT:
  5412. if (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNAUDIO) == BST_CHECKED)
  5413. {
  5414. IDataObject *pdo;
  5415. if (SUCCEEDED(_CreateDataObject(&pdo)))
  5416. {
  5417. POINTL pt = {0};
  5418. if (SUCCEEDED(Drop(pdo, 0, pt, NULL)))
  5419. {
  5420. _SetCompletionState();
  5421. }
  5422. pdo->Release();
  5423. }
  5424. }
  5425. if (_punkSite)
  5426. {
  5427. IWizardSite *pws;
  5428. if (SUCCEEDED(_punkSite->QueryInterface(IID_PPV_ARG(IWizardSite, &pws))))
  5429. {
  5430. HPROPSHEETPAGE hpage;
  5431. if (SUCCEEDED(pws->GetNextPage(&hpage)))
  5432. {
  5433. PropSheet_SetCurSel(GetParent(hwnd), hpage, -1);
  5434. SetWindowLongPtr(hwnd, DWLP_MSGRESULT, (LPARAM)-1);
  5435. }
  5436. pws->Release();
  5437. }
  5438. }
  5439. fRet = TRUE;
  5440. break;
  5441. case PSN_KILLACTIVE:
  5442. _fSelectMusic = (IsDlgButtonChecked(hwnd, IDC_BURNWIZ_BURNAUDIO) == BST_CHECKED);
  5443. break;
  5444. }
  5445. break;
  5446. }
  5447. }
  5448. return fRet;
  5449. }