Extract .cab file in C# - c#

I am developing a c# application and I a need to extract a cab file.
I couldn't find a library that does that in C# ) I cannot use Microsoft.Deployment.Compression.Cab.dll because of a licensing issue.
I found this code, but the problem is that when I use it I am able to find and extract only the first file in the cabinet.
OutputFileClose is called only if OutputFileOpen returns something either then IntPtr.Zero.
but if OutputFileClose is calles, then the enumeration is stopped.
So for this code OutputFileClose can be called only for one file
Can someone please help me figuring out how to write a code that will extract all the files?

I found out that Microsoft.Deployment.Compression.cab DLL can also be obtained from here
if you look at previous versions such as version 3.5 you will see that they were licensed with Common Public License Version 1.0 (CPL).
It seems that only in later versions the license was changes to MS-RL.
I was also able to create a solution of my own, but it is not optimal( I stopped working on it since I found that I can use Microsoft.Deployment.Compression.cab).
This is the code:
public class CabExtractor : IDisposable
{
private static class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
internal class CabError //Cabinet API: "ERF"
{
public int erfOper;
public int erfType;
public int fError;
}
[StructLayout(LayoutKind.Sequential)]
internal class FdiNotification //Cabinet API: "FDINOTIFICATION"
{
internal int cb;
//not sure if this should be a IntPtr or a strong
internal IntPtr psz1;
internal IntPtr psz2;
internal IntPtr psz3;
internal IntPtr pv;
internal IntPtr hf;
internal short date;
internal short time;
internal short attribs;
internal short setID;
internal short iCabinet;
internal short iFolder;
internal int fdie;
}
internal enum FdiNotificationType
{
CabinetInfo,
PartialFile,
CopyFile,
CloseFileInfo,
NextCabinet,
Enumerate
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr FdiMemAllocDelegate(int numBytes);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void FdiMemFreeDelegate(IntPtr mem);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr FdiFileOpenDelegate(string fileName, int oflag, int pmode);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileReadDelegate(IntPtr hf,
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2,
ArraySubType = UnmanagedType.U1)] byte[] buffer, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileWriteDelegate(IntPtr hf,
[In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2,
ArraySubType = UnmanagedType.U1)] byte[] buffer, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileCloseDelegate(IntPtr hf);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate Int32 FdiFileSeekDelegate(IntPtr hf, int dist, int seektype);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate IntPtr FdiNotifyDelegate(
FdiNotificationType fdint, [In] [MarshalAs(UnmanagedType.LPStruct)] FdiNotification fdin);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDICreate", CharSet = CharSet.Ansi)]
internal static extern IntPtr FdiCreate(
FdiMemAllocDelegate fnMemAlloc,
FdiMemFreeDelegate fnMemFree,
FdiFileOpenDelegate fnFileOpen,
FdiFileReadDelegate fnFileRead,
FdiFileWriteDelegate fnFileWrite,
FdiFileCloseDelegate fnFileClose,
FdiFileSeekDelegate fnFileSeek,
int cpuType,
[MarshalAs(UnmanagedType.LPStruct)] CabError erf);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDIDestroy", CharSet = CharSet.Ansi)]
internal static extern bool FdiDestroy(IntPtr hfdi);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "FDICopy", CharSet = CharSet.Ansi)]
internal static extern bool FdiCopy(
IntPtr hfdi,
string cabinetName,
string cabinetPath,
int flags,
FdiNotifyDelegate fnNotify,
IntPtr fnDecrypt,
IntPtr userData);
}
internal class ArchiveFile
{
public IntPtr Handle { get; set; }
public string Name { get; set; }
public bool Found { get; set; }
public int Length { get; set; }
public byte[] Data { get; set; }
}
#region fields and properties
/// Very important!
/// Do not try to call directly to this methods, instead use the delegates. if you use them directly it may cause application crashes, corruption and data loss.
/// Using fields to save the delegate so that the delegate won't be garbage collected !
/// When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
private readonly NativeMethods.FdiMemAllocDelegate _fdiAllocMemHandler;
private readonly NativeMethods.FdiMemFreeDelegate _fdiFreeMemHandler;
private readonly NativeMethods.FdiFileOpenDelegate _fdiOpenStreamHandler;
private readonly NativeMethods.FdiFileReadDelegate _fdiReadStreamHandler;
private readonly NativeMethods.FdiFileWriteDelegate _fdiWriteStreamHandler;
private readonly NativeMethods.FdiFileCloseDelegate _fdiCloseStreamHandler;
private readonly NativeMethods.FdiFileSeekDelegate _fdiSeekStreamHandler;
private ArchiveFile _currentFileToDecompress;
readonly List<string> _fileNames = new List<string>();
private readonly NativeMethods.CabError _erf;
private const int CpuTypeUnknown = -1;
private readonly byte[] _inputData;
private bool _disposed;
/// <summary>
///
/// </summary>
private readonly List<string> _subDirectoryToIgnore = new List<string>();
/// <summary>
/// Path to the folder where the files will be extracted to
/// </summary>
private readonly string _extractionFolderPath;
/// <summary>
/// The name of the folder where the files will be extracted to
/// </summary>
public const string ExtractedFolderName = "ExtractedFiles";
public const string CabFileName = "setup.cab";
#endregion
public CabExtractor(string cabFilePath, IEnumerable<string> subDirectoryToUnpack)
: this(cabFilePath)
{
if (subDirectoryToUnpack != null)
_subDirectoryToIgnore.AddRange(subDirectoryToUnpack);
}
public CabExtractor(string cabFilePath)
{
var cabBytes =
File.ReadAllBytes(cabFilePath);
_inputData = cabBytes;
var cabFileLocation = Path.GetDirectoryName(cabFilePath) ?? "";
_extractionFolderPath = Path.Combine(cabFileLocation, ExtractedFolderName);
_erf = new NativeMethods.CabError();
FdiContext = IntPtr.Zero;
_fdiAllocMemHandler = MemAlloc;
_fdiFreeMemHandler = MemFree;
_fdiOpenStreamHandler = InputFileOpen;
_fdiReadStreamHandler = FileRead;
_fdiWriteStreamHandler = FileWrite;
_fdiCloseStreamHandler = InputFileClose;
_fdiSeekStreamHandler = FileSeek;
FdiContext = FdiCreate(_fdiAllocMemHandler, _fdiFreeMemHandler, _fdiOpenStreamHandler, _fdiReadStreamHandler, _fdiWriteStreamHandler, _fdiCloseStreamHandler, _fdiSeekStreamHandler, _erf);
}
public bool ExtractCabFiles()
{
if (!FdiIterate())
{
throw new Exception("Failed to iterate cab files");
}
foreach (var file in _fileNames)
{
ExtractFile(file);
}
return true;
}
private void ExtractFile(string fileName)
{
try
{
_currentFileToDecompress = new ArchiveFile { Name = fileName };
FdiCopy();
CreateAllRelevantDirectories(fileName);
if (_currentFileToDecompress.Data != null)
{
File.WriteAllBytes(Path.Combine(_extractionFolderPath, _currentFileToDecompress.Name), _currentFileToDecompress.Data);
}
}
catch (Exception ex)
{
SbaLogger.Instance.Error(ex);
SbaLogger.Instance.Error(string.Format("Failed to cextract file file {0}", fileName));
}
}
private void CreateAllRelevantDirectories(string filePath)
{
try
{
if (!Directory.Exists(_extractionFolderPath))
{
Directory.CreateDirectory(_extractionFolderPath);
}
var fullPathToFile = Path.GetDirectoryName(filePath);
if (fullPathToFile != null &&
!Directory.Exists(Path.Combine(_extractionFolderPath, fullPathToFile)))
{
Directory.CreateDirectory(Path.Combine(_extractionFolderPath, fullPathToFile));
}
}
catch (Exception ex)
{
SbaLogger.Instance.Error(ex);
SbaLogger.Instance.Error(string.Format("Failed to create directories for the file {0}",filePath));
}
}
private static string GetFileName(NativeMethods.FdiNotification notification)
{
var encoding = ((int)notification.attribs & 128) != 0 ? Encoding.UTF8 : Encoding.Default;
int length = 0;
while (Marshal.ReadByte(notification.psz1, length) != 0)
checked { ++length; }
var numArray = new byte[length];
Marshal.Copy(notification.psz1, numArray, 0, length);
string path = encoding.GetString(numArray);
if (Path.IsPathRooted(path))
path = path.Replace(String.Concat(Path.VolumeSeparatorChar), "");
return path;
}
private IntPtr ExtractCallback(NativeMethods.FdiNotificationType fdint, NativeMethods.FdiNotification fdin)
{
switch (fdint)
{
case NativeMethods.FdiNotificationType.CopyFile:
return CopyFiles(fdin);
case NativeMethods.FdiNotificationType.CloseFileInfo:
return OutputFileClose(fdin);
default:
return IntPtr.Zero;
}
}
private IntPtr IterateCallback(NativeMethods.FdiNotificationType fdint, NativeMethods.FdiNotification fdin)
{
switch (fdint)
{
case NativeMethods.FdiNotificationType.CopyFile:
return OutputFileOpen(fdin);
default:
return IntPtr.Zero;
}
}
private IntPtr InputFileOpen(string fileName, int oflag, int pmode)
{
var stream = new MemoryStream(_inputData);
GCHandle gch = GCHandle.Alloc(stream);
return (IntPtr)gch;
}
private int InputFileClose(IntPtr hf)
{
var stream = StreamFromHandle(hf);
stream.Close();
((GCHandle)(hf)).Free();
return 0;
}
/// <summary>
/// Copies the contents of input to output. Doesn't close either stream.
/// </summary>
public static void CopyStream(Stream input, Stream output)
{
var buffer = new byte[8 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
private IntPtr CopyFiles(NativeMethods.FdiNotification fdin)
{
var fileName = GetFileName(fdin);
var extractFile = _currentFileToDecompress.Name == fileName ? _currentFileToDecompress : null;
if (extractFile != null)
{
var stream = new MemoryStream();
GCHandle gch = GCHandle.Alloc(stream);
extractFile.Handle = (IntPtr)gch;
return extractFile.Handle;
}
//Do not extract this file
return IntPtr.Zero;
}
private IntPtr OutputFileOpen(NativeMethods.FdiNotification fdin)
{
try
{
var extractFile = new ArchiveFile { Name = GetFileName(fdin) };
if (ShouldIgnoreFile(extractFile))
{
//ignore this file.
return IntPtr.Zero;
}
var stream = new MemoryStream();
GCHandle gch = GCHandle.Alloc(stream);
extractFile.Handle = (IntPtr)gch;
AddToListOfFiles(extractFile);
}
catch (Exception ex)
{
SbaLogger.Instance.Verbose(ex);
}
//return IntPtr.Zero so that the iteration will keep on going
return IntPtr.Zero;
}
private bool ShouldIgnoreFile(ArchiveFile extractFile)
{
var rootFolder = GetFileRootFolder(extractFile.Name);
return _subDirectoryToIgnore.Any(dir => dir.Equals(rootFolder, StringComparison.InvariantCultureIgnoreCase));
}
private string GetFileRootFolder(string path)
{
try
{
return path.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault();
}
catch (Exception)
{
return string.Empty;
}
}
private void AddToListOfFiles(ArchiveFile extractFile)
{
if (!_fileNames.Any(file => file.Equals(extractFile.Name)))
{
_fileNames.Add(extractFile.Name);
}
}
private IntPtr OutputFileClose(NativeMethods.FdiNotification fdin)
{
var extractFile = _currentFileToDecompress.Handle == fdin.hf ? _currentFileToDecompress : null;
var stream = StreamFromHandle(fdin.hf);
if (extractFile != null)
{
extractFile.Found = true;
extractFile.Length = (int)stream.Length;
if (stream.Length > 0)
{
extractFile.Data = new byte[stream.Length];
stream.Position = 0;
stream.Read(extractFile.Data, 0, (int)stream.Length);
}
}
stream.Close();
return IntPtr.Zero;
}
private static IntPtr FdiCreate(
NativeMethods.FdiMemAllocDelegate fnMemAlloc,
NativeMethods.FdiMemFreeDelegate fnMemFree,
NativeMethods.FdiFileOpenDelegate fnFileOpen,
NativeMethods.FdiFileReadDelegate fnFileRead,
NativeMethods.FdiFileWriteDelegate fnFileWrite,
NativeMethods.FdiFileCloseDelegate fnFileClose,
NativeMethods.FdiFileSeekDelegate fnFileSeek,
NativeMethods.CabError erf)
{
return NativeMethods.FdiCreate(fnMemAlloc, fnMemFree, fnFileOpen, fnFileRead, fnFileWrite,
fnFileClose, fnFileSeek, CpuTypeUnknown, erf);
}
private static int FileRead(IntPtr hf, byte[] buffer, int cb)
{
var stream = StreamFromHandle(hf);
return stream.Read(buffer, 0, cb);
}
private static int FileWrite(IntPtr hf, byte[] buffer, int cb)
{
var stream = StreamFromHandle(hf);
stream.Write(buffer, 0, cb);
return cb;
}
private static Stream StreamFromHandle(IntPtr hf)
{
return (Stream)((GCHandle)hf).Target;
}
private IntPtr MemAlloc(int cb)
{
return Marshal.AllocHGlobal(cb);
}
private void MemFree(IntPtr mem)
{
Marshal.FreeHGlobal(mem);
}
private int FileSeek(IntPtr hf, int dist, int seektype)
{
var stream = StreamFromHandle(hf);
return (int)stream.Seek(dist, (SeekOrigin)seektype);
}
private bool FdiCopy()
{
try
{
return NativeMethods.FdiCopy(FdiContext, "<notused>", "<notused>", 0, ExtractCallback, IntPtr.Zero, IntPtr.Zero);
}
catch (Exception)
{
return false;
}
}
private bool FdiIterate()
{
return NativeMethods.FdiCopy(FdiContext, "<notused>", "<notused>", 0, IterateCallback, IntPtr.Zero, IntPtr.Zero);
}
private IntPtr FdiContext { get; set; }
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
{
if (!_disposed)
{
if (FdiContext != IntPtr.Zero)
{
NativeMethods.FdiDestroy(FdiContext);
FdiContext = IntPtr.Zero;
}
_disposed = true;
}
}
}
}

Have you tried exploring the 7zip sdk http://www.7-zip.org/sdk.html
Try this codeproject article - http://www.codeproject.com/Articles/15397/Cabinet-File-CAB-Compression-and-Extraction

Memory leak in the accepted answer.
method:
private IntPtr OutputFileClose(NativeMethods.FdiNotification fdin)
should call:
GCHandle.FromIntPtr(fdin.hf).Free();

Related

SEHException when calling WTSVirtualChannelClose

I'm building application that will communicate with server using Remote Desktop Service API.
I'm building my application using code found here: https://code.google.com/p/tstunnels/ and here: http://www.codeproject.com/Articles/16374/How-to-Write-a-Terminal-Services-Add-in-in-Pure-C
I build client library that is loaded when I do Remote Desktop Connection and I'm able to send and receive messages.
On server side I get SEHException when I'm trying to close my application.
I use this methods using DLLImport:
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern IntPtr WTSVirtualChannelOpen(IntPtr server, int sessionId, [MarshalAs(UnmanagedType.LPStr)] string virtualName);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelQuery(IntPtr channelHandle, WtsVirtualClass Class, out IntPtr data, out int bytesReturned);
[DllImport("Wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelWrite(IntPtr channelHandle, byte[] data, int length, out int bytesWritten);
[DllImport("Wtsapi32.dll")]
public static extern bool WTSVirtualChannelClose(IntPtr channelHandle);
public static Stream WTSVirtualChannelQuery_WTSVirtualFileHandle(IntPtr channelHandle)
{
int len;
IntPtr buffer;
var b = WTSVirtualChannelQuery(channelHandle, WtsVirtualClass.WTSVirtualFileHandle, out buffer, out len);
if (!b) throw new Win32Exception();
var fileHandle = new SafeFileHandle(Marshal.ReadIntPtr(buffer), true);
WTSFreeMemory(buffer);
return new FileStream(fileHandle, FileAccess.ReadWrite, 0x1000, true);
}
Then inside my application I'm doing this:
mHandle = Native.WTSVirtualChannelOpen(IntPtr.Zero, -1, ChannelName);
if (mHandle == IntPtr.Zero)
{
Log("RDP Virtual channel Open Failed: " + new Win32Exception().Message);
return;
}
try
{
var stream = Native.WTSVirtualChannelQuery_WTSVirtualFileHandle(mHandle);
reader = new BinaryReader(new BufferedStream(stream));
}
catch (Win32Exception ex)
{
Log("RDP Virtual channel Query Failed: " + ex.Message);
return;
}
After that I'm able to write and read thru Virtual Channel, but when I try to close using below code I get error.
if (reader != null) reader.Close();
var ret = Native.WTSVirtualChannelClose(mHandle);
Here is error:
System.Runtime.InteropServices.SEHException (0x80004005): External
component has thrown an exception. at
Server.Native.WTSVirtualChannelClose(IntPtr channelHandle) at
Server.Server.Disconnect() in
i:\Tomka\TS\v2\TSAddin\Server\Server.cs:line 73
I tried getting last Win32Error using Marshal.GetLastWin32Error() but it is returning 1008.
Looking in msdn I found that error description:
ERROR_NO_TOKEN 1008 (0x3F0) An attempt was made to reference a token
that does not exist.
I found article on MSDN but code is in C++ so that didn't help, anyway here is the link: http://msdn.microsoft.com/en-us/library/aa383852(v=vs.85).aspx
EDIT 1
I did as #Hans wrote.
Here is first exception I got:
First-chance exception at 0x00000000771105B7 (ntdll.dll) in Server.exe:
0xC0000008: An invalid handle was specified.
EDIT 2
Here are classes I'm using on server:
class Native
{
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern IntPtr WTSVirtualChannelOpen(IntPtr server, int sessionId, [MarshalAs(UnmanagedType.LPStr)] string virtualName);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelQuery(IntPtr channelHandle, WtsVirtualClass Class, out IntPtr data, out int bytesReturned);
[DllImport("Wtsapi32.dll")]
public static extern void WTSFreeMemory(IntPtr memory);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelWrite(IntPtr channelHandle, byte[] data, int length, out int bytesWritten);
[DllImport("Wtsapi32.dll", SetLastError = true)]
public static extern bool WTSVirtualChannelRead(IntPtr channelHandle, int timeOut, IntPtr data, int length, out int bytesRead);
[DllImport("Wtsapi32.dll")]
public static extern bool WTSVirtualChannelClose(IntPtr channelHandle);
public static Stream WTSVirtualChannelQuery_WTSVirtualFileHandle(IntPtr channelHandle)
{
int len;
IntPtr buffer;
var b = WTSVirtualChannelQuery(channelHandle, WtsVirtualClass.WTSVirtualFileHandle, out buffer, out len);
if (!b) throw new Win32Exception();
var fileHandle = new SafeFileHandle(Marshal.ReadIntPtr(buffer), true);
WTSFreeMemory(buffer);
return new FileStream(fileHandle, FileAccess.ReadWrite, 0x1000, true);
}
public enum WtsVirtualClass
{
WTSVirtualClient = 0,
WTSVirtualFileHandle = 1
}
}
and Server.cs:
public class Server
{
private IntPtr mHandle;
private BinaryReader reader;
public bool IsConnected { get; private set; }
private bool SeenHello;
public const string ChannelName = "TEST";
private delegate void Action();
public void Connect()
{
mHandle = Native.WTSVirtualChannelOpen(IntPtr.Zero, -1, ChannelName);
if (mHandle == IntPtr.Zero)
{
Log("RDP Virtual channel Open Failed: " + new Win32Exception().Message);
return;
}
try
{
var stream = Native.WTSVirtualChannelQuery_WTSVirtualFileHandle(mHandle);
reader = new BinaryReader(new BufferedStream(stream));
}
catch (Win32Exception ex)
{
Log("RDP Virtual channel Query Failed: " + ex.Message);
return;
}
IsConnected = true;
Log("Connected");
Action process = Process;
process.BeginInvoke(process.EndInvoke, null);
Action hello = () =>
{
while (!SeenHello && IsConnected)
{
WriteMessage("HELLO");
Log("Sending HELLO");
Thread.Sleep(200);
}
};
hello.BeginInvoke(hello.EndInvoke, null);
}
public void Disconnect()
{
IsConnected = false;
if (reader != null) reader.Close();
var ret = Native.WTSVirtualChannelClose(mHandle);
}
private void Process()
{
while (IsConnected)
{
try
{
var len = reader.ReadInt32();
Log(len.ToString());
byte[] buff = reader.ReadBytes(len);
Log(Encoding.UTF8.GetString(buff));
MessageReceived(Encoding.UTF8.GetString(buff));
}
catch (OperationCanceledException ex)
{
Log(ex);
return;
}
catch (Exception ex)
{
Log(ex);
}
}
}
public bool WriteMessage(string msg)
{
byte[] data = Encoding.UTF8.GetBytes(msg);
int written;
var ret = Native.WTSVirtualChannelWrite(mHandle, data, data.Length, out written);
if (ret) return true;
var ex = new Win32Exception();
if (!SeenHello && ex.NativeErrorCode == 1 /* Incorrect Function */) return false;
Log("RDP Virtual channel Write Failed: " + ex.Message);
return false;
}
public void MessageReceived(string msg)
{
Log(msg);
if (msg.ToUpper() == "HELLO:RESPONSE")
{
if (!SeenHello)
{
SeenHello = true;
OnConnected(msg);
}
}
OnMessage(msg);
}
public EventHandler<MessageEventArgs> Connected;
protected void OnConnected(string msg)
{
if (Connected != null)
Connected(this, new MessageEventArgs(msg));
}
public EventHandler<MessageEventArgs> Message;
protected void OnMessage(string msg)
{
if (Message != null)
Message(this, new MessageEventArgs(msg));
}
public void Log(object message)
{
OnMessageLogged(message.ToString());
}
public EventHandler<MessageEventArgs> MessageLogged;
protected void OnMessageLogged(string message)
{
if (MessageLogged != null)
MessageLogged(this, new MessageEventArgs(message));
}
}
and usage looks like this:
In main form constructor I'm creating server object, in Load I'm calling Server.Connect() and in FormClosed Server.Disconnect().

Error on opening MemoryMappedFile in another process, C# compact framework

I have created a MemoryMappedFile using C#.Net Compact Framework on WinCE7.
When I am trying to open the same MemorymappedFile in another process, I am getting a null file handle.
Here is the code I am using.
namespace MMFWriteDemo
{
[Serializable]
public class FileMapIOException : IOException
{
private int m_win32Error;
public int Win32ErrorCode
{
get { return m_win32Error; }
}
public override string Message
{
get
{
if (Win32ErrorCode != 0)
return base.Message + " (" + Win32ErrorCode + ")";
return base.Message;
}
}
public FileMapIOException(int error)
: base()
{
m_win32Error = error;
}
public FileMapIOException(string message)
: base(message)
{
}
public FileMapIOException(string message, Exception innerException)
: base(message, innerException)
{
}
} // class FileMapIOException
public enum MapAccess
{
FileMapCopy = 0x0001,
FileMapWrite = 0x0002,
FileMapRead = 0x0004,
FileMapAllAccess = 0x001f,
}
[Flags]
public enum MapProtection
{
PageNone = 0x00000000,
// protection - mutually exclusive, do not or
PageReadOnly = 0x00000002,
PageReadWrite = 0x00000004,
PageWriteCopy = 0x00000008,
// attributes - or-able with protection
SecImage = 0x01000000,
SecReserve = 0x04000000,
SecCommit = 0x08000000,
SecNoCache = 0x10000000,
}
internal class Win32MapApis
{
[DllImport("coredll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(
String lpFileName, int dwDesiredAccess, int dwShareMode,
IntPtr lpSecurityAttributes, int dwCreationDisposition,
int dwFlagsAndAttributes, IntPtr hTemplateFile);
[DllImport("coredll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFileMapping(
IntPtr hFile, IntPtr lpAttributes, int flProtect,
int dwMaximumSizeLow, int dwMaximumSizeHigh,
String lpName);
[DllImport("coredll", SetLastError = true)]
public static extern bool FlushViewOfFile(
IntPtr lpBaseAddress, IntPtr dwNumBytesToFlush);
[DllImport("coredll", SetLastError = true)]
public static extern IntPtr MapViewOfFile(
IntPtr hFileMappingObject, int dwDesiredAccess, int dwFileOffsetHigh,
int dwFileOffsetLow, IntPtr dwNumBytesToMap);
//[DllImport("coredll", SetLastError = true, CharSet = CharSet.Auto)]
//public static extern IntPtr OpenFileMapping(
// int dwDesiredAccess, bool bInheritHandle, String lpName);
public static IntPtr OpenFileMapping(uint dwDesiredAccess, bool bInheritHandle, string lpName)
{
IntPtr t_pHandle = Win32MapApis.CreateFileMapping(new IntPtr(-1), IntPtr.Zero, (int)MapAccess.FileMapRead, 0, 0, lpName);
return t_pHandle;
}
[DllImport("coredll", SetLastError = true)]
public static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
[DllImport("coredll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle);
[DllImport("coredll.dll", SetLastError = true)]
public static extern Int32 GetLastError();
} // class Win32MapApis
public class MemoryMappedFile : MarshalByRefObject, IDisposable
{
//! handle to MemoryMappedFile object
private IntPtr _hMap = IntPtr.Zero;
private MapProtection _protection = MapProtection.PageNone;
private string _fileName = "";
public string FileName { get { return _fileName; } }
private long _maxSize;
private readonly bool _is64bit;
public long MaxSize { get { return _maxSize; } }
#region Constants
private const int GENERIC_READ = unchecked((int)0x80000000);
private const int GENERIC_WRITE = unchecked((int)0x40000000);
private const int OPEN_ALWAYS = 4;
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private static readonly IntPtr NULL_HANDLE = IntPtr.Zero;
#endregion // Constants
#region Properties
public bool IsOpen
{
get { return (_hMap != NULL_HANDLE); }
}
public bool Is64bit
{
get { return _is64bit; }
}
private MemoryMappedFile()
{
_is64bit = IntPtr.Size == 8;
}
~MemoryMappedFile()
{
Dispose(false);
}
#region Create Overloads
public static MemoryMappedFile
Create(MapProtection protection, long maxSize, string name)
{
return Create(null, protection, maxSize, name);
}
public static MemoryMappedFile
Create(MapProtection protection, long maxSize)
{
return Create(null, protection, maxSize, null);
}
public static MemoryMappedFile
Create(string fileName, MapProtection protection)
{
return Create(fileName, protection, 0, null);
}
public static MemoryMappedFile
Create(string fileName, MapProtection protection,
long maxSize)
{
return Create(fileName, protection, maxSize, null);
}
public static MemoryMappedFile
Create(string fileName, MapProtection protection,
long maxSize, String name)
{
MemoryMappedFile map = new MemoryMappedFile();
if (!map.Is64bit && maxSize > uint.MaxValue)
throw new ConstraintException("32bit systems support max size of 4gb.");
// open file first
IntPtr hFile = INVALID_HANDLE_VALUE;
if (!string.IsNullOrEmpty(fileName))
{
if (maxSize == 0)
{
if (!File.Exists(fileName))
{
throw new Exception(string.Format("Winterdom.IO.FileMap.MemoryMappedFile.Create - \"{0}\" does not exist ==> Unable to map entire file", fileName));
}
FileInfo backingFileInfo = new FileInfo(fileName);
maxSize = backingFileInfo.Length;
if (maxSize == 0)
{
throw new Exception(string.Format("Winterdom.IO.FileMap.MemoryMappedFile.Create - \"{0}\" is zero bytes ==> Unable to map entire file", fileName));
}
}
// determine file access needed
// we'll always need generic read access
int desiredAccess = GENERIC_READ;
if ((protection == MapProtection.PageReadWrite) ||
(protection == MapProtection.PageWriteCopy))
{
desiredAccess |= GENERIC_WRITE;
}
// open or create the file
// if it doesn't exist, it gets created
hFile = Win32MapApis.CreateFile(
fileName, desiredAccess, 0,
IntPtr.Zero, OPEN_ALWAYS, 0, IntPtr.Zero
);
if (hFile == INVALID_HANDLE_VALUE)
//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());
throw new FileMapIOException("MMF");
map._fileName = fileName;
}
map._hMap = Win32MapApis.CreateFileMapping(
hFile, IntPtr.Zero, (int)protection,
(int)((maxSize >> 32) & 0xFFFFFFFF),
(int)(maxSize & 0xFFFFFFFF), "unique"
);
// close file handle, we don't need it
if (hFile != INVALID_HANDLE_VALUE) Win32MapApis.CloseHandle(hFile);
if (map._hMap == NULL_HANDLE)
//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());
throw new FileMapIOException("MMF");
map._protection = protection;
map._maxSize = maxSize;
return map;
}
#endregion // Create Overloads
public static MemoryMappedFile Open(MapAccess access, String name)
{
MemoryMappedFile map = new MemoryMappedFile
{
_hMap = Win32MapApis.OpenFileMapping((uint)access, false, name)
};
if (map._hMap == NULL_HANDLE)
throw new FileMapIOException("MMF");
//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());
map._maxSize = -1; // debug unknown
return map;
}
public void Close()
{
Dispose(true);
}
public IntPtr MapView(MapAccess access, long offset, long size)
{
if (!IsOpen)
throw new ObjectDisposedException("Winterdom.IO.FileMap.MemoryMappedFile.MapView - MMF already closed");
// Throws OverflowException if (a) this is a 32-bit platform AND (b) size is out of bounds (ie. int bounds) with respect to this platform
IntPtr mapSize = new IntPtr(size);
IntPtr baseAddress = Win32MapApis.MapViewOfFile(
_hMap, (int)access,
(int)((offset >> 32) & 0xFFFFFFFF),
(int)(offset & 0xFFFFFFFF), mapSize
);
if (baseAddress == IntPtr.Zero)
throw new FileMapIOException("MMF");
//throw new FileMapIOException(Marshal.GetHRForLastWin32Error());
return baseAddress;
}
public MapViewStream MapAsStream()
{
if (!IsOpen)
throw new ObjectDisposedException("Winterdom.IO.FileMap.MemoryMappedFile.MapView - MMF already closed");
// sws should verify against _protection
// Don't know what to do about FILE_MAP_COPY et al
bool isWriteable = (_protection & MapProtection.PageReadWrite) == MapProtection.PageReadWrite;
return new MapViewStream(this, MaxSize, isWriteable);
}
public void UnMapView(IntPtr mapBaseAddr)
{
Win32MapApis.UnmapViewOfFile(mapBaseAddr);
}
public void UnMapView(MapViewStream mappedViewStream)
{
UnMapView(mappedViewStream.ViewBaseAddr);
}
public void Flush(IntPtr viewBaseAddr)
{
// Throws OverflowException if (a) this is a 32-bit platform AND (b) size is out of bounds (ie. int bounds) with respect to this platform
IntPtr flushLength = new IntPtr(MaxSize);
Win32MapApis.FlushViewOfFile(viewBaseAddr, flushLength);
}
public void Flush(MapViewStream mappedViewStream)
{
Flush(mappedViewStream.ViewBaseAddr);
}
#region IDisposable implementation
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (IsOpen)
Win32MapApis.CloseHandle(_hMap);
_hMap = NULL_HANDLE;
if (disposing)
GC.SuppressFinalize(this);
}
#endregion // IDisposable implementation
} // class MemoryMappedFile
public class MapViewStream : Stream, IDisposable
{
#region Map/View Related Fields
protected MemoryMappedFile _backingFile;
protected MapAccess _access = MapAccess.FileMapWrite;
protected bool _isWriteable;
IntPtr _viewBaseAddr = IntPtr.Zero; // Pointer to the base address of the currently mapped view
protected long _mapSize;
protected long _viewStartIdx = -1;
protected long _viewSize = -1;
long _position; //! our current position in the stream buffer
#region Properties
public IntPtr ViewBaseAddr
{
get { return _viewBaseAddr; }
}
public bool IsViewMapped
{
get { return (_viewStartIdx != -1) && (_viewStartIdx + _viewSize) <= (_mapSize); }
}
#endregion
#endregion // Map/View Related Fields
#region Map / Unmap View
#region Unmap View
protected void UnmapView()
{
if (IsViewMapped)
{
_backingFile.UnMapView(this);
_viewStartIdx = -1;
_viewSize = -1;
}
}
#endregion
#region Map View
protected void MapView(ref long viewStartIdx, ref long viewSize)
{
// Now map the view
_viewBaseAddr = _backingFile.MapView(_access, viewStartIdx, viewSize);
_viewStartIdx = viewStartIdx;
_viewSize = viewSize;
}
#endregion
#endregion
#region Constructors
internal MapViewStream(MemoryMappedFile backingFile, long mapSize, bool isWriteable)
{
if (backingFile == null)
{
throw new Exception("MapViewStream.MapViewStream - backingFile is null");
}
if (!backingFile.IsOpen)
{
throw new Exception("MapViewStream.MapViewStream - backingFile is not open");
}
if ((mapSize < 1) || (mapSize > backingFile.MaxSize))
{
throw new Exception(string.Format("MapViewStream.MapViewStream - mapSize is invalid. mapSize == {0}, backingFile.MaxSize == {1}", mapSize, backingFile.MaxSize));
}
_backingFile = backingFile;
_isWriteable = isWriteable;
_access = isWriteable ? MapAccess.FileMapWrite : MapAccess.FileMapRead;
// Need a backingFile.SupportsAccess function that takes a MapAccess compares it against its stored MapProtection protection and returns bool
_mapSize = mapSize;
_isOpen = true;
// Map the first view
Seek(0, SeekOrigin.Begin);
}
#endregion
#region Stream Properties
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return _isWriteable; }
}
public override long Length
{
get { return _mapSize; }
}
public override long Position
{
get { return _position; }
set { Seek(value, SeekOrigin.Begin); }
}
#endregion // Stream Properties
#region Stream Methods
public override void Flush()
{
if (!IsOpen)
throw new ObjectDisposedException("Winterdom.IO.FileMap.MapViewStream.Flush - Stream is closed");
// flush the view but leave the buffer intact
_backingFile.Flush(this);
}
public override int Read(byte[] buffer, int offset, int count)
{
if (!IsOpen)
throw new ObjectDisposedException("Stream is closed");
if (buffer.Length - offset < count)
throw new ArgumentException("Invalid Offset");
int bytesToRead = (int)Math.Min(Length - _position, count);
Marshal.Copy((IntPtr)(_viewBaseAddr.ToInt64() + _position), buffer, offset, bytesToRead);
_position += bytesToRead;
return bytesToRead;
}
public override void Write(byte[] buffer, int offset, int count)
{
if (!IsOpen)
throw new ObjectDisposedException("Stream is closed");
if (!CanWrite)
throw new FileMapIOException("Stream cannot be written to");
if (buffer.Length - offset < count)
throw new ArgumentException("Invalid Offset");
int bytesToWrite = (int)Math.Min(Length - _position, count);
if (bytesToWrite == 0)
return;
Marshal.Copy(buffer, offset, (IntPtr)(_viewBaseAddr.ToInt64() + _position), bytesToWrite);
_position += bytesToWrite;
}
public override long Seek(long offset, SeekOrigin origin)
{
if (!IsOpen)
throw new ObjectDisposedException("Stream is closed");
long newpos = 0;
switch (origin)
{
case SeekOrigin.Begin: newpos = offset; break;
case SeekOrigin.Current: newpos = Position + offset; break;
case SeekOrigin.End: newpos = Length + offset; break;
}
// sanity check
if (newpos < 0 || newpos > Length)
throw new FileMapIOException("Invalid Seek Offset");
_position = newpos;
if (!IsViewMapped)
{
MapView(ref newpos, ref _mapSize); // use _mapsize here??
}
return newpos;
}
public override void SetLength(long value)
{
// not supported!
throw new NotSupportedException("Winterdom.IO.FileMap.MapViewStream.SetLength - Can't change map size");
}
public override void Close()
{
Dispose(true);
}
#endregion // Stream methods
#region IDisposable Implementation
private bool _isOpen;
public bool IsOpen { get { return _isOpen; } }
public new void Dispose()
{
Dispose(true);
}
protected new virtual void Dispose(bool disposing)
{
if (IsOpen)
{
Flush();
UnmapView();
_isOpen = false;
}
if (disposing)
GC.SuppressFinalize(this);
}
~MapViewStream()
{
Dispose(false);
}
#endregion // IDisposable Implementation
} // class MapViewStream
public class GenericMemoryMappedArray<TValue> : IDisposable, IEnumerable<TValue>
where TValue : struct
{
#region Private fields
private string _path;
private string _fileName;
private string _uniqueName = "mmf-" + "12345Test";//Guid.NewGuid();
private long _fileSize;
private MemoryMappedFile _map;
private int _dataSize;
private bool _deleteFile = true;
private byte[] _buffer;
private IntPtr _memPtr;
private bool _autogrow = true;
private Dictionary<int, MapViewStream> _inUse = new Dictionary<int, MapViewStream>(10);
private Dictionary<int, DateTime> _lastUsedThread = new Dictionary<int, DateTime>();
private readonly object _lockObject = new object();
//private Timer _pooltimer;
private bool _isDisposed;
#endregion
#region Properties
public string UniqueName
{
get { return _uniqueName; }
set { _uniqueName = value; }
}
public long Length
{
get
{
return _fileSize / _dataSize;
}
}
public long Position
{
set
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;
Stream s = GetView(threadId);
s.Position = value * _dataSize;
}
}
public bool AutoGrow
{
get { return _autogrow; }
set { _autogrow = value; }
}
public override string ToString()
{
return string.Format("Length {0}", Length);
}
#endregion
#region Constructor
public GenericMemoryMappedArray(long size, string path)
{
_path = path;
_fileName = Path.Combine(path, _uniqueName + ".bin");
//_fileName = Path.Combine(path, "mmfTest.bin");
// Get the size of TValue
_dataSize = Marshal.SizeOf(typeof(TValue));
// Allocate a global buffer for this instance
_buffer = new byte[_dataSize];
// Allocate a global unmanaged buffer for this instance
_memPtr = Marshal.AllocHGlobal(_dataSize);
SetFileSize(size);
}
#endregion
#region Finalizer
~GenericMemoryMappedArray()
{
Dispose(false);
}
#endregion
#region Private methods
private Stream GetView(int threadId)
{
MapViewStream s;
if (!_inUse.TryGetValue(threadId, out s))
{
// create new view and add to pool
MapViewStream mvs = _map.MapAsStream();
lock (_lockObject)
{
_inUse.Add(threadId, mvs);
}
return mvs;
}
return s;
}
private void SetFileSize(long size)
{
_fileSize = _dataSize * size;
_map = MemoryMappedFile.Create(_fileName, MapProtection.PageReadWrite, _fileSize);
}
#endregion
#region Public methods
public void Write(byte[] buffer)
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;
Stream s = GetView(threadId);
s.Write(buffer, 0, buffer.Length);
}
public void WriteByte(byte b)
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;
Stream s = GetView(threadId);
byte[] buffer = new byte[1] { b };
s.Write(buffer, 0, 1);
}
public int Read()
{
int threadId = Thread.CurrentThread.ManagedThreadId;
_lastUsedThread[threadId] = DateTime.UtcNow;
Stream s = GetView(threadId);
int count = s.Read(_buffer, 0, _buffer.Length);
return count;
}
public byte ReadByte()
{
int threadId = Thread.CurrentThread.ManagedThreadId;
Stream s = GetView(threadId);
return (byte)s.ReadByte();
}
public TValue this[long index]
{
get
{
lock (this)
{
if (index >= Length)
{
throw new ArgumentOutOfRangeException("index", "Tried to access item outside the array boundaries");
}
Position = index;
Read();
TValue value = ConvertToTValue();
return value;
}
}
set
{
lock (this)
{
if (index >= Length)
{
if (_autogrow)
Grow(index, 10);
else
{
throw new ArgumentOutOfRangeException("index", "Tried to access item outside the array");
}
}
Position = index;
ConvertToBytes(value);
Write(_buffer);
}
}
}
private void ConvertToBytes(TValue value)
{
// Could set the last parameter to false if TValue only contains value types
// Safer to leave it to true for all purposes.
Marshal.StructureToPtr(value, _memPtr, true);
Marshal.Copy(_memPtr, _buffer, 0, _dataSize);
}
private TValue ConvertToTValue()
{
Marshal.Copy(_buffer, 0, _memPtr, _dataSize);
object obj = Marshal.PtrToStructure(_memPtr, typeof(TValue));
return (TValue)obj;
}
private void Grow(long size, int percentage)
{
_deleteFile = false;
lock (_lockObject)
{
Dispose(true);
long oldSize = _fileSize;
_fileSize = (long)((float)size * _dataSize * ((100F + percentage) / 100F)); //required filesize
if (_fileSize < (oldSize + _dataSize))
{
_fileSize = oldSize + _dataSize;
}
_map = MemoryMappedFile.Create(_fileName, MapProtection.PageReadWrite, _fileSize);
}
}
#endregion
#region Clone Members
public GenericMemoryMappedArray<TValue> Clone()
{
string copyName = _uniqueName + Guid.NewGuid();
string currentPath = Path.Combine(_path, copyName + ".bin");
File.Copy(_fileName, currentPath);
GenericMemoryMappedArray<TValue> current = new GenericMemoryMappedArray<TValue>(Length, currentPath);
return current;
}
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed && disposing)
{
lock (_lockObject)
{
// Clean up all views
foreach (KeyValuePair<int, MapViewStream> pair in _inUse)
{
pair.Value.Dispose();
pair.Value.Close();
}
_inUse.Clear();
_lastUsedThread.Clear();
}
if (_map != null)
{
_map.Close();
}
}
try
{
if (_deleteFile)
{
Marshal.DestroyStructure(_memPtr, typeof(TValue)); // Clear unmanaged buffer data
Marshal.FreeHGlobal(_memPtr); // Free unmanaged buffer
if (File.Exists(_fileName)) File.Delete(_fileName);
}
}
catch (Exception)
{
// TODO: Handle files which for some reason didn't want to be deleted
throw;
}
_deleteFile = true;
}
#endregion
#region IEnumerable<TValue> Members
public IEnumerator<TValue> GetEnumerator()
{
lock (this)
{
Position = 0;
for (int i = 0; i < Length; i++)
{
Read();
yield return ConvertToTValue();
}
}
}
#endregion
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}
}
Application for creating the memory mapped file and writing to it, this works OK.
namespace MMFWriteDemo
{
class Program
{
static void Main(string[] args)
{
GenericMemoryMappedArray<byte> mmfTest = new GenericMemoryMappedArray<byte>(1024 * 1024 * 8, #"internal\data");
int i = 0;
byte b = 0;
while (i < 1000)
{
mmfTest.WriteByte(b);
b++;
if (b == 255)
{
b = 0;
}
i++;
}
Console.ReadLine();
}
}
}
This works good, i can see the file created and I can write to it.
Application for opening the memory mapped file and reading from it, it does not work.
namespace MMFReaderDemo
{
class Program
{
static void Main(string[] args)
{
GenericMemoryMappedArray<byte> mmfTest = new GenericMemoryMappedArray<byte>(1024 * 1024 * 8, #"internal\data");
int i = 0;
while (i < 100)
{
Console.WriteLine(mmfTest.ReadByte());
i++;
}
}
}
}
ErrorCode: 0x80000005
Actually I am trying to share the memory mapped file between driver(written in C++) and user app(written in C#).

Unpack cab file in memory

I am uploading a cab file from a web form, and want to unpack it in memory. I've tried tackling the issue with a CabInfo file, but without success. I do know how to unpack a cab file to my local disk, but do not know how to apply this in memory.
Any assistance would be appreciated.
If using another library is possible, take a look at this. The description clearly states the library will allow you to extract into memoty.
WIX is an open source project. You could always ask for this feature, ask for a better solution on their forum or simply modify the code for your need.
Vadim
Your initial question seems to indicate that you are allowed to use the Microsoft.Deployment.Compression DLL. If true, the code below should get you close to what you want:
CabEngine engine = new CabEngine();
foreach (ArchiveFileInfo archiveFileInfo in engine.GetFileInfo(fileStream))
{
Stream stream = engine.Unpack(fileStream, archiveFileInfo.Name);
byte[] buffer = new byte[stream.Length];
//The (int) below is a dirty trick for demonstration purposes only;
//re-work for production code
stream.Read(buffer, 0, (int)stream.Length);
}
Later answers from you seem to indicate that you are not allowed to use 3rd party DLLs in which case you will want to use the FDI* API. This link has some code that you will be able to modify from using hard-coded paths to (memory) streams: Extract .cab file in C#
There is a ready to use project that you can download: Cabinet File (*.CAB) Compression and Extraction
According to version history since Sep 2008 "..you can extract directly to memory."
Here is a utility class that should work in memory (it supports x86 or x64 compilation). Here is out you would use it, if we suppose a .CAB file has been uploaded into ASP.NET using the standard upload protocol:
using (CabFile file = new CabFile(HttpContext.Current.Request.Files[0].InputStream))
{
file.EntryExtract += CabEntryExtract;
file.ExtractEntries();
}
static void CabEntryExtract(object sender, CabEntryExtractEventArgs e)
{
// e.Entry.Name contains the entry name
// e.Entry.Data contains a byte[] with the entry data
// e.Entry.LastWriteTime contains the entry last write time
// e.Entry.Size contains the entry uncompressed size
}
And here is the utility and associated classes:
public sealed class CabFile : IDisposable
{
private IntPtr _hfdi;
private ERF _erf;
private GCHandle _erfHandle;
private byte[] _data;
private Dictionary<IntPtr, object> _handles = new Dictionary<IntPtr, object>();
private MemoryStream _currentEntryData;
private FNALLOC _alloc;
private FNCLOSE _close;
private FNFREE _free;
private FNOPEN _open;
private FNREAD _read;
private FNWRITE _write;
private FNSEEK _seek;
private FNFDINOTIFY _extract;
public event EventHandler<CabEntryExtractEventArgs> EntryExtract;
public CabFile(string filePath)
: this(GetStream(filePath))
{
}
private static Stream GetStream(string filePath)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
return new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
}
public CabFile(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
using (MemoryStream data = new MemoryStream())
{
stream.CopyTo(data);
_data = data.ToArray();
}
_erf = new ERF();
_alloc = new FNALLOC(FnAlloc);
_free = new FNFREE(FnFree);
_close = new FNCLOSE(FnClose);
_open = new FNOPEN(FnOpen);
_read = new FNREAD(FnRead);
_write = new FNWRITE(FnWrite);
_seek = new FNSEEK(FnSeek);
_extract = new FNFDINOTIFY(FnNotifyExtract);
_erfHandle = GCHandle.Alloc(_erf, GCHandleType.Pinned);
_hfdi = FDICreate(
Marshal.GetFunctionPointerForDelegate(_alloc),
Marshal.GetFunctionPointerForDelegate(_free),
Marshal.GetFunctionPointerForDelegate(_open),
Marshal.GetFunctionPointerForDelegate(_read),
Marshal.GetFunctionPointerForDelegate(_write),
Marshal.GetFunctionPointerForDelegate(_close),
Marshal.GetFunctionPointerForDelegate(_seek)
, -1, _erfHandle.AddrOfPinnedObject());
}
public void ExtractEntries()
{
FDICopy(_hfdi, string.Empty, string.Empty, 0, Marshal.GetFunctionPointerForDelegate(_extract), IntPtr.Zero, IntPtr.Zero);
}
public void Dispose()
{
if (_hfdi != IntPtr.Zero)
{
FDIDestroy(_hfdi);
_hfdi = IntPtr.Zero;
}
_erfHandle.Free();
}
private void OnEntryExtract(CabEntry entry)
{
EventHandler<CabEntryExtractEventArgs> handler = EntryExtract;
if (handler != null)
{
handler(this, new CabEntryExtractEventArgs(entry));
}
}
private IntPtr FnAlloc(int cb)
{
return Marshal.AllocHGlobal(cb);
}
private void FnFree(IntPtr pv)
{
Marshal.FreeHGlobal(pv);
}
private IntPtr FnOpen(string pszFile, int oflag, int pmode)
{
// only used for reading archive
IntPtr h = new IntPtr(_handles.Count + 1);
_handles.Add(h, 0);
return h;
}
private int FnRead(IntPtr hf, byte[] pv, int cb)
{
// only used for reading archive
int pos = (int)_handles[hf];
int left = _data.Length - pos;
int read = Math.Min(left, cb);
if (read > 0)
{
Array.Copy(_data, pos, pv, 0, read);
_handles[hf] = pos + read;
}
return read;
}
private int FnWrite(IntPtr hf, byte[] pv, int cb)
{
// only used for writing entries
_currentEntryData.Write(pv, 0, cb);
return cb;
}
private int FnClose(IntPtr hf)
{
object o = _handles[hf];
CabEntry entry = o as CabEntry;
if (entry != null)
{
entry.Data = _currentEntryData.ToArray();
_currentEntryData.Dispose();
}
_handles.Remove(hf);
return 0;
}
private int FnSeek(IntPtr hf, int dist, SeekOrigin seektype)
{
// only used for seeking archive
int pos;
switch (seektype)
{
case SeekOrigin.Begin:
pos = dist;
break;
case SeekOrigin.Current:
pos = (int)_handles[hf] + dist;
break;
//case SeekOrigin.End:
default:
pos = _data.Length + dist;
break;
}
_handles[hf] = pos;
return pos;
}
private IntPtr FnNotifyExtract(FDINOTIFICATIONTYPE fdint, FDINOTIFICATION fdin)
{
CabEntry entry;
switch (fdint)
{
case FDINOTIFICATIONTYPE.COPY_FILE:
entry = new CabEntry(fdin);
entry._handle = new IntPtr(_handles.Count + 1);
_handles.Add(entry._handle, entry);
_currentEntryData = new MemoryStream();
return entry._handle;
case FDINOTIFICATIONTYPE.CLOSE_FILE_INFO:
entry = (CabEntry)_handles[fdin.hf];
FnClose(fdin.hf);
OnEntryExtract(entry);
return new IntPtr(1);
default:
return IntPtr.Zero;
}
}
private enum FDINOTIFICATIONTYPE
{
CABINET_INFO = 0,
PARTIAL_FILE = 1,
COPY_FILE = 2,
CLOSE_FILE_INFO = 3,
NEXT_CABINET = 4,
ENUMERATE = 5,
}
[StructLayout(LayoutKind.Sequential)]
private struct ERF
{
public int erfOper;
public int erfType;
public int fError;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
internal class FDINOTIFICATION
{
public int cb;
public IntPtr psz1;
public IntPtr psz2;
public IntPtr psz3;
public IntPtr pv;
public IntPtr hf;
public ushort date;
public ushort time;
public ushort attribs;
public ushort setID;
public ushort iCabinet;
public ushort iFolder;
public int fdie;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr FNALLOC(int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void FNFREE(IntPtr pv);
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private delegate IntPtr FNOPEN(string pszFile, int oflag, int pmode);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNREAD(IntPtr hf, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNWRITE(IntPtr hf, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pv, int cb);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNCLOSE(IntPtr hf);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int FNSEEK(IntPtr hf, int dist, SeekOrigin seektype);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr FNFDINOTIFY(FDINOTIFICATIONTYPE fdint, FDINOTIFICATION pfdin);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr FDICreate(IntPtr pfnalloc, IntPtr pfnfree, IntPtr pfnopen, IntPtr pfnread, IntPtr pfnwriter, IntPtr pfnclose, IntPtr pfnseek, int cpuType, IntPtr perf);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr FDIDestroy(IntPtr hdfi);
[DllImport("cabinet.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern IntPtr FDICopy(IntPtr hdfi, string pszCabinet, string pszCabPath, int flags, IntPtr fnNotify, IntPtr fnDecrypt, IntPtr userData);
}
public sealed class CabEntry
{
internal IntPtr _handle;
internal CabEntry(CabFile.FDINOTIFICATION fdin)
{
Name = Marshal.PtrToStringAnsi(fdin.psz1);
Size = fdin.cb;
LastWriteTime = new DateTime(1980 + GetMask(fdin.date, 9, 15), GetMask(fdin.date, 5, 8), GetMask(fdin.date, 0, 4),
GetMask(fdin.time, 11, 15), GetMask(fdin.time, 5, 10), 2 * GetMask(fdin.time, 0, 4));
}
private static int GetMask(int value, byte startByte, byte endByte)
{
int final = 0;
int v = 1;
for (byte b = startByte; b <= endByte; b++)
{
if ((value & (1 << b)) != 0)
{
final += v;
}
v = v * 2;
}
return final;
}
public string Name { get; private set; }
public int Size { get; private set; }
public DateTime LastWriteTime { get; private set; }
public byte[] Data { get; internal set; }
}
public sealed class CabEntryExtractEventArgs : EventArgs
{
public CabEntryExtractEventArgs(CabEntry entry)
{
if (entry == null)
throw new ArgumentNullException("entry");
Entry = entry;
}
public CabEntry Entry { get; private set; }
}
NOTE: this code allocates big byte[] chunks so it could be optimized to use things like ChunkedMemoryStream (available for example in this library: CodeFluent Runtime Client) instead of byte[] to avoid impacting the LOH (Large Object Heap) too much.

Forcing closed an open file by C# [duplicate]

I've seen several of answers about using Handle or Process Monitor, but I would like to be able to find out in my own code (C#)
which process is locking a file.
I have a nasty feeling that I'm going to have to spelunk around in the win32 API, but if anyone has already done this and can put me on the right track, I'd really appreciate the help.
Update
Links to similar questions
How does one figure out what process locked a file using c#?
Command line tool
Across a Network
Locking a USB device
Unit test fails with locked file
deleting locked file
Long ago it was impossible to reliably get the list of processes locking a file because Windows simply did not track that information. To support the Restart Manager API, that information is now tracked.
I put together code that takes the path of a file and returns a List<Process> of all processes that are locking that file.
using System.Runtime.InteropServices;
using System.Diagnostics;
using System;
using System.Collections.Generic;
static public class FileUtil
{
[StructLayout(LayoutKind.Sequential)]
struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
///
/// </remarks>
static public List<Process> WhoIsLocking(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
List<Process> processes = new List<Process>();
int res = RmStartSession(out handle, 0, key);
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
try
{
const int ERROR_MORE_DATA = 234;
uint pnProcInfoNeeded = 0,
pnProcInfo = 0,
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new string[] { path }; // Just checking on one resource.
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
if (res != 0) throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if (res == ERROR_MORE_DATA)
{
// Create an array to store the process results
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if (res == 0)
{
processes = new List<Process>((int)pnProcInfo);
// Enumerate all of the results and add them to the
// list to be returned
for (int i = 0; i < pnProcInfo; i++)
{
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
// catch the error -- in case the process is no longer running
catch (ArgumentException) { }
}
}
else throw new Exception("Could not list processes locking resource.");
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally
{
RmEndSession(handle);
}
return processes;
}
}
Using from Limited Permission (e.g. IIS)
This call accesses the registry. If the process does not have permission to do so, you will get ERROR_WRITE_FAULT, meaning An operation was unable to read or write to the registry. You could selectively grant permission to your restricted account to the necessary part of the registry. It is more secure though to have your limited access process set a flag (e.g. in the database or the file system, or by using an interprocess communication mechanism such as queue or named pipe) and have a second process call the Restart Manager API.
Granting other-than-minimal permissions to the IIS user is a security risk.
This question had an original answer that is now over 7 years old. That code is preserved at https://gist.github.com/i-e-b/2290426
This old version might work for you if you need to use Windows XP for some reason.
A much better answer is at How to check for file lock?
I've replicated Eric J's answer below (with using statements added, and class & method names to match the old code that was here) Please note that the comments to this answer may be out-of-date.
Research by user 'Walkman' is ongoing to improve the older code, as there are some conditions where the Restart Manager does not list all locks. See Github repo: https://github.com/Walkman100/FileLocks
Use like:
List<Process> locks = Win32Processes.GetProcessesLockingFile(#"C:\Hello.docx");
Code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace FileLockInfo
{
public static class Win32Processes
{
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
/// </remarks>
public static List<Process> GetProcessesLockingFile(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
int res = RmStartSession(out handle, 0, key);
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
try
{
const int MORE_DATA = 234;
uint pnProcInfoNeeded, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone;
string[] resources = {path}; // Just checking on one resource.
res = RmRegisterResources(handle, (uint) resources.Length, resources, 0, null, 0, null);
if (res != 0) throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if (res == MORE_DATA)
{
return EnumerateProcesses(pnProcInfoNeeded, handle, lpdwRebootReasons);
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally
{
RmEndSession(handle);
}
return new List<Process>();
}
[StructLayout(LayoutKind.Sequential)]
public struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
public enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)] public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFilenames,
uint nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, uint nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded,
ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
private static List<Process> EnumerateProcesses(uint pnProcInfoNeeded, uint handle, uint lpdwRebootReasons)
{
var processes = new List<Process>(10);
// Create an array to store the process results
var processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
var pnProcInfo = pnProcInfoNeeded;
// Get the list
var res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if (res != 0) throw new Exception("Could not list processes locking resource.");
for (int i = 0; i < pnProcInfo; i++)
{
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
catch (ArgumentException) { } // catch the error -- in case the process is no longer running
}
return processes;
}
}
}
It is very complex to invoke Win32 from C#.
You should use the tool Handle.exe.
After that your C# code have to be the following:
string fileName = #"c:\aaa.doc";//Path to locked file
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = fileName+" /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach(Match match in Regex.Matches(outputTool, matchPattern))
{
Process.GetProcessById(int.Parse(match.Value)).Kill();
}
One of the good things about handle.exe is that you can run it as a subprocess and parse the output.
We do this in our deployment script - works like a charm.
The code I found here,
https://vmccontroller.svn.codeplex.com/svn/VmcController/VmcServices/DetectOpenFiles.cs
Works for me much better than the code provided by Iain. Iain's code seemed to be acquiring a lock of its own. Here is my slightly modified version of the code above modified to return the string path of the files locked instead of the FileSystemInfo object,
using System;
using System.Collections.Generic;
//using System.EnterpriseServices;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;
namespace Crmc.Core.BuildTasks
{
using System.Diagnostics;
using System.Linq;
#region ENUMs
internal enum NT_STATUS
{
STATUS_SUCCESS = 0x00000000,
STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L),
STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L)
}
internal enum SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
}
internal enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
[Flags]
internal enum ProcessAccessRights
{
PROCESS_DUP_HANDLE = 0x00000040
}
[Flags]
internal enum DuplicateHandleOptions
{
DUPLICATE_CLOSE_SOURCE = 0x1,
DUPLICATE_SAME_ACCESS = 0x2
}
#endregion
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeObjectHandle()
: base(true)
{ }
internal SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return NativeMethods.CloseHandle(base.handle);
}
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeProcessHandle()
: base(true)
{ }
internal SafeProcessHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return NativeMethods.CloseHandle(base.handle);
}
}
#region Native Methods
internal static class NativeMethods
{
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[In] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[In] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern SafeProcessHandle OpenProcess(
[In] ProcessAccessRights dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DuplicateHandle(
[In] IntPtr hSourceProcessHandle,
[In] IntPtr hSourceHandle,
[In] IntPtr hTargetProcessHandle,
[Out] out SafeObjectHandle lpTargetHandle,
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] DuplicateHandleOptions dwOptions);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int GetProcessId(
[In] IntPtr Process);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int QueryDosDevice(
[In] string lpDeviceName,
[Out] StringBuilder lpTargetPath,
[In] int ucchMax);
}
#endregion
//[ComVisible(true), EventTrackingEnabled(true)]
public class DetectOpenFiles// : ServicedComponent
{
private static Dictionary<string, string> deviceMap;
private const string networkDevicePrefix = "\\Device\\LanmanRedirector\\";
private const int MAX_PATH = 260;
private enum SystemHandleType
{
OB_TYPE_UNKNOWN = 0,
OB_TYPE_TYPE = 1,
OB_TYPE_DIRECTORY,
OB_TYPE_SYMBOLIC_LINK,
OB_TYPE_TOKEN,
OB_TYPE_PROCESS,
OB_TYPE_THREAD,
OB_TYPE_UNKNOWN_7,
OB_TYPE_EVENT,
OB_TYPE_EVENT_PAIR,
OB_TYPE_MUTANT,
OB_TYPE_UNKNOWN_11,
OB_TYPE_SEMAPHORE,
OB_TYPE_TIMER,
OB_TYPE_PROFILE,
OB_TYPE_WINDOW_STATION,
OB_TYPE_DESKTOP,
OB_TYPE_SECTION,
OB_TYPE_KEY,
OB_TYPE_PORT,
OB_TYPE_WAITABLE_PORT,
OB_TYPE_UNKNOWN_21,
OB_TYPE_UNKNOWN_22,
OB_TYPE_UNKNOWN_23,
OB_TYPE_UNKNOWN_24,
//OB_TYPE_CONTROLLER,
//OB_TYPE_DEVICE,
//OB_TYPE_DRIVER,
OB_TYPE_IO_COMPLETION,
OB_TYPE_FILE
};
private const int handleTypeTokenCount = 27;
private static readonly string[] handleTypeTokens = new string[] {
"", "", "Directory", "SymbolicLink", "Token",
"Process", "Thread", "Unknown7", "Event", "EventPair", "Mutant",
"Unknown11", "Semaphore", "Timer", "Profile", "WindowStation",
"Desktop", "Section", "Key", "Port", "WaitablePort",
"Unknown21", "Unknown22", "Unknown23", "Unknown24",
"IoCompletion", "File"
};
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_HANDLE_ENTRY
{
public int OwnerPid;
public byte ObjectType;
public byte HandleFlags;
public short HandleValue;
public int ObjectPointer;
public int AccessMask;
}
/// <summary>
/// Gets the open files enumerator.
/// </summary>
/// <param name="processId">The process id.</param>
/// <returns></returns>
public static IEnumerable<String> GetOpenFilesEnumerator(int processId)
{
return new OpenFiles(processId);
}
public static List<Process> GetProcessesUsingFile(string fName)
{
List<Process> result = new List<Process>();
foreach (var p in Process.GetProcesses())
{
try
{
if (DetectOpenFiles.GetOpenFilesEnumerator(p.Id).Contains(fName))
{
result.Add(p);
}
}
catch { }//some processes will fail
}
return result;
}
private sealed class OpenFiles : IEnumerable<String>
{
private readonly int processId;
internal OpenFiles(int processId)
{
this.processId = processId;
}
#region IEnumerable<FileSystemInfo> Members
public IEnumerator<String> GetEnumerator()
{
NT_STATUS ret;
int length = 0x10000;
// Loop, probing for required memory.
do
{
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees that the address of the allocated
// memory is actually assigned to ptr if an
// asynchronous exception occurs.
ptr = Marshal.AllocHGlobal(length);
}
int returnLength;
ret = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out returnLength);
if (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH)
{
// Round required memory up to the nearest 64KB boundary.
length = ((returnLength + 0xffff) & ~0xffff);
}
else if (ret == NT_STATUS.STATUS_SUCCESS)
{
int handleCount = Marshal.ReadInt32(ptr);
int offset = sizeof(int);
int size = Marshal.SizeOf(typeof(SYSTEM_HANDLE_ENTRY));
for (int i = 0; i < handleCount; i++)
{
SYSTEM_HANDLE_ENTRY handleEntry = (SYSTEM_HANDLE_ENTRY)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SYSTEM_HANDLE_ENTRY));
if (handleEntry.OwnerPid == processId)
{
IntPtr handle = (IntPtr)handleEntry.HandleValue;
SystemHandleType handleType;
if (GetHandleType(handle, handleEntry.OwnerPid, out handleType) && handleType == SystemHandleType.OB_TYPE_FILE)
{
string devicePath;
if (GetFileNameFromHandle(handle, handleEntry.OwnerPid, out devicePath))
{
string dosPath;
if (ConvertDevicePathToDosPath(devicePath, out dosPath))
{
if (File.Exists(dosPath))
{
yield return dosPath; // return new FileInfo(dosPath);
}
else if (Directory.Exists(dosPath))
{
yield return dosPath; // new DirectoryInfo(dosPath);
}
}
}
}
}
offset += size;
}
}
}
finally
{
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
//sw.Flush();
//sw.Close();
}
}
while (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
#region Private Members
private static bool GetFileNameFromHandle(IntPtr handle, int processId, out string fileName)
{
IntPtr currentProcess = NativeMethods.GetCurrentProcess();
bool remote = (processId != NativeMethods.GetProcessId(currentProcess));
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try
{
if (remote)
{
processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
{
handle = objectHandle.DangerousGetHandle();
}
}
return GetFileNameFromHandle(handle, out fileName, 200);
}
finally
{
if (remote)
{
if (processHandle != null)
{
processHandle.Close();
}
if (objectHandle != null)
{
objectHandle.Close();
}
}
}
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName, int wait)
{
using (FileNameFromHandleState f = new FileNameFromHandleState(handle))
{
ThreadPool.QueueUserWorkItem(new WaitCallback(GetFileNameFromHandle), f);
if (f.WaitOne(wait))
{
fileName = f.FileName;
return f.RetValue;
}
else
{
fileName = string.Empty;
return false;
}
}
}
private class FileNameFromHandleState : IDisposable
{
private ManualResetEvent _mr;
private IntPtr _handle;
private string _fileName;
private bool _retValue;
public IntPtr Handle
{
get
{
return _handle;
}
}
public string FileName
{
get
{
return _fileName;
}
set
{
_fileName = value;
}
}
public bool RetValue
{
get
{
return _retValue;
}
set
{
_retValue = value;
}
}
public FileNameFromHandleState(IntPtr handle)
{
_mr = new ManualResetEvent(false);
this._handle = handle;
}
public bool WaitOne(int wait)
{
return _mr.WaitOne(wait, false);
}
public void Set()
{
try
{
_mr.Set();
}
catch{}
}
#region IDisposable Members
public void Dispose()
{
if (_mr != null)
_mr.Close();
}
#endregion
}
private static void GetFileNameFromHandle(object state)
{
FileNameFromHandleState s = (FileNameFromHandleState)state;
string fileName;
s.RetValue = GetFileNameFromHandle(s.Handle, out fileName);
s.FileName = fileName;
s.Set();
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName)
{
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
int length = 0x200; // 512 bytes
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees the assignment of the allocated
// memory address to ptr, if an ansynchronous exception
// occurs.
ptr = Marshal.AllocHGlobal(length);
}
NT_STATUS ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW)
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees that the previous allocation is freed,
// and that the newly allocated memory address is
// assigned to ptr if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
ptr = Marshal.AllocHGlobal(length);
}
ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
}
if (ret == NT_STATUS.STATUS_SUCCESS)
{
fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2);
return fileName.Length != 0;
}
}
finally
{
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
}
fileName = string.Empty;
return false;
}
private static bool GetHandleType(IntPtr handle, int processId, out SystemHandleType handleType)
{
string token = GetHandleTypeToken(handle, processId);
return GetHandleTypeFromToken(token, out handleType);
}
private static bool GetHandleType(IntPtr handle, out SystemHandleType handleType)
{
string token = GetHandleTypeToken(handle);
return GetHandleTypeFromToken(token, out handleType);
}
private static bool GetHandleTypeFromToken(string token, out SystemHandleType handleType)
{
for (int i = 1; i < handleTypeTokenCount; i++)
{
if (handleTypeTokens[i] == token)
{
handleType = (SystemHandleType)i;
return true;
}
}
handleType = SystemHandleType.OB_TYPE_UNKNOWN;
return false;
}
private static string GetHandleTypeToken(IntPtr handle, int processId)
{
IntPtr currentProcess = NativeMethods.GetCurrentProcess();
bool remote = (processId != NativeMethods.GetProcessId(currentProcess));
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try
{
if (remote)
{
processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
{
handle = objectHandle.DangerousGetHandle();
}
}
return GetHandleTypeToken(handle);
}
finally
{
if (remote)
{
if (processHandle != null)
{
processHandle.Close();
}
if (objectHandle != null)
{
objectHandle.Close();
}
}
}
}
private static string GetHandleTypeToken(IntPtr handle)
{
int length;
NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
ptr = Marshal.AllocHGlobal(length);
}
if (NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) == NT_STATUS.STATUS_SUCCESS)
{
return Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x60));
}
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return string.Empty;
}
private static bool ConvertDevicePathToDosPath(string devicePath, out string dosPath)
{
EnsureDeviceMap();
int i = devicePath.Length;
while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1)
{
string drive;
if (deviceMap.TryGetValue(devicePath.Substring(0, i), out drive))
{
dosPath = string.Concat(drive, devicePath.Substring(i));
return dosPath.Length != 0;
}
}
dosPath = string.Empty;
return false;
}
private static void EnsureDeviceMap()
{
if (deviceMap == null)
{
Dictionary<string, string> localDeviceMap = BuildDeviceMap();
Interlocked.CompareExchange<Dictionary<string, string>>(ref deviceMap, localDeviceMap, null);
}
}
private static Dictionary<string, string> BuildDeviceMap()
{
string[] logicalDrives = Environment.GetLogicalDrives();
Dictionary<string, string> localDeviceMap = new Dictionary<string, string>(logicalDrives.Length);
StringBuilder lpTargetPath = new StringBuilder(MAX_PATH);
foreach (string drive in logicalDrives)
{
string lpDeviceName = drive.Substring(0, 2);
NativeMethods.QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH);
localDeviceMap.Add(NormalizeDeviceName(lpTargetPath.ToString()), lpDeviceName);
}
localDeviceMap.Add(networkDevicePrefix.Substring(0, networkDevicePrefix.Length - 1), "\\");
return localDeviceMap;
}
private static string NormalizeDeviceName(string deviceName)
{
if (string.Compare(deviceName, 0, networkDevicePrefix, 0, networkDevicePrefix.Length, StringComparison.InvariantCulture) == 0)
{
string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDevicePrefix.Length) + 1);
return string.Concat(networkDevicePrefix, shareName);
}
return deviceName;
}
#endregion
}
}
Not very straightforward, but on Windows Vista and above you can use the Restart Manager APIs to see who is using a file. Internet Explorer caches settings includes details on using this to detect which process has iexplore.exe open.
Omitting a lot of detail:
// Start an RM session
RmStartSession(&sessionHandle, 0, sessionKey);
// Register the file you are checking
RmRegisterResources(sessionHandle, 1, filePathArray, 0, NULL, 0, NULL);
// Get all processes that have that file open.
RmGetList(sessionHAndle, &nProcInfoNeeded, &nProcInfo, processes, &rebootReason);
RmEndSession(sessionHandle);
Handle, from Windows Sysinternals. This is a free command-line utility provided by Microsoft.
You could run it, and parse the result.
I had issues with stefan's solution. Below is a modified version which seems to work well.
using System;
using System.Collections;
using System.Diagnostics;
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a)
{
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses();
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++)
{
myProcess = processes[i];
//if (!myProcess.HasExited) //This will cause an "Access is denied" error
if (myProcess.Threads.Count > 0)
{
try
{
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++)
{
if ((modules[j].FileName.ToLower().CompareTo(strFile.ToLower()) == 0))
{
myProcessArray.Add(myProcess);
break;
// TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception)
{
//MsgBox(("Error : " & exception.Message))
}
}
}
return myProcessArray;
}
}
UPDATE
If you just want to know which process(es) are locking a particular DLL, you can execute and parse the output of tasklist /m YourDllName.dll. Works on Windows XP and later. See
What does this do? tasklist /m "mscor*"
This works for DLLs locked by other processes. This routine will not find out for example that a text file is locked by a word process.
C#:
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a) {
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses;
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++) {
myProcess = processes(i);
if (!myProcess.HasExited) {
try {
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++) {
if ((modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) == 0)) {
myProcessArray.Add(myProcess);
break; // TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception) {
}
//MsgBox(("Error : " & exception.Message))
}
}
return myProcessArray;
}
}
VB.Net:
Imports System.Management
Imports System.IO
Module Module1
Friend myProcessArray As New ArrayList
Private myProcess As Process
Sub Main()
Dim strFile As String = "c:\windows\system32\msi.dll"
Dim a As ArrayList = getFileProcesses(strFile)
For Each p As Process In a
Debug.Print(p.ProcessName)
Next
End Sub
Private Function getFileProcesses(ByVal strFile As String) As ArrayList
myProcessArray.Clear()
Dim processes As Process() = Process.GetProcesses
Dim i As Integer
For i = 0 To processes.GetUpperBound(0) - 1
myProcess = processes(i)
If Not myProcess.HasExited Then
Try
Dim modules As ProcessModuleCollection = myProcess.Modules
Dim j As Integer
For j = 0 To modules.Count - 1
If (modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) = 0) Then
myProcessArray.Add(myProcess)
Exit For
End If
Next j
Catch exception As Exception
'MsgBox(("Error : " & exception.Message))
End Try
End If
Next i
Return myProcessArray
End Function
End Module
The following was produced based on Iain Ballard's code dump. It is broken: it will occasionally lock up when you retrieve the handle name. This code doesn't contain any work-arounds for that issue, and .NET leaves few options: Thread.Abort can no longer abort a thread that's currently in a native method.
So, with that disclaimer, here is the code to retrieve handles which has been adapted to work (apart from the occasional lock-up) both in 32 and 64 bit modes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace BrokenHandleRetrieval
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enumerates open handles.");
Console.WriteLine("This *will* lock up on calling HandleInfo.Name from time to time. Thread.Abort() won't help.");
foreach (var hi in HandleUtil.GetHandles().Where(hi => hi.Type == HandleType.File))
Console.WriteLine("pid: " + hi.ProcessId + ", name: " + hi.Name);
}
}
public enum HandleType
{
Unknown,
Other,
File, Directory, SymbolicLink, Key,
Process, Thread, Job, Session, WindowStation,
Timer, Desktop, Semaphore, Token,
Mutant, Section, Event, KeyedEvent, IoCompletion, IoCompletionReserve,
TpWorkerFactory, AlpcPort, WmiGuid, UserApcReserve,
}
public class HandleInfo
{
public int ProcessId { get; private set; }
public ushort Handle { get; private set; }
public int GrantedAccess { get; private set; }
public byte RawType { get; private set; }
public HandleInfo(int processId, ushort handle, int grantedAccess, byte rawType)
{
ProcessId = processId;
Handle = handle;
GrantedAccess = grantedAccess;
RawType = rawType;
}
private static Dictionary<byte, string> _rawTypeMap = new Dictionary<byte, string>();
private string _name, _typeStr;
private HandleType _type;
public string Name { get { if (_name == null) initTypeAndName(); return _name; } }
public string TypeString { get { if (_typeStr == null) initType(); return _typeStr; } }
public HandleType Type { get { if (_typeStr == null) initType(); return _type; } }
private void initType()
{
if (_rawTypeMap.ContainsKey(RawType))
{
_typeStr = _rawTypeMap[RawType];
_type = HandleTypeFromString(_typeStr);
}
else
initTypeAndName();
}
bool _typeAndNameAttempted = false;
private void initTypeAndName()
{
if (_typeAndNameAttempted)
return;
_typeAndNameAttempted = true;
IntPtr sourceProcessHandle = IntPtr.Zero;
IntPtr handleDuplicate = IntPtr.Zero;
try
{
sourceProcessHandle = NativeMethods.OpenProcess(0x40 /* dup_handle */, true, ProcessId);
// To read info about a handle owned by another process we must duplicate it into ours
// For simplicity, current process handles will also get duplicated; remember that process handles cannot be compared for equality
if (!NativeMethods.DuplicateHandle(sourceProcessHandle, (IntPtr) Handle, NativeMethods.GetCurrentProcess(), out handleDuplicate, 0, false, 2 /* same_access */))
return;
// Query the object type
if (_rawTypeMap.ContainsKey(RawType))
_typeStr = _rawTypeMap[RawType];
else
{
int length;
NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(length);
if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS)
return;
_typeStr = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 0x58 + 2 * IntPtr.Size));
_rawTypeMap[RawType] = _typeStr;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
_type = HandleTypeFromString(_typeStr);
// Query the object name
if (_typeStr != null && GrantedAccess != 0x0012019f && GrantedAccess != 0x00120189 && GrantedAccess != 0x120089) // don't query some objects that could get stuck
{
int length;
NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(length);
if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS)
return;
_name = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 2 * IntPtr.Size));
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
}
finally
{
NativeMethods.CloseHandle(sourceProcessHandle);
if (handleDuplicate != IntPtr.Zero)
NativeMethods.CloseHandle(handleDuplicate);
}
}
public static HandleType HandleTypeFromString(string typeStr)
{
switch (typeStr)
{
case null: return HandleType.Unknown;
case "File": return HandleType.File;
case "IoCompletion": return HandleType.IoCompletion;
case "TpWorkerFactory": return HandleType.TpWorkerFactory;
case "ALPC Port": return HandleType.AlpcPort;
case "Event": return HandleType.Event;
case "Section": return HandleType.Section;
case "Directory": return HandleType.Directory;
case "KeyedEvent": return HandleType.KeyedEvent;
case "Process": return HandleType.Process;
case "Key": return HandleType.Key;
case "SymbolicLink": return HandleType.SymbolicLink;
case "Thread": return HandleType.Thread;
case "Mutant": return HandleType.Mutant;
case "WindowStation": return HandleType.WindowStation;
case "Timer": return HandleType.Timer;
case "Semaphore": return HandleType.Semaphore;
case "Desktop": return HandleType.Desktop;
case "Token": return HandleType.Token;
case "Job": return HandleType.Job;
case "Session": return HandleType.Session;
case "IoCompletionReserve": return HandleType.IoCompletionReserve;
case "WmiGuid": return HandleType.WmiGuid;
case "UserApcReserve": return HandleType.UserApcReserve;
default: return HandleType.Other;
}
}
}
public static class HandleUtil
{
public static IEnumerable<HandleInfo> GetHandles()
{
// Attempt to retrieve the handle information
int length = 0x10000;
IntPtr ptr = IntPtr.Zero;
try
{
while (true)
{
ptr = Marshal.AllocHGlobal(length);
int wantedLength;
var result = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out wantedLength);
if (result == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH)
{
length = Math.Max(length, wantedLength);
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
else if (result == NT_STATUS.STATUS_SUCCESS)
break;
else
throw new Exception("Failed to retrieve system handle information.");
}
int handleCount = IntPtr.Size == 4 ? Marshal.ReadInt32(ptr) : (int) Marshal.ReadInt64(ptr);
int offset = IntPtr.Size;
int size = Marshal.SizeOf(typeof(SystemHandleEntry));
for (int i = 0; i < handleCount; i++)
{
var struc = (SystemHandleEntry) Marshal.PtrToStructure((IntPtr) ((int) ptr + offset), typeof(SystemHandleEntry));
yield return new HandleInfo(struc.OwnerProcessId, struc.Handle, struc.GrantedAccess, struc.ObjectTypeNumber);
offset += size;
}
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct SystemHandleEntry
{
public int OwnerProcessId;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public IntPtr Object;
public int GrantedAccess;
}
}
enum NT_STATUS
{
STATUS_SUCCESS = 0x00000000,
STATUS_BUFFER_OVERFLOW = unchecked((int) 0x80000005L),
STATUS_INFO_LENGTH_MISMATCH = unchecked((int) 0xC0000004L)
}
enum SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
}
enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
static class NativeMethods
{
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[In] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[In] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwProcessId);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateHandle(
[In] IntPtr hSourceProcessHandle,
[In] IntPtr hSourceHandle,
[In] IntPtr hTargetProcessHandle,
[Out] out IntPtr lpTargetHandle,
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwOptions);
}
}
This is probably irrelevant and if it is please someone comment but there was a work-around I've used in explorer before to get around file locks.
If a file was locked by a process that had died Windows often wouldn't let you delete it but if you created a new file of the same name somewhere else, moved it to the folder it would succeed. You could then delete the new file and all was well.
To use this for your app you'd have to be able to read the file and hold it in memory before you did this then you write it back out after you'd got rid of the old one.
Maybe it will help, maybe not but it's worth trying.
Try Unlocker. If you try and delete the file that is locked by another process, it will list the process(es) that have the file locked. You can then unlock the file by shutting down those processes.
foreach (var process in Process.GetProcessesByName("excel")) //whatever you need to close
{
if (process.MainWindowTitle.Contains("test.xlsx"))
{
process.Kill();
break;
}
}
or
foreach (var process in Process.GetProcesses())
{
if (process.MainWindowTitle.Contains("test.dat"))
{
process.Kill();
break;
}
}
I believe that you need code running in kernel mode to completely answer the question (but I haven't looked at the restart manager API).
You can enumerate all processes and their modules - so if the file you're looking for is a module (DLL, EXE, OCX...), you're good to go. But if it's a text file for example, you have to look at the kernel handle table which you cannot see from user mode. Handle.exe has a kernel driver in order to do that.
I rewrote the GetProcessesLockingFile() method in the solution. The code was not working.
For example, you have a folder "C:\folder1\folder2" and a process in folder2 (process1). If the process was running, GetProcessesLockingFile() was returning "C:\folder1\folder2". So the condition if (files.Contains(filePath)) => if ("C:\folder1\folder2".contains("C:\folder1\folder2\process1")) was never true.
So this is my solution:
public static List<Process> GetProcessesLockingFile(FileInfo file)
{
var procs = new List<Process>();
var processListSnapshot = Process.GetProcesses();
foreach (var process in processListSnapshot)
{
if (process.Id <= 4) { continue; } // system processes
List<string> paths = GetFilesLockedBy(process);
foreach (string path in paths)
{
string pathDirectory = path;
if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH))
{
pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH;
}
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory));
if (file.FullName.Contains(lastFolderName))
{
procs.Add(process);
}
}
}
return procs;
}
Or with a string parameter:
public static List<Process> GetProcessesLockingFile(string filePath)
{
var procs = new List<Process>();
var processListSnapshot = Process.GetProcesses();
foreach (var process in processListSnapshot)
{
if (process.Id <= 4) { continue; } // system processes
List<string> paths = GetFilesLockedBy(process);
foreach (string path in paths)
{
string pathDirectory = path;
if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH))
{
pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH;
}
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory));
if (filePath.Contains(lastFolderName))
{
procs.Add(process);
}
}
}
return procs;
}
You absolutely don't need to run in Kernel mode (!!!)
It's a Win32 FAQ since Windows 95 (!) (in C, Google groups, Win32) : read the handle table, from User mode of course, and get the PID from the File handle ...
Using dotnet core (net6) I solved this problem by using the win32 restart manager (as others have also mentioned). However some of the linked articles have elaborate code importing DLLs and calling those.
After finding an app to kill processes that lock a file written by meziantou. I found out that he publishes .Net wrappers for win32 dlls (including the restart manager).
Leveraging his work, I was able to fix this problem with the following code:
using Meziantou.Framework.Win32;
public static IEnumerable<Process> GetProcessesLockingFile(string filePath)
{
using var session = RestartManager.CreateSession();
session.RegisterFile(filePath);
return session.GetProcessesLockingResources();
}
simpler with linq:
public void KillProcessesAssociatedToFile(string file)
{
GetProcessesAssociatedToFile(file).ForEach(x =>
{
x.Kill();
x.WaitForExit(10000);
});
}
public List<Process> GetProcessesAssociatedToFile(string file)
{
return Process.GetProcesses()
.Where(x => !x.HasExited
&& x.Modules.Cast<ProcessModule>().ToList()
.Exists(y => y.FileName.ToLowerInvariant() == file.ToLowerInvariant())
).ToList();
}

How do I find out which process is locking a file using .NET?

I've seen several of answers about using Handle or Process Monitor, but I would like to be able to find out in my own code (C#)
which process is locking a file.
I have a nasty feeling that I'm going to have to spelunk around in the win32 API, but if anyone has already done this and can put me on the right track, I'd really appreciate the help.
Update
Links to similar questions
How does one figure out what process locked a file using c#?
Command line tool
Across a Network
Locking a USB device
Unit test fails with locked file
deleting locked file
Long ago it was impossible to reliably get the list of processes locking a file because Windows simply did not track that information. To support the Restart Manager API, that information is now tracked.
I put together code that takes the path of a file and returns a List<Process> of all processes that are locking that file.
using System.Runtime.InteropServices;
using System.Diagnostics;
using System;
using System.Collections.Generic;
static public class FileUtil
{
[StructLayout(LayoutKind.Sequential)]
struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)]
public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle,
UInt32 nFiles,
string[] rgsFilenames,
UInt32 nApplications,
[In] RM_UNIQUE_PROCESS[] rgApplications,
UInt32 nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle,
out uint pnProcInfoNeeded,
ref uint pnProcInfo,
[In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
///
/// </remarks>
static public List<Process> WhoIsLocking(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
List<Process> processes = new List<Process>();
int res = RmStartSession(out handle, 0, key);
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
try
{
const int ERROR_MORE_DATA = 234;
uint pnProcInfoNeeded = 0,
pnProcInfo = 0,
lpdwRebootReasons = RmRebootReasonNone;
string[] resources = new string[] { path }; // Just checking on one resource.
res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);
if (res != 0) throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if (res == ERROR_MORE_DATA)
{
// Create an array to store the process results
RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
pnProcInfo = pnProcInfoNeeded;
// Get the list
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if (res == 0)
{
processes = new List<Process>((int)pnProcInfo);
// Enumerate all of the results and add them to the
// list to be returned
for (int i = 0; i < pnProcInfo; i++)
{
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
// catch the error -- in case the process is no longer running
catch (ArgumentException) { }
}
}
else throw new Exception("Could not list processes locking resource.");
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally
{
RmEndSession(handle);
}
return processes;
}
}
Using from Limited Permission (e.g. IIS)
This call accesses the registry. If the process does not have permission to do so, you will get ERROR_WRITE_FAULT, meaning An operation was unable to read or write to the registry. You could selectively grant permission to your restricted account to the necessary part of the registry. It is more secure though to have your limited access process set a flag (e.g. in the database or the file system, or by using an interprocess communication mechanism such as queue or named pipe) and have a second process call the Restart Manager API.
Granting other-than-minimal permissions to the IIS user is a security risk.
This question had an original answer that is now over 7 years old. That code is preserved at https://gist.github.com/i-e-b/2290426
This old version might work for you if you need to use Windows XP for some reason.
A much better answer is at How to check for file lock?
I've replicated Eric J's answer below (with using statements added, and class & method names to match the old code that was here) Please note that the comments to this answer may be out-of-date.
Research by user 'Walkman' is ongoing to improve the older code, as there are some conditions where the Restart Manager does not list all locks. See Github repo: https://github.com/Walkman100/FileLocks
Use like:
List<Process> locks = Win32Processes.GetProcessesLockingFile(#"C:\Hello.docx");
Code:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace FileLockInfo
{
public static class Win32Processes
{
/// <summary>
/// Find out what process(es) have a lock on the specified file.
/// </summary>
/// <param name="path">Path of the file.</param>
/// <returns>Processes locking the file</returns>
/// <remarks>See also:
/// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
/// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
/// </remarks>
public static List<Process> GetProcessesLockingFile(string path)
{
uint handle;
string key = Guid.NewGuid().ToString();
int res = RmStartSession(out handle, 0, key);
if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker.");
try
{
const int MORE_DATA = 234;
uint pnProcInfoNeeded, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone;
string[] resources = {path}; // Just checking on one resource.
res = RmRegisterResources(handle, (uint) resources.Length, resources, 0, null, 0, null);
if (res != 0) throw new Exception("Could not register resource.");
//Note: there's a race condition here -- the first call to RmGetList() returns
// the total number of process. However, when we call RmGetList() again to get
// the actual processes this number may have increased.
res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);
if (res == MORE_DATA)
{
return EnumerateProcesses(pnProcInfoNeeded, handle, lpdwRebootReasons);
}
else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
}
finally
{
RmEndSession(handle);
}
return new List<Process>();
}
[StructLayout(LayoutKind.Sequential)]
public struct RM_UNIQUE_PROCESS
{
public int dwProcessId;
public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
}
const int RmRebootReasonNone = 0;
const int CCH_RM_MAX_APP_NAME = 255;
const int CCH_RM_MAX_SVC_NAME = 63;
public enum RM_APP_TYPE
{
RmUnknownApp = 0,
RmMainWindow = 1,
RmOtherWindow = 2,
RmService = 3,
RmExplorer = 4,
RmConsole = 5,
RmCritical = 1000
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RM_PROCESS_INFO
{
public RM_UNIQUE_PROCESS Process;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] public string strAppName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] public string strServiceShortName;
public RM_APP_TYPE ApplicationType;
public uint AppStatus;
public uint TSSessionId;
[MarshalAs(UnmanagedType.Bool)] public bool bRestartable;
}
[DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
static extern int RmRegisterResources(uint pSessionHandle, uint nFiles, string[] rgsFilenames,
uint nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, uint nServices,
string[] rgsServiceNames);
[DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);
[DllImport("rstrtmgr.dll")]
static extern int RmEndSession(uint pSessionHandle);
[DllImport("rstrtmgr.dll")]
static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded,
ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
ref uint lpdwRebootReasons);
private static List<Process> EnumerateProcesses(uint pnProcInfoNeeded, uint handle, uint lpdwRebootReasons)
{
var processes = new List<Process>(10);
// Create an array to store the process results
var processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
var pnProcInfo = pnProcInfoNeeded;
// Get the list
var res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
if (res != 0) throw new Exception("Could not list processes locking resource.");
for (int i = 0; i < pnProcInfo; i++)
{
try
{
processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
}
catch (ArgumentException) { } // catch the error -- in case the process is no longer running
}
return processes;
}
}
}
It is very complex to invoke Win32 from C#.
You should use the tool Handle.exe.
After that your C# code have to be the following:
string fileName = #"c:\aaa.doc";//Path to locked file
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = fileName+" /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach(Match match in Regex.Matches(outputTool, matchPattern))
{
Process.GetProcessById(int.Parse(match.Value)).Kill();
}
One of the good things about handle.exe is that you can run it as a subprocess and parse the output.
We do this in our deployment script - works like a charm.
The code I found here,
https://vmccontroller.svn.codeplex.com/svn/VmcController/VmcServices/DetectOpenFiles.cs
Works for me much better than the code provided by Iain. Iain's code seemed to be acquiring a lock of its own. Here is my slightly modified version of the code above modified to return the string path of the files locked instead of the FileSystemInfo object,
using System;
using System.Collections.Generic;
//using System.EnterpriseServices;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;
namespace Crmc.Core.BuildTasks
{
using System.Diagnostics;
using System.Linq;
#region ENUMs
internal enum NT_STATUS
{
STATUS_SUCCESS = 0x00000000,
STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L),
STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L)
}
internal enum SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
}
internal enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
[Flags]
internal enum ProcessAccessRights
{
PROCESS_DUP_HANDLE = 0x00000040
}
[Flags]
internal enum DuplicateHandleOptions
{
DUPLICATE_CLOSE_SOURCE = 0x1,
DUPLICATE_SAME_ACCESS = 0x2
}
#endregion
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeObjectHandle()
: base(true)
{ }
internal SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return NativeMethods.CloseHandle(base.handle);
}
}
[SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)]
internal sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeProcessHandle()
: base(true)
{ }
internal SafeProcessHandle(IntPtr preexistingHandle, bool ownsHandle)
: base(ownsHandle)
{
base.SetHandle(preexistingHandle);
}
protected override bool ReleaseHandle()
{
return NativeMethods.CloseHandle(base.handle);
}
}
#region Native Methods
internal static class NativeMethods
{
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[In] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[In] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern SafeProcessHandle OpenProcess(
[In] ProcessAccessRights dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DuplicateHandle(
[In] IntPtr hSourceProcessHandle,
[In] IntPtr hSourceHandle,
[In] IntPtr hTargetProcessHandle,
[Out] out SafeObjectHandle lpTargetHandle,
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] DuplicateHandleOptions dwOptions);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int GetProcessId(
[In] IntPtr Process);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern int QueryDosDevice(
[In] string lpDeviceName,
[Out] StringBuilder lpTargetPath,
[In] int ucchMax);
}
#endregion
//[ComVisible(true), EventTrackingEnabled(true)]
public class DetectOpenFiles// : ServicedComponent
{
private static Dictionary<string, string> deviceMap;
private const string networkDevicePrefix = "\\Device\\LanmanRedirector\\";
private const int MAX_PATH = 260;
private enum SystemHandleType
{
OB_TYPE_UNKNOWN = 0,
OB_TYPE_TYPE = 1,
OB_TYPE_DIRECTORY,
OB_TYPE_SYMBOLIC_LINK,
OB_TYPE_TOKEN,
OB_TYPE_PROCESS,
OB_TYPE_THREAD,
OB_TYPE_UNKNOWN_7,
OB_TYPE_EVENT,
OB_TYPE_EVENT_PAIR,
OB_TYPE_MUTANT,
OB_TYPE_UNKNOWN_11,
OB_TYPE_SEMAPHORE,
OB_TYPE_TIMER,
OB_TYPE_PROFILE,
OB_TYPE_WINDOW_STATION,
OB_TYPE_DESKTOP,
OB_TYPE_SECTION,
OB_TYPE_KEY,
OB_TYPE_PORT,
OB_TYPE_WAITABLE_PORT,
OB_TYPE_UNKNOWN_21,
OB_TYPE_UNKNOWN_22,
OB_TYPE_UNKNOWN_23,
OB_TYPE_UNKNOWN_24,
//OB_TYPE_CONTROLLER,
//OB_TYPE_DEVICE,
//OB_TYPE_DRIVER,
OB_TYPE_IO_COMPLETION,
OB_TYPE_FILE
};
private const int handleTypeTokenCount = 27;
private static readonly string[] handleTypeTokens = new string[] {
"", "", "Directory", "SymbolicLink", "Token",
"Process", "Thread", "Unknown7", "Event", "EventPair", "Mutant",
"Unknown11", "Semaphore", "Timer", "Profile", "WindowStation",
"Desktop", "Section", "Key", "Port", "WaitablePort",
"Unknown21", "Unknown22", "Unknown23", "Unknown24",
"IoCompletion", "File"
};
[StructLayout(LayoutKind.Sequential)]
private struct SYSTEM_HANDLE_ENTRY
{
public int OwnerPid;
public byte ObjectType;
public byte HandleFlags;
public short HandleValue;
public int ObjectPointer;
public int AccessMask;
}
/// <summary>
/// Gets the open files enumerator.
/// </summary>
/// <param name="processId">The process id.</param>
/// <returns></returns>
public static IEnumerable<String> GetOpenFilesEnumerator(int processId)
{
return new OpenFiles(processId);
}
public static List<Process> GetProcessesUsingFile(string fName)
{
List<Process> result = new List<Process>();
foreach (var p in Process.GetProcesses())
{
try
{
if (DetectOpenFiles.GetOpenFilesEnumerator(p.Id).Contains(fName))
{
result.Add(p);
}
}
catch { }//some processes will fail
}
return result;
}
private sealed class OpenFiles : IEnumerable<String>
{
private readonly int processId;
internal OpenFiles(int processId)
{
this.processId = processId;
}
#region IEnumerable<FileSystemInfo> Members
public IEnumerator<String> GetEnumerator()
{
NT_STATUS ret;
int length = 0x10000;
// Loop, probing for required memory.
do
{
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees that the address of the allocated
// memory is actually assigned to ptr if an
// asynchronous exception occurs.
ptr = Marshal.AllocHGlobal(length);
}
int returnLength;
ret = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out returnLength);
if (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH)
{
// Round required memory up to the nearest 64KB boundary.
length = ((returnLength + 0xffff) & ~0xffff);
}
else if (ret == NT_STATUS.STATUS_SUCCESS)
{
int handleCount = Marshal.ReadInt32(ptr);
int offset = sizeof(int);
int size = Marshal.SizeOf(typeof(SYSTEM_HANDLE_ENTRY));
for (int i = 0; i < handleCount; i++)
{
SYSTEM_HANDLE_ENTRY handleEntry = (SYSTEM_HANDLE_ENTRY)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SYSTEM_HANDLE_ENTRY));
if (handleEntry.OwnerPid == processId)
{
IntPtr handle = (IntPtr)handleEntry.HandleValue;
SystemHandleType handleType;
if (GetHandleType(handle, handleEntry.OwnerPid, out handleType) && handleType == SystemHandleType.OB_TYPE_FILE)
{
string devicePath;
if (GetFileNameFromHandle(handle, handleEntry.OwnerPid, out devicePath))
{
string dosPath;
if (ConvertDevicePathToDosPath(devicePath, out dosPath))
{
if (File.Exists(dosPath))
{
yield return dosPath; // return new FileInfo(dosPath);
}
else if (Directory.Exists(dosPath))
{
yield return dosPath; // new DirectoryInfo(dosPath);
}
}
}
}
}
offset += size;
}
}
}
finally
{
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
//sw.Flush();
//sw.Close();
}
}
while (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH);
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
#region Private Members
private static bool GetFileNameFromHandle(IntPtr handle, int processId, out string fileName)
{
IntPtr currentProcess = NativeMethods.GetCurrentProcess();
bool remote = (processId != NativeMethods.GetProcessId(currentProcess));
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try
{
if (remote)
{
processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
{
handle = objectHandle.DangerousGetHandle();
}
}
return GetFileNameFromHandle(handle, out fileName, 200);
}
finally
{
if (remote)
{
if (processHandle != null)
{
processHandle.Close();
}
if (objectHandle != null)
{
objectHandle.Close();
}
}
}
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName, int wait)
{
using (FileNameFromHandleState f = new FileNameFromHandleState(handle))
{
ThreadPool.QueueUserWorkItem(new WaitCallback(GetFileNameFromHandle), f);
if (f.WaitOne(wait))
{
fileName = f.FileName;
return f.RetValue;
}
else
{
fileName = string.Empty;
return false;
}
}
}
private class FileNameFromHandleState : IDisposable
{
private ManualResetEvent _mr;
private IntPtr _handle;
private string _fileName;
private bool _retValue;
public IntPtr Handle
{
get
{
return _handle;
}
}
public string FileName
{
get
{
return _fileName;
}
set
{
_fileName = value;
}
}
public bool RetValue
{
get
{
return _retValue;
}
set
{
_retValue = value;
}
}
public FileNameFromHandleState(IntPtr handle)
{
_mr = new ManualResetEvent(false);
this._handle = handle;
}
public bool WaitOne(int wait)
{
return _mr.WaitOne(wait, false);
}
public void Set()
{
try
{
_mr.Set();
}
catch{}
}
#region IDisposable Members
public void Dispose()
{
if (_mr != null)
_mr.Close();
}
#endregion
}
private static void GetFileNameFromHandle(object state)
{
FileNameFromHandleState s = (FileNameFromHandleState)state;
string fileName;
s.RetValue = GetFileNameFromHandle(s.Handle, out fileName);
s.FileName = fileName;
s.Set();
}
private static bool GetFileNameFromHandle(IntPtr handle, out string fileName)
{
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
int length = 0x200; // 512 bytes
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees the assignment of the allocated
// memory address to ptr, if an ansynchronous exception
// occurs.
ptr = Marshal.AllocHGlobal(length);
}
NT_STATUS ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW)
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
// CER guarantees that the previous allocation is freed,
// and that the newly allocated memory address is
// assigned to ptr if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
ptr = Marshal.AllocHGlobal(length);
}
ret = NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length);
}
if (ret == NT_STATUS.STATUS_SUCCESS)
{
fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2);
return fileName.Length != 0;
}
}
finally
{
// CER guarantees that the allocated memory is freed,
// if an asynchronous exception occurs.
Marshal.FreeHGlobal(ptr);
}
fileName = string.Empty;
return false;
}
private static bool GetHandleType(IntPtr handle, int processId, out SystemHandleType handleType)
{
string token = GetHandleTypeToken(handle, processId);
return GetHandleTypeFromToken(token, out handleType);
}
private static bool GetHandleType(IntPtr handle, out SystemHandleType handleType)
{
string token = GetHandleTypeToken(handle);
return GetHandleTypeFromToken(token, out handleType);
}
private static bool GetHandleTypeFromToken(string token, out SystemHandleType handleType)
{
for (int i = 1; i < handleTypeTokenCount; i++)
{
if (handleTypeTokens[i] == token)
{
handleType = (SystemHandleType)i;
return true;
}
}
handleType = SystemHandleType.OB_TYPE_UNKNOWN;
return false;
}
private static string GetHandleTypeToken(IntPtr handle, int processId)
{
IntPtr currentProcess = NativeMethods.GetCurrentProcess();
bool remote = (processId != NativeMethods.GetProcessId(currentProcess));
SafeProcessHandle processHandle = null;
SafeObjectHandle objectHandle = null;
try
{
if (remote)
{
processHandle = NativeMethods.OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId);
if (NativeMethods.DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS))
{
handle = objectHandle.DangerousGetHandle();
}
}
return GetHandleTypeToken(handle);
}
finally
{
if (remote)
{
if (processHandle != null)
{
processHandle.Close();
}
if (objectHandle != null)
{
objectHandle.Close();
}
}
}
}
private static string GetHandleTypeToken(IntPtr handle)
{
int length;
NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally
{
ptr = Marshal.AllocHGlobal(length);
}
if (NativeMethods.NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) == NT_STATUS.STATUS_SUCCESS)
{
return Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x60));
}
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return string.Empty;
}
private static bool ConvertDevicePathToDosPath(string devicePath, out string dosPath)
{
EnsureDeviceMap();
int i = devicePath.Length;
while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1)
{
string drive;
if (deviceMap.TryGetValue(devicePath.Substring(0, i), out drive))
{
dosPath = string.Concat(drive, devicePath.Substring(i));
return dosPath.Length != 0;
}
}
dosPath = string.Empty;
return false;
}
private static void EnsureDeviceMap()
{
if (deviceMap == null)
{
Dictionary<string, string> localDeviceMap = BuildDeviceMap();
Interlocked.CompareExchange<Dictionary<string, string>>(ref deviceMap, localDeviceMap, null);
}
}
private static Dictionary<string, string> BuildDeviceMap()
{
string[] logicalDrives = Environment.GetLogicalDrives();
Dictionary<string, string> localDeviceMap = new Dictionary<string, string>(logicalDrives.Length);
StringBuilder lpTargetPath = new StringBuilder(MAX_PATH);
foreach (string drive in logicalDrives)
{
string lpDeviceName = drive.Substring(0, 2);
NativeMethods.QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH);
localDeviceMap.Add(NormalizeDeviceName(lpTargetPath.ToString()), lpDeviceName);
}
localDeviceMap.Add(networkDevicePrefix.Substring(0, networkDevicePrefix.Length - 1), "\\");
return localDeviceMap;
}
private static string NormalizeDeviceName(string deviceName)
{
if (string.Compare(deviceName, 0, networkDevicePrefix, 0, networkDevicePrefix.Length, StringComparison.InvariantCulture) == 0)
{
string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDevicePrefix.Length) + 1);
return string.Concat(networkDevicePrefix, shareName);
}
return deviceName;
}
#endregion
}
}
Not very straightforward, but on Windows Vista and above you can use the Restart Manager APIs to see who is using a file. Internet Explorer caches settings includes details on using this to detect which process has iexplore.exe open.
Omitting a lot of detail:
// Start an RM session
RmStartSession(&sessionHandle, 0, sessionKey);
// Register the file you are checking
RmRegisterResources(sessionHandle, 1, filePathArray, 0, NULL, 0, NULL);
// Get all processes that have that file open.
RmGetList(sessionHAndle, &nProcInfoNeeded, &nProcInfo, processes, &rebootReason);
RmEndSession(sessionHandle);
Handle, from Windows Sysinternals. This is a free command-line utility provided by Microsoft.
You could run it, and parse the result.
I had issues with stefan's solution. Below is a modified version which seems to work well.
using System;
using System.Collections;
using System.Diagnostics;
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a)
{
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses();
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++)
{
myProcess = processes[i];
//if (!myProcess.HasExited) //This will cause an "Access is denied" error
if (myProcess.Threads.Count > 0)
{
try
{
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++)
{
if ((modules[j].FileName.ToLower().CompareTo(strFile.ToLower()) == 0))
{
myProcessArray.Add(myProcess);
break;
// TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception)
{
//MsgBox(("Error : " & exception.Message))
}
}
}
return myProcessArray;
}
}
UPDATE
If you just want to know which process(es) are locking a particular DLL, you can execute and parse the output of tasklist /m YourDllName.dll. Works on Windows XP and later. See
What does this do? tasklist /m "mscor*"
This works for DLLs locked by other processes. This routine will not find out for example that a text file is locked by a word process.
C#:
using System.Management;
using System.IO;
static class Module1
{
static internal ArrayList myProcessArray = new ArrayList();
private static Process myProcess;
public static void Main()
{
string strFile = "c:\\windows\\system32\\msi.dll";
ArrayList a = getFileProcesses(strFile);
foreach (Process p in a) {
Debug.Print(p.ProcessName);
}
}
private static ArrayList getFileProcesses(string strFile)
{
myProcessArray.Clear();
Process[] processes = Process.GetProcesses;
int i = 0;
for (i = 0; i <= processes.GetUpperBound(0) - 1; i++) {
myProcess = processes(i);
if (!myProcess.HasExited) {
try {
ProcessModuleCollection modules = myProcess.Modules;
int j = 0;
for (j = 0; j <= modules.Count - 1; j++) {
if ((modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) == 0)) {
myProcessArray.Add(myProcess);
break; // TODO: might not be correct. Was : Exit For
}
}
}
catch (Exception exception) {
}
//MsgBox(("Error : " & exception.Message))
}
}
return myProcessArray;
}
}
VB.Net:
Imports System.Management
Imports System.IO
Module Module1
Friend myProcessArray As New ArrayList
Private myProcess As Process
Sub Main()
Dim strFile As String = "c:\windows\system32\msi.dll"
Dim a As ArrayList = getFileProcesses(strFile)
For Each p As Process In a
Debug.Print(p.ProcessName)
Next
End Sub
Private Function getFileProcesses(ByVal strFile As String) As ArrayList
myProcessArray.Clear()
Dim processes As Process() = Process.GetProcesses
Dim i As Integer
For i = 0 To processes.GetUpperBound(0) - 1
myProcess = processes(i)
If Not myProcess.HasExited Then
Try
Dim modules As ProcessModuleCollection = myProcess.Modules
Dim j As Integer
For j = 0 To modules.Count - 1
If (modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) = 0) Then
myProcessArray.Add(myProcess)
Exit For
End If
Next j
Catch exception As Exception
'MsgBox(("Error : " & exception.Message))
End Try
End If
Next i
Return myProcessArray
End Function
End Module
The following was produced based on Iain Ballard's code dump. It is broken: it will occasionally lock up when you retrieve the handle name. This code doesn't contain any work-arounds for that issue, and .NET leaves few options: Thread.Abort can no longer abort a thread that's currently in a native method.
So, with that disclaimer, here is the code to retrieve handles which has been adapted to work (apart from the occasional lock-up) both in 32 and 64 bit modes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
namespace BrokenHandleRetrieval
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Enumerates open handles.");
Console.WriteLine("This *will* lock up on calling HandleInfo.Name from time to time. Thread.Abort() won't help.");
foreach (var hi in HandleUtil.GetHandles().Where(hi => hi.Type == HandleType.File))
Console.WriteLine("pid: " + hi.ProcessId + ", name: " + hi.Name);
}
}
public enum HandleType
{
Unknown,
Other,
File, Directory, SymbolicLink, Key,
Process, Thread, Job, Session, WindowStation,
Timer, Desktop, Semaphore, Token,
Mutant, Section, Event, KeyedEvent, IoCompletion, IoCompletionReserve,
TpWorkerFactory, AlpcPort, WmiGuid, UserApcReserve,
}
public class HandleInfo
{
public int ProcessId { get; private set; }
public ushort Handle { get; private set; }
public int GrantedAccess { get; private set; }
public byte RawType { get; private set; }
public HandleInfo(int processId, ushort handle, int grantedAccess, byte rawType)
{
ProcessId = processId;
Handle = handle;
GrantedAccess = grantedAccess;
RawType = rawType;
}
private static Dictionary<byte, string> _rawTypeMap = new Dictionary<byte, string>();
private string _name, _typeStr;
private HandleType _type;
public string Name { get { if (_name == null) initTypeAndName(); return _name; } }
public string TypeString { get { if (_typeStr == null) initType(); return _typeStr; } }
public HandleType Type { get { if (_typeStr == null) initType(); return _type; } }
private void initType()
{
if (_rawTypeMap.ContainsKey(RawType))
{
_typeStr = _rawTypeMap[RawType];
_type = HandleTypeFromString(_typeStr);
}
else
initTypeAndName();
}
bool _typeAndNameAttempted = false;
private void initTypeAndName()
{
if (_typeAndNameAttempted)
return;
_typeAndNameAttempted = true;
IntPtr sourceProcessHandle = IntPtr.Zero;
IntPtr handleDuplicate = IntPtr.Zero;
try
{
sourceProcessHandle = NativeMethods.OpenProcess(0x40 /* dup_handle */, true, ProcessId);
// To read info about a handle owned by another process we must duplicate it into ours
// For simplicity, current process handles will also get duplicated; remember that process handles cannot be compared for equality
if (!NativeMethods.DuplicateHandle(sourceProcessHandle, (IntPtr) Handle, NativeMethods.GetCurrentProcess(), out handleDuplicate, 0, false, 2 /* same_access */))
return;
// Query the object type
if (_rawTypeMap.ContainsKey(RawType))
_typeStr = _rawTypeMap[RawType];
else
{
int length;
NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(length);
if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS)
return;
_typeStr = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 0x58 + 2 * IntPtr.Size));
_rawTypeMap[RawType] = _typeStr;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
_type = HandleTypeFromString(_typeStr);
// Query the object name
if (_typeStr != null && GrantedAccess != 0x0012019f && GrantedAccess != 0x00120189 && GrantedAccess != 0x120089) // don't query some objects that could get stuck
{
int length;
NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out length);
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.AllocHGlobal(length);
if (NativeMethods.NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS)
return;
_name = Marshal.PtrToStringUni((IntPtr) ((int) ptr + 2 * IntPtr.Size));
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
}
finally
{
NativeMethods.CloseHandle(sourceProcessHandle);
if (handleDuplicate != IntPtr.Zero)
NativeMethods.CloseHandle(handleDuplicate);
}
}
public static HandleType HandleTypeFromString(string typeStr)
{
switch (typeStr)
{
case null: return HandleType.Unknown;
case "File": return HandleType.File;
case "IoCompletion": return HandleType.IoCompletion;
case "TpWorkerFactory": return HandleType.TpWorkerFactory;
case "ALPC Port": return HandleType.AlpcPort;
case "Event": return HandleType.Event;
case "Section": return HandleType.Section;
case "Directory": return HandleType.Directory;
case "KeyedEvent": return HandleType.KeyedEvent;
case "Process": return HandleType.Process;
case "Key": return HandleType.Key;
case "SymbolicLink": return HandleType.SymbolicLink;
case "Thread": return HandleType.Thread;
case "Mutant": return HandleType.Mutant;
case "WindowStation": return HandleType.WindowStation;
case "Timer": return HandleType.Timer;
case "Semaphore": return HandleType.Semaphore;
case "Desktop": return HandleType.Desktop;
case "Token": return HandleType.Token;
case "Job": return HandleType.Job;
case "Session": return HandleType.Session;
case "IoCompletionReserve": return HandleType.IoCompletionReserve;
case "WmiGuid": return HandleType.WmiGuid;
case "UserApcReserve": return HandleType.UserApcReserve;
default: return HandleType.Other;
}
}
}
public static class HandleUtil
{
public static IEnumerable<HandleInfo> GetHandles()
{
// Attempt to retrieve the handle information
int length = 0x10000;
IntPtr ptr = IntPtr.Zero;
try
{
while (true)
{
ptr = Marshal.AllocHGlobal(length);
int wantedLength;
var result = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out wantedLength);
if (result == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH)
{
length = Math.Max(length, wantedLength);
Marshal.FreeHGlobal(ptr);
ptr = IntPtr.Zero;
}
else if (result == NT_STATUS.STATUS_SUCCESS)
break;
else
throw new Exception("Failed to retrieve system handle information.");
}
int handleCount = IntPtr.Size == 4 ? Marshal.ReadInt32(ptr) : (int) Marshal.ReadInt64(ptr);
int offset = IntPtr.Size;
int size = Marshal.SizeOf(typeof(SystemHandleEntry));
for (int i = 0; i < handleCount; i++)
{
var struc = (SystemHandleEntry) Marshal.PtrToStructure((IntPtr) ((int) ptr + offset), typeof(SystemHandleEntry));
yield return new HandleInfo(struc.OwnerProcessId, struc.Handle, struc.GrantedAccess, struc.ObjectTypeNumber);
offset += size;
}
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct SystemHandleEntry
{
public int OwnerProcessId;
public byte ObjectTypeNumber;
public byte Flags;
public ushort Handle;
public IntPtr Object;
public int GrantedAccess;
}
}
enum NT_STATUS
{
STATUS_SUCCESS = 0x00000000,
STATUS_BUFFER_OVERFLOW = unchecked((int) 0x80000005L),
STATUS_INFO_LENGTH_MISMATCH = unchecked((int) 0xC0000004L)
}
enum SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation = 0,
SystemPerformanceInformation = 2,
SystemTimeOfDayInformation = 3,
SystemProcessInformation = 5,
SystemProcessorPerformanceInformation = 8,
SystemHandleInformation = 16,
SystemInterruptInformation = 23,
SystemExceptionInformation = 33,
SystemRegistryQuotaInformation = 37,
SystemLookasideInformation = 45
}
enum OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation = 0,
ObjectNameInformation = 1,
ObjectTypeInformation = 2,
ObjectAllTypesInformation = 3,
ObjectHandleInformation = 4
}
static class NativeMethods
{
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQuerySystemInformation(
[In] SYSTEM_INFORMATION_CLASS SystemInformationClass,
[In] IntPtr SystemInformation,
[In] int SystemInformationLength,
[Out] out int ReturnLength);
[DllImport("ntdll.dll")]
internal static extern NT_STATUS NtQueryObject(
[In] IntPtr Handle,
[In] OBJECT_INFORMATION_CLASS ObjectInformationClass,
[In] IntPtr ObjectInformation,
[In] int ObjectInformationLength,
[Out] out int ReturnLength);
[DllImport("kernel32.dll")]
internal static extern IntPtr GetCurrentProcess();
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwProcessId);
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool CloseHandle(
[In] IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DuplicateHandle(
[In] IntPtr hSourceProcessHandle,
[In] IntPtr hSourceHandle,
[In] IntPtr hTargetProcessHandle,
[Out] out IntPtr lpTargetHandle,
[In] int dwDesiredAccess,
[In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle,
[In] int dwOptions);
}
}
This is probably irrelevant and if it is please someone comment but there was a work-around I've used in explorer before to get around file locks.
If a file was locked by a process that had died Windows often wouldn't let you delete it but if you created a new file of the same name somewhere else, moved it to the folder it would succeed. You could then delete the new file and all was well.
To use this for your app you'd have to be able to read the file and hold it in memory before you did this then you write it back out after you'd got rid of the old one.
Maybe it will help, maybe not but it's worth trying.
Try Unlocker. If you try and delete the file that is locked by another process, it will list the process(es) that have the file locked. You can then unlock the file by shutting down those processes.
foreach (var process in Process.GetProcessesByName("excel")) //whatever you need to close
{
if (process.MainWindowTitle.Contains("test.xlsx"))
{
process.Kill();
break;
}
}
or
foreach (var process in Process.GetProcesses())
{
if (process.MainWindowTitle.Contains("test.dat"))
{
process.Kill();
break;
}
}
I believe that you need code running in kernel mode to completely answer the question (but I haven't looked at the restart manager API).
You can enumerate all processes and their modules - so if the file you're looking for is a module (DLL, EXE, OCX...), you're good to go. But if it's a text file for example, you have to look at the kernel handle table which you cannot see from user mode. Handle.exe has a kernel driver in order to do that.
I rewrote the GetProcessesLockingFile() method in the solution. The code was not working.
For example, you have a folder "C:\folder1\folder2" and a process in folder2 (process1). If the process was running, GetProcessesLockingFile() was returning "C:\folder1\folder2". So the condition if (files.Contains(filePath)) => if ("C:\folder1\folder2".contains("C:\folder1\folder2\process1")) was never true.
So this is my solution:
public static List<Process> GetProcessesLockingFile(FileInfo file)
{
var procs = new List<Process>();
var processListSnapshot = Process.GetProcesses();
foreach (var process in processListSnapshot)
{
if (process.Id <= 4) { continue; } // system processes
List<string> paths = GetFilesLockedBy(process);
foreach (string path in paths)
{
string pathDirectory = path;
if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH))
{
pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH;
}
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory));
if (file.FullName.Contains(lastFolderName))
{
procs.Add(process);
}
}
}
return procs;
}
Or with a string parameter:
public static List<Process> GetProcessesLockingFile(string filePath)
{
var procs = new List<Process>();
var processListSnapshot = Process.GetProcesses();
foreach (var process in processListSnapshot)
{
if (process.Id <= 4) { continue; } // system processes
List<string> paths = GetFilesLockedBy(process);
foreach (string path in paths)
{
string pathDirectory = path;
if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH))
{
pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH;
}
string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory));
if (filePath.Contains(lastFolderName))
{
procs.Add(process);
}
}
}
return procs;
}
You absolutely don't need to run in Kernel mode (!!!)
It's a Win32 FAQ since Windows 95 (!) (in C, Google groups, Win32) : read the handle table, from User mode of course, and get the PID from the File handle ...
Using dotnet core (net6) I solved this problem by using the win32 restart manager (as others have also mentioned). However some of the linked articles have elaborate code importing DLLs and calling those.
After finding an app to kill processes that lock a file written by meziantou. I found out that he publishes .Net wrappers for win32 dlls (including the restart manager).
Leveraging his work, I was able to fix this problem with the following code:
using Meziantou.Framework.Win32;
public static IEnumerable<Process> GetProcessesLockingFile(string filePath)
{
using var session = RestartManager.CreateSession();
session.RegisterFile(filePath);
return session.GetProcessesLockingResources();
}
simpler with linq:
public void KillProcessesAssociatedToFile(string file)
{
GetProcessesAssociatedToFile(file).ForEach(x =>
{
x.Kill();
x.WaitForExit(10000);
});
}
public List<Process> GetProcessesAssociatedToFile(string file)
{
return Process.GetProcesses()
.Where(x => !x.HasExited
&& x.Modules.Cast<ProcessModule>().ToList()
.Exists(y => y.FileName.ToLowerInvariant() == file.ToLowerInvariant())
).ToList();
}

Categories

Resources