Sometimes you want to include a file with your ClickOnce application and then update it using input from your customer. Or about your customer. Or about what your customer is doing with your application. (“You say you didn’t hit the delete button, but according to the log file, you hit it exactly 47 minutes ago.”)
To include the file in your deployment, add it to your project, and set the properties correctly. “Build Action” should be set to “Content”, and “Copy to Output Directory” to “Copy Always”.
When you deploy a file with a ClickOnce application, it is kept with the installed files. When you deploy a new version of the same application, it creates a new folder for the new version, and installs the files there, and you lose access to the original file, which defeats the whole purpose of saving the user’s changes to it. So if you don’t want to lose the file when there is an update, you need to move it out of the deployment folder.
If you are running Windows Vista or Windows 7, there are very few places to which you can write data on the user’s computer (Cancel or Allow?). You could put it in MyDocuments, but the user is likely to stumble over it and hurt his toe and sue you, or he may delete it because he doesn’t know what it is. So to store data used by your application, the recommended location is Local Application Data.
Create a folder in LocalApplicationData using your company name or product name. You could use another company’s name (like, say, ‘Microsoft’), but this might lead to unpleasant surprises down the road. You could also call it George, but you’ll forget that and then spend an hour looking through all the folders trying to find your file.
The first time the user runs the application, you want to create your folder and copy the file over there. When the user runs it the next time, you don’t want to copy the file over there. If you did that, it would be exactly what ClickOnce does, and would defeat the purpose of this entire blog post.
So the most effective thing to do is check for the directory. If it’s not there, create it. Then check for the file in the directory, and if the file’s not there, copy it over. You could assume if the directory is there, the file will be, but if your application mysteriously crashes (like the user’s laptop gets run over by a truck at that exact moment of execution), then the file could be missing. It only costs you one line of code to protect yourself from an embarrassing execution error.
I’ve tried to make this narrow enough to actually fit in my blog. Ordinarily, I wouldn’t use all the extra variables. I’m posting this in both C# and VB because I’m a C# developer trapped in a VB developer’s body.
Here’s the code in C#:
string localAppData = Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData); string userFilePath = Path.Combine(localAppData, "MyCompany"); if (!Directory.Exists(userFilePath)) Directory.CreateDirectory(userFilePath); //if it's not already there, //copy the file from the deployment location to the folder string sourceFilePath = Path.Combine( System.Windows.Forms.Application.StartupPath, "MyFile.txt"); string destFilePath = Path.Combine(userFilePath, "MyFile.txt"); if (!File.Exists(destFilePath)) File.Copy(sourceFilePath, destFilePath);
Here’s the code in VB:
Dim localAppData as String = _ Environment.GetFolderPath( _ Environment.SpecialFolder.LocalApplicationData) Dim userFilePath as String = _ Path.Combine(localAppData, "MyCompany") If (Not Directory.Exists(userFilePath)) Then Directory.CreateDirectory(userFilePath) End If 'if it's not already there, 'copy the file from the deployment location to the folder Dim sourceFilePath as String = Path.Combine( _ System.Windows.Forms.Application.StartupPath, "MyFile.txt") Dim destFilePath as String = _ Path.Combine(userFilePath, "MyFile.txt") If (Not File.Exists(destFilePath)) Then File.Copy(sourceFilePath, destFilePath) End If
One thing to note is that when the user uninstalls the application, the data will not be removed, it will remain there forever, or until the user’s laptop gets run over by a truck, and then technically it’s still there even though he may no longer be able to access it. If the user donates his laptop to one of those electronics recycling centers, and instead of wiping the hard drive like they claim, they actually look at the files on it, they will see your file. (You might not want to store the user’s social security number in the file w/o encrypting it.)
On the other hand, if the user ends up having to uninstall and reinstall the application because you accidentally changed the target CPU setting in the deployment properties and didn’t realize it would change the deployment identity and everybody in the company plus a bunch of customers would call you and ask why their upgrade didn’t work (don’t ask), his cached data is still available (small comfort, but better than nothing).
You could actually use this method to handle deploying a SQLServer Compact Edition database or an Access database. Or you could copy Hamlet over to the folder and have the computer read it aloud to your customer. (Everything I learned about Hamlet, I learned from that episode of Gilligan’s Island where they did Hamlet to the music from Bizet’s Carmen. “Neither a borrower not a lender be…” “I ask to be, or not to be, and that is the question that I ask of thee…”)