In C#, you can specify a filter on an OpenFileDialog object.
var dlg = new OpenFileDialog();
dlg.DefaultExt = ".xml";
dlg.Filter = "XML Files|*.xml";
Is there a way to automatically select files by name? For example, if I navigated to a folder of xml files, is there any filtering option that would automatically target "myxml.xml"?
Yes, just set the FileName property of the OpenFileDialog like this:
dlg.FileName = "myxml.xml";
However, it would be more appropriate if you use the name in the filter. Just place it instead of the star which acts as a wildcard:
dlg.Filter = "XML Files|myxml.xml";
And always remember you can have multiple filters like this: (It may be useful in the future):
"Image Files (*.bmp, *.jpg)|*.bmp;*.jpg"
// -- OR --
"Text Files (*.txt)|*.txt|All Files (*.*)|*.*"
More documentation on filters at MSDN.
Yes, you can actually set the filter to a complete filename:
dlg.Filter = "myxml Files|myxml.xml";
Note that when this filter is selected, you won't be able to select other XML files. If you simply want to default to that filename while showing and allowing selection of any XML file, go with Fᴀʀʜᴀɴ Aɴᴀᴍ's (original) answer. And now that he copied my answer into his, you can just go with his.
What you can do is either set the FileName property like this:
dlg.FileName = "myxml.xml";
or set the Filter property like this:
dlg.Filter = "XML files|file.xml";
(it's important to check that there's no space at the end like this "file.xml ", because if there is, your file won't show up, in other words OpenFileDialog doesn't trim the Filter property)
if you don't know what the file name is beforehand, you can use DirectoryInfo and FileInfo like this:
DirectoryInfo dir = new DirectoryInfo("PATHHERE");
FileInfo[] files = dir.GetFiles();
and loop through the files to find the one you are looking for
Step 1: Add this method to your code:
[DllImport("shell32.dll", SetLastError = true)]
public static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [In, MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags);
[DllImport("shell32.dll", SetLastError = true)]
public static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, [Out] out IntPtr pidl, uint sfgaoIn, [Out] out uint psfgaoOut);
public static void OpenFolderAndSelectItem(string folderPath, string file)
{
IntPtr nativeFolder;
uint psfgaoOut;
SHParseDisplayName(folderPath, IntPtr.Zero, out nativeFolder, 0, out psfgaoOut);
if (nativeFolder == IntPtr.Zero)
return;
IntPtr nativeFile;
SHParseDisplayName(System.IO.Path.Combine(folderPath, file), IntPtr.Zero, out nativeFile, 0, out psfgaoOut);
IntPtr[] fileArray;
if (nativeFile == IntPtr.Zero)
{
fileArray = new IntPtr[0];
}
else
{
fileArray = new IntPtr[] { nativeFile };
}
SHOpenFolderAndSelectItems(nativeFolder, (uint)fileArray.Length, fileArray, 0);
Marshal.FreeCoTaskMem(nativeFolder);
if (nativeFile != IntPtr.Zero)
{
Marshal.FreeCoTaskMem(nativeFile);
}
}
Step 2: Call the method OpenFolderAndSelectItem(string folderPath, string file) to use.
Step 3: Enjoy!
Related
I am writing a class that will encapsulate a temporary folder. The folder will hold files as the program works with them and then delete itself when the object is disposed.
I would like to prevent the temporary folder from being messed with while in use. I know it is possible to keep a folder "locked" without altering any of the permissions-- for example, if you open a command prompt and set the working directory to a particular folder, you won't be able to delete that folder unless you close the command prompt or change the working directory to something else. Is there a way to do that in c#, programmatically?
I know it is possible with this:
var path = #"c:\temp\MyFolderName";
System.IO.Directory.CreateDirectory(path);
System.Environment.CurrentDirectory = path;
System.IO.Directory.Delete(path); //Throws error
...but this limits me to one working folder (and seems sort of kludgy).
I know we can lock a file just by opening it, but there does not appear to be a way to "open" a directory.
The most straightforward way is just create temp file in that directory and lock it (open with FileShare.None):
var directory = #"G:\tmp\so\locked";
var fileHandle = new FileStream(
Path.Combine(directory, ".lock"),
FileMode.Create,
FileAccess.ReadWrite,
FileShare.None);
try {
// now, you cannot delete or rename that folder until you press a key
Console.ReadKey();
}
finally {
fileHandle.Dispose();
}
If that for whatever reason does not satisfy you - you can obtain handle to directory itself. I'm not aware of a way to do that with pure .NET, but you can do that with native winapi (CreateFile, but despite its name, it doesn't create file or directory, just obtains handle):
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
private static extern SafeFileHandle CreateFile(string lpFileName, FileAccess dwDesiredAccess, FileShare dwShareMode, IntPtr securityAttrs, FileMode dwCreationDisposition, int dwFlagsAndAttributes, IntPtr hTemplateFile);
var directory = #"G:\tmp\so\locked";
var directoryHandle = CreateFile(
directory,
FileAccess.ReadWrite,
FileShare.Read,
IntPtr.Zero,
FileMode.Open,
0x02000000, // << this flag is needed to obtain handle to directory
IntPtr.Zero);
if (directoryHandle.IsInvalid)
throw new Exception("Failed to obtain handle to directory");
try {
Console.ReadKey();
}
finally {
directoryHandle.Dispose();
}
It will have the same effect (directory cannot be deleted or renamed until you release the handle), but without additional files.
There is a DirectorySecurity class in System.Security.AccessControl
Example taken straight from the documentation :
AddDirectorySecurity(DirectoryName, #"MYDOMAIN\MyAccount", FileSystemRights.ReadData, AccessControlType.Allow);
public static void AddDirectorySecurity(string FileName, string Account, FileSystemRights Rights, AccessControlType ControlType)
{
// Create a new DirectoryInfo object.
DirectoryInfo dInfo = new DirectoryInfo(FileName);
// Get a DirectorySecurity object that represents the
// current security settings.
DirectorySecurity dSecurity = dInfo.GetAccessControl();
// Add the FileSystemAccessRule to the security settings.
dSecurity.AddAccessRule(new FileSystemAccessRule(Account, Rights, ControlType));
// Set the new access settings.
dInfo.SetAccessControl(dSecurity);
}
I'm trying to get the icon out of a .lnk file without the .lnk overlay appearing. The documentation has information about a flag SHGFI_ADDOVERLAYS which can be set to add an overlay, but I am looking to remove the overlay.
I have read this question, as well as the links inside of it, but I am still unable to get it working in c#.
Here is the code I have tried:
SHFILEINFO shFileInfo = new SHFILEINFO();
SHGetFileInfo(
pathToLnk,
0,
ref shFileInfo,
(uint)Marshal.SizeOf(shFileInfo),
SHGFI_ICON & ~SHGFI_LINKOVERLAY);
As well as some other configurations of the flags.
Thanks in advance for the help.
The solution is as follows:
[DllImport("Comctl32.dll")]
public static extern IntPtr ImageList_GetIcon(IntPtr himl, int i, uint flags);
SHFILEINFO fileInfo = new SHFILEINFO();
IntPtr list = SHGetFileInfo(
pathToLnk,
FileAttributes,
ref fileInfo,
(uint)Marshal.SizeOf(fileInfo),
SHGFI_SYSICONINDEX);
var iconHandle = ImageList_GetIcon(list, fileInfo.iIcon.ToInt32(), FileFlags);
Icon icn = Icon.FromHandle(iconHandle);
I need to create files with path exceeding the MAX_PATH limit. What I expected to work is to shorten the already existing directory name like this:
public static String GetShortPathName(String longPath)
{
StringBuilder shortPath = new StringBuilder(longPath.Length + 1);
if (0 == GetShortPathName(longPath, shortPath, shortPath.Capacity))
{
throw new Exception("Path shortenning failed");
}
return shortPath.ToString();
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern Int32 GetShortPathName(String path, StringBuilder shortPath, Int32 shortPathLength);
and then use the result to create the new file like:
using (FileStream writeStream = File.Create(shortPath + newFile))
...
But it still throws PathTooLongException like before. Any idea why?
I know I can use Delimon Lib or other lib, but I just cannot figure out why it does not work.
Thanks a lot.
I need to checksum every single file on a given USB disk in a C# application. I suspect the bottleneck here is the actual read off the disk so I'm looking to make this as fast as possible.
I suspect this would be much quicker if I could read the files on the disk sequentially, in the actual order they appear on the disk (assuming the drive is not fragmented).
How can I find this information for each file from it's standard path? i.e. given a file at "F:\MyFile.txt", how can I find the start location of this file on the disk?
I'm running a C# application in Windows.
Now... I don't really know if it will be useful for you:
[StructLayout(LayoutKind.Sequential)]
public struct StartingVcnInputBuffer
{
public long StartingVcn;
}
public static readonly int StartingVcnInputBufferSizeOf = Marshal.SizeOf(typeof(StartingVcnInputBuffer));
[StructLayout(LayoutKind.Sequential)]
public struct RetrievalPointersBuffer
{
public uint ExtentCount;
public long StartingVcn;
public long NextVcn;
public long Lcn;
}
public static readonly int RetrievalPointersBufferSizeOf = Marshal.SizeOf(typeof(RetrievalPointersBuffer));
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern SafeFileHandle CreateFileW(
[MarshalAs(UnmanagedType.LPWStr)] string filename,
[MarshalAs(UnmanagedType.U4)] FileAccess access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes,
IntPtr templateFile);
[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)]
static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
ref StartingVcnInputBuffer lpInBuffer, int nInBufferSize,
out RetrievalPointersBuffer lpOutBuffer, int nOutBufferSize,
out int lpBytesReturned, IntPtr lpOverlapped);
// Returns a FileStream that can only Read
public static void GetStartLogicalClusterNumber(string fileName, out FileStream file, out long startLogicalClusterNumber)
{
SafeFileHandle handle = CreateFileW(fileName, FileAccess.Read | (FileAccess)0x80 /* FILE_READ_ATTRIBUTES */, FileShare.Read, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);
if (handle.IsInvalid)
{
throw new Win32Exception();
}
file = new FileStream(handle, FileAccess.Read);
var svib = new StartingVcnInputBuffer();
int error;
RetrievalPointersBuffer rpb;
int bytesReturned;
DeviceIoControl(handle.DangerousGetHandle(), (uint)589939 /* FSCTL_GET_RETRIEVAL_POINTERS */, ref svib, StartingVcnInputBufferSizeOf, out rpb, RetrievalPointersBufferSizeOf, out bytesReturned, IntPtr.Zero);
error = Marshal.GetLastWin32Error();
switch (error)
{
case 38: /* ERROR_HANDLE_EOF */
startLogicalClusterNumber = -1; // empty file. Choose how to handle
break;
case 0: /* NO:ERROR */
case 234: /* ERROR_MORE_DATA */
startLogicalClusterNumber = rpb.Lcn;
break;
default:
throw new Win32Exception();
}
}
Note that the method will return a FileStream that you can keep open and use to read the file, or you can easily modify it to not return it (and not create it) and then reopen the file when you want to hash it.
To use:
string[] fileNames = Directory.GetFiles(#"D:\");
foreach (string fileName in fileNames)
{
try
{
long startLogicalClusterNumber;
FileStream file;
GetStartLogicalClusterNumber(fileName, out file, out startLogicalClusterNumber);
}
catch (Exception e)
{
Console.WriteLine("Skipping: {0} for {1}", fileName, e.Message);
}
}
I'm using the API described here: https://web.archive.org/web/20160130161216/http://www.wd-3.com/archive/luserland.htm . The program is much easier because you only need the initial Logical Cluster Number (the first version of the code could extract all the LCN extents, but it would be useless, because you have to hash a file from first to last byte). Note that empty files (files with length 0) don't have any cluster allocated. The function returns -1 for the cluster (ERROR_HANDLE_EOF). You can choose how to handle it.
If your drives are SSD or based on memory stick technology - forget it.
Memory sticks and other similar devices are generally based on SSD (or similar) technology, where the problem of random read/write access is actually not a problem. So you can just enumerate files and run your checksum.
You can try running this in several threads, but I am not sure that could speed up the process, it's something you may need to test. It may also vary from device to device.
Bonus
#xanatos mentioned an interesting point: "I always noticed that copying thousand of files on a memory stick is much slower than copying a single big file"
It is indeed much faster to copy one big file, rather than a pile of small files. And the reason is (usually) not because the files are located close to each other, so it's easier for hardware to read them sequentially. The problem comes to the OS that needs to keep tracking of each file.
If you ever run a procmon on Windows, you would observe huge amount of FileCreates, FileReads and FileWrites. In order to copy 100 files, OS would open each file, read its content, write to another file, close both files + lots of update operations that are sent to the file system, such as update attributes for both files, update security descriptors for both files, update directory information etc. So one copy operation has many satellite operations.
IM using the TWAIN Scanner & gdiplus.dll.
i scanned the file , and can save as image format like *.jpg, *.bmp
but it is not allow to save as in PDF format. Showing an error unknown format picture.
this is the code,
public static bool SaveDIBAs(string picname, IntPtr bminfo, IntPtr pixdat)
{
SaveFileDialog sd = new SaveFileDialog();
sd.FileName = picname;
sd.Title = "Save bitmap as...";
sd.Filter = "PDF (*.pdf)|*.pdf";
sd.Filter = "Bitmap file (*.bmp)|*.bmp|TIFF file (*.tif)|*.tif|JPEG file (*.jpg)|*.jpg|PNG file (*.png)|*.png|PDF file (*.pdf)|*.pdf|All files (*.*)|*.*";
sd.FilterIndex = 1;
if (sd.ShowDialog() == DialogResult.OK)
return false;
Guid clsid;
if (!GetCodecClsid(sd.FileName, out clsid))
{
//MessageBox.Show("Unknown picture format for extension " + Path.GetExtension(sd.FileName),
"Image Codec", MessageBoxButtons.OK, MessageBoxIcon.Information);
return false;
}
IntPtr img = IntPtr.Zero;
int st = GdipCreateBitmapFromGdiDib( bminfo, pixdat, ref img );
if( (st != 0) || (img == IntPtr.Zero) )
return false;
st = GdipSaveImageToFile(img, sd.FileName, ref clsid, IntPtr.Zero);
GdipDisposeImage(img);
return st == 0;
}
[DllImport("gdiplus.dll", ExactSpelling=true)]
internal static extern int GdipCreateBitmapFromGdiDib( IntPtr bminfo, IntPtr pixdat, ref IntPtr image );
[DllImport("gdiplus.dll", ExactSpelling=true, CharSet=CharSet.Unicode)]
internal static extern int GdipSaveImageToFile( IntPtr image, string filename, [In] ref Guid clsid, IntPtr encparams );
[DllImport("gdiplus.dll", ExactSpelling=true)]
internal static extern int GdipDisposeImage( IntPtr image );
}
****The above code doesnt allow to save as in PDF format.****
first you'd need to acquire the image using either TWAIN or WIA and then once you've captured that image you need to convert it to PDF using something like abcPDF
Not entirely sure what you are talking about, but this library: http://sourceforge.net/projects/pdflibrary/ is excellent for saving a PDF.
For scanning (from a TWAIN Scanner?) check out http://www.codeproject.com/KB/dotnet/twaindotnet.aspx Ive done that before, and it seems to work pretty well.
It will not save as PDF at all, because GDI library of Microsoft doesnt have PDF facility, the best way to do is save your file as JPEG first in temporary file. And then use iTextSharp library or PDFSharp library to create PDF out of JPEG, you can embed your JPG/Bitmap any sort of file inside PDF using these two libraries.
if you're using GDI. Print directly to a PDF printer (I use bullzip pdf myself because it's free and has a silent print feature)