lynxx
Lt. Junior Grade
- Registriert
- Feb. 2005
- Beiträge
- 470
Hallo,
ich habe immer wieder das Problem das Dateien unter Win7 angeblich "in Benutzung" sind und deshalb nicht gelöscht/überschrieben werden können, hauptsächlich stört mich das im Microsoft Visual C# 2010 Express.
Damals (lang, lang ists her
) auf dem Amiga hatte ich mir einen Patch geschrieben der zu löschende Dateien nicht wirklich löscht sondern in einen Ordner "KAN" verschiebt, also so eine Art automatischer Papierkorb.
Also habe ich als erst ein kleines Programm "Cleanup" geschrieben das einfach alle Dateien im übergebenen Verzeichnis nach X:\KAN\ verschiebt und dann löscht.
In Microsoft Visual C# 2010 Express trage ich dann bei Projekten als Präbuild:
Cleanup "$(TargetDir)" ein, und das funktioniert auch wunderbar, keine Probleme mehr mit Neukompilierungsfehlern wegen "in Benutzung".
Aber ich hätte lieber eine globalere Lösung für das Problem, also habe ich ein Tool geschrieben das die DeleteFileA & DeleteFileW-Funktionen der kernel32.dll patch, ehergesagt umleitet - dort wird genau das selbe gemacht, Datei erst nach KAN verschieben und danach löschen (wenn deletemode = true).
Auch das Tool funktioniert wunderbar, Problem ist nur das in Win7 alle Threads eine "eigene" kernel32.dll haben, dieser Patch also nur für das aktuelle Programm aktiv ist.
Jetzt weiss ich nicht weiter, soll ich versuchen den Code im Ring0 (kernelmode) ausführen zu lassen (ich weiss, ist nicht trivial), oder hat sich schonmal jemand mit Layered Filter Drivers beschäftigt, imho kann man damit das selbe erreichen. Oder hat jemand eine andere Idee woher dieser "in Benutzung"-Bug kommt ? Indexer ist auf Projekt-Verzeichnis ausgeschaltet, Virenscanner ebenfalls. Im Sysinternals-Processmonitor sieht man nur das System oft ewig die Dateien offen hält, weswegen Delete nicht funktioniert.
Ich habe schonmal das Windows Driver Kit Version 7.1.0 heruntergeladen - damit kann man wohl Kerneltreiber erstellen, leider sind keine Samples für C# dabei.
Hier mein nur für den aktuellen Thread aktive KANSystem: (Kommentare sind weiter rechts.
)
P.S: Wieso sind TABS hier so riesig.
Edit:
P.P.S: Wieso hat CODE-Tag kein Syntaxhighlightning? Hab stattdessen PHP genommen ..
ich habe immer wieder das Problem das Dateien unter Win7 angeblich "in Benutzung" sind und deshalb nicht gelöscht/überschrieben werden können, hauptsächlich stört mich das im Microsoft Visual C# 2010 Express.
Damals (lang, lang ists her

Also habe ich als erst ein kleines Programm "Cleanup" geschrieben das einfach alle Dateien im übergebenen Verzeichnis nach X:\KAN\ verschiebt und dann löscht.
In Microsoft Visual C# 2010 Express trage ich dann bei Projekten als Präbuild:
Cleanup "$(TargetDir)" ein, und das funktioniert auch wunderbar, keine Probleme mehr mit Neukompilierungsfehlern wegen "in Benutzung".
Aber ich hätte lieber eine globalere Lösung für das Problem, also habe ich ein Tool geschrieben das die DeleteFileA & DeleteFileW-Funktionen der kernel32.dll patch, ehergesagt umleitet - dort wird genau das selbe gemacht, Datei erst nach KAN verschieben und danach löschen (wenn deletemode = true).
Auch das Tool funktioniert wunderbar, Problem ist nur das in Win7 alle Threads eine "eigene" kernel32.dll haben, dieser Patch also nur für das aktuelle Programm aktiv ist.
Jetzt weiss ich nicht weiter, soll ich versuchen den Code im Ring0 (kernelmode) ausführen zu lassen (ich weiss, ist nicht trivial), oder hat sich schonmal jemand mit Layered Filter Drivers beschäftigt, imho kann man damit das selbe erreichen. Oder hat jemand eine andere Idee woher dieser "in Benutzung"-Bug kommt ? Indexer ist auf Projekt-Verzeichnis ausgeschaltet, Virenscanner ebenfalls. Im Sysinternals-Processmonitor sieht man nur das System oft ewig die Dateien offen hält, weswegen Delete nicht funktioniert.
Ich habe schonmal das Windows Driver Kit Version 7.1.0 heruntergeladen - damit kann man wohl Kerneltreiber erstellen, leider sind keine Samples für C# dabei.
Hier mein nur für den aktuellen Thread aktive KANSystem: (Kommentare sind weiter rechts.

PHP:
// Patches kernel32.dll DeleteFileA & DeleteFileW to new functions, which move the file first to X:\KAN\.. and then delete it.
// Done by Holger 'Lynxx' Hippenstiel in Sep.2011
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
namespace KANSystem {
static class KANSystem {
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr LoadLibrary(string strFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", EntryPoint = "MoveFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
static extern bool MoveFile(string lpExistingFileName, string lpNewFileName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteFileA([MarshalAs(UnmanagedType.LPStr)]string lpFileName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool DeleteFileW([MarshalAs(UnmanagedType.LPWStr)]string lpFileName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
static extern IntPtr memcpy(IntPtr dest, IntPtr src, int count);
[Flags]
public enum Prot { // Possible Protectionflags for VirtualProtect
PAGE_NOACCESS = 0x01,
PAGE_READONLY = 0x02,
PAGE_READWRITE = 0x04,
PAGE_WRITECOPY = 0x08,
PAGE_EXECUTE = 0x10,
PAGE_EXECUTE_READ = 0x20,
PAGE_EXECUTE_READWRITE = 0x40,
PAGE_EXECUTE_WRITECOPY = 0x80,
PAGE_GUARD = 0x100,
PAGE_NOCACHE = 0x200,
PAGE_WRITECOMBINE = 0x400
}
static bool debug = false; // Use local stack or FPO in Assemblercode
static bool deletemode = false; // Delete after moving?
static byte[] sourcearray = { 0x90, 0x90, 0x90, 0x90, 0x90, 0x8B, 0xFF }; // Original Code in Functions should be: 5x nop + mov edi, edi
static byte[] deleteFileACodeDebugger = { // Call got own Local stack
0x8b, 0x45, 0x8, // mov eax, [ebp+8]
0x90, // nop (to have same offset as in NoDebug-Code)
0x89, 0x5, 0,0,0,0, // mov [4byte-adr], eax - store Argumentptr in deleteFileAArg
0xE8, 0,0,0,0, // call 4-byte adr - points to NewDeleteFileA
0xC9, // leave
0xC3 // ret
};
static byte[] deleteFileACodeNoDebug = { // "Frame Pointer Omission" active
0x8b, 0x44, 0x24, 0x8, // mov eax,[esp+8]
0x89, 0x5, 0,0,0,0, // mov [4byte-adr], eax - store Argumentptr in deleteFileAArg
0xE8, 0,0,0,0, // call 4-byte adr - points to NewDeleteFileA
0xC9, // leave
0xC3 // ret
};
static byte[] deleteFileWCodeDebugger = { // Call got own Local stack
0x8b, 0x45, 0x8, // mov eax, [ebp+8]
0x90, // nop (to have same offset as in NoDebug-Code)
0x89, 0x5, 0,0,0,0, // mov [4byte-adr], eax - store Argumentptr in deleteFileWArg
0xE8, 0,0,0,0, // call 4-byte adr - points to NewDeleteFileW
0xC9, // leave
0xC3 // ret
};
static byte[] deleteFileWCodeNoDebug = { // "Frame Pointer Omission" active
0x8b, 0x44, 0x24, 0x8, // mov eax,[esp+8]
0x89, 0x5, 0,0,0,0, // mov [4byte-adr], eax - store Argumentptr in deleteFileWArg
0xE8, 0,0,0,0, // call 4-byte adr - points to NewDeleteFileW
0xC9, // leave
0xC3 // ret
};
static byte[] deleteFileACode; // Used code depends if Debugger attached/Frameworkversion
static byte[] deleteFileWCode; // Used code depends if Debugger attached/Frameworkversion
static byte[] deleteFileAArg = new byte[4]; // Argument for DeleteFileA
static byte[] deleteFileWArg = new byte[4]; // Argument for DeleteFileW
static long oldDeleteFileA = 0; // Old DeleteFileA-Function
static long oldDeleteFileW = 0; // Old DeleteFileW-Function
[STAThread]
static void Main(String[] mainargs) {
debug = Debugger.IsAttached; // Use FPO-Assembler when no Debugger is attached
if (Environment.Version.Major == 2) debug = false; // .NET 2.0 always calls with FPO
deleteFileWCode = debug ? deleteFileWCodeDebugger : deleteFileWCodeNoDebug; // Debugger / NoDebug contains different code
deleteFileACode = debug ? deleteFileACodeDebugger : deleteFileACodeNoDebug; // Debugger / NoDebug contains different code
bool bError = false;
// Set new Functions for DeleteFileA & DeleteFileW
oldDeleteFileA = HotPatch((NewDeleteFileADelegate)NewDeleteFileA, deleteFileACode, deleteFileAArg, "DeleteFileA");
oldDeleteFileW = HotPatch((NewDeleteFileWDelegate)NewDeleteFileW, deleteFileWCode, deleteFileWArg, "DeleteFileW");
if (oldDeleteFileW != 0 && oldDeleteFileA != 0) {
Print("Functions patched, testing functionality ...");
bool bOrgdeletemode = deletemode; // Store old deletemode-setting
deletemode = false; // Switch of deleting for test
String strTempFile = Path.GetTempFileName(); // Create a testfile
Print("Testfile created: '" + strTempFile + "', will now 'Delete' it.");
File.Delete(strTempFile); // And "delete" it
String strDrive = Path.GetDirectoryName(strTempFile); // Drive of testfile
strDrive = Path.Combine(strDrive.Substring(0, strDrive.IndexOf(":\\") + 2), "KAN"); // X:\KAN
String strKANName = Path.Combine(strDrive, Path.GetFileName(strTempFile)); // X:\KAN\tempfile
bError = !File.Exists(strKANName); // File should now be in X:\KAN\...
if (!bError) { // No error should occurred
Print("Deleting: '" + strKANName + "'");
Function(oldDeleteFileW, strKANName); // Delete the testfile
}
deletemode = bOrgdeletemode; // Restore old deletemode-setting
}
else
bError = true;
if (bError) Print("Error patching."); // Either functions didnt match source, or move to KAN failed
else {
Print("Successfully patched.\nPress Return ...");
Console.In.ReadLine();
}
if (oldDeleteFileA != 0) UnPatch("DeleteFileA"); // Remove patches ..
if (oldDeleteFileW != 0) UnPatch("DeleteFileW");
Print("Patches removed.");
}
delegate bool CFuncDelegate(IntPtr strFileName); // Call original Assembler-Function
static bool Function(long CFunc, string strFileName) {
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer((IntPtr)CFunc, typeof(CFuncDelegate));
char[] cFilename = strFileName.ToCharArray();
return func(Marshal.UnsafeAddrOfPinnedArrayElement(cFilename, 0));
}
delegate bool NewDeleteFileADelegate(); // Declare delegate -- defines required signature
static bool NewDeleteFileA() { // Move to X:\KAN, then delete
bool retVal = false;
String strFileName = getFileName(deleteFileAArg); // Get stored IntelPtr in deleteFileAArg
if (strFileName != null) {
Print("NewDeleteFileA called: '" + strFileName + "'");
String strNewName = MoveFileToKAN(strFileName); // Either move the File to KAN and remove new name or return old name
if (deletemode)
retVal = Function(oldDeleteFileA, strNewName); // Delete after moving, or in case it couldn't be moved - delete original
else {
if (strNewName == strFileName) // Filename same as old name = couldn't be moved
retVal = Function(oldDeleteFileA, strFileName); // Delete original
}
}
else
Print("NewDeleteFileA: Error No filename");
return retVal;
}
delegate bool NewDeleteFileWDelegate(); // Declare delegate -- defines required signature
static bool NewDeleteFileW() { // Move to X:\KAN, then delete
bool retVal = false;
String strFileName = getFileName(deleteFileWArg); // Get stored IntelPtr in deleteFileWArg
if (strFileName != null) {
Print("NewDeleteFileW called: '" + strFileName + "'");
String strNewName = MoveFileToKAN(strFileName); // Either move the File to KAN and remove new name or return old name
if (deletemode)
retVal = Function(oldDeleteFileW, strNewName); // Delete after moving, or in case it couldn't be moved - delete original
else {
if (strNewName == strFileName) // Filename same as old name = couldn't be moved
retVal = Function(oldDeleteFileW, strFileName); // Delete original
}
}
else
Print("NewDeleteFileW: Error No filename");
return retVal;
}
static String getFileName(byte[] ptr) { // Get String from IntelPtr
long ptrFileName = GetIntelPtr(ptr);
String strFileName = null;
if (ptrFileName != 0) {
unsafe {
byte* ptrFile = (byte*)ptrFileName;
int len = 0; while (ptrFile[len] != 0) len += 2;
char[] b = new char[len / 2];
for (int i = 0, j = 0; i < len / 2; i++, j += 2)
b[i] = (char)ptrFile[j];
strFileName = new String(b);
}
}
return strFileName;
}
static String MoveFileToKAN(String strOldName) { // Move file to X:\KAN, then delete it
strOldName = Path.GetFullPath(strOldName); // In case relative path used ..
if (strOldName.IndexOf(":\\") == -1) return strOldName; // Error no Drive in Name ..
String strTempFile = Path.GetFileName(strOldName); // Original filename
String strDriveName = strOldName.Substring(0, strOldName.IndexOf(":\\") + 2); // Drive of filename
String strKANName = Path.Combine(strDriveName, "KAN"); // X:\KAN
if (!Directory.Exists(strKANName)) Directory.CreateDirectory(strKANName); // If KAN-Directory doesn't exist, create it
if (File.Exists(Path.Combine(strKANName, strTempFile))) { // File exists in KAN ?
String strNewTempFile = Path.GetTempFileName(); // Get a new unique Name
Function(oldDeleteFileW, strNewTempFile); // Delete the tempfile
strTempFile = Path.GetFileNameWithoutExtension(strNewTempFile) + "_" + strTempFile; // New name should be TMPNAME_orgname.orgextension
}
String strNewName = Path.Combine(strKANName, strTempFile); // X:\KAN\filename
if (!MoveFile(strOldName, strNewName)) { // Move file to KAN
Print("Move failed, old: '" + strOldName + "' new: '" + strNewName + "'");
return strOldName; // Couldn't move, return old name
}
Print("File moved from: '" + strOldName + "' to: '" + strNewName + "'");
return strNewName; // Return new name
}
// Redirect Kernel-functioncall strFunctionName to array, which redirects to NewFunctionDelegate
static long HotPatch(Delegate NewFunctionDelegate, byte[] array, byte[] arg, String strFunctionName) {
long OldFunction = getKernelFunction(strFunctionName); // Function from kernel32.dll
if (OldFunction == 0) return 0;
long codePtr = (long)Marshal.UnsafeAddrOfPinnedArrayElement(array, 0); // Additional code to get argument from [ebp+8] or [esp+8]
long argPtr = (long)Marshal.UnsafeAddrOfPinnedArrayElement(arg, 0); // Argument will be stored here
long NewFunction = (long)Marshal.GetFunctionPointerForDelegate(NewFunctionDelegate); // New Function call
long lpNewOldFunctionPointer = OldFunction - 5; // -5 because of the nop instructions
if (debug) { // Dump out hex-ptrs
Print("Old" + strFunctionName, lpNewOldFunctionPointer);
Print("New" + strFunctionName, NewFunction);
Print("Code" + strFunctionName, codePtr);
Print("Arg" + strFunctionName, argPtr);
}
uint dwOldProtV, dwNewProtV;
VirtualProtect((IntPtr)argPtr, 4, (uint)Prot.PAGE_READWRITE, out dwOldProtV); // Make argument writeable
VirtualProtect((IntPtr)codePtr, (uint)array.Length, (uint)Prot.PAGE_EXECUTE_READWRITE, out dwOldProtV); // Make code writeable & executable
VirtualProtect((IntPtr)lpNewOldFunctionPointer, 7, (uint)Prot.PAGE_EXECUTE_READWRITE, out dwOldProtV); // Make old function writeable
bool bSrcMatches = CheckMatchSource(lpNewOldFunctionPointer); // Check if Source matches 5x nop + mov edi, edi
if (bSrcMatches) {
long lpCallAdr = StoreIntelPtr(argPtr, codePtr + 6); // set adr for mov [4byte-adr], eax
long lpNewCallDestination = NewFunction - lpCallAdr - 5; // Calculating the call destination (-5 because of the call size) - RELATIVE Ptr
StoreIntelPtr(lpNewCallDestination, lpCallAdr + 1); // Store 4byte-Ptr in reverse order
long lpCodeCallDestination = codePtr - lpNewOldFunctionPointer - 5; // Calculating the call destination (-5 because of the call size) - RELATIVE Ptr
long workmem = StoreAsmBytes(new byte[] { 0xE8 }, lpNewOldFunctionPointer); // x86-asm CALL
workmem = StoreIntelPtr(lpCodeCallDestination, workmem); // Store 4byte-Ptr in reverse order
StoreAsmBytes(new byte[] { 0xEB, 0xF9 }, workmem); // x86-asm JMP -5
if (CheckMatchSource(lpNewOldFunctionPointer)) bSrcMatches = false; // After patching, should NOT Match now
}
VirtualProtect((IntPtr)lpNewOldFunctionPointer, 7, dwOldProtV, out dwNewProtV); // Set back the old Protection
if (!bSrcMatches) { Print("Can't patch: " + strFunctionName); return 0; }
return OldFunction + 2; // Reentry-Point
}
static long StoreAsmBytes(byte[] bytes, long mem) { // Store Bytearray at mem
memcpy((IntPtr)mem, Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0), bytes.Length);
return mem + bytes.Length;
}
static long StoreIntelPtr(long ptr, long mem) { // Store ptr as IntelPtr (reverse order) at mem
byte[] array = new byte[4];
SetIntelPtr(ptr, array);
return StoreAsmBytes(array, mem);
}
static void SetIntelPtr(long ptr, byte[] array) { // Store ptr as IntelPtr (reverse order) in array
array[0] = (byte)ptr;
array[1] = (byte)(ptr >> 8);
array[2] = (byte)(ptr >> 16);
array[3] = (byte)(ptr >> 24);
}
static long GetIntelPtr(byte[] array) { // Get ptr as InterPtr (reverse order) from array
return (array[0] | array[1] << 8 | array[2] << 16 | array[3] << 24);
}
static byte[] GetAsmBytes(byte[] bytes, long mem) { // get n-bytes from mem
IntPtr memPtr = Marshal.UnsafeAddrOfPinnedArrayElement(bytes, 0);
memcpy(memPtr, (IntPtr)mem, bytes.Length);
return bytes;
}
static bool CheckMatchSource(long mem) { // Check if mem matches sourcearray (5x nop + mov edi, edi)
byte[] srcarray = GetAsmBytes(new byte[sourcearray.Length], mem);
bool bSrcMatches = true;
for (int i = 0; i < srcarray.Length; i++) {
if (srcarray[i] != sourcearray[i])
bSrcMatches = false;
}
return bSrcMatches;
}
static void UnPatch(String strFunctionName) { // Restore function to (5x nop + mov edi, edi)
long OldFunction = getKernelFunction(strFunctionName); // Function from kernel32.dll
IntPtr lpNewOldFunctionPointer = (IntPtr)(OldFunction - 5); // -5 because of the nop instructions
uint dwOldProtV, dwNewProtV;
VirtualProtect(lpNewOldFunctionPointer, 7, (uint)Prot.PAGE_EXECUTE_READWRITE, out dwOldProtV); // Make writeable
StoreAsmBytes(sourcearray, (long)lpNewOldFunctionPointer); // Write 5x nop + mov edi, edi
VirtualProtect(lpNewOldFunctionPointer, 7, dwOldProtV, out dwNewProtV); // Set back the old Protection
}
static long getKernelFunction(String strFunctionName) { // get functionptr of function in kernel
return (long)GetProcAddress(LoadLibrary("kernel32.dll"), strFunctionName);
}
static void Print(String txt) { // Write "txt" to console
Console.Out.WriteLine(txt);
}
static void Print(String txt, long ptr) { // Write "txt: ptrAShex" to console
Print(txt + ": " + ptr.ToString("X"));
}
}
}
P.S: Wieso sind TABS hier so riesig.

Edit:
P.P.S: Wieso hat CODE-Tag kein Syntaxhighlightning? Hab stattdessen PHP genommen ..
Zuletzt bearbeitet: