//! \file Shell.cs
//! \date Tue Aug 02 13:48:55 2011
//! \brief Win32 shell functions.
//
// Copyright (C) 2011 by poddav
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace GARbro.Shell
{
static class File
{
[DllImport("kernel32.dll", EntryPoint="MoveFileExW", SetLastError=true, CharSet=CharSet.Unicode)]
public static extern bool MoveFileEx (string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags);
[Flags]
public enum MoveFileFlags : uint
{
ReplaceExisting = 0x00000001,
CopyAllowed = 0x00000002,
DelayUntilReboot = 0x00000004,
WriteThrough = 0x00000008,
CreateHardlink = 0x00000010,
FailIfNotTrackable = 0x00000020
}
///
/// Wrapper around MoveFileEx WINAPI call.
///
public static bool Rename (string szFrom, string szTo)
{
return MoveFileEx (szFrom, szTo, MoveFileFlags.ReplaceExisting);
}
public static int GetLastError ()
{
return Marshal.GetLastWin32Error();
}
///
/// Possible flags for the SHFileOperation method.
///
[Flags]
public enum FileOperationFlags : ushort
{
///
/// Do not show a dialog during the process
///
FOF_SILENT = 0x0004,
///
/// Do not ask the user to confirm selection
///
FOF_NOCONFIRMATION = 0x0010,
///
/// Delete the file to the recycle bin. (Required flag to send a file to the bin
///
FOF_ALLOWUNDO = 0x0040,
///
/// Do not show the names of the files or folders that are being recycled.
///
FOF_SIMPLEPROGRESS = 0x0100,
///
/// Surpress errors, if any occur during the process.
///
FOF_NOERRORUI = 0x0400,
///
/// Warn if files are too big to fit in the recycle bin and will need
/// to be deleted completely.
///
FOF_WANTNUKEWARNING = 0x4000,
}
///
/// File Operation Function Type for SHFileOperation
///
public enum FileOperationType : uint
{
///
/// Move the objects
///
FO_MOVE = 0x0001,
///
/// Copy the objects
///
FO_COPY = 0x0002,
///
/// Delete (or recycle) the objects
///
FO_DELETE = 0x0003,
///
/// Rename the object(s)
///
FO_RENAME = 0x0004,
}
///
/// SHFILEOPSTRUCT for SHFileOperation from COM
///
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
private struct SHFILEOPSTRUCT32
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public FileOperationType wFunc;
[MarshalAs(UnmanagedType.LPTStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPTStr)]
public string pTo;
public FileOperationFlags fFlags;
[MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszProgressTitle;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SHFILEOPSTRUCT64
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public FileOperationType wFunc;
[MarshalAs(UnmanagedType.LPTStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPTStr)]
public string pTo;
public FileOperationFlags fFlags;
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszProgressTitle;
}
[DllImport("shell32.dll", EntryPoint = "SHFileOperationW", CharSet = CharSet.Unicode)]
private static extern int SHFileOperation32 (ref SHFILEOPSTRUCT32 FileOp);
[DllImport("shell32.dll", EntryPoint = "SHFileOperationW", CharSet = CharSet.Unicode)]
private static extern int SHFileOperation64 (ref SHFILEOPSTRUCT64 lpFileOp);
private static int SHFileOperation (FileOperationType func, string path, FileOperationFlags flags, IntPtr parent)
{
if (Marshal.SizeOf(typeof(IntPtr)) == 4)
{
var fs = new SHFILEOPSTRUCT32
{
hwnd = parent,
wFunc = func,
pFrom = path,
fFlags = flags
};
return SHFileOperation32 (ref fs);
}
else
{
var fs = new SHFILEOPSTRUCT64
{
hwnd = parent,
wFunc = func,
pFrom = path,
fFlags = flags
};
return SHFileOperation64 (ref fs);
}
}
///
/// Send file to recycle bin
///
/// Location of directory or file to recycle
/// FileOperationFlags to add in addition to FOF_ALLOWUNDO
public static bool Delete (string path, FileOperationFlags flags, IntPtr parent = default(IntPtr))
{
return 0 == SHFileOperation (FileOperationType.FO_DELETE, path+'\0'+'\0',
FileOperationFlags.FOF_ALLOWUNDO | flags, parent);
}
public static bool Delete (IEnumerable file_list, FileOperationFlags flags, IntPtr parent = default(IntPtr))
{
var files = new StringBuilder();
foreach (var file in file_list)
{
files.Append (file);
files.Append ('\0');
}
if (0 == files.Length)
return false;
files.Append ('\0');
return 0 == SHFileOperation (FileOperationType.FO_DELETE, files.ToString(),
FileOperationFlags.FOF_ALLOWUNDO | flags, parent);
}
public static bool Delete (IEnumerable file_list, IntPtr parent = default(IntPtr))
{
return Delete (file_list, FileOperationFlags.FOF_WANTNUKEWARNING, parent);
}
///
/// Send file to recycle bin. Display dialog, display warning if files are too big to fit (FOF_WANTNUKEWARNING)
///
/// Location of directory or file to recycle
public static bool Delete (string path, IntPtr parent = default(IntPtr))
{
return Delete (path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_WANTNUKEWARNING, parent);
}
///
/// Send file silently to recycle bin. Surpress dialog, surpress errors, delete if too large.
///
/// Location of directory or file to recycle
public static bool MoveToRecycleBin (string path, IntPtr parent = default(IntPtr))
{
return Delete (path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_NOERRORUI | FileOperationFlags.FOF_SILENT, parent);
}
[DllImport("shlwapi.dll", EntryPoint="PathCompactPathExW", CharSet = CharSet.Unicode)]
static extern bool PathCompactPathEx ([Out] StringBuilder pszOut, string szPath, int cchMax, int dwFlags);
const int MAX_PATH = 0x104;
///
/// Truncates a path to fit within a certain number of characters by replacing path components with ellipses.
///
public static string CompactPath (string name, int length)
{
var sb = new StringBuilder (MAX_PATH);
PathCompactPathEx (sb, name, Math.Min (length+1, MAX_PATH), 0);
return sb.ToString();
}
}
public class TemporaryFile : IDisposable
{
private string m_name;
public string Name { get { return m_name; } }
public TemporaryFile ()
{
m_name = Path.GetRandomFileName();
}
public TemporaryFile (string filename)
{
m_name = filename;
}
public TemporaryFile (string path, string filename)
{
m_name = Path.Combine (path, filename);
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing)
{
if (!disposed)
{
if (disposing)
{
System.IO.File.Delete (m_name);
}
disposed = true;
}
}
#endregion
};
///
/// Wrapper around SHGetFileInfo WINAPI call.
///
class FileInfo
{
[DllImport("shell32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SHGetFileInfo(
string pszPath, Int32 dwFileAttributes,
ref SHFILEINFO psfi, int cbFileInfo, int uFlags);
[DllImport("User32.dll")]
public static extern int DestroyIcon(IntPtr hIcon);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public uint dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
public SHFILEINFO(bool b)
{
hIcon = IntPtr.Zero;
iIcon = 0;
dwAttributes = 0;
szDisplayName = "";
szTypeName = "";
}
};
[Flags]
public enum SHGFI : uint
{
/// get icon
Icon = 0x000000100,
/// get display name
DisplayName = 0x000000200,
/// get type name
TypeName = 0x000000400,
/// get attributes
Attributes = 0x000000800,
/// get icon location
IconLocation = 0x000001000,
/// return exe type
ExeType = 0x000002000,
/// get system icon index
SysIconIndex = 0x000004000,
/// put a link overlay on icon
LinkOverlay = 0x000008000,
/// show icon in selected state
Selected = 0x000010000,
/// get only specified attributes
Attr_Specified = 0x000020000,
/// get large icon
LargeIcon = 0x000000000,
/// get small icon
SmallIcon = 0x000000001,
/// get open icon
OpenIcon = 0x000000002,
/// get shell size icon
ShellIconSize = 0x000000004,
/// pszPath is a pidl
PIDL = 0x000000008,
/// use passed dwFileAttribute
UseFileAttributes= 0x000000010,
/// apply the appropriate overlays
AddOverlays = 0x000000020,
/// Get the index of the overlay in the upper 8 bits of the iIcon
OverlayIndex = 0x000000040,
}
public static string GetTypeName (string filename)
{
SHFILEINFO info = new SHFILEINFO(true);
int szInfo = Marshal.SizeOf (info);
int result = (int)SHGetFileInfo (filename, 0, ref info, szInfo, (int)SHGFI.TypeName);
// If uFlags does not contain SHGFI_EXETYPE or SHGFI_SYSICONINDEX,
// the return value is nonzero if successful, or zero otherwise.
if (result != 0)
return info.szTypeName;
else
return string.Empty;
}
public static SHFILEINFO? GetInfo (string filename, SHGFI flags)
{
SHFILEINFO info = new SHFILEINFO(true);
int szInfo = Marshal.SizeOf (info);
int result = (int)SHGetFileInfo (filename, 0, ref info, szInfo, (int)flags);
return result != 0? new Nullable (info): null;
}
}
}