Programmatic solution of course...
http://www.daveamenta.com/2008-05/c-delete-a-file-to-the-recycle-bin/
From above:
using Microsoft.VisualBasic;
string path = #"c:\myfile.txt";
FileIO.FileSystem.DeleteDirectory(path,
FileIO.UIOption.OnlyErrorDialogs,
RecycleOption.SendToRecycleBin);
You need to delve into unmanaged code. Here's a static class that I've been using:
public static class Recycle
{
private const int FO_DELETE = 3;
private const int FOF_ALLOWUNDO = 0x40;
private const int FOF_NOCONFIRMATION = 0x0010;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
public struct SHFILEOPSTRUCT
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public int wFunc;
public string pFrom;
public string pTo;
public short fFlags;
[MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
public string lpszProgressTitle;
}
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern int SHFileOperation(ref SHFILEOPSTRUCT FileOp);
public static void DeleteFileOperation(string filePath)
{
SHFILEOPSTRUCT fileop = new SHFILEOPSTRUCT();
fileop.wFunc = FO_DELETE;
fileop.pFrom = filePath + '\0' + '\0';
fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
SHFileOperation(ref fileop);
}
}
Addendum:
Tsk tsk # Jeff for "using Microsoft.VisualBasic" in C# code.
Tsk tsk # MS for putting all the goodies in VisualBasic namespace.
The best way I have found is to use the VB function FileSystem.DeleteFile.
Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(file.FullName,
Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs,
Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
It requires adding Microsoft.VisualBasic as a reference, but this is part of the .NET framework and so isn't an extra dependency.
Alternate solutions require a P/Invoke to SHFileOperation, as well as defining all the various structures/constants. Including Microsoft.VisualBasic is much neater by comparison.
Related
I have a simple application to copy the properties of the printer devmode and restore them after a manual change.
I read through topics the whole day and can't understand why my code isn't working. I compared the output of two different printer settings against each other and can confirm that they are different, so my guess is that something with restoring these settings isn't working.
I also tried thisĀ applicationĀ but found some strange behavior. When I export the devmode data, change the properties manually and load the save again, the data only shows changed if I call the printer property dialog directly with the button in the application itself. If I go to windows settings and navigate to the printer properties myself, the data isn't changed.
Here is the code, got it from this thread
public static class PrinterSettingsExtensions
{
public static byte[] GetDevModeData(this PrinterSettings settings)
{
//Contract.Requires(settings != null);
byte[] devModeData;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// cer since hDevMode is not a SafeHandle
}
finally
{
var hDevMode = settings.GetHdevmode();
try
{
IntPtr pDevMode = NativeMethods.GlobalLock(hDevMode);
try
{
var devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
pDevMode, typeof(NativeMethods.DEVMODE));
var devModeSize = devMode.dmSize + devMode.dmDriverExtra;
devModeData = new byte[devModeSize];
Marshal.Copy(pDevMode, devModeData, 0, devModeSize);
}
finally
{
NativeMethods.GlobalUnlock(hDevMode);
}
}
finally
{
Marshal.FreeHGlobal(hDevMode);
}
}
return devModeData;
}
public static void SetDevModeData(this PrinterSettings settings, byte[] data)
{
//Contract.Requires(settings != null);
//Contract.Requires(data != null);
//Contract.Requires(data.Length >= Marshal.SizeOf(typeof(NativeMethods.DEVMODE)));
RuntimeHelpers.PrepareConstrainedRegions();
try
{
// cer since AllocHGlobal does not return SafeHandle
}
finally
{
var pDevMode = Marshal.AllocHGlobal(data.Length);
try
{
// we don't have to worry about GlobalLock since AllocHGlobal only uses LMEM_FIXED
Marshal.Copy(data, 0, pDevMode, data.Length);
var devMode = (NativeMethods.DEVMODE)Marshal.PtrToStructure(
pDevMode, typeof(NativeMethods.DEVMODE));
// The printer name must match the original printer, otherwise an AV will be thrown
settings.PrinterName = devMode.dmDeviceName;
// SetHDevmode creates a copy of the devmode, so we don't have to keep ours around
settings.SetHdevmode(pDevMode);
}
finally
{
Marshal.FreeHGlobal(pDevMode);
}
}
}
}
static class NativeMethods
{
private const string Kernel32 = "kernel32.dll";
[DllImport(Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern IntPtr GlobalLock(IntPtr handle);
[DllImport(Kernel32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
public static extern bool GlobalUnlock(IntPtr handle);
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Auto)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 32;
private const int CCHFORMNAME = 32;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public int dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCHFORMNAME)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
}
I'm using .Net Core 6 and Windows 11 22000.795 if this helps.
DevMode is what I am working on at the moment. Using .net 4.8.1 unfortunately but I am using Heap functions and I am pulling the default settings using p/Invoke.
My goal is to modify settings and then stream the changed settings to another API. So, I'll look into this and see what I can find.
Several steps could be giving you problems with settings. Mostly around the printer/document/settings/defaults/drivers themselves. Once you make your settings and pass need to make sure your end test is picking up the changes. But, as you suggest, most likely an invalid format due to each driver being unique.
Any progress?
Appears for the data to show it has been changed, you need to flag the fields that have been modified in public int dmFields; (Says it is defined as a long).
dmFields
Specifies bit flags identifying which of the following DEVMODEW members are in use. For example, the DM_ORIENTATION flag is set when the dmOrientation member contains valid data. The DM_XXX flags are defined in wingdi.h.
Files can have a change date. This date is not the same as the last modified date or the last access date. Change date is not visible through the UI or .NET API. There a two Win32 functions GetFileInformationByHandleEx for reading and SetFileInformationByHandle for writing file information.
I want to read out the change date, add some hours to it, and write the new date back as the change date of the file.
For now I have following code:
class Program
{
static void Main(string[] args)
{
using (var file = new FileStream(#"c:\path\to\file", FileMode.Open))
{
var fileInfo = new FILE_BASIC_INFO();
GetFileInformationByHandleEx(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
out fileInfo,
(uint)Marshal.SizeOf(fileInfo));
SetFileInformationByHandle(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
fileInfo,
(uint)Marshal.SizeOf(fileInfo));
}
}
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandleEx(
IntPtr hFile,
FILE_INFO_BY_HANDLE_CLASS infoClass,
out FILE_BASIC_INFO fileInfo,
uint dwBufferSize);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
FILE_INFO_BY_HANDLE_CLASS infoClass,
FILE_BASIC_INFO fileInfo,
uint dwBufferSize);
private enum FILE_INFO_BY_HANDLE_CLASS
{
FileBasicInfo = 0
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct FILE_BASIC_INFO
{
public LARGE_INTEGER CreationTime;
public LARGE_INTEGER LastAccessTime;
public LARGE_INTEGER LastWriteTime;
public LARGE_INTEGER ChangeTime;
public uint FileAttributes;
}
[StructLayout(LayoutKind.Explicit, Size = 8)]
private struct LARGE_INTEGER
{
[FieldOffset(0)]
public Int64 QuadPart;
[FieldOffset(0)]
public UInt32 LowPart;
[FieldOffset(4)]
public Int32 HighPart;
}
}
I can read out the change date into that awful structure LARGE_INTEGER. What I want to have is a function which can convert that type into a System.DateTime and vice versa.
The second problem that I have is that the siganture of the SetFileInformationByHandle method is wrong. I get a PInvokeStackImbalance with this additional information:
Additional information: A call to PInvoke function 'Program::SetFileInformationByHandle' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
Who can help me?
To answer the first part..On how to convert "that awful Large_Interger" to DateTime..
The below code snippet should help..
using (var file = new System.IO.FileStream(#"sample.log", System.IO.FileMode.Open))
{
var fileInfo = new FILE_BASIC_INFO();
GetFileInformationByHandleEx(
file.Handle,
FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo,
out fileInfo,
(uint)System.Runtime.InteropServices.Marshal.SizeOf(fileInfo));
var changeTime = DateTime.FromFileTime(fileInfo.ChangeTime.QuadPart);
Console.WriteLine(changeTime);
System.TimeSpan changedForHowLong = DateTime.Now.Subtract(changeTime);
Console.WriteLine(changedForHowLong.Days);
}
I tested the above snippet it seems to work fine..
Let me try repro'ing the issue you faced with the PInvokeStackImbalance..
Take Care,
I found this signature on PInvoke
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
int FileInformationClass,
IntPtr lpFileInformation,
Int32 dwBufferSize);
Somehow this did not work. I had to change the type of the parameter lpFileInformation to FILE_BASIC_INFO to make it work.
This is the complete C# example called from PowerShell:
$fu = #"
using System;
using System.IO;
using System.Runtime.InteropServices;
public class FileUtility
{
private struct FILE_BASIC_INFO
{
[MarshalAs(UnmanagedType.I8)]
public Int64 CreationTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 LastAccessTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 LastWriteTime;
[MarshalAs(UnmanagedType.I8)]
public Int64 ChangeTime;
[MarshalAs(UnmanagedType.U4)]
public UInt32 FileAttributes;
}
[DllImport("Kernel32.dll", SetLastError = true)]
private static extern bool SetFileInformationByHandle(
IntPtr hFile,
int FileInformationClass,
FILE_BASIC_INFO lpFileInformation,
Int32 dwBufferSize);
public void SetFileChangeTime()
{
using (FileStream fs = new FileStream(#"c:\path\to\file", FileMode.Open))
{
FILE_BASIC_INFO fileInfo = new FILE_BASIC_INFO();
fileInfo.ChangeTime = 943044610000000;
SetFileInformationByHandle(
fs.Handle,
0, // the same as FILE_INFO_BY_HANDLE_CLASS.FileBasicInfo
fileInfo,
Marshal.SizeOf(fileInfo));
}
}
}
"#
Add-Type -TypeDefinition $fu -IgnoreWarnings
$f = New-Object -TypeName FileUtility
$f.SetFileChangeTime()
I have run the example with the other date properties since they are shown in the explorer and it worked.
Edit
This code does not run in debug mode within VS. As mentioned above it throws the exception. Running the EXE in the command line does not throw an exception. But the change date is not updated. However it works only in PowerShell. Strange.
I'm processing a large number of files, therefore, I don't want to wait until the whole search is finished before the array is returned. So I don't want to use Directory.GetFiles()
According to this answer , I need to use EnumerateFiles() in order to get results during the search process. However, I'm using NET2.0 and this function seems to be introduced starting from NET 4.0
What is the equivalent of EnumerateFiles() in Net 2.0 ?
Any hints would be highly appreciated
What you need are the WinAPI calls for FindFirstFile and FindNextFile.
Here's some code that uses the wrapped api calls.
IEnumerable<string> EnumerateFiles(string path)
{
APIWrapper.FindData findData = new APIWrapper.FindData();
APIWrapper.SafeFindHandle handle = APIWrapper.SafeNativeMethods.FindFirstFile(System.IO.Path.Combine(path, "*"), findData);
if(!handle.IsInvalid && !handle.IsClosed)
{
yield return findData.fileName;
while(!APIWrapper.SafeNativeMethods.FindNextFile(handle, findData))
yield return findData.fileName;
handle.Close();
}
}
I just hand typed EnumerateFiles so treat it as pseudo code, but the class it relies on is production ready, this is it here
internal class APIWrapper
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal sealed class FILETIME
{
public int Low;
public int High;
public Int64 ToInt64()
{
Int64 h = High;
h = h << 32;
return h + Low;
}
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
internal sealed class FindData
{
public int fileAttributes;
public FILETIME CreationTime;
public FILETIME LastAccessTime;
public FILETIME LastWriteTime;
public int FileSizeHigh;
public int FileSizeLow;
public int dwReserved0;
public int dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public String fileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public String alternateFileName;
}
internal sealed class SafeFindHandle : Microsoft.Win32.SafeHandles.SafeHandleMinusOneIsInvalid
{
/// <summary>
/// Constructor
/// </summary>
public SafeFindHandle()
: base(true)
{
}
/// <summary>
/// Release the find handle
/// </summary>
/// <returns>true if the handle was released</returns>
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
protected override bool ReleaseHandle()
{
return SafeNativeMethods.FindClose(handle);
}
}
internal enum SearchOptions
{
NameMatch,
LimitToDirectories,
LimitToDevices
}
[SecurityPermissionAttribute(SecurityAction.Assert, UnmanagedCode = true)]
internal static class SafeNativeMethods
{
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern SafeFindHandle FindFirstFile(String fileName, [In, Out] FindData findFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern SafeFindHandle FindFirstFileEx(
String fileName, //__in LPCTSTR lpFileName,
[In] int infoLevel, //__in FINDEX_INFO_LEVELS fInfoLevelId,
[In, Out] FindData findFileData, //__out LPVOID lpFindFileData,
[In, Out] SearchOptions SerchOps, //__in FINDEX_SEARCH_OPS fSearchOp,
[In] int SearchFilter, //__reserved LPVOID lpSearchFilter,
[In] int AdditionalFlags); //__in DWORD dwAdditionalFlags
[DllImport("kernel32", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FindNextFile(SafeFindHandle hFindFile, [In, Out] FindData lpFindFileData);
[DllImport("kernel32", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FindClose(IntPtr hFindFile);
}
}
Specially added as a new answer..
Since .NET 2.0 There is IENumerable and yield keyword does Lazy Initialization/deferred execution..With these, you can get your wants.
public IEnumerable<string> GetFiles(string rootPath, string [] fileNameStartChars, string[] extensionsFilter)
{
FileSystemInfo[] fsi = null;
for(int i = 0; i < fileNameStartChars.Length; i++)
{
for(int k = 0; k<extensionsFilter.Length; k++)
{
fsi = new DirectoryInfo(rootPath).GetFileSystemInfos(fileNameStartChars[i]+extensionsFilter[k]);
if (fsi.Length > 0)
{
for (int j = 0; j < fsi.Length; j++)
{
/// .Name returns the filename with extension..if you need, please implement here a substring for eliminate the extension of the file
yield return fsi[j].Name;
}
}
}
}
}
And usage :
possible filenames startsWithChar table
public string[] table = new string[]
{
"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z",
"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z",
"1","2","3","4","5","6","7","8","9","0","#","_","-",".","#","+",",","%","&","(",")","[","]","{","}","*",
"<",">","^"," ","|",";","`"
};
And extensions :
string[] Exts = new string[] { ".mp3", ".midi", ".wav"};
with this method, you can filter your data within small parts as such as using startswithchar filtering, so you won't get Memory problem which depends to your files count..This is the tricky part of trying to imitate .net v4's EnumerateFiles method with 100% .net v2 managed code..
IEnumerable<string> strNumerable = GetFiles(#"D:\Music", table, Exts);
///Since its deferred execution, method didn't get any memory alloc for your data till now..Memory Alloc will start within this foreach..
foreach (string s in strNumerable)
{
//do your work
}
Since .NET 2.0 There is IENumerable and yield keyword does Lazy Initialization..With these, you can get your wants.
With a pseudo :
public IENumerable GetFiles(string Path, string FileExtension)
{
// Create a new IENumerable instance
// Get FileCount with DirectoryInfo or some similar
// Implement a for-loop with File count
// If DirectoryFiles [ indexOfForLoop ] .Extension == FileExtension
yield return DirectoryFiles [indexOfForLoop ]
}
In this pseudo the yield keyword take responsibility of the filtering..If filtering returns true the yield return immediately return the result to the IENumerable instance / callee..
And IEnumerable takes responsibility of Lazy Loading..
Depends to your needs, Also you can use yield break keyword in loop to not include the result..
And with a simple call :
List<string> FilesInDirectory = GetFiles( path, "*.txt").ToList();
Hope this helps..
I'm working on a windows mobile project using compact framework.
One thing I have to do is log when users perform actions, this can mean any action from pressing a button to using the barcode scanner. The time it happened also needs to be logged.
My plan is to override all controls to include logging functionality built into them but this might not be the right way to go about it, seems like a very tedious thing to do..
Is there a better way?
I would go with IL Weaving. Here is a library that I would recommend: http://www.sharpcrafters.com/aop.net/msil-injection What it does is that you mark your class with an attribute and you can intercept all function calls. In this interception you would put in your logging logic.
I'd say it depends greatly on the definition of "action". I'd be highly inclined to see if the (undocumented) QASetWindowsJournalHook API would work. It's probably going to grab most of what you want, with not a lot of code required. A native example of usage can be found on Codeproject here.
SetWindowsHook with WH_JOURNALRECORD might also be worth a look. Yeah, I know it's "unsupported" but it works just fine, and it's unlikely to be removed from a device you've got fielded (plus it's been in the OS for at least 10 years).
Some P/Invoke declarations, all derived from pwinuser.h, for them both are as follows:
[StructLayout(LayoutKind.Sequential)]
public struct JournalHookStruct
{
public int message { get; set; }
public int paramL { get; set; }
public int paramH { get; set; }
public int time { get; set; }
public IntPtr hwnd { get; set; }
}
internal enum HookType
{
JournalRecord = 0,
JournalPlayback = 1,
KeyboardLowLevel = 20
}
internal enum HookCode
{
Action = 0,
GetNext = 1,
Skip = 2,
NoRemove = 3,
SystemModalOn = 4,
SystemModalOff = 5
}
public const int HC_ACTION = 0;
public const int LLKHF_EXTENDED = 0x1;
public const int LLKHF_INJECTED = 0x10;
public const int LLKHF_ALTDOWN = 0x20;
public const int LLKHF_UP = 0x80;
public const int VK_TAB = 0x9;
public const int VK_CONTROL = 0x11;
public const int VK_ESCAPE = 0x1B;
public const int VK_DELETE = 0x2E;
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr SetWindowsHookEx(HookType idHook, HookProc lpfn, IntPtr hMod, int
[DllImport("coredll.dll", SetLastError = true)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("coredll.dll", SetLastError = true)]
public static extern int CallNextHookEx(IntPtr hhk, HookCode nCode, IntPtr wParam, IntPtr
[DllImport("coredll.dll", SetLastError = true)]
public static extern IntPtr QASetWindowsJournalHook(HookType nFilterType, HookProc pfnFilterProc, ref JournalHookStruct pfnEventMsg);
Would writing these messages to a log file not solve your problem?
#if PocketPC
private static string _appPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
#else
private static string _appPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), Application.CompanyName);
#endif
public const int KILOBYTE = 1024;
public static string ErrorFile { get { return _appPath + #"\error.log"; } }
public static void Log(string message)
{
if (String.IsNullOrEmpty(message)) return;
using (FileStream stream = File.Open(ErrorFile, FileMode.Append, FileAccess.Write))
{
using (StreamWriter sw = new StreamWriter(stream, Encoding.UTF8, KILOBYTE))
{
sw.WriteLine(string.Format("{0:MM/dd/yyyy HH:mm:ss} - {1}", DateTime.Now, message));
}
}
}
You could have issues though if you have threading going on and multiple routines try to write at the same time. In that case, you could add additional logic to lock the routine while it is in use.
That's how I do it, anyway.
By the #if regions, you can see this is also used by my Windows PC applications.
how to open an file's Properties dialog by a button
private void button_Click(object sender, EventArgs e)
{
string path = #"C:\Users\test\Documents\tes.text";
// how to open this propertie
}
Thank you.
For example if want the System properties
Process.Start("sysdm.cpl");
But how do i get the Properties dialog for a file path?
Solution is:
using System.Runtime.InteropServices;
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
static extern bool ShellExecuteEx(ref SHELLEXECUTEINFO lpExecInfo);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct SHELLEXECUTEINFO
{
public int cbSize;
public uint fMask;
public IntPtr hwnd;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpVerb;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpFile;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpParameters;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpDirectory;
public int nShow;
public IntPtr hInstApp;
public IntPtr lpIDList;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpClass;
public IntPtr hkeyClass;
public uint dwHotKey;
public IntPtr hIcon;
public IntPtr hProcess;
}
private const int SW_SHOW = 5;
private const uint SEE_MASK_INVOKEIDLIST = 12;
public static bool ShowFileProperties(string Filename)
{
SHELLEXECUTEINFO info = new SHELLEXECUTEINFO();
info.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(info);
info.lpVerb = "properties";
info.lpFile = Filename;
info.nShow = SW_SHOW;
info.fMask = SEE_MASK_INVOKEIDLIST;
return ShellExecuteEx(ref info);
}
// button click
private void button1_Click(object sender, EventArgs e)
{
string path = #"C:\Users\test\Documents\test.text";
ShowFileProperties(path);
}
Call Process.Start, passing a ProcessStartInfo containing the name of the file, and with the ProcessStartInfo.Verb set to properties. (For more info, see the description of the unmanaged SHELLEXECUTEINFO structure, which is what ProcessStartInfo wraps, and in particular the lpVerb member.)
Various file properties are available from the FileInfo class:
FileInfo info = new FileInfo(path);
Console.WriteLine(info.CreationTime);
Console.WriteLine(info.Attributes);
...
Solution is to use ShellExecute () api.
How to invoke this api using C# :
http://weblogs.asp.net/rchartier/442339
This works fine for me without CharSet attribute both in Debug and Release mode.
To simplify handling Shell32 stuff and such, you could also use Vanara like:
using Vanara.PInvoke;
using System.Runtime.InteropServices;
// ...
void ShowProperties(string filepath)
{
var info = new Shell32.SHELLEXECUTEINFO();
info.cbSize = Marshal.SizeOf(info);
info.lpVerb = "properties";
info.lpFile = filepath;
info.nShellExecuteShow = ShowWindowCommand.SW_SHOW;
info.fMask = Shell32.ShellExecuteMaskFlags.SEE_MASK_INVOKEDLIST;
Shell32.ShellExecuteEx(ref i);
}
and call it like:
ShowProperties(#"C:\The\Path\To\The\File.txt");