Brute force check to ensure access to the TEMP directory in order to use the XmlSerializer
If you want to use the XmlSerilializer, you'll (ASPNET user) need write access to the Windows/Temp folder. Otherwise you may see this as the temporary assembly fails to be saved:
File or assembly name zmp0husw.dll, or one of its dependencies, was
not found.
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.IO.FileNotFoundException: File or assembly
name zmp0husw.dll, or one of its dependencies, was not found.
Internally, there are Security Demands, to see if you have the "right" from a Code Access Security point of view, but noone actually CHECKS to see if you have the ACL rights.
So, here's a brute force way to find out, once per AppDomain, to check if you have access. I reflectored into XmlSerializer to find out what they were doing to find our what path to write to. They were using GetTempPath from kernel32.dll, so we could PInvoke as well. (Update: However, Kevin Dente points out that Path.GetTempPath() will do the PInvoke for you. I was mirroring XmlSerializer's code, but as long as we do the same thing in essense, we're OK. Edits below, line numbers changed. Thanks Kevin!)
1 public sealed class SomeUtilityThingieClass
2 {
3 const string ErrorMessage = "We neede write access to: {0}";
6
7 private static bool tempFileAccess = false;
8 private static object tempFileAccessLock = new object();
9
10 public static bool EnsureTempFileAccess()
11 {
12 if (tempFileAccess == true)
13 {
14 return true;
15 }
16
17 if(tempFileAccess == false)
18 {
19 lock(tempFileAccessLock)
20 {
21 if(tempFileAccess == false)
22 {
29 string tempFile = Path.Combine(Path.GetTempPath(),"WriteTest.txt");
30 try
31 {
32 using(StreamWriter file = File.CreateText(tempFile))
33 {
34 file.Write("This is a test to see if we can write
to this TEMP folder and consequently make XmlSerializer
assemblies without trouble.");
35 }
36 }
37 catch(System.IO.IOException ex)
38 {
39 throw new System.IO.IOException(
string.Format(ErrorMessage,tempFullPath),ex);
40 }
41 catch(UnauthorizedAccessException ex)
42 {
43 throw new UnauthorizedAccessException(
string.Format(ErrorMessage,tempFullPath),ex);
44 }
45
46 if (File.Exists(tempFile))
47 {
48 File.Delete(tempFile);
49 }
50 tempFileAccess = true;
51 }
52 }
53 }
54 return tempFileAccess;
55 }
56 }
Once the write has worked once, we catch the success in a static and return that. If multiple threads get in here together, only one will make it past the lock. The others will wait. After we learn of success or failure from the first thread, the threads that were waiting for the lock will check the (now change) tempFileAccess boolean, and find it changed. The file write will happen only once per AppDomain. Rather than calling "throw;" or not catching the exceptions at all, I add a little extra polite message and wrap and throw. They won't know the line number that went wrong, but they WILL get a nice message.
The most interesting stuff to the beginner is the classic check/lock/doublecheck threadsafety. Note also that we have an explicit object tempFileAccessLock that exists ONLY for the purposes of locking.
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
However, I don't see a Path static property on System.IO.Path...? I think you meant (and were right) about GetTempPath(), though.
Are you saying (here) that you need to run independently of any user account?
No, what I'm saying is that:
1. there is a temporary assembly created when one reflects over an object with the XmlSerializer.
2. that assembly is saved in a directory that is one of 4 places (see MSDN) but likely c:\windows\temp.
3. the ASPNET account has no User Profile (You can't login as ASPNET on the desktop), so it will be %WINDIR%\temp, and write access is needed
4. the error you get if the running account doesn't have access is obscure at best.
5. This code is a per-appdomain check to see if one has access.
Comments are closed.