How to Determine If a Word Document Is Read-Only? - c#

I use Word.Interop to work with Word Document and let user to open a file from hard disk.
Sometimes I get error saying that the file that user has chosen is readonly.
How can I check if a file is readonly or not?

Are you sure you are actually talking about the File attribute (that can be set via the Windows file properties dialog)? If so, you can use FileInfo.IsReadOnly:
FileInfo fileInfo = new FileInfo(#"path\to\file");
if (fileInfo.IsReadOnly)
{
// do something
}
otherwise, refer to this answer if another process is using the file.

Related

C# OpenFileDialog multiple filename filters including exclude

I have a requirement to allow users to open a specific file for processing. The open file dialog is currently
OpenFileDialog ofg = new OpenFileDialog
{
FileName = "BaseFileName*",
Filter = "CSV File (*.CSV)|*.csv",
Multiselect = false,
InitialDirectory = #"N:\Downloads"
};
However the process adds a suffix of _Processed along with timestamp data to the filename and I want to exclude these renamed files the next time the OpenFileDialog is used to prevent the user trying to reprocess the same file.
I have to leave the original files where they are for internal audit reasons.
So I need an additional filename filter of not equal to "_Processed".
Is there any way to do this with OpenFileDialog or does anyone know of a custom c#/.net component that can do this?
You are asking to omit specific items from the file dialog view.
According to MSDN, this is no longer possible as of Windows 7, but was possible previously.
The C# file dialogs (both WPF and WinForms) use the IFileDialog API.
Here is the function that could have made this work, but is no longer supported:
https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifiledialog-setfilter
As it is, you are stuck with checking the file for correctness after the user has already selected it and confirmed it with OK.
You can help the situation a little bit: If you enjoy pain, then you can copy the whole IFileDialog COM interop code from the .NET source code, and implement IFileDialogEvents. This way, when the user clicks "OK", you can deny the selection and display an error before the dialog closes, leaving the dialog open so the user can select a different file.
If you are sane and you don't want to do that, then you'll have to open the dialog again after the verification fails.
The easy way is just saving the processed data with another extension e.g. "BaseFileName_Processed_20105640640.cvs1", that way you keep the data and your file dialog will not show this file.
Another way could be to call the OpenFileDialog() in an if statement (and compare the return to DialogResult.OK), then split the file name for {'_','.'}, then run a loop to count the occurrences of the word Processed( >0), and possibly as a safety check determine whether a timestamp is present in one of the split strings. Finally, reload the FileOpenDialog in the same folder when the wrong file was selected.

Store metadata outside of file: Any standard approach on modern Windows?

My C# app syncs files from a remote document management system to a filesystem.
The document management system has metadata (date of last audit, secrecy, author...) which is associated with each file but not stored WITHIN each file.
The files can be anything (bmp, xwd, pdf, unknown binary)
I want to make these metadata visible on the local Windows filesystem.
But I can't store metadata WITHIN each file. For instance, changing the secrecy of a file must NOT modify the checksum of the file.
What is the best way to store this metadata?
I have heard about NTFS extended file attributes, is it something that applies to my scenario? This question about setting extended file properties has all answers talking about modifying the files themselves, which I must avoid.
If there is no standard solution, then I will store the metadata in a local SQLite database. But I would really prefer to use a standard solution so that other apps (explorer, gallery apps, etc) can display/modify the properties they understand (like "author")
Alternate data streams is one of NTFS' less-known features. Quote from the page:
C:\test>echo "ADS" > test.txt:hidden.txt
C:\test>dir
Volume in drive C has no label.
Volume Serial Number is B889-75DB
Directory of C:>test
10/22/2003 11:22 AM
. 10/22/2003 11:22 AM
.. 10/22/2003 11:22 AM 0 test.txt
C:\test> notepad test.txt:hidden.txt
This will open the file in notepad and allow you to edit it and save it.
It is similar to the Macintosh resource fork, i.e. it allows associating arbitrary data with files, without it being part of the file itself. Explorer doesn't understand it by default, but you can write a column handler for it.
EDIT
Some metadata (such as Author and Title) can be saved using OLE document properties. I don't know if it modifies the file itself or not, though:
private void button1_Click(object sender, EventArgs e)
{
//This is the PDF file we want to update.
string filename = #"c:\temp\MyFile.pdf";
//Create the OleDocumentProperties object.
DSOFile.OleDocumentProperties dso = new DSOFile.OleDocumentProperties();
//Open the file for writing if we can. If not we will get an exception.
dso.Open(filename, false,
DSOFile.dsoFileOpenOptions.dsoOptionOpenReadOnlyIfNoWriteAccess);
//Set the summary properties that you want.
dso.SummaryProperties.Title = "This is the Title";
dso.SummaryProperties.Subject = "This is the Subject";
dso.SummaryProperties.Company = "RTDev";
dso.SummaryProperties.Author = "Ron T.";
//Save the Summary information.
dso.Save();
//Close the file.
dso.Close(false);
}

Stoping the generation of duplicate of the output file

I have an application where I generate Report at the end of all the materials, by Hitting generate Report button. The report is generated in Excel format. The problem is that whenever I create one report, I can create another report with the same name on the same location. It basically overrides the first report.
I want to give the user a box saying that you can generate a report with the same name or the name already exists and choose a different name.
Thanks for the help!
Just before you save the file you should know what the Filename you are going to save it as. If so then just test if the File already exists. If it does then prompt the user for a new name and save it as the new name e.g.
string filename = #"C:\File.txt";
if(File.Exists(filename)){
// Prompt for new one.
// save the report to the new name instead.
}else
{
// save to filename
}
How about before saving a file, check if the file with this name already exists and if it does, offer to rename the file. Something like this:
if(File.Exists(proposedFileName)){
showDialog("file exists, please choose other name");
}
I always do what DarkXphenomenon suggested, I append a mildate timestamp to the filename of the form:
<filename>_YYMMDD_HHMMSS.ext
While this is't rught for every situation, it has a lot of advantages:
Its simple, and it works
It saves me from having to write in all kinds of gyrations for going back and forth with the user over the name, overwriting, renaming, canceling, etc. Usually deep in code that was never intended to have a user interface.
It makes automation much easier.
It makes testing easier.
It makes diagnosing user problems easier: there's no question over when a file was created or in what order they were created.
Before creating the report you can iterate through the existing files and check whether the name already exists and give proper error message.
string newFileName = "new file";
string[] fileNames = Directory.GetFiles("path");
foreach (string file in fileNames)
{
if (file == newFileName)
{
MessageBox.Show("Error");
break;
}
}

Write string to text file and ensure it always overwrites the existing content.

I have a string with a C# program that I want to write to a file and always overwrite the existing content. If the file isn't there, the program should create a new file instead of throwing an exception.
System.IO.File.WriteAllText (#"D:\path.txt", contents);
If the file exists, this overwrites it.
If the file does not exist, this creates it.
Please make sure you have appropriate privileges to write at the location, otherwise you will get an exception.
Use the File.WriteAllText method. It creates the file if it doesn't exist and overwrites it if it exists.
Generally, FileMode.Create is what you're looking for.
Use the file mode enum to change the File.Open behavior. This works for binary content as well as text.
Since FileMode.Open and FileMode.OpenOrCreate load the existing content to the file stream, if you want to replace the file completely you need to first clear the existing content, if any, before writing to the stream. FileMode.Truncate performs this step automatically
// OriginalFile:
oooooooooooooooooooooooooooooo
// NewFile:
----------------
// Write to file stream with FileMode.Open:
----------------oooooooooooooo
var exists = File.Exists(path);
var fileMode = exists
? FileMode.Truncate // overwrites all of the content of an existing file
: FileMode.CreateNew // creates a new file
using (var destinationStream = File.Open(path, fileMode)
{
await newContentStream.CopyToAsync(destinationStream);
}
FileMode Enum
If your code doesn't require the file to be truncated first, you can use the FileMode.OpenOrCreate to open the filestream, which will create the file if it doesn't exist or open it if it does. You can use the stream to point at the front and start overwriting the existing file?
I'm assuming your using a streams here, there are other ways to write a file.

Creating and saving a text file to the server

In C# ASP.Net, I would like to create and save a text file to a server. This will be happening daily (by a user action, not scheduled).
I would like the location to not be in the application path but in a separate folder (for this question, lets say the folder is off the root).
I am new to this site and if this question is too "open", feel free to let me know.
Thanks for your assistance.
I agree with Dan Herbert. Put the path in web.config and make sure the permissions for the folder are correct.
Also, make sure that path is not on the C drive. That way, if something goes wrong, or if the site is attacked, the c drive won't fill up and crash the server.
Be careful with the permissions; even a simple text file can be dangerous if a hacker can muck with the path somehow. Think about someone overwriting the server's hosts file, for example.
One good practice to follow is to use a web.config app setting key to define the output path of your application.
You'd use the ConfigurationManager.AppSettings class for retrieving values from a web.config. You can read how to do this here:
http://msdn.microsoft.com/en-us/library/system.configuration.configurationmanager.appsettings.aspx
Here is an example:
// Pass a path and filename to the StreamWriter's
// constructor. The path must already exist but the
// file will be created if it does not already exist.
using (TextWriter tw = new StreamWriter("c:\\foo\\bar.txt"))
{
tw.WriteLine("hello world");
}
You can use System.IO classes to save to a file on the local filesystem.
using(var w = new StreamWriter(filename))
{
w.WriteLine("Hello world!");
}
Just to add on what others have said, when writing to a file in a multi-threaded application, you need to synchronize the access to this resource. You could use ReaderWriterLockSlim to achieve this:
// Make this a static field
ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
_lock.EnterWriteLock();
try
{
File.WriteAllText(#"c:\test.txt", "some info to write");
}
finally
{
_lock.ExitWriteLock();
}
I don't see any specific issue in doing so. You just need to make sure the user running ASP.NET is granted the required permissions to write to the output folder.
You'll want to use Server.MapPath to get to the physical path of your folder.
using (TextWriter tw = new StreamWriter(Server.MapPath("Folder1")))
{
tw.WriteLine("hello world");
}

Categories

Resources