The Problem
Silverlight 4 reserves 1MB for Isolated Storage when running in the browser, this is increased to 25MB when running Out-Of-Browser (OOB).
Enterprise deployment of OOB apps is done using SLlauncher.exe – straight from the Silverlight Enterprise Deployment Guide (.docx):
%ProgramFiles%Microsoft Silverlightsllauncher.exe /install:”xapFile” /origin:”xapURI”
HOWEVER… using this method to install an OOB application will NOT increase the quota for that application to 25MB! It stays at 1MB!
This is a really really frustrating and annoying bug in Silverlight 4 RTW (unsure of later versions).
The Workaround
The quotas for Silverlight Isolated Storage are actually stored as Hex values in a quota.dat file hidden deep in the USERPROFILE structure.
My workaround is to execute a console application immediately after installation to recurse through the folder structure until a recently created quota.dat file is found, and then to modify it writing HEX to the filestream… however another catch is that the application must have been run previously in order for the quota.dat to exist! So this is hacky to say the least… but it works!
Code as follows:
public static class FixQuota { public static void Run() { string strUserProfilePath = System.Environment.GetEnvironmentVariable("USERPROFILE"); string strXpSlRoot = strUserProfilePath + @"Local SettingsApplication DataMicrosoftSilverlight"; string strVistaSlRoot = strUserProfilePath + @"AppDataLocalLowMicrosoftSilverlight"; string strSlRoot = string.Empty; if (Environment.OSVersion.Version.Major == 5) { if (Environment.OSVersion.Version.Minor == 1) { // XP strSlRoot = strXpSlRoot; } else if (Environment.OSVersion.Version.Minor == 2) { // Server 2003 & XP 64-bit strSlRoot = strXpSlRoot; } } else if (Environment.OSVersion.Version.Major >= 6) { // Vista+ strSlRoot = strVistaSlRoot; } Log("Silverlight Root: " + strSlRoot); // Start the application; latest file in SL Root + OutOfBrowserindex is what we want: DirectoryInfo di = new DirectoryInfo(strSlRoot + @"OutOfBrowserindex"); FileSystemInfo[] files = di.GetFileSystemInfos(); // Sort files var sortedFiles = files.OrderByDescending(f => f.LastWriteTime); // Newest file var file = sortedFiles.ElementAt(0); Log("Found most recently modified file " + file.FullName); string strContents = string.Empty; // File structure is: // 123456789 http://domain.com/silverlight.xap // 789468796 http://domain.com/silverlight2.xap strContents = File.ReadAllText(file.FullName, Encoding.Unicode); // Grab the AppId string strAppId = Regex.Match(strContents, "([0-9]+)").Value; Log("strAppId: " + strAppId); // Grab the Domain e.g. domain.com string strDomain = Regex.Match(strContents, "http://([^/]*)/").Groups[1].Value; Log("Domain: " + strDomain); // Get program files path string strProgFilesPath = string.Empty; if( 8 == IntPtr.Size || (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432")))) strProgFilesPath = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); else strProgFilesPath = Environment.GetEnvironmentVariable("ProgramFiles"); var slLauncherPath = strProgFilesPath + @"Microsoft Silverlightsllauncher.exe"; var appId = strAppId + "." + strDomain; Log("Launching " + slLauncherPath + " " + appId); // Launch the app var app = System.Diagnostics.Process.Start(slLauncherPath, appId); // Give Silverlight time to create Isolated Storage Thread.Sleep(4000); // Kill recently started app foreach (var program in System.Diagnostics.Process.GetProcessesByName("sllauncher")) { program.Kill(); } // Give Silverlight time to recover Thread.Sleep(2000); // Isolated Storage quota file should be created now string strQuotaFile = FindQuotaFile(strSlRoot + @"is"); if(strQuotaFile != string.Empty) { using (var stream = new FileStream(strQuotaFile, FileMode.Open, FileAccess.ReadWrite)) { stream.Position = 2; stream.WriteByte(0xa0); stream.Position = 3; stream.WriteByte(0x01); } Log("Fixed file " + strQuotaFile + " - increased to 26MB"); } } /// <summary> /// Recursive function to find most recently created quota.dat /// </summary> /// <param name="strRoot"></param> /// <returns></returns> private static string FindQuotaFile(string strRoot) { if (Directory.Exists(strRoot)) { Log("Searching " + strRoot); string[] files = Directory.GetFiles(strRoot); foreach (string strFilename in files) { if (strFilename.EndsWith("quota.dat")) { DateTime dtCreated = File.GetCreationTime(strFilename); Log("Found quota.dat created " + dtCreated.ToString("s")); if (dtCreated > DateTime.Now.AddMinutes(-10)) { Log("Using " + strFilename); return strFilename; } } } string[] dirs = Directory.GetDirectories(strRoot); foreach (string strDir in dirs) { string strResult = FindQuotaFile(strDir); if(strResult != string.Empty) { return strResult; } } } return string.Empty; } private static void Log(string strMessage) { Console.WriteLine(strMessage); } }
I hope that helps somebody – enough people complaining about it on the Silverlight forums so thought I should post it up.
Comments welcome.
Leave a Reply