nfd_win.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720
  1. /*
  2. Native File Dialog
  3. http://www.frogtoss.com/labs
  4. */
  5. /* only locally define UNICODE in this compilation unit */
  6. #ifndef UNICODE
  7. #define UNICODE
  8. #endif
  9. #include <wchar.h>
  10. #include <stdio.h>
  11. #include <assert.h>
  12. //ATOMIC: Removed reference to ATL
  13. //#include <atlbase.h>
  14. #include <windows.h>
  15. #include <ShObjIdl.h>
  16. #include "nfd_common.h"
  17. // allocs the space in outPath -- call free()
  18. static void CopyWCharToNFDChar( const wchar_t *inStr, nfdchar_t **outStr )
  19. {
  20. int inStrCharacterCount = static_cast<int>(wcslen(inStr));
  21. int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0,
  22. inStr, inStrCharacterCount,
  23. NULL, 0, NULL, NULL );
  24. assert( bytesNeeded );
  25. bytesNeeded += 1;
  26. *outStr = (nfdchar_t*)NFDi_Malloc( bytesNeeded );
  27. if ( !*outStr )
  28. return;
  29. int bytesWritten = WideCharToMultiByte( CP_UTF8, 0,
  30. inStr, -1,
  31. *outStr, bytesNeeded,
  32. NULL, NULL );
  33. assert( bytesWritten ); _NFD_UNUSED( bytesWritten );
  34. }
  35. /* includes NULL terminator byte in return */
  36. static size_t GetUTF8ByteCountForWChar( const wchar_t *str )
  37. {
  38. int bytesNeeded = WideCharToMultiByte( CP_UTF8, 0,
  39. str, -1,
  40. NULL, 0, NULL, NULL );
  41. assert( bytesNeeded );
  42. return bytesNeeded+1;
  43. }
  44. // write to outPtr -- no free() necessary. No memory stomp tests are done -- they must be done
  45. // before entering this function.
  46. static int CopyWCharToExistingNFDCharBuffer( const wchar_t *inStr, nfdchar_t *outPtr )
  47. {
  48. int inStrCharacterCount = static_cast<int>(wcslen(inStr));
  49. int bytesNeeded = static_cast<int>(GetUTF8ByteCountForWChar( inStr ));
  50. /* invocation copies null term */
  51. int bytesWritten = WideCharToMultiByte( CP_UTF8, 0,
  52. inStr, -1,
  53. outPtr, bytesNeeded,
  54. NULL, 0 );
  55. assert( bytesWritten );
  56. return bytesWritten;
  57. }
  58. // allocs the space in outStr -- call free()
  59. static void CopyNFDCharToWChar( const nfdchar_t *inStr, wchar_t **outStr )
  60. {
  61. int inStrByteCount = static_cast<int>(strlen(inStr));
  62. int charsNeeded = MultiByteToWideChar(CP_UTF8, 0,
  63. inStr, inStrByteCount,
  64. NULL, 0 );
  65. assert( charsNeeded );
  66. assert( !*outStr );
  67. charsNeeded += 1; // terminator
  68. *outStr = (wchar_t*)NFDi_Malloc( charsNeeded * sizeof(wchar_t) );
  69. if ( !*outStr )
  70. return;
  71. int ret = MultiByteToWideChar(CP_UTF8, 0,
  72. inStr, inStrByteCount,
  73. *outStr, charsNeeded);
  74. (*outStr)[charsNeeded-1] = '\0';
  75. #ifdef _DEBUG
  76. int inStrCharacterCount = static_cast<int>(NFDi_UTF8_Strlen(inStr));
  77. assert( ret == inStrCharacterCount );
  78. #else
  79. _NFD_UNUSED(ret);
  80. #endif
  81. }
  82. /* ext is in format "jpg", no wildcards or separators */
  83. static int AppendExtensionToSpecBuf( const char *ext, char *specBuf, size_t specBufLen )
  84. {
  85. const char SEP[] = ";";
  86. assert( specBufLen > strlen(ext)+3 );
  87. if ( strlen(specBuf) > 0 )
  88. {
  89. strncat( specBuf, SEP, specBufLen - strlen(specBuf) - 1 );
  90. specBufLen += strlen(SEP);
  91. }
  92. char extWildcard[NFD_MAX_STRLEN];
  93. int bytesWritten = sprintf_s( extWildcard, NFD_MAX_STRLEN, "*.%s", ext );
  94. assert( bytesWritten == strlen(ext)+2 );
  95. strncat( specBuf, extWildcard, specBufLen - strlen(specBuf) - 1 );
  96. return NFD_OKAY;
  97. }
  98. static nfdresult_t AddFiltersToDialog( ::IFileDialog *fileOpenDialog, const char *filterList )
  99. {
  100. const wchar_t EMPTY_WSTR[] = L"";
  101. const wchar_t WILDCARD[] = L"*.*";
  102. if ( !filterList || strlen(filterList) == 0 )
  103. return NFD_OKAY;
  104. // Count rows to alloc
  105. size_t filterCount = 1; /* guaranteed to have one filter on a correct, non-empty parse */
  106. const char *p_filterList;
  107. for ( p_filterList = filterList; *p_filterList; ++p_filterList )
  108. {
  109. if ( *p_filterList == ';' )
  110. ++filterCount;
  111. }
  112. assert(filterCount);
  113. if ( !filterCount )
  114. {
  115. NFDi_SetError("Error parsing filters.");
  116. return NFD_ERROR;
  117. }
  118. /* filterCount plus 1 because we hardcode the *.* wildcard after the while loop */
  119. COMDLG_FILTERSPEC *specList = (COMDLG_FILTERSPEC*)NFDi_Malloc( sizeof(COMDLG_FILTERSPEC) * (filterCount + 1) );
  120. if ( !specList )
  121. {
  122. return NFD_ERROR;
  123. }
  124. for (size_t i = 0; i < filterCount+1; ++i )
  125. {
  126. specList[i].pszName = NULL;
  127. specList[i].pszSpec = NULL;
  128. }
  129. size_t specIdx = 0;
  130. p_filterList = filterList;
  131. char typebuf[NFD_MAX_STRLEN] = {0}; /* one per comma or semicolon */
  132. char *p_typebuf = typebuf;
  133. char filterName[NFD_MAX_STRLEN] = {0};
  134. char specbuf[NFD_MAX_STRLEN] = {0}; /* one per semicolon */
  135. while ( 1 )
  136. {
  137. if ( NFDi_IsFilterSegmentChar(*p_filterList) )
  138. {
  139. /* append a type to the specbuf (pending filter) */
  140. AppendExtensionToSpecBuf( typebuf, specbuf, NFD_MAX_STRLEN );
  141. p_typebuf = typebuf;
  142. memset( typebuf, 0, sizeof(char)*NFD_MAX_STRLEN );
  143. }
  144. if ( *p_filterList == ';' || *p_filterList == '\0' )
  145. {
  146. /* end of filter -- add it to specList */
  147. // Empty filter name -- Windows describes them by extension.
  148. specList[specIdx].pszName = EMPTY_WSTR;
  149. CopyNFDCharToWChar( specbuf, (wchar_t**)&specList[specIdx].pszSpec );
  150. memset( specbuf, 0, sizeof(char)*NFD_MAX_STRLEN );
  151. ++specIdx;
  152. if ( specIdx == filterCount )
  153. break;
  154. }
  155. if ( !NFDi_IsFilterSegmentChar( *p_filterList ))
  156. {
  157. *p_typebuf = *p_filterList;
  158. ++p_typebuf;
  159. }
  160. ++p_filterList;
  161. }
  162. /* Add wildcard */
  163. specList[specIdx].pszSpec = WILDCARD;
  164. specList[specIdx].pszName = EMPTY_WSTR;
  165. fileOpenDialog->SetFileTypes( static_cast<UINT>(filterCount+1), specList );
  166. /* free speclist */
  167. for ( size_t i = 0; i < filterCount; ++i )
  168. {
  169. NFDi_Free( (void*)specList[i].pszSpec );
  170. }
  171. NFDi_Free( specList );
  172. return NFD_OKAY;
  173. }
  174. static nfdresult_t AllocPathSet( IShellItemArray *shellItems, nfdpathset_t *pathSet )
  175. {
  176. const char ERRORMSG[] = "Error allocating pathset.";
  177. assert(shellItems);
  178. assert(pathSet);
  179. // How many items in shellItems?
  180. DWORD numShellItems;
  181. HRESULT result = shellItems->GetCount(&numShellItems);
  182. if ( !SUCCEEDED(result) )
  183. {
  184. NFDi_SetError(ERRORMSG);
  185. return NFD_ERROR;
  186. }
  187. pathSet->count = static_cast<size_t>(numShellItems);
  188. assert( pathSet->count > 0 );
  189. pathSet->indices = (size_t*)NFDi_Malloc( sizeof(size_t)*pathSet->count );
  190. if ( !pathSet->indices )
  191. {
  192. return NFD_ERROR;
  193. }
  194. /* count the total bytes needed for buf */
  195. size_t bufSize = 0;
  196. for ( DWORD i = 0; i < numShellItems; ++i )
  197. {
  198. ::IShellItem *shellItem;
  199. result = shellItems->GetItemAt(i, &shellItem);
  200. if ( !SUCCEEDED(result) )
  201. {
  202. NFDi_SetError(ERRORMSG);
  203. return NFD_ERROR;
  204. }
  205. // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it.
  206. SFGAOF attribs;
  207. result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs );
  208. if ( !SUCCEEDED(result) )
  209. {
  210. NFDi_SetError(ERRORMSG);
  211. return NFD_ERROR;
  212. }
  213. if ( !(attribs & SFGAO_FILESYSTEM) )
  214. continue;
  215. LPWSTR name;
  216. shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name);
  217. // Calculate length of name with UTF-8 encoding
  218. bufSize += GetUTF8ByteCountForWChar( name );
  219. }
  220. assert(bufSize);
  221. pathSet->buf = (nfdchar_t*)NFDi_Malloc( sizeof(nfdchar_t) * bufSize );
  222. memset( pathSet->buf, 0, sizeof(nfdchar_t) * bufSize );
  223. /* fill buf */
  224. nfdchar_t *p_buf = pathSet->buf;
  225. for (DWORD i = 0; i < numShellItems; ++i )
  226. {
  227. ::IShellItem *shellItem;
  228. result = shellItems->GetItemAt(i, &shellItem);
  229. if ( !SUCCEEDED(result) )
  230. {
  231. NFDi_SetError(ERRORMSG);
  232. return NFD_ERROR;
  233. }
  234. // Confirm SFGAO_FILESYSTEM is true for this shellitem, or ignore it.
  235. SFGAOF attribs;
  236. result = shellItem->GetAttributes( SFGAO_FILESYSTEM, &attribs );
  237. if ( !SUCCEEDED(result) )
  238. {
  239. NFDi_SetError(ERRORMSG);
  240. return NFD_ERROR;
  241. }
  242. if ( !(attribs & SFGAO_FILESYSTEM) )
  243. continue;
  244. LPWSTR name;
  245. shellItem->GetDisplayName(SIGDN_FILESYSPATH, &name);
  246. int bytesWritten = CopyWCharToExistingNFDCharBuffer(name, p_buf);
  247. ptrdiff_t index = p_buf - pathSet->buf;
  248. assert( index >= 0 );
  249. pathSet->indices[i] = static_cast<size_t>(index);
  250. p_buf += bytesWritten;
  251. }
  252. return NFD_OKAY;
  253. }
  254. static nfdresult_t SetDefaultPath( IFileDialog *dialog, const char *defaultPath )
  255. {
  256. if ( !defaultPath || strlen(defaultPath) == 0 )
  257. return NFD_OKAY;
  258. wchar_t *defaultPathW = {0};
  259. CopyNFDCharToWChar( defaultPath, &defaultPathW );
  260. IShellItem *folder;
  261. HRESULT result = SHCreateItemFromParsingName( defaultPathW, NULL, IID_PPV_ARGS(&folder) );
  262. // Valid non results.
  263. if ( result == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || result == HRESULT_FROM_WIN32(ERROR_INVALID_DRIVE) )
  264. {
  265. NFDi_Free( defaultPathW );
  266. return NFD_OKAY;
  267. }
  268. if ( !SUCCEEDED(result) )
  269. {
  270. NFDi_SetError("Error creating ShellItem");
  271. NFDi_Free( defaultPathW );
  272. return NFD_ERROR;
  273. }
  274. // Could also call SetDefaultFolder(), but this guarantees defaultPath -- more consistency across API.
  275. dialog->SetFolder( folder );
  276. NFDi_Free( defaultPathW );
  277. folder->Release();
  278. return NFD_OKAY;
  279. }
  280. /* public */
  281. nfdresult_t NFD_OpenDialog( const char *filterList,
  282. const nfdchar_t *defaultPath,
  283. nfdchar_t **outPath )
  284. {
  285. nfdresult_t nfdResult = NFD_ERROR;
  286. // Init COM library.
  287. HRESULT result = ::CoInitializeEx(NULL,
  288. ::COINIT_APARTMENTTHREADED |
  289. ::COINIT_DISABLE_OLE1DDE );
  290. if ( !SUCCEEDED(result))
  291. {
  292. NFDi_SetError("Could not initialize COM.");
  293. goto end;
  294. }
  295. ::IFileOpenDialog *fileOpenDialog(NULL);
  296. // Create dialog
  297. result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
  298. CLSCTX_ALL, ::IID_IFileOpenDialog,
  299. reinterpret_cast<void**>(&fileOpenDialog) );
  300. if ( !SUCCEEDED(result) )
  301. {
  302. NFDi_SetError("Could not create dialog.");
  303. goto end;
  304. }
  305. // Build the filter list
  306. if ( !AddFiltersToDialog( fileOpenDialog, filterList ) )
  307. {
  308. goto end;
  309. }
  310. // Set the default path
  311. if ( !SetDefaultPath( fileOpenDialog, defaultPath ) )
  312. {
  313. goto end;
  314. }
  315. // Show the dialog.
  316. result = fileOpenDialog->Show(NULL);
  317. if ( SUCCEEDED(result) )
  318. {
  319. // Get the file name
  320. ::IShellItem *shellItem(NULL);
  321. result = fileOpenDialog->GetResult(&shellItem);
  322. if ( !SUCCEEDED(result) )
  323. {
  324. NFDi_SetError("Could not get shell item from dialog.");
  325. goto end;
  326. }
  327. wchar_t *filePath(NULL);
  328. result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
  329. if ( !SUCCEEDED(result) )
  330. {
  331. NFDi_SetError("Could not get file path for selected.");
  332. goto end;
  333. }
  334. CopyWCharToNFDChar( filePath, outPath );
  335. CoTaskMemFree(filePath);
  336. if ( !*outPath )
  337. {
  338. /* error is malloc-based, error message would be redundant */
  339. goto end;
  340. }
  341. nfdResult = NFD_OKAY;
  342. shellItem->Release();
  343. }
  344. else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
  345. {
  346. nfdResult = NFD_CANCEL;
  347. }
  348. else
  349. {
  350. NFDi_SetError("File dialog box show failed.");
  351. nfdResult = NFD_ERROR;
  352. }
  353. end:
  354. ::CoUninitialize();
  355. return nfdResult;
  356. }
  357. nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
  358. const nfdchar_t *defaultPath,
  359. nfdpathset_t *outPaths )
  360. {
  361. nfdresult_t nfdResult = NFD_ERROR;
  362. // Init COM library.
  363. HRESULT result = ::CoInitializeEx(NULL,
  364. ::COINIT_APARTMENTTHREADED |
  365. ::COINIT_DISABLE_OLE1DDE );
  366. if ( !SUCCEEDED(result))
  367. {
  368. NFDi_SetError("Could not initialize COM.");
  369. return NFD_ERROR;
  370. }
  371. ::IFileOpenDialog *fileOpenDialog(NULL);
  372. // Create dialog
  373. result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
  374. CLSCTX_ALL, ::IID_IFileOpenDialog,
  375. reinterpret_cast<void**>(&fileOpenDialog) );
  376. if ( !SUCCEEDED(result) )
  377. {
  378. NFDi_SetError("Could not create dialog.");
  379. goto end;
  380. }
  381. // Build the filter list
  382. if ( !AddFiltersToDialog( fileOpenDialog, filterList ) )
  383. {
  384. goto end;
  385. }
  386. // Set the default path
  387. if ( !SetDefaultPath( fileOpenDialog, defaultPath ) )
  388. {
  389. goto end;
  390. }
  391. // Set a flag for multiple options
  392. DWORD dwFlags;
  393. result = fileOpenDialog->GetOptions(&dwFlags);
  394. if ( !SUCCEEDED(result) )
  395. {
  396. NFDi_SetError("Could not get options.");
  397. goto end;
  398. }
  399. result = fileOpenDialog->SetOptions(dwFlags | FOS_ALLOWMULTISELECT);
  400. if ( !SUCCEEDED(result) )
  401. {
  402. NFDi_SetError("Could not set options.");
  403. goto end;
  404. }
  405. // Show the dialog.
  406. result = fileOpenDialog->Show(NULL);
  407. if ( SUCCEEDED(result) )
  408. {
  409. IShellItemArray *shellItems;
  410. result = fileOpenDialog->GetResults( &shellItems );
  411. if ( !SUCCEEDED(result) )
  412. {
  413. NFDi_SetError("Could not get shell items.");
  414. goto end;
  415. }
  416. if ( AllocPathSet( shellItems, outPaths ) == NFD_ERROR )
  417. {
  418. goto end;
  419. }
  420. shellItems->Release();
  421. nfdResult = NFD_OKAY;
  422. }
  423. else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
  424. {
  425. nfdResult = NFD_CANCEL;
  426. }
  427. else
  428. {
  429. NFDi_SetError("File dialog box show failed.");
  430. nfdResult = NFD_ERROR;
  431. }
  432. end:
  433. ::CoUninitialize();
  434. return nfdResult;
  435. }
  436. nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
  437. const nfdchar_t *defaultPath,
  438. nfdchar_t **outPath )
  439. {
  440. nfdresult_t nfdResult = NFD_ERROR;
  441. // Init COM library.
  442. HRESULT result = ::CoInitializeEx(NULL,
  443. ::COINIT_APARTMENTTHREADED |
  444. ::COINIT_DISABLE_OLE1DDE );
  445. if ( !SUCCEEDED(result))
  446. {
  447. NFDi_SetError("Could not initialize COM.");
  448. return NFD_ERROR;
  449. }
  450. ::IFileSaveDialog *fileSaveDialog(NULL);
  451. // Create dialog
  452. result = ::CoCreateInstance(::CLSID_FileSaveDialog, NULL,
  453. CLSCTX_ALL, ::IID_IFileSaveDialog,
  454. reinterpret_cast<void**>(&fileSaveDialog) );
  455. if ( !SUCCEEDED(result) )
  456. {
  457. NFDi_SetError("Could not create dialog.");
  458. goto end;
  459. }
  460. // Build the filter list
  461. if ( !AddFiltersToDialog( fileSaveDialog, filterList ) )
  462. {
  463. goto end;
  464. }
  465. // Set the default path
  466. if ( !SetDefaultPath( fileSaveDialog, defaultPath ) )
  467. {
  468. goto end;
  469. }
  470. // Show the dialog.
  471. result = fileSaveDialog->Show(NULL);
  472. if ( SUCCEEDED(result) )
  473. {
  474. // Get the file name
  475. ::IShellItem *shellItem;
  476. result = fileSaveDialog->GetResult(&shellItem);
  477. if ( !SUCCEEDED(result) )
  478. {
  479. NFDi_SetError("Could not get shell item from dialog.");
  480. goto end;
  481. }
  482. wchar_t *filePath(NULL);
  483. result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
  484. if ( !SUCCEEDED(result) )
  485. {
  486. NFDi_SetError("Could not get file path for selected.");
  487. goto end;
  488. }
  489. CopyWCharToNFDChar( filePath, outPath );
  490. CoTaskMemFree(filePath);
  491. if ( !*outPath )
  492. {
  493. /* error is malloc-based, error message would be redundant */
  494. goto end;
  495. }
  496. nfdResult = NFD_OKAY;
  497. shellItem->Release();
  498. }
  499. else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
  500. {
  501. nfdResult = NFD_CANCEL;
  502. }
  503. else
  504. {
  505. NFDi_SetError("File dialog box show failed.");
  506. nfdResult = NFD_ERROR;
  507. }
  508. end:
  509. ::CoUninitialize();
  510. return nfdResult;
  511. }
  512. nfdresult_t NFD_ChooseDirectory(const nfdchar_t *prompt, const nfdchar_t *defaultPath,
  513. nfdchar_t **outPath )
  514. {
  515. nfdresult_t nfdResult = NFD_ERROR;
  516. // Init COM library.
  517. HRESULT result = ::CoInitializeEx(NULL,
  518. ::COINIT_APARTMENTTHREADED |
  519. ::COINIT_DISABLE_OLE1DDE );
  520. wchar_t *promptW = {0};
  521. if (prompt && strlen(prompt))
  522. CopyNFDCharToWChar( prompt, &promptW );
  523. if ( !SUCCEEDED(result))
  524. {
  525. NFDi_SetError("Could not initialize COM.");
  526. goto end;
  527. }
  528. ::IFileOpenDialog *fileOpenDialog(NULL);
  529. // Create dialog
  530. result = ::CoCreateInstance(::CLSID_FileOpenDialog, NULL,
  531. CLSCTX_ALL, ::IID_IFileOpenDialog,
  532. reinterpret_cast<void**>(&fileOpenDialog) );
  533. if ( !SUCCEEDED(result) )
  534. {
  535. NFDi_SetError("Could not create dialog.");
  536. goto end;
  537. }
  538. DWORD dwOptions;
  539. if (SUCCEEDED(fileOpenDialog->GetOptions(&dwOptions)))
  540. {
  541. fileOpenDialog->SetOptions(dwOptions | FOS_PICKFOLDERS);
  542. }
  543. // Set the default path
  544. if ( !SetDefaultPath( fileOpenDialog, defaultPath ) )
  545. {
  546. goto end;
  547. }
  548. fileOpenDialog->SetTitle(promptW);
  549. // Show the dialog.
  550. result = fileOpenDialog->Show(NULL);
  551. if ( SUCCEEDED(result) )
  552. {
  553. // Get the file name
  554. ::IShellItem *shellItem(NULL);
  555. result = fileOpenDialog->GetResult(&shellItem);
  556. if ( !SUCCEEDED(result) )
  557. {
  558. NFDi_SetError("Could not get shell item from dialog.");
  559. goto end;
  560. }
  561. wchar_t *filePath(NULL);
  562. result = shellItem->GetDisplayName(::SIGDN_FILESYSPATH, &filePath);
  563. if ( !SUCCEEDED(result) )
  564. {
  565. NFDi_SetError("Could not get file path for selected.");
  566. goto end;
  567. }
  568. CopyWCharToNFDChar( filePath, outPath );
  569. CoTaskMemFree(filePath);
  570. if ( !*outPath )
  571. {
  572. /* error is malloc-based, error message would be redundant */
  573. goto end;
  574. }
  575. nfdResult = NFD_OKAY;
  576. shellItem->Release();
  577. }
  578. else if (result == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
  579. {
  580. nfdResult = NFD_CANCEL;
  581. }
  582. else
  583. {
  584. NFDi_SetError("File dialog box show failed.");
  585. nfdResult = NFD_ERROR;
  586. }
  587. end:
  588. if (prompt && strlen(prompt));
  589. NFDi_Free( promptW );
  590. ::CoUninitialize();
  591. return nfdResult;
  592. }