A Good Way to Handle Multi Language Resource Files


If you are working with SharePoint you should also be using ressource (resx) files in your projects.

The problem is that SharePoint is quite annoying in the way it handles fallback to the default language resx when there is no culture specific version.

For instance in a Danish site it will look for MySolution.da-DK.resx and fallback to MySolution.resx if it isn’t o found.

Nice.

The Problem

However SharePoint will spam your ULS log with messages like:

04/20/2012 13:56:37.25     w3wp.exe (0x3758)     0x344C    SharePoint Foundation     General     b9y9    High     Failed to read resource file "C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Resources\MySolution.da-DK.resx" from feature id "(null)".    5b6991c9-5b39-4c95-b895-ed282bc00034 
04/20/2012 13:56:37.25     w3wp.exe (0x3758)     0x344C    SharePoint Foundation     General     8e26    Medium     Failed to open the language resource keyfile MySolution.    5b6991c9-5b39-4c95-b895-ed282bc00034 
04/20/2012 13:56:37.25     w3wp.exe (0x3758)     0x2C94    SharePoint Foundation     General     b9y3    High     Failed to open the file 'C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\Resources\MySolution.da-DK.resx'.    492a45e8-e416-4321-a83a-12f56c0341c1 
04/20/2012 13:56:37.25     w3wp.exe (0x3758)     0x2C94    SharePoint Foundation     General     b9y4    High     #20015: "" kan ikke åbnes: Filen eller mappen findes ikke.    492a45e8-e416-4321-a83a-12f56c0341c1 
04/20/2012 13:56:37.25     w3wp.exe (0x3758)     0x2C94    SharePoint Foundation     General     b9y4    High     (#2: "" kan ikke åbnes: Filen eller mappen findes ikke.)    492a45e8-e416-4321-a83a-12f56c0341c1 

And to make matters worse they are actually marked with “high” importance. In my mind this is a very normal way to code (for non-SharePoint projects) and should certainly not be marked with high importance. For now I am forced to look for the “unexpected” importance instead when troubleshooting.

The Simple Fix

There are a number of ways to correct this, I chose:

  1. Delete the duplicate localized version (MySolution.da-DK.resx) from source control
  2. Add a prebuild event to copy MySolution.resx to MySolution.da-DK.resx
    1. Write something like ‘copy /Y “$(ProjectDir)Resources\$(TargetName).resx” “$(ProjectDir)Resources\$(TargetName).da-DK.resx” ‘ (change to suit your needs and ensure quotes are not html mangled)
  3. Make sure to include MySolution.da-DK.resx in the project and mark it with “Build Action: Content”, “Copy to Output Directory: Do not copy”.
  4. Choose to exclude MySolution.da-DK.resx from Source Control in Visual Studio (File / Source Control / Exclude …)

    (otherwise Visual Studio will likely face write protected files)

All in all it should look somewhat like this:

Resx handling in the project

Other Ways

Other possible solutions would be to symbolic soft/hard link to the file (mklink.exe), however care should be taken as TFS really don’t like symbolic links.

The trick with exclusion from source control would likely work too, however a file copy just seemed like a simpler solution…

Another creative way would be to mess around with the packaging options it’s likely possible.

Advertisements

Fixing the Timer Service when everything breaks down


This is a pure troubleshooting post (that will save you days!) if you experience any of the following problems:

  • You have trouble deploying solutions to a your farm (more than one node) where nothing happens the deployment never completes
    • You may be able to complete deployments by executing “stsadm –o execadmsvcjobs” on every server in the farm
  • You experience frequent CPU spikes of 100% every few minutes on your frontend servers and they are all but unresponsive at those times
  • You find one of the following lines in the ULS logs (the SharePoint logs)
    • OWSTimer.exe, w3wp.exe (Id: 888k) : “File system cache monitor encountered error, flushing in memory cache”
    • OWSTimer.exe (Id: 8xqx): “Exception in RefreshCache. Exception message :Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.”
    • OWSTimer.exe (5utx): “The timer service could not initialize its configuration, please check the configuration database. Will retry later.”
  • “Some” of the administrative pages in the central administration occasionally fails with the error
    • “Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information”
  • The timer and topology cache is never updated (see next section…)
  • PSConfig (GUI wizard or not) fails to configure your server and the log file reveals the “LoaderExceptions” error above.

The above problems will probably affect all servers in the farm.

Note: The fusion log troubleshooting will be applicable to all .NET loader errors, not just SharePoint specific stuff.

First shot: Clear the Config Cache

A little known fact is that SharePoint maintains a disk cache (and memory) based configuration on every server that contains the topology information and timer job definitions. Go have a look at “C:\Documents and Settings\All Users\Application Data\Microsoft\SharePoint\Config\<guid>\”.

Sometimes old stuff can get stuck in there and you can kick start the timer by clearing it.

The procedure is simple (do it for every server):

  1. Stop the Administration and Timer service
  2. Delete all XML files in the directory (not the folder itself)
  3. Open “Cache.ini” and write the number 1 instead of the existing number (you might want to make a note of it)
  4. Start the services again
  5. Wait for a minute or two and see if the folder starts to fill up with xml files. It is likely that it will contain less than before clearing it.
  6. Check the cache.ini file. If it’s accessible and the number is considerable greater than 1 your cache has been properly initialized and chances are that your problems are now fixed. It didn’t fix my problem, so you may need to read on… (if you didn’t have the “888k” log entry mentioned above you probably have it now)

The above procedure is grabbed from tomblog’s excellent post (that will also help if you actually did delete the folder too)

[Updated] Or you can run this batch file. [/Updated]

If the procedure didn’t fix the problem you’ll notice that the xml files are updated (timestamp) every few minutes coinciding with the CPU spikes.

Second shot: Digging in (Using the fusion log)

The core problem in my case was that some .NET class/assembly could not be loaded as the message “Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information” strongly hints. It may be a little surprising how many times an assembly, completely unrelated to the task at hand (e.g. deployment), is actually loaded. To fix the problem all we have to do is identify the assembly and “make it available”.

Enabling Fusion Log

You need to debug the failed assembly bindings you need to look into the fusion log. Fusion is an ancient (.Net 1.0) codename for the GAC or assembly loader (I think).

To enable the log you need to add/change three keys in the registry:

HKLM\Software\Microsoft\Fusion\LogPath    REG_SZ    (path to local directory)

HKLM\Software\Microsoft\Fusion\LogFailues    DWORD     1

HKLM\Software\Microsoft\Fusion\EnableLog    DWORD    1

It is not strictly necessary to restart anything, but I recommend that you now restart your timer service in order for it to log any binding errors that occurs. Try starting it a couple of times with 10 minutes in between. That should reveal any binding error patterns.

(Refer to Brad Adams for more info)

Interpreting the Log

Finally we will use the “fuslogvw.exe” program that is part of the .NET SDK to view the actual logs. Your development machine will have this file; copy the executable to your server.

It is not a very good program. It gets the job done, but it’s hard to figure out, you can’t order errors by date you can’t resize the window, etc..

Hopefully your window will contain a lot less entries (in my case the offending entry is the highlighted one. It also failed with similar errors from the psconfig wizard and powershell).

Look for patterns using the timestamps. Did you get a group of binding errors a few minutes after you started the timer service? Or are there just some errors that look spurious? I’ll recommend skipping the core internal microsoft dll’s to begin with (msvcm80 is the c runtime library version 8 that .Net 2.0 uses).

So what goes into the log? Every assembly binding failure, which is (at least):

  • If an assembly cannot be located (it will show you where it searched and any assembly redirects)
  • If the dll load methods within the assembly throws an exception – e.g. a dependent assemly could not be found – it sadly looks exactly like the dll file could not be found (other exceptions are possible but unlikely for .NET people). The one way to distinguish is to look for the line “LOG: GAC Lookup was unsuccessful.” if it is not there then it was (probably) found in the GAC and a dependent assembly failed.

The last bullet means that if assembly A depends on B and B could not be found then both the binding for A and B fails (at the same time). To distinguish the two I’ll recommend that you look at the “Calling Assembly” in the bind log.

In my case:

  1. The Nintex.Workflow dll failed to load and the calling assembly was Microsoft.SharePoint
  2. The Nintex.Workflow.Charting.dll failed to load and the calling assembly was Nintex.Workflow. Aha, so the Nintex.Workflow.dll was actually found but failed to find the dependent Nintex.Workflow.Charting assembly.
  3. Found the assembly on a development machine and copied it to the server. Retried. Added another missing assembly
  4. And everything worked!

I should stress that neither the error type nor the troubleshooting is Nintex workflow specific.

The bind log failure from step 2 above was:

*** Assembly Binder Log Entry (1/13/2009 @ 4:34:15 PM) ***

The operation failed.

Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll

Running under executable C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN\STSADM.EXE

— A detailed error log follows.

=== Pre-bind state information ===

LOG: User = ….

LOG: DisplayName = Nintex.Charting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=913f6bae0ca5ae12

(Fully-specified)

LOG: Appbase = file:///C:/Program Files/Common Files/Microsoft Shared/web server extensions/12/BIN/

LOG: Initial PrivatePath = NULL

LOG: Dynamic Base = NULL

LOG: Cache Base = NULL

LOG: AppName = NULL

Calling assembly : Nintex.Workflow, Version=1.0.0.0, Culture=neutral, PublicKeyToken=913f6bae0ca5ae12.

===

LOG: This bind starts in default load context.

LOG: No application configuration file found.

LOG: Using machine configuration file from C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\config\machine.config.

LOG: Post-policy reference: Nintex.Charting, Version=1.0.0.0, Culture=neutral, PublicKeyToken=913f6bae0ca5ae12

LOG: GAC Lookup was unsuccessful.

LOG: Attempting download of new URL file:///C:/Program Files/Common Files/Microsoft Shared/web server extensions/12/BIN/Nintex.Charting.DLL.

LOG: Attempting download of new URL file:///C:/Program Files/Common Files/Microsoft Shared/web server extensions/12/BIN/Nintex.Charting/Nintex.Charting.DLL.

LOG: Attempting download of new URL file:///C:/Program Files/Common Files/Microsoft Shared/web server extensions/12/BIN/Nintex.Charting.EXE.

LOG: Attempting download of new URL file:///C:/Program Files/Common Files/Microsoft Shared/web server extensions/12/BIN/Nintex.Charting/Nintex.Charting.EXE.

LOG: All probing URLs attempted and failed.

Note: To grab a dll from the GAC on one machine you need to use a shell (cmd, bash or powershell) to go into the c:\windows\assembly\gac_msil\… folder structure and copy it.

Why did it go wrong in the First Place?

It’s always a useful exercise to figure out what went wrong and why instead of just fixing the error at hand.

The root cause of the error was that the dll’s in questions were distributed through a wsp solution file which was referenced in other code. When that particular solution was undeployed the dll’s were removed (from the GAC) and the new updated wsp file only contained the new versions. Suddenly something went missing 😦

It can also happen if you use some auto wsp packaging procedures like vbs scripts or the ubiquitous WSPBuilder. What if one of the SharePoint system dll’s are marked as “copy local” on one (of the others’) local develop environment and automatically included in the wsp file? It will be deployed, no problems, but when undeployed everything stops as the SharePoint system dll just got removed from the GAC. Oops. Prevention is obviously to use a build server to make clean builds, to educate your people and to have the means to troubleshoot when it didn’t work out.

Automatic Configuration of Asp.NET Ajax Extension 1.0


I’m currently testing the world famous RadEditor for MOSS from Telerik and come across a rather annoying deployment issue. Don’t get me wrong the RadEditor is a fantastic product; however it relies on the Asp.NET Ajax Extension 1.0 from Microsoft that really is a royal pain to install and configure.

Installation of the Ajax extension is fairly straight forward, just run through the installer.

Configuring a given website (here it’s a SharePoint site with a lengthy web.config file to begin with) is a rather cumbersome task, described in detail at Mike Ammerlaan’s nice blog post. In total there are about 20 additional tags that need to be inserted into your web.config file in just the right places.

It’s a tedious and error prone process that I’m unwilling to go through with 10+ websites replicated in 2 different environments. Googling for an hour revealed no real solution; it seems that no one has properly automated the process.

I’ve used a small tool ConfigMerge with bit of powershell to do the magic. I can now just run a script that will deploy the configuration settings to all my web.config files at my SharePoint sites (have to run it on every server) 🙂

Read on…

Web.Config Modifications

I’ve gathered the required modifications in a xml file that resembles the structure of a “normal” web.config file, which are then merged into the real web.config files. The file is “ajax35.config” in the downloadable zip file.

Please note that the following applies to Asp.NET Ajax Extensions 1.0 for a .NET 3.5 web sites, slight variations are likely for 2.0 and 3.0 sites.

Auto Configuration

[Updated 23-12-2008]

The key component in the auto configuration is the ConfigMerge program found on CodeProject that will merge ajax config file with the existing web.config file. If the nodes are already there they will be updated, not duplicated.

ConfigMerge uses a small list of attributes to identify similar nodes which I’ve had to extend just a bit for our needs (look for my comment on the CodeProject page near the bottom). The point is that it is now idempotent – if you run it more than once, no changes are performed.
I’ve included both the new binary and the modified source in the zip package.

The usage of the ConfigMerge utility is:

ConfigMerge.exe ExistingConfigFile ChangesConfigFile OutputFile

However I prefer to use powershell to call it, and perform (necessary) backup of web.config.

Note: If you don’t like powershell stop reading now and just use the above line in your own bat files and you’ll be good.

Powershelling

To get started with powershell read my old post, though this time I’m actually not accessing the SharePoint API.

Usage for the EnableAjax.ps1 script is (from within powershell):

EnableAjax.ps1 WebConfigPath [AjaxConfigFile]

Alternatively from a batch file/cmd use:

Powershell –command EnableAjax.ps1 WebConfigPath [AjaxConfigFile]

WebConfigPath is the path where the script should search for web.config files. It’ll go recursively down from the specified directory. If you only want to work on a single web application then use the root dir for that web application, if you want to run on all SharePoint applications you can use something like “c:\inetpub\wwwroot\wss\virtualdirectories”.

AjaxConfigFile is the name of the config file you want to merge in. It defaults to “ajax35.config” in the same dir as EnableAjax.ps1.

When the script is about to fix a web.config file it performs a backup first named with the current time, e.g. “web_ajax_2008_12_21 21_06_09.backup”.

Finally here is the actual script (EnableAjax.ps1):

# Apply Asp.NET Ajax Extensions to a number of web.config files easily.
#
# 21/12-2008 Søren L. Nielsen (soerennielsen.wordpress.com)
#
$ajaxconfig = "./ajax35.config"
$path = "."
if( $args.length -eq 2 ){
    $path = $args[0]
    $ajaxconfig = $args[1]
}
elseif ( $args.length -eq 1 ){
    $path = $args[0]
}
else {
    Write "Usage: ./EnableAjax.ps1 [WebConfigPath [AjaxConfigFile]]"
    Write ""
    Write "WebConfigPath - Path where the script should look for web.config files"
    Write "                Script will search for web.config files recursively and "
    write "                modify all it finds."
    Write ""
    Write ("AjaxConfigFile - Default is " + $ajaxconfig + " set it to a xml config")
    Write "                 file with the same structure as a 'normal' web.config file"
    Write "                 but only containing the nodes that should be added/updated"
    Write "                 in the web config files."
    Write "                 The script is build for Asp.Net Ajax extensions it will however"
    Write "                 be useful in many settings, e.g. manipulating dev, QA, Prod "
    Write "                 environment settings."
    Exit
}
write ("Modifying web.configs found in " + $args[0])
foreach( $f in Get-ChildItem $args[0] -filter web.config -recurse ){
    $realname = $f.FullName
    $backup = $f.FullName.ToLower().Replace(".config", "_ajax_" +
[DateTime]::Now.ToString("yyyy_MM_dd HH_mm_ss") + ".backup"
    Write ("Updating " + $f.FullName )
    Write ("Backup file " + $backup )
    $f.MoveTo( $backup )
    ./ConfigMerge.exe $backup $ajaxconfig $realname
}

As the web.config is fairly critical for your application you should be thorough when you test this script, I’ll recommend using WinMerge when comparing before and after config files.

Or you could just trust me an know that I take absolutely no responsibilty for any harm that comes your way 😉

[Updated 23-12-2009] Note: You need to run the script for every server, however if you use UNC paths as input to the script you can run it for every server from just one. I’ll recommend that you only install powershell on your backend servers not your frontends (if you’re on Server 2003).

Note 2: You need to rerun the script if you provision new web applications, or if you add a new server to the farm (on that server). The web.config modifications performed by SharePoint should not interfere with the script.

Download (Source, Scripts and Binaries)

Auto Asp Net Ajax Config

Final Notes

Please note that the files provided here only configures Asp.NET Ajax Extentions 1.0; it does install or configure the telerik RadEditor. It requires two more keys documented in the install guide (and a number of other small steps), that you can easily add through the SharePoint API (WebConfigModifications).

I should stress again that the version of ConfigMerge used here has been modified a bit. If you use the unmodified version you risk that some of the existing tags are “reused”/changed for the Ajax configuration and then SharePoint might well be in trouble.

MOSS does .NET 3.5 Surprisingly Well


I’m sure that a lot of you guys out there are smarter than me and laugh at me writing this.

But…

MOSS does .NET 3.5 without any worries! It works as good as you could possibly hope for – I expected it not to 😉

My experience is hampering me here, as I remember all the problems we had with .NET 2.0 for WSSv2 and/or SPS 2003 (hint: One of the two works) that effectively resulted in us staying with version 1.1 far longer than anyone wanted (same thing happened with the shift from 1.0). The average developer usually want to switch to the v.next right around the beta 2 comes out (don’t we ever get smarter?).

I just wished somebody had written it plainly sooner so I wouldn’t have to go through the hoops myself and we could have started to use .NET 3.5 much sooner.

It is slightly curious how this works though. In the IIS management your SharePoint site (or any plain ASP.NET web application) that you want to use .NET 3.5 in should be set to “ASP.NET 2.0.xx” mode – there is no 3.0/3.5 mode. They really should re-label the option in IIS to something less confusing (I haven’t checked how it’s labeled in IIS7).

How can this be? It’s been a while since I did compiler design courses (I had the “tiger” book) but I can still give some educated guesses:

  • The CLR is unchanged between 2.0, 3.0 and 3.5.
  • Lambda expressions and Linq are handled at compile time so the actual MSIL code are unchanged along with some new libraries – in my younger days we used to call it “syntactic sugar”
  • .NET already knows how to link to libraries of dihfferent versions, provided that they use the same version of the CLR (as far as I know there are three 1.0, 1.1 and 2.0 plus some beta versions in between). Your .NET 3.5 code will use the libraries associated with that version of .NET and the .NET 2.0 code will use its own libraries – the w3wp process that runs both the .NET 2.0 and 3.5 code (as the MOSS site will do) therefore loads both the 2.0 and 3.5 libraries as needed.

I apologize to everyone who already knew this and thinks it obviously should just work. You may be smarter or less experienced/pessimistic (is there a difference?)

Enjoy!