Clarification on Build Action - c#

I have a project that uses an Access DB file for reference tables. This is to be used at work, but I am developing it at home. Up until now, I've simply run the debugger in VS2010, then copied the relevant class files, exe, etc from the /bin folder to a flash drive, and it's worked fine. But with the DB added in, it suddenly crashes on launch.
I know the problem is the file location of the DB file. Originally the Build Action of the DB was sent to Content. I have changed it to Embedded Resource, which as far as I understand means it will be part of the exe file now.
Am I correct in this? If not, what option do I need to select to have the DB become just a compiled part of the exe, or one of the other dll's?

If the db file is embedded, you can't access it to add/removes rows etc.
Why did you change the build action to Embedded Resource ? It'll be better to put as Content, so the db is a separate file than the exe (but still in the same directory), and then build the path to the db file (i.e. using Application.StartupPath).
Anyway, if you want to set it as Embedded you'll need to extract the db at runtime and store it somewhere before using it.
Here is a method that can extract a file from the embedded resources (of course you'll need to change the filename, or pass it as argument):
private void ExtractFromAssembly()
{
string strPath = Application.LocalUserAppDataPath + "\\MyFile.db";
if (File.Exists(strPath)) return; // already exist, don't overwrite
Assembly assembly = Assembly.GetExecutingAssembly();
//In the next line you should provide NameSpace.FileName.Extension that you have embedded
var input = assembly.GetManifestResourceStream("MyFile.db");
var output = File.Open(strPath, FileMode.CreateNew);
CopyStream(input, output);
input.Dispose();
output.Dispose();
System.Diagnostics.Process.Start(strPath);
}
private void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[32768];
while (true)
{
int read = input.Read(buffer, 0, buffer.Length);
if (read <= 0)
return;
output.Write(buffer, 0, read);
}
}
The file will be copied in the local application path, in the user directory. It'll be done the first time the app is started, because otherwise the db file will be overwritten each time the application start (overwritten with the clean db package in the exe)

Related

File.WriteAllText not flushing data to disk

I've had 3 reports now of user's machines crashing while using my software.. the crashes are not related to my program but when they restart the config files my program writes are all corrupt.
There is nothing special to how the files are being written, simply creating a Json representation and dumping it to disk using File.WriteAllText()
// save our contents to the disk
string json = JsonConvert.SerializeObject(objectInfo, Formatting.Indented);
// write the contents
File.WriteAllText(path, json);
I've had a user send me one of the files and the length looks about right (~3kb) but the contents are all 0x00.
According to the post below File.WriteAllText should close the file handle, flushing any unwritten contents to the disk:
In my C# code does the computer wait until output is complete before moving on?
BUT, as pointed out by Alberto in the comments:
System.IO.File.WriteAllText when completes, will flush all the text to
the filesystem cache, then, it will be lazily written to the drive.
So I presume what is happening here is that the file is being cleared and initialized with 0x00 but the data is not yet written when the system crashes.
I was thinking of maybe using some sort of temp file so the process would be like this:
Write new contents to temp file
Delete original file
Rename temp file to original
I don't think that will solve the problem as I presume Windows will just move the file even though the IO is still pending.
Is there any way I can force the machine to dump that data to disk instead of it deciding when to do it or perhaps a better way to update a file?
UPDATE:
Based on suggestions by #usr, #mikez and #llya luzyanin I've created a new WriteAllText function that performs the write using the following logic:
Create a temp file with the new contents using the FileOptions.WriteThrough flag
Writes the data to disk (won't return until the write has completed)
File.Replace to copy the contents of the new temp file to the real file, making a backup
With that logic, if the final file fails to load, my code an check for a backup file and load that instead
Here is the code:
public static void WriteAllTextWithBackup(string path, string contents)
{
// generate a temp filename
var tempPath = Path.GetTempFileName();
// create the backup name
var backup = path + ".backup";
// delete any existing backups
if (File.Exists(backup))
File.Delete(backup);
// get the bytes
var data = Encoding.UTF8.GetBytes(contents);
// write the data to a temp file
using (var tempFile = File.Create(tempPath, 4096, FileOptions.WriteThrough))
tempFile.Write(data, 0, data.Length);
// replace the contents
File.Replace(tempPath, path, backup);
}
You can use FileStream.Flush to force the data to disk. Write to a temp file and use File.Replace to atomically replace the target file.
I believe this is guaranteed to work. File systems give weak guarantees. These guarantees are hardly ever documented and they are complex.
Alternatively, you can use Transactional NTFS if available. It is available for .NET.
FileOptions.WriteThrough can replace Flush but you still need the temp file if your data can exceed a single cluster in size.

How to open or run unknown file converted into byte[]

I use to store document/file in byte[] in database, and I want user can view/run that file from my application.
You need to know the file extension for the file you're writing, so the OS can run the default program based on the extension. The code would be something like this:
byte[] bytes = GetYourBytesFromDataBase();
string extension = GetYourFileExtension(); //.doc for example
string path = Path.GetTempFileName() + extension;
try
{
using(BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)))
{
writer.Write(yourBytes);
}
// open it with default application based in the
// file extension
Process p = System.Diagnostics.Process.Start(path);
p.Wait();
}
finally
{
//clean the tmp file
File.Delete(path);
}
You will need to store the file extension in the database too. If you don't have the file extension the problem becomes very difficult as you cannot rely on the operating system to work out which program to launch to handle the file.
You can use the following pattern:
Load data from database and save to file using the original file extension.
Start a new System.Diagnostics.Process that points to the saved file path.
As you have saved the file with the original file extension, the OS will look for a program that is registered for the extension to open the file.
As chibacity and Daniel suggest, storing the file extension in the db, and agreed -- storing the file extension, or at least some indicator that tells you the file type, is a good idea.
If these files are of a format of your own creation then you might also want to store information about which version of the file format the data is stored in. During development file formats are prone to changing, and if you don't remember which version you used to store the data then you have a hard job recovering the information.
The same problems are faced in object persistence generally.

Embed a Word Document in C#

I want to open a MS Word document from my program. At the moment, it can find it when in designer mode but when i publish my program it can't find the file. I believe I need to embed it into my program but I don't know how to do this. This is my current code to open the document:
System.Diagnostics.Process.Start("Manual.docx");
I think the Word document needs to be embedded into the resources of the .exe but i don't know how to to do this.
Can anyone help with some suggestions?
Aaron is pretty right on adding an embedded resource. Do the following to access an embedded resource:
Assembly thisAssembly;
thisAssembly = Assembly.GetExecutingAssembly();
Stream someStream;
someStream = thisAssembly.GetManifestResourceStream("Namespace.Resources.FilenameWithExt");
More info here:
How to embed and access resources by using Visual C#
Edit: Now to actually run the file you will need to copy the file in some temp dir. You can use the following function to save the stream.
public void SaveStreamToFile(string fileFullPath, Stream stream)
{
if (stream.Length == 0) return;
// Create a FileStream object to write a stream to a file
using (FileStream fileStream = System.IO.File.Create(fileFullPath, (int)stream.Length))
{
// Fill the bytes[] array with the stream data
byte[] bytesInStream = new byte[stream.Length];
stream.Read(bytesInStream, 0, (int)bytesInStream.Length);
// Use FileStream object to write to the specified file
fileStream.Write(bytesInStream, 0, bytesInStream.Length);
}
}
Right click the folder where you want to store the file within the Solution and choose Add -> Existing Item.
Once you add the file you can change the Build Action of the file within your project to be an Embedded Resource, versus a Resource. This can be done by going to the Properties within VS of the file and modifying the Build Action property.
Just include it to your project (add existing item) and from the menu that opens, select all files and select your word document. Also Copy the document into your Bin/Debug folder. If you are using an installer, include the document in the installer and it should work.

Set permissions on folder used by winform application

My winform application periodically pulls a flash file from an SQL database, writes it to a temporary folder, displays it in a webbroswer control, and then deletes it from the temporary folder. My application creates the temporary folder if it does not exist. The temporary folder is located in 'System.Environment.CurrentDirectory'.
My problem is the permissions of the temporary folder frequently become read only and then my application cannot delete the file. Sometimes the problem occurs immediately, and sometimes I can run the application several times before it occurs.
How do I insure that the file is deleted?
I added code to delete the temporary folder and then re-create it each time it writes to it, but this did not resolve my problem.
Only my application needs to access this folder, and the folder only holds these flash images.
I thought about using the generic 'temp' folder, but read somewhere that that could lead to problems.
Also, I got the same problem when I located the temporary folder at 'D:\'.
I'm using VS2008 on Windows XP. The application is to run on XP, Vista and 7.
Here is code.
DataSet dsFlashQuizRandom = new DataSet();
dsFlashQuizRandom = objUserDAO.GetFlashQuizRandom(intAge);
if (dsFlashQuizRandom.Tables[0].Rows[0]["large_image_blob"] != null && dsFlashQuizRandom.Tables[0].Rows[0]["file_name"].ToString().Trim() != string.Empty)
{
byte[] b = (byte[])dsFlashQuizRandom.Tables[0].Rows[0]["large_image_blob"];
if (b != null)
{
string flashFileName = dsFlashQuizRandom.Tables[0].Rows[0]["file_name"].ToString().Trim();
string targetPath = System.Environment.CurrentDirectory.ToString() + #"\images\";
string strFileName = targetPath + flashFileName;
//Delete the current version of the folder (if it exists); then create a new version of it.
if (System.IO.Directory.Exists(targetPath))
System.IO.Directory.Delete(targetPath, true);
if (!System.IO.Directory.Exists(targetPath))
System.IO.Directory.CreateDirectory(targetPath);
//Write the file to a FileStream, assign that stream to the webbrowser control.
FileStream fs = new FileStream(strFileName, FileMode.CreateNew, FileAccess.Write);
fs.Write(b, 0, b.Length);
fs.Close();
webBrowserQuizFlash.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(webBrowserQuizFlash_DocumentCompleted);
webBrowserQuizFlash.Url = new System.Uri(strFileName, System.UriKind.Absolute);
}
}
//Delete the Flash Webbrowser file once it has completed loading.
private void webBrowserQuizFlash_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
FileInfo fi = new FileInfo(strFileName);
try
{
fi.Delete();
}
catch (IOException ex)
{
MessageBox.Show("IOException = " + ex); //test code
}
}
Any suggestions or a point in the right direction would be appreciated.
Cheers,
Frederick
PS--When copying my code to this post I see the color of the text is all red after the #"\images\"; Is there a problem with this part of my code, or is this a display artifact? Should I use this instead: #"\images\\";
You could use System.IO.Path.GetTempPath() to get a temp folder to use.
I assume that you're having problems with the delete due to the file being locked by some process. You might be able to get around that by using the MoveFileEx function to delete it at next reboot.
I think the accessing problem comes from another application that locks the file. One common application group that does such things would be the on access scanner from your anti-virus program.
To get a deeper look into, who accesses your file you should take a deeper look with Process Monitor to find out who will block your file.
Also you can maybe make a little change to your code:
//Write the file to a FileStream, assign that stream to the webbrowser control.
using(FileStream fs = new FileStream(strFileName, FileMode.CreateNew, FileAccess.Write))
{
fs.Write(b, 0, b.Length);
}

File.Copy and WPF

I have a little problem with the File.Copy method in WPF, my code is very simple and I get an exception when I run it,
Could not find a part of the path 'Images\37c31987-52ee-4804-8601-a7b9b4d439fd.png'.
where Images is a relative folder.
Here is my code, as I said simple and the same code works fine in a console application, no problem at all.
string filenamae = System.IO.Path.Combine(images, Guid.NewGuid().ToString() + System.IO.Path.GetExtension(imageFile)); ;
System.IO.File.Copy(imageFile, filenamae);
this.ImageLocation = string.Empty;
So if any can help, thanks.
Does the images folder exist? File.Copy doesn't create it automatically.
Do you know what your current directory is? File open/save boxes can change that. So it's always safer to work with absolute paths.
Do a
Path.GetFullPath(filename)
and see where that points to. Is it the right location?
If you use the absolute instead of the relative path, does it work then?
Before you access a file, you should call System.IO.File.Exists(). It's not clear from your error description if the origin file exists or not before the copy.
If you don't specify an absolute path, your relative path with often be resolved from unexpected places, usually the current working directory of the process. Calling this method may tell you were the process is currently running:
System.IO.Directory.GetCurrentDirectory()
You should never make assumptions about the current working directory of a running process as the user could start your program from anywhere. Even if you think you always control the current working directory, you will be surprised how often you will be wrong.
Do you have a debugger? Why not insert a breakpoint and check the values used at each step?
If the file system says "cannot find file", I wouldn't bother arguing with it...
use \\ for the file path directory if it in local.. if your file exists in network path use \\\\(atfirst).. So that it look for network drive..
Thanks
It is necessary to embed all external files into the executable and change your code to work with these embedded files rather than to expect files on the disk.
To use images or whatever you need files("xml/txt/doc"), you need to set the build action of your file to Embedded Resource, and call the method with the fully qualified name of the file, where the name is assembled like this:
[RootNameSpaceOfTheProject].[NameOfFolderInTheProject].[FileNameWithExtension]
Example:
Call the method:
var b = ResourceOperations.GetResourceAsByteArray("Store.Resources.EmbeddedIcons.toolbox.png");
Now you can write the byte array to a temporary file for example and use this as an image source, or you can build an image from the byte array directly. At least, you've got your data...
and to save this files to a disk we should write a code by #Jon Skeet :
public static void CopyStream(Stream input, Stream output)
{
// Insert null checking here for production
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, bytesRead);
}
}
then call it:
using (Stream input = assembly.GetManifestResourceStream(resourceName))
using (Stream output = File.Create(path))
{
CopyStream(input, output);
}

Categories

Resources