System.UnauthorizedAccessException on Clean or Build

Topics: Sandcastle
Jun 18, 2008 at 10:43 AM
I made a few changes to a DocProject today: updated a couple of MAML files, added a couple of new conceptual topics, and re-orderered the first two conceptual topics.

Now when I try to build or clean the project, I get the following error:

System.UnauthorizedAccessException was thrown while the project was being cleaned:

Access to the path '8adeb9ab-980b-4289-9b1f-91c3d52d4d4e.xml' is denied.

I tried deleting all the xml files in the Help/html folder (they are not under source control and are regenerated from reflection, aren't they), then rebooted. No luck.

Any ideas?

Jun 18, 2008 at 2:25 PM
Hi,

The files in Help\Html are generated automatically by Sandcastle during builds, but the files should all be .htm, not .xml.  You could try deleting the hidden buildhelp folder since that's most likely where the .xml file in question resides.

Try checking the Application event log for more information, such as a stack trace and the full path to the file.  For help with finding the log, see How To Diagnose and Resolve Issues.

Even after rebooting you're still getting the same exception?  Does the exception occur when you open the project and immediately try to build or is it possible that you're doing something in Topic Explorer first that might possibly cause the error?  For example, try opening the project and building without opening Topic Explorer first, just to see if it works.

Is the project under source control?

Are you using Vista?  In my experience with Vista I've had a similar problem before with UnauthorizedAccessException, although simply waiting for 30 seconds fixes the problem.  For some reason Vista will lock on to one or two files during a build (probably due to the search indexer not being able to handle so many new files being generated in a short period of time?) and the only way to get around the problem is to wait a few seconds for Vista to release the lock on the file before building again.

I can't reproduce UnauthorizedAccessException on demand though, especially not one that's durable even through a reboot, so if you can please try to figure out what steps you're taking that result in this error I'd appreciate it.

Thanks for the feedback, 
Dave
Jun 18, 2008 at 4:07 PM


davedev wrote:
Hi,

The files in Help\Html are generated automatically by Sandcastle during builds, but the files should all be .htm, not .xml.  You could try deleting the hidden buildhelp folder since that's most likely where the .xml file in question resides.

Try checking the Application event log for more information, such as a stack trace and the full path to the file.  For help with finding the log, see How To Diagnose and Resolve Issues.

Even after rebooting you're still getting the same exception?  Does the exception occur when you open the project and immediately try to build or is it possible that you're doing something in Topic Explorer first that might possibly cause the error?  For example, try opening the project and building without opening Topic Explorer first, just to see if it works.

Is the project under source control?

Are you using Vista?  In my experience with Vista I've had a similar problem before with UnauthorizedAccessException, although simply waiting for 30 seconds fixes the problem.  For some reason Vista will lock on to one or two files during a build (probably due to the search indexer not being able to handle so many new files being generated in a short period of time?) and the only way to get around the problem is to wait a few seconds for Vista to release the lock on the file before building again.

I can't reproduce UnauthorizedAccessException on demand though, especially not one that's durable even through a reboot, so if you can please try to figure out what steps you're taking that result in this error I'd appreciate it.

Thanks for the feedback, 
Dave


Thanks for your help, Dave.
I can confirm that there was a solitary XML file in the buildHelp folder and it had a read-only attribute set. Having cleared this atribute, I am now able to clean/build the documentation project correctly.
FYI, I am running Vista. The project is under source control, but the buildHelp folder does not show up in Source Safe so I don't know how that file got to be read-only.

When things were going wrong, it indeed failed following a reboot. I didn't have TopicExplorer open.

Here's something from the event log:

Event: Clean project.

System.UnauthorizedAccessException: Access to the path '8adeb9ab-980b-4289-9b1f-91c3d52d4d4e.xml' is denied.

at System.IO.Directory.DeleteHelper(String fullPath, String userPath, Boolean recursive)

at System.IO.Directory.Delete(String fullPath, String userPath, Boolean recursive)

at DaveSexton.DocProject.VSProjectItem.Delete()

at DaveSexton.DocProject.Engine.BuildEngine`2.CleanProjectDirectory(String path)

at DaveSexton.DocProject.Engine.BuildEngine`2.CleanProjectSubdirectory(String relativePath)

at DaveSexton.DocProject.Sandcastle.SandcastleBuildEngine.CleanInternal(BuildContext context)

at DaveSexton.DocProject.Engine.BuildEngine`2.Clean(BuildContext context)


Cheers
Mike

Jun 18, 2008 at 7:17 PM
Hi Mike,

Thanks for the info.

It makes sense to me now that rebooting didn't fix the problem.  Obviously the read-only flag was the issue.

I'll see if I can repro the problem and fix it for 1.12 Release.

- Dave
Jun 19, 2008 at 11:32 AM
NB, each time I do a build, several XML files are created in buildHelp/assembler/DdueXML with read-only attributes.

With my current project configuration three out of four files are created as read-only.
Jun 19, 2008 at 1:30 PM
Edited Jun 19, 2008 at 1:50 PM
Hi Mike,

I'll create a work item for turning off the read-only flag on files created in the DdueXML folder.

For now you can workaround this issue by adding some code to your project's Build Process Component.  Insert a dynamic build step, after the Create Conceptual Build Files step, that loops through each file in the DdueXML folder to change the read-only flags.  Here's an example in C#:

public override void BuildStarting(BuildContext context)
{
    // existing code goes here







base
.InsertAfterBuildStep("Create Conceptual Build Files", "Bug fix: Remove read-only flags in DdueXML", false /*runInBackground*/, false /*continueOnError*/, false /*includeInPartialBuild*/, delegate(BuildContext stepContext) { SystemPath files = stepContext.ProjectDirectory + @"\buildhelp\assembler\DdueXML\*.xml"; foreach (System.IO.FileInfo file in files.GetFileInfos()) file.Attributes &= ~System.IO.FileAttributes.ReadOnly; }); }
- Dave
Jun 19, 2008 at 2:53 PM


davedev wrote:
Hi Mike,

I'll create a work item for turning off the read-only flag on files created in the DdueXML folder.

For now you can workaround this issue by adding some code to your project's Build Process Component.  Insert a dynamic build step, after the Create Conceptual Build Files step, that loops through each file in the DdueXML folder to change the read-only flags.  Here's an example in C#:

public override void BuildStarting(BuildContext context)
{
    // existing code goes here







base
.InsertAfterBuildStep("Create Conceptual Build Files", "Bug fix: Remove read-only flags in DdueXML", false /*runInBackground*/, false /*continueOnError*/, false /*includeInPartialBuild*/, delegate(BuildContext stepContext) { SystemPath files = stepContext.ProjectDirectory + @"\buildhelp\assembler\DdueXML\*.xml"; foreach (System.IO.FileInfo file in files.GetFileInfos()) file.Attributes &= ~System.IO.FileAttributes.ReadOnly; }); }
- Dave


Ta. You might want to think about making the fix recursive on the buildHelp folder, cos I'm now getting similar problems with artwork items in the Output folder.

Mike
Jun 19, 2008 at 7:38 PM
Hi Mike,

Thanks for pointing that out.

The reason why the art files are copied to the working directory is that Sandcastle's ResolveArtLinksComponent always copies the files to some output folder.  Although, the art files that are copied to the working directory aren't used at all, so I had forgotten about them :)  It's a hack that I don't like but I had no choice aside from creating a custom version of the component, which I felt was overkill.

Note that files in the Comments folder are also copied with the read-only flag if it's set, but if you've already made my sample code act recursively you shouldn't have the problem with comment files either.

However, instead of adding a new build step to DocProject I'm just going to update the individual copy operations for each of the known folders with this issue to remove the read-only flag after each file is copied.  Aside from the Art folder there's only the operations that copy files from the Comments and Topics folders.

I'm going to submit a feature request to Sandcastle for the ResolveArtLinksComponent issue (whenever the Sandcastle project settles in one place) to either have the component remove the read-only flags itself or, preferably, to not require any output path at all so that the files in the Help\Art folder don't have to be copied to some unused location during builds.  This means that for the next release of DocProject, if the issue hasn't been fixed by then in the Sandcastle component) you may have to continue to use the workaround that I posted above to solve the problem with the Art files only.  An alternative workaround will be to perform a non-exclusive checkout of the Art folder before builds to make sure that the files are not read-only.

- Dave
Jun 19, 2008 at 7:39 PM
Here's the DocProject work item: 

http://www.codeplex.com/DocProject/WorkItem/View.aspx?WorkItemId=17102
Oct 20, 2010 at 8:53 PM
davedev wrote:
...
Note that files in the Comments folder are also copied with the read-only flag if it's set, but if you've already made my sample code act recursively you shouldn't have the problem with comment files either.

However, instead of adding a new build step to DocProject I'm just going to update the individual copy operations for each of the known folders with this issue to remove the read-only flag after each file is copied.  Aside from the Art folder there's only the operations that copy files from the Comments and Topics folders....

- Dave

David --

I have made that code-hack a recursive call (I think) and I am still getting errors like this...

Error 1 Access to the path 'C:\Code\Team\DocProject\Tapi\buildhelp\assembler\Comments\PerforceToolLibrary.xml' is denied.


...which really stinks.

Below is my code-hack, which does not seem to be working.

Any thoughts or hints or etc?

 

using System;
using System.Collections.Generic;
using System.Text;
using DaveSexton.DocProject;
using DaveSexton.DocProject.Engine;

namespace Tapi
{
	/// <summary>
	/// Hooks into the DocProject build process for the project in which it's defined.
	/// </summary>
	/// <remarks>
	/// <para>
	/// This class must be registered with the DocSite in the <em>Active Projects</em>
	/// tools options page in order for DocProject to instantiate it during a help build.
	/// </para>
	/// <para>
	/// To cancel the build at any time call the <see cref="BuildContext.Cancel" /> 
	/// method.  The build process will end after the current step is executed, 
	/// unless the step is being executed in the background.  In that case, it may 
	/// end immediately.
	/// </para>
	/// <para>
	/// Note: Do not cache instances of the <see cref="BuildContext" /> class.  A new 
	/// <see cref="BuildContext" /> is created each time the project is built.
	/// </para>
	/// </remarks>
	public class BuildProcess : BuildProcessComponent
	{
		DateTime buildStart, stepStart;

		/// <summary>
		/// Called before the project's help build starts.
		/// </summary>
		/// <param name="context">Provides information about the build process.</param>
		public override void BuildStarting(BuildContext context)
		{
			// Uncomment the following line to break into the debugger: 
			// System.Diagnostics.Debugger.Break();

			buildStart = DateTime.Now;


			base.InsertAfterBuildStep("Create Conceptual Build Files",
					"Bug fix: Remove read-only flags in DdueXML",
					false /*runInBackground*/, false /*continueOnError*/, false /*includeInPartialBuild*/,
					delegate(BuildContext stepContext)
					{
						//HACK.
						SystemPath files = stepContext.ProjectDirectory + @"\buildhelp\assembler\";

						//HACK.
						foreach (System.IO.FileInfo file in files.GetFileInfos())
						{
							file.Attributes &= ~System.IO.FileAttributes.ReadOnly;
						}

						//HACK.
						foreach (System.IO.DirectoryInfo myDirectoryTemp in files.GetDirectoryInfos())
						{
							RemoveReadOnlyFlag(myDirectoryTemp);
						}
					});

		}

		//HACK.
		public static void RemoveReadOnlyFlag(System.IO.DirectoryInfo targetDirectory)
		{
			foreach (System.IO.FileInfo file in targetDirectory.GetFiles())
			{
				file.Attributes &= ~System.IO.FileAttributes.ReadOnly;
			}

			foreach (System.IO.DirectoryInfo myDirectoryTemp in targetDirectory.GetDirectories())
			{
				//Recursion.
				RemoveReadOnlyFlag(myDirectoryTemp);
			}
		}

		/// <summary>
		/// Called before a <paramref name="step" /> is executed during a help build.
		/// </summary>
		/// <param name="step"><see cref="IBuildStep" /> implementation to be executed.</param>
		/// <param name="context">Provides information about the build process.</param>
		/// <returns><b>true</b> indicates that the process should continue, otherwise, 
		/// <b>false</b> indicates that the process should skip this step.</returns>
		public override bool BeforeExecuteStep(IBuildStep step, BuildContext context)
		{
			stepStart = DateTime.Now;

			return true;
		}

		/// <summary>
		/// Called after a <paramref name="step" /> has been executed during a help build.
		/// </summary>
		/// <param name="step"><see cref="IBuildStep" /> implementation that was executed.</param>
		/// <param name="context">Provides information about the build process.</param>
		public override void AfterExecuteStep(IBuildStep step, BuildContext context)
		{
			TraceLine();
			TraceLine("Step {0} Time Elapsed: {1}", context.CurrentStepIndex + 1, DateTime.Now - stepStart);
		}

		/// <summary>
		/// Called after the project's help build has finished.
		/// </summary>
		/// <remarks>
		/// The <see cref="BuildContext.Cancel" /> method has no affect at this 
		/// point in the build process.  This method is the final step before the 
		/// build statistics are displayed.
		/// <para>
		/// This method is always invoked if <see cref="BuildStarting" /> is invoked, 
		/// regardless of whether an exception is thrown in any of the other methods, 
		/// <see cref="BuildContext.Cancel" /> has been called, or an exeception has
		/// been thrown by the build engine.
		/// </para>
		/// <para>
		/// To determine whether a help build failed or succeeded, examine the value of the
		/// <see cref="BuildContext.BuildState" /> property.
		/// </para>
		/// </remarks>
		/// <param name="context">Provides information about the build process.</param>
		public override void BuildCompleted(BuildContext context)
		{
			TraceLine();
			TraceLine("Total Time Elapsed: {0}", DateTime.Now - buildStart);
		}
	}
}

 

 

 

 

 

Oct 21, 2010 at 2:28 PM
Edited Oct 21, 2010 at 2:28 PM

Hi,

Although it does seem like your recursive code is correct, the SystemPath class supports recursion itself, so perhaps try the following instead.  I've also added a call to TraceLine so that you can see in the output window which files were supposed to be modified, if any.

If you get the error again then I suggest checking the file on disc to see if the read-only flag is still set.  If so, you may want to attach another instance of VS to your first instance and put a break point in the custom build step to see if the files are in fact being modified.

And just to be sure, are you getting the same error at the same step of the build process as the OP?  (I.e., according to the event log, it states Event: Clean project?)

base.InsertAfterBuildStep("Create Conceptual Build Files", 
	"Bug fix: Remove read-only flags in DdueXML",
	false /*runInBackground*/, false /*continueOnError*/, false /*includeInPartialBuild*/,
	delegate(BuildContext stepContext)
	{
		SystemPath files = stepContext.ProjectDirectory + @"\buildhelp\assembler\**\*.*";

		foreach (System.IO.FileInfo file in files.GetFileInfos())
		{
			TraceLine(file);

			file.Attributes &= ~System.IO.FileAttributes.ReadOnly;
		}
	});

- Dave

Oct 28, 2010 at 8:22 PM

 

David --

I appreciate the follow-up.

Unfortunately, I was not able to make progress on DocProject any further; so, I switched to a plain-vanilla SHFB project with a command-line script to run it, with a ScheduledTask to run it.

As such, we are GEFN, which keeps my customer (my boss) happy, and so I think we are set for now.

I may revisit DocProject if and when I have the time.

>>> And just to be sure, are you getting the same error at the same step of the build process as the OP?  (I.e., according to the event log, it states Event: Clean project?)

I think so; but, I am not certain. I seem to remember it that way.

Regardless, it is so nice of you to provide such excellent work and help.

Thank you.

-- Mark Kamoski