Set your FileAccess!

When I was working on the latest release of the BlogThisUsingPostXINGPlugin, I kept running into file access issues.

The whole thing was an excersize in refactoring - well, really, the whole thing WAS a refactor, since the basic functionality already existed, but I was making additions in how things were going to work.

First, I changed the XsltStream to return a FileStream or a ManifestResourceStream, based on the existence of a user-specific file:

  1 		Stream XsltStream
  2 		{
  3 			get
  4 			{
  5 				string filePath = Path.Combine(ConfigurationPath, this.BlogType.ToString() + ".xslt");
  6 				if(File.Exists(filePath)){
  7 					//read from a file stream.
  8 					FileStream fs = new FileStream(filePath, FileMode.Open);
  9 					return (Stream)fs;
 10 				}else{
 11 					string resourceName = "PostXING.Rss.BlogExtensions.Resources." + this.BlogType.ToString() + ".xslt";
 12 					return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
 13 				}
 14 			}
 15 		}

Again, this part was shamelessly lifted  modified from haacked who generously gave out the source code for his BlogThisUsingWbloggarPlugin. (the configurationPath property was changed a bit to enable more than one file to go into that particular path).

Then I added a button next to each option in the config dialog that says simply “Customize…”. Pressing this button opens a modal form that loads up the text from the user-defined file if it exists, else from the resource stream.:

  1 		string transformPath;
  2 
  3 		public CustomizeForm()
  4 		{
  5 			//
  6 			// Required for Windows Form Designer support
  7 			//
  8 			InitializeComponent();
  9 
 10 		}
 11 
 12 		public CustomizeForm(string TransformToCustomize) : this(){
 13 			transformPath = Path.Combine(BlogThisUsingPostXINGPlugin.ConfigurationPath, string.Format("{0}.xslt", TransformToCustomize));
 14 
 15 			this.LoadTransform(TransformToCustomize);
 16 		}	
 17 
 18 		private void LoadTransform(string TransformToCustomize){			
 19 			string resourceName = string.Format("PostXING.Rss.BlogExtensions.Resources.{0}.xslt", TransformToCustomize);
 20 			//Stream resourceStream;
 21 			StreamReader sr;
 22 
 23 			if(File.Exists(transformPath)){
 24 				sr = new StreamReader(transformPath);
 25 				this.rtbTransform.Text = sr.ReadToEnd();
 26 				sr.Close();
 27 				
 28 			}else{
 29 				sr = new StreamReader(ResourceStream(resourceName));
 30 				this.rtbTransform.Text = sr.ReadToEnd();
 31 				sr.Close();
 32 				
 33 			}
 34 		}
 35 
 36 		Stream ResourceStream(string resourceName){
 37 			return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
 38 		}

3 buttons were added to the form: Revert to Original, Apply, and Cancel. I didn’t really think that I needed DialogResults, since you have to invoke the plugin via its menu every time you want to use it. Revert to Original just deletes the user-defined file. Cancel simply closes the form and does nothing. Apply does this:

  1 		private void btnApply_Click(object sender, System.EventArgs e) {
  2 			if(File.Exists(transformPath)){
  3 				File.Delete(transformPath);
  4 			}
  5 
  6 			StreamWriter sw = new StreamWriter(transformPath, false); //doesn't release file handle?
  7 			sw.Write(this.rtbTransform.Text);
  8 			sw.Flush();
  9 			sw.Close();
 10 
 11 			this.Close();
 12 		}

Notice my little note to myself? It turned out that I tried using the using statement, using File.CreateText(transformPath) (which is why the pre-step File.Delete is in there) and I kept getting “Cannot access file ‘blah’ because it is in use by another process.” When I opened up sysinternal’s process explorer and searched for the file, it said that the file handle was opened by RssBandit. grr.

All I ended up having to do was to add one argument to File.Open from the XsltStream property getter:

  1 		Stream XsltStream
  2 		{
  3 			get
  4 			{
  5 				string filePath = Path.Combine(ConfigurationPath, this.BlogType.ToString() + ".xslt");
  6 				if(File.Exists(filePath)){
  7 					//read from a file stream.
  8 					FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
  9 					return (Stream)fs;
 10 				}else{
 11 					string resourceName = "PostXING.Rss.BlogExtensions.Resources." + this.BlogType.ToString() + ".xslt";
 12 					return Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
 13 				}
 14 			}
 15 		}

And bam! the problem went away. It looks like the default for a new FileStream (sans FileAccess attribute) is FileAccess.ReadWrite. Anyways, I hope this helps someone else out there from banging their head too much.

[ Currently Playing : Inertiatic ESP - Mars Volta - De-Loused In The Comatorium (4:23) ]