@@ -235,7 +235,7 @@ pub fn start_drag<W: HasWindowHandle, F: Fn(DragResult, CursorPosition) + Send +
235235 paths. push ( dunce:: canonicalize ( f) ?) ;
236236 }
237237
238- let data_object: IDataObject = get_file_data_object ( & paths) . unwrap ( ) ;
238+ let data_object: IDataObject = get_file_data_object ( & paths) ? ;
239239 let drop_source: IDropSource = DropSource :: new ( ) . into ( ) ;
240240
241241 unsafe {
@@ -276,7 +276,7 @@ pub fn start_drag<W: HasWindowHandle, F: Fn(DragResult, CursorPosition) + Send +
276276
277277 let paths = vec ! [ dunce:: canonicalize( "./" ) ?] ;
278278
279- let data_object: IDataObject = get_file_data_object ( & paths) . unwrap ( ) ;
279+ let data_object: IDataObject = get_file_data_object ( & paths) ? ;
280280 let drop_source: IDropSource = DummyDropSource :: new ( ) . into ( ) ;
281281
282282 unsafe {
@@ -365,26 +365,84 @@ pub fn create_instance<T: Interface + ComInterface>(clsid: &GUID) -> Result<T> {
365365 unsafe { CoCreateInstance ( clsid, None , CLSCTX_ALL ) }
366366}
367367
368- fn get_file_data_object ( paths : & [ PathBuf ] ) -> Option < IDataObject > {
368+ fn get_file_data_object ( paths : & [ PathBuf ] ) -> crate :: Result < IDataObject > {
369369 unsafe {
370- let shell_item_array = get_shell_item_array ( paths) . unwrap ( ) ;
371- shell_item_array. BindToHandler ( None , & BHID_DataObject ) . ok ( )
370+ let shell_item_array = get_shell_item_array ( paths) ? ;
371+ Ok ( shell_item_array. BindToHandler ( None , & BHID_DataObject ) ? )
372372 }
373373}
374374
375- fn get_shell_item_array ( paths : & [ PathBuf ] ) -> Option < IShellItemArray > {
375+ fn get_shell_item_array ( paths : & [ PathBuf ] ) -> crate :: Result < IShellItemArray > {
376376 unsafe {
377- let list: Vec < * const Common :: ITEMIDLIST > = paths
378- . iter ( )
379- . map ( |path| get_file_item_id ( path) . cast_const ( ) )
380- . collect ( ) ;
381- SHCreateShellItemArrayFromIDLists ( & list) . ok ( )
377+ let mut list: Vec < * const Common :: ITEMIDLIST > = Vec :: with_capacity ( paths. len ( ) ) ;
378+ for path in paths {
379+ match get_file_item_id ( path) {
380+ Ok ( pidl) => list. push ( pidl. cast_const ( ) ) ,
381+ Err ( e) => {
382+ for pidl in & list {
383+ CoTaskMemFree ( Some ( * pidl as * const _ as * mut _ ) ) ;
384+ }
385+ return Err ( e) ;
386+ }
387+ }
388+ }
389+ let result = SHCreateShellItemArrayFromIDLists ( & list) ;
390+ for pidl in & list {
391+ CoTaskMemFree ( Some ( * pidl as * const _ as * mut _ ) ) ;
392+ }
393+ Ok ( result?)
382394 }
383395}
384396
385- fn get_file_item_id ( path : & Path ) -> * mut Common :: ITEMIDLIST {
397+ fn get_file_item_id ( path : & Path ) -> crate :: Result < * mut Common :: ITEMIDLIST > {
386398 unsafe {
387399 let wide_path: Vec < u16 > = path. as_os_str ( ) . encode_wide ( ) . chain ( once ( 0 ) ) . collect ( ) ;
388- windows:: Win32 :: UI :: Shell :: ILCreateFromPathW ( PCWSTR :: from_raw ( wide_path. as_ptr ( ) ) )
400+ let pidl =
401+ windows:: Win32 :: UI :: Shell :: ILCreateFromPathW ( PCWSTR :: from_raw ( wide_path. as_ptr ( ) ) ) ;
402+ if pidl. is_null ( ) {
403+ Err ( crate :: Error :: InvalidShellPath ( path. to_path_buf ( ) ) )
404+ } else {
405+ Ok ( pidl)
406+ }
407+ }
408+ }
409+
410+ #[ cfg( test) ]
411+ mod tests {
412+ use super :: * ;
413+
414+ #[ test]
415+ fn get_file_item_id_returns_error_for_nonexistent_path ( ) {
416+ let path = PathBuf :: from ( r"C:\__nonexistent_drag_rs_test_path__\file.txt" ) ;
417+ let result = get_file_item_id ( & path) ;
418+ assert ! ( result. is_err( ) ) ;
419+ }
420+
421+ #[ test]
422+ fn get_file_item_id_returns_error_for_unc_nonexistent ( ) {
423+ let path = PathBuf :: from ( r"\\nonexistent_server_drag_rs\share\file.png" ) ;
424+ let result = get_file_item_id ( & path) ;
425+ assert ! ( result. is_err( ) ) ;
426+ }
427+
428+ #[ test]
429+ fn get_shell_item_array_returns_error_for_invalid_paths ( ) {
430+ let paths = vec ! [ PathBuf :: from( r"C:\__nonexistent_drag_rs_test__\a.txt" ) ] ;
431+ let result = get_shell_item_array ( & paths) ;
432+ assert ! ( result. is_err( ) ) ;
433+ }
434+
435+ #[ test]
436+ fn get_file_data_object_returns_error_not_panic_for_bad_paths ( ) {
437+ let paths = vec ! [ PathBuf :: from( r"\\nonexistent_server\share\file.png" ) ] ;
438+ let result = get_file_data_object ( & paths) ;
439+ assert ! ( result. is_err( ) ) ;
440+ }
441+
442+ #[ test]
443+ fn get_file_item_id_succeeds_for_existing_file ( ) {
444+ let path = PathBuf :: from ( r"C:\Windows\System32\notepad.exe" ) ;
445+ let result = get_file_item_id ( & path) ;
446+ assert ! ( result. is_ok( ) ) ;
389447 }
390448}
0 commit comments