Making a file browser with loads of functions, coming back to fine tune some of my methods to find this:
foreach (ListViewItem item in listView1.SelectedItems)
{
FileSystem.DeleteDirectory(item.SubItems[3].Text,
UIOption.AllDialogs,
RecycleOption.SendToRecycleBin,
UICancelOption.ThrowException);
}
which works great to send a SINGLE directory or file to the recycle bin, but it will prompt for every selected item. Not great for deleting a pile of files and folders.
Any way to achieve this without the excess prompts? Or do I have to delve into SHFILEOPSTRUCT?
Thanks for your help, so far 90% of my questions were already answered here, best website ever.
If you don't want the prompts, you could use Directory.Delete instead of the FileSystem method. This will delete the directory and files and subdirectories (provided you specify that you want it to do so).
This seems to be the only way to do what you have required
Moving the files and directory to the recycle bin without prompt
using System.Runtime.InteropServices;
class Win32ApiUtils
{
// Don't declare a value for the Pack size. If you omit it, the correct value is used when
// marshaling and a single SHFILEOPSTRUCT can be used for both 32-bit and 64-bit operation.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public int wFunc;
[MarshalAs(UnmanagedType.LPWStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPWStr)]
public string pTo;
public ushort fFlags;
[MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszProgressTitle;
}
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);
const int FO_DELETE = 3;
const int FOF_ALLOWUNDO = 0x40;
const int FOF_NOCONFIRMATION = 0x10; //Don't prompt the user.;
public static int DeleteFilesToRecycleBin(string filename)
{
SHFILEOPSTRUCT shf = new SHFILEOPSTRUCT();
shf.wFunc = FO_DELETE;
shf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
shf.pFrom = filename + "\0"; // <--- this "\0" is critical !!!!!
int result = SHFileOperation(ref shf);
// Any value different from zero is an error to lookup
return result;
}
}
foreach (ListViewItem item in listView1.SelectedItems)
{
int result = Win32ApiUtils.DeleteFilesToRecycleBin(item.SubItems[3].Text);
if(result != 0) ...... // ??? throw ??? message to user and contine ???
}
-- Warning --
This code needs to be tested. I have found the layout of SHFILEOPSTRUCT on PInvoke site and on that link there are some notes about the declaration of the strings used.
Well. tested on my Win7 64bit with a single directory to delete. Works like a charm....
Related
I am using System.Windows.Clipboard to copy some text, and I would like to know is there a chance to get the origin source,
e.g. a the file where I copyied it from, or the website, folder.... ?
Thanks
The Win32 GetClipboardOwner() function can be used to get the Handle of the Window that last placed data into the Clipboard.
You can then pass the returned handle to GetWindowThreadProcessId() to get the Process ID and Thread ID of that Window.
Back in .Net territory, you can use the Process ID as the parameter to pass to the System.Diagnostics.Process.GetProcessById() method, to retrieve the information needed.
Note that you have to build a 64bit application to fully inspect a 64bit
process. If your project has the Prefer 32-bit option set, some
information will not be available.
See also:
How to get the process ID or name of the application that has updated the clipboard?
Windows API declarations. The overloaded GetClipboardOwnerProcessID() wrapper method returns the ProcessID of the ClipBoard Owner and, optionally, its Thread ID.
public class WinApi
{
[DllImport("user32.dll")]
private static extern IntPtr GetClipboardOwner();
//The return value is the identifier of the thread that created the window.
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
//Wrapper used to return the Window processId
public static uint GetClipboardOwnerProcessID()
{
uint processId = 0;
GetWindowThreadProcessId(SafeNativeMethods.GetClipboardOwner(), out processId);
return processId;
}
//Overload that returns a reference to the Thread ID
public static uint GetClipboardOwnerProcessID(ref uint threadId)
{
uint processId = 0;
threadId = GetWindowThreadProcessId(SafeNativeMethods.GetClipboardOwner(), out processId);
return processId;
}
}
The wrapper can be called like this, if you just need the Process Id:
uint ClipBoadrOwnerProcessId = WinApi.GetClipboardOwnerProcessID();
Or this way, if you also need the Thread Id:
uint ClipBoadrOwnerThreadId = 0;
uint ClipBoadrOwnerProcessId = WinApi.GetClipboardOwnerProcessID(ref ClipBoadrOwnerThreadId);
Pass the returned value to the Process.GetProcessById() method:
Process ClipBoardOwnerProcess = Process.GetProcessById((int)WinApi.GetClipboardOwnerProcessID());
string ProcessName = ClipBoardOwnerProcess.ProcessName;
string ProcessWindowTitle = ClipBoardOwnerProcess.MainWindowTitle;
string ProcessFileName = ClipBoardOwnerProcess.MainModule.FileName;
//(...)
If you copy some text from your browser, the ProcessName will be the name of your browser and the ProcessFileName the path to its executable.
I want to change "Enable access based enumeration", "allowing cache of share" and "encrypt data access" attributes on share folder using COM Api's or WMI's.
I was previously using Win32_Share to create share but this does not have properties to assign these attributes.
but then I came to know about 'MSFT_SmbShare' class but I can see only CreateShare method. I was to enable/disable these flags on exiting share but not able to find any UpdateShare method.
MSFT_SmbShare class
Please suggest a way to toggle these flags on a share by any of COM API's or WMI's.
To change these settings, you can use the NetShareSetInfo function available in the Windows SDK with the level 1005.
All flags are defined here, but note the documentation does not exhibit the SHI1005_FLAGS_ENCRYPT_DATA (0x08000) which is indeed in the corresponding Windows header file LMERR.H.
Here is a sample that demonstrate how to use it in a C# console app:
class Program
{
static void Main(string[] args)
{
// get flags of "myshare" share
var flags = NetShareUtilities.Get1005Flags(null, "myshare");
// add the "Require encryption" flag
flags |= SHI1005_FLAGS.SHI1005_FLAGS_ENCRYPT_DATA;
// save flags (you'll need to have admin rights for this)
NetShareUtilities.Set1005Flags(null, "myshare", flags);
}
}
Here is the NetShareUtilities class that uses P/Invoke to get to the Windows API:
public static class NetShareUtilities
{
[DllImport("netapi32.dll")]
private extern static int NetShareSetInfo([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string netname, int level, ref SHI1005_FLAGS buf, IntPtr parm_err);
[DllImport("netapi32.dll")]
private extern static int NetShareGetInfo([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string netname, int level, out IntPtr bufptr);
[DllImport("netapi32.dll")]
private static extern IntPtr NetApiBufferFree(IntPtr Buffer);
public static SHI1005_FLAGS Get1005Flags(string serverName, string name)
{
IntPtr ptr;
int err = NetShareGetInfo(serverName, name, 1005, out ptr);
if (err != 0)
throw new Win32Exception(err);
var flags = (SHI1005_FLAGS)Marshal.ReadInt32(ptr);
NetApiBufferFree(ptr);
return flags;
}
public static void Set1005Flags(string serverName, string name, SHI1005_FLAGS flags)
{
// note: you need to have enough rights to call this
int err = NetShareSetInfo(serverName, name, 1005, ref flags, IntPtr.Zero);
if (err != 0)
throw new Win32Exception(err);
}
}
[Flags]
public enum SHI1005_FLAGS
{
// note: all values are taken from LMERR.H
SHI1005_FLAGS_DFS = 0x0001,
SHI1005_FLAGS_DFS_ROOT = 0x0002,
// these 3 ones are not strict flags, you'll need to use a mask as specified in the official documentation
CSC_CACHE_AUTO_REINT = 0x0010,
CSC_CACHE_VDO = 0x0020,
CSC_CACHE_NONE = 0x0030,
SHI1005_FLAGS_RESTRICT_EXCLUSIVE_OPENS = 0x00100,
SHI1005_FLAGS_FORCE_SHARED_DELETE = 0x00200,
SHI1005_FLAGS_ALLOW_NAMESPACE_CACHING = 0x00400,
SHI1005_FLAGS_ACCESS_BASED_DIRECTORY_ENUM = 0x00800,
SHI1005_FLAGS_FORCE_LEVELII_OPLOCK = 0x01000,
SHI1005_FLAGS_ENABLE_HASH = 0x02000,
SHI1005_FLAGS_ENABLE_CA = 0x04000,
SHI1005_FLAGS_ENCRYPT_DATA = 0x08000,
SHI1005_FLAGS_RESERVED = 0x10000,
}
These values all seem to be properties on the MSFT_SmbShare class; specifically, FolderEnumerationMode, CachingMode and EncryptData.
After updating the values, use the Put_ method to update.
I'm trying to iterate through some files and fetch their shell icons; to accomplish this, I'm using DirectoryInfo.EnumerateFileSystemInfos and some P/Invoke to call the Win32 SHGetFileInfo function. But the combination of the two seems to corrupt memory somewhere internally, resulting in ugly crashes.
I've boiled down my code to two similar test cases, both of which crash seemingly without reason. If I don't call DirectoryInfo.EnumerateFileSystemInfos, no crash appears; if I don't call SHGetFileInfo, no crash appears. Note that I've removed the actual use of the FileSystemInfo objects in my code, since I can get it to reproduce simply by iterating over them and asking for the text file icon over and over. But why?
Here are my complete, minimal test cases. Run them under the VS debugger to ensure no optimizations are enabled:
using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
namespace IconCrashRepro
{
// Compile for .NET 4 (I'm using 4.5.1).
// Also seems to fail in 3.5 with GetFileSystemInfos() instead of EnumerateFileSystemInfos()
public class Program
{
// Compile for .NET 4 (I'm using 4.5.1)
public static void Main()
{
// Keep a list of the objects we generate so
// that they're not garbage collected right away
var sources = new List<BitmapSource>();
// Any directory seems to do the trick, so long
// as it's not empty. Within VS, '.' should be
// the Debug folder
var dir = new DirectoryInfo(#".");
// Track the number of iterations, just to see
ulong iteration = 0;
while (true)
{
// This is where things get interesting -- without the EnumerateFileSystemInfos,
// the bug does not appear. Without the call to SHGetFileInfo, the bug also
// does not appear. It seems to be the combination that causes problems.
var infos = dir.EnumerateFileSystemInfos().ToList();
Debug.Assert(infos.Count > 0);
foreach (var info in infos)
{
var shFileInfo = new SHFILEINFO();
var result = SHGetFileInfo(".txt", (uint)FileAttributes.Normal, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
//var result = SHGetFileInfo(info.FullName, (uint)info.Attributes, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
if (result != IntPtr.Zero && shFileInfo.hIcon != IntPtr.Zero)
{
var bmpSource = Imaging.CreateBitmapSourceFromHIcon(
shFileInfo.hIcon,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
sources.Add(bmpSource);
// Originally I was releasing the handle, but even if
// I don't the bug occurs!
//DestroyIcon(shFileInfo.hIcon);
}
// Execution fails during Collect; if I remove the
// call to Collect, execution fails later during
// CreateBitmapSourceFromHIcon (it calls
// AddMemoryPressure internally which I suspect
// results in a collect at that point).
GC.Collect();
++iteration;
}
}
}
public static void OtherBugRepro()
{
// Rename this to Main() to run.
// Removing any single line from this method
// will stop it from crashing -- including the
// empty if and the Debug.Assert!
var sources = new List<BitmapSource>();
var dir = new DirectoryInfo(#".");
var infos = dir.EnumerateFileSystemInfos().ToList();
Debug.Assert(infos.Count > 0);
// Crashes on the second iteration -- says that
// `infos` has been modified during loop execution!!
foreach (var info in infos)
{
var shFileInfo = new SHFILEINFO();
var result = SHGetFileInfo(".txt", (uint)FileAttributes.Normal, ref shFileInfo, (uint)Marshal.SizeOf(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SMALLICON);
if (result != IntPtr.Zero && shFileInfo.hIcon != IntPtr.Zero)
{
if (sources.Count == 1000) { }
}
}
}
[StructLayout(LayoutKind.Sequential)]
private 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;
}
private const uint SHGFI_ICON = 0x100;
private const uint SHGFI_LARGEICON = 0x0;
private const uint SHGFI_SMALLICON = 0x1;
private const uint SHGFI_USEFILEATTRIBUTES = 0x10;
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SHGetFileInfo([MarshalAs(UnmanagedType.LPWStr)] string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool DestroyIcon(IntPtr hIcon);
}
}
Can anyone spot the bug? Any help is appreciated!
You are calling the Unicode version of the function, but passing the ANSI version of the struct. You need to specify the CharSet in the SHFILEINFO struct declaration.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
i am trying to get all directories from my usb drive using following code
DirectoryInfo di = new DirectoryInfo("H:\\");
DirectoryInfo[] allDir = di.GetDirectories();
this code works perfectly for directories with ascii names. but one directory has a name of
" "(unicode value U+00A0). GetDirectories() cannot get that directory. is there a way to get directory wiht unicode name?
Alright, so what you're fighting is the .NET framework uses this signature of FindFirstFile:
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
internal static SafeFindHandle FindFirstFile(string fileName, [In, Out] Win32Native.WIN32_FIND_DATA data);
To then add insult to injury, you're fighting the language:
Automatically marshal strings appropriately for the target operating system. The default is Unicode on Windows NT, Windows 2000, Windows XP, and the Windows Server 2003 family; the default is Ansi on Windows 98 and Windows Me. Although the common language runtime default is Auto, languages may override this default. For example, by default C# marks all methods and types as Ansi.
(from the CharSet enumeration documentation emphasis added)
The issue at hand is the CharSet parameter on the DllImport. This means that you're left with one approach; leverage the P/Invoke on your own. You'll need quite a few things. First you'll need the data structure that is returned:
[BestFitMapping(false)]
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal class WIN32_FIND_DATA
{
internal int dwFileAttributes;
internal uint ftCreationTime_dwLowDateTime;
internal uint ftCreationTime_dwHighDateTime;
internal uint ftLastAccessTime_dwLowDateTime;
internal uint ftLastAccessTime_dwHighDateTime;
internal uint ftLastWriteTime_dwLowDateTime;
internal uint ftLastWriteTime_dwHighDateTime;
internal int nFileSizeHigh;
internal int nFileSizeLow;
internal int dwReserved0;
internal int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
internal string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
internal string cAlternateFileName;
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public WIN32_FIND_DATA()
{
}
}
Next you'll need the right import for FindFirstFile:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
internal static SafeFindHandle FindFirstFile(string fileName, [In, Out] Win32Native.WIN32_FIND_DATA data);
Next you'll need GetLastWin32Error to check for errors; that can be accessed via Marshal.GetLastWin32Error in the InteropServices namespace.
Next you're going to need to iterate, so you'll need FindNextFile:
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true, BestFitMapping = false)]
internal static bool FindNextFile(SafeFindHandle hndFindFile, [MarshalAs(UnmanagedType.LPStruct), In, Out] Win32Native.WIN32_FIND_DATA lpFindFileData);
Armed with these you can build your own iterator. However, for your benefit here is what Microsoft does for "init":
[SecurityCritical]
private void CommonInit()
{
string fileName = Path.InternalCombine(this.searchData.fullPath, this.searchCriteria);
Win32Native.WIN32_FIND_DATA wiN32FindData = new Win32Native.WIN32_FIND_DATA();
this._hnd = Win32Native.FindFirstFile(fileName, wiN32FindData);
if (this._hnd.IsInvalid)
{
int lastWin32Error = Marshal.GetLastWin32Error();
switch (lastWin32Error)
{
case 2:
case 18:
this.empty = this.searchData.searchOption == SearchOption.TopDirectoryOnly;
break;
default:
this.HandleError(lastWin32Error, this.searchData.fullPath);
break;
}
}
if (this.searchData.searchOption == SearchOption.TopDirectoryOnly)
{
if (this.empty)
{
this._hnd.Dispose();
}
else
{
SearchResult searchResult = this.CreateSearchResult(this.searchData, wiN32FindData);
if (!this._resultHandler.IsResultIncluded(searchResult))
return;
this.current = this._resultHandler.CreateObject(searchResult);
}
}
else
{
this._hnd.Dispose();
this.searchStack.Add(this.searchData);
}
}
and here is what they do for "iterate" for what you're looking for:
if (this.searchData != null && this._hnd != null)
{
while (Win32Native.FindNextFile(this._hnd, wiN32FindData))
{
SearchResult searchResult = this.CreateSearchResult(this.searchData, wiN32FindData);
if (this._resultHandler.IsResultIncluded(searchResult))
{
if (this.needsParentPathDiscoveryDemand)
{
this.DoDemand(this.searchData.fullPath);
this.needsParentPathDiscoveryDemand = false;
}
this.current = this._resultHandler.CreateObject(searchResult);
return true;
}
}
int lastWin32Error = Marshal.GetLastWin32Error();
if (this._hnd != null)
this._hnd.Dispose();
if (lastWin32Error != 0 && lastWin32Error != 18 && lastWin32Error != 2)
this.HandleError(lastWin32Error, this.searchData.fullPath);
}
NOTE: you're not going to be able to use this code directly, you'll have to fit it to your solution, but this is a huge jump start on the API's. Get you a copy of dotPeek to fill in the gaps.
NOTE: the fileName parameter accepted by FindFirstFile is going to the parent directory. In your case, H:\.
Could anyone help me to know how to get the PID of a Windows service?
I need to get the PID in order to run the following command:
Process.Start(new ProcessStartInfo
{
Filename = "cmd.exe",
CreateNoWindow = true,
UseShellExecute = false,
Arguments = string.Format("/c taskkill /pid {0} /f", pidnumber)
});
What the other answers neglect is the fact that a single process can also host multiple, autonomous services. The multiple instances of the svchost.exe process, each hosting a couple of services, is the best example.
So in general, it is absolutely unsafe to try to kill an arbitrary service by killing it's hosting process (I assume that is what you attempt to do, since you refer to taskkill.exe). You might take down several unrelated services in the process.
If you do know that the service's process only hosts the service you care about, than you can choose the strategy as suggested by #M C in his/her answer.
Alternatively, you can also use the ServiceController class to open a handle to your service and then use it (via the ServiceHandle property) to P/Invoke the QueryServiceStatusEx function to find out the Process ID you want to know.
If you need more details, you should clarify what it is that you're actually trying to achieve. It is not clear from your question.
Update Here is some code I ripped out of an existing project that should do what you want, given you have a ServiceController instance. _As said above, use with care!__
[StructLayout(LayoutKind.Sequential)]
internal sealed class SERVICE_STATUS_PROCESS
{
[MarshalAs(UnmanagedType.U4)]
public uint dwServiceType;
[MarshalAs(UnmanagedType.U4)]
public uint dwCurrentState;
[MarshalAs(UnmanagedType.U4)]
public uint dwControlsAccepted;
[MarshalAs(UnmanagedType.U4)]
public uint dwWin32ExitCode;
[MarshalAs(UnmanagedType.U4)]
public uint dwServiceSpecificExitCode;
[MarshalAs(UnmanagedType.U4)]
public uint dwCheckPoint;
[MarshalAs(UnmanagedType.U4)]
public uint dwWaitHint;
[MarshalAs(UnmanagedType.U4)]
public uint dwProcessId;
[MarshalAs(UnmanagedType.U4)]
public uint dwServiceFlags;
}
internal const int ERROR_INSUFFICIENT_BUFFER = 0x7a;
internal const int SC_STATUS_PROCESS_INFO = 0;
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool QueryServiceStatusEx(SafeHandle hService, int infoLevel, IntPtr lpBuffer, uint cbBufSize, out uint pcbBytesNeeded);
public static int GetServiceProcessId(this ServiceController sc)
{
if (sc == null)
throw new ArgumentNullException("sc");
IntPtr zero = IntPtr.Zero;
try
{
UInt32 dwBytesNeeded;
// Call once to figure the size of the output buffer.
QueryServiceStatusEx(sc.ServiceHandle, SC_STATUS_PROCESS_INFO, zero, 0, out dwBytesNeeded);
if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
{
// Allocate required buffer and call again.
zero = Marshal.AllocHGlobal((int)dwBytesNeeded);
if (QueryServiceStatusEx(sc.ServiceHandle, SC_STATUS_PROCESS_INFO, zero, dwBytesNeeded, out dwBytesNeeded))
{
var ssp = new SERVICE_STATUS_PROCESS();
Marshal.PtrToStructure(zero, ssp);
return (int)ssp.dwProcessId;
}
}
}
finally
{
if (zero != IntPtr.Zero)
{
Marshal.FreeHGlobal(zero);
}
}
return -1;
}
Assuming you know the name of the EXE the service uses and there is exactly one of them:
int procID = Process.GetProcessesByName("yourservice")[0].Id;
The method Process.GetProcessesByName("yourservice") returns an Array of Processes with your specified name, so in case you don't know how much of "yourservice.exe" runs simultaneously you might need a foreach loop.
See this answer on a similar question:
Finding out Windows service's running process name
Using a WMI query you can -
Find all services related to a single exe (a single exe can host multiple services):
select Name from Win32_Service where ProcessId = 588
Or, to answer this question, you can get the PID of the process that a service is running in:
select ProcessId from Win32_Service where Name = 'wuauserv'