I usually only use English Software, but sometimes have to create documents in German.

The English Office 2010 Pro has proofing tools for English, French and Spanish, but not for German.

The German Office supports German, English, French and Italian. But after installing the German version, I get the UI in German which I can't stand. You can buy a language Pack at office.com for 25 US dollars, but if you have a second license and both the English and German versions of Office do the following:

Install Office, I would use with the secondary language first, then start the install process for the Office in your primary language.

It recognizes that Offices is already installed in a different language. If you click "Repair" you get the option to add a second display language.

You can change the languages in options or in the separate “Microsoft Office 2010 Language Preferences” tool.

Office language settings dialog

This only works if you have both language versions and you may need two full Office licenses which I have, but I am not sure about that.


 
Categories: Tools

My use case for this requirement was that users can add text on a web site and could include links (a tags). I want all these links
to open a new window when clicked on. So I needed to add a target="_blank" to all the a tags in the text.

My first idea was to use pure Regular Expressions, but I couldn't figure out how to use the negative lookahead feature, I needed
this because I can't simple add a target="_blank" to each a tag, what if it already has one?

So instead I am using a simple Regex to find the appropriate tags and then use a MatchEvaluator, which is a very cool
feature of the .Net regular expression class. It allows you to compute the replacement string in a method for each
match.

public class HtmlHelper
{
    string _attrib = string.Empty;

    public string AddAttribute(string source, string tagName, string attrib)
    {
        _attrib = attrib;

        string term = "<" + tagName + " [^>]+>";

        Regex r = new Regex(term, RegexOptions.IgnoreCase);

        MatchEvaluator myEvaluator = new MatchEvaluator(this.ProcessMatch);

        return r.Replace(source, myEvaluator);

    }

    private string ProcessMatch(Match m)
    {
        string tag = m.Value;
        if (tag.IndexOf(_attrib) == -1)
        {
            tag = tag.Replace(">", " " + _attrib + ">");
        }

        return tag;
    }
}
This can then be used like this:
HtmlHelper helper = new HtmlHelper();
htmlText = helper.AddAttribute(htmlText, "a", "target=\"_blank\"");

 
Categories: ASP.Net | Web

Recently I saw some people complaining about the Gmail user interface: Jeff @ Coding Horror, there was also a reference to this in one of the Mix 10 talks but I can't remember which one, because I watched too many of them.

So all the super smart guys now work for Google, but even they don't always get it right.

There are many factors that make up good software and one of them is to follow the standards of the Operating system it runs on.

Here are two examples of why I don't like some Google Software.

There is a process named "GoogleToolbarNotifier.exe" on the computer I am sitting at. I guess this has something to do with the Google Toolbar in web browsers. I do not run a browser though. So lets open Task Manager to kill the process: "Access Denied". What, why can't I kill it. It runs under my own user account and I should have all the rights to stop it.

The only way to kill it is to use a really low level StopProcess API that is usually not available to the end-user.

The other example is the Chrome browser. So far I had only used the portable version of it, which is not installed on the computer. Yesterday I installed the normal version and in the setup wizard chose to not create entries in the Start menu entries or on the desktop. After the wizard was finished I tried to start the browser and had a hard time finding it. Nothing in "Program Files", it turned out it was installed under my user profile directory which is according to Microsoft for application data and settings but not for executables.

Also because as I usually installed Software as an administrator, my normal user account who is not an admin, did not have access to the Software. I had to run setup again under my normal user account, download the whole package again from Google and then ended up with a second copy of the application on my hard drive.

There is actually a standalone installation package for Chrome, but it is not mentioned on the Chrome pages and you have to google with Bing for it.

The fact that the setup wizard doesn't allow me to choose a destination for the software I install, pisses me off. What if I have my user profile on a remote network drive? This is now filled up with Google junk.

Don't get me wrong, Chrome seems to be a nice browser but until Google gets its act together I will not install it on my computers. At least I can use the portable version.


 
Categories: IT Pro

Some third party Windows Services think they are very important.
They do not give you the option to stop them in the Services MMC console.
You can kill the process using Process Explorer but you get an "Access Denied" message or the services are coming back right away after you killed them. That is because they have their Recovery options set to "Restart Service". When you try to changes this or even disable the service, you get an another "Access Denied".

One way around this is, to open Autoruns.exe, go to the Services tab and double-click the entry in question. This will open the registry editor with the key for the service selected. Delete the "FailureActions" value, this will reset the recovery options to the default which is 'do nothing', now you can kill the services and it wont come back.

If you want to disable it, set the Start value from 2 (Automatic) to 4 (Disabled).

However they are services that think they are super important, one of these is "Service for G-Buster Browser Defense". Access to its configuration in Windows tools is denied. When you change the start value in the registry to 'disabled', within seconds it is changed back to 'Automatic'.
This is done by the winlogon.exe process, one of the core Windows services. A dll of the G-Buster software 'gbiehAbn.dll' is loaded into the winlogon.exe process. (I think this happens because of the entry
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\GbPluginAbn\DllName). The Software also comes with a kernel mode driver that starts even earlier in the startup process. However it does not come with an un-installer.

The Software comes from a Brazilian bank and does not seem to do any harm.

Deleting the FailureActions registry value and killing the process with RootkitUnhooker.exe works, but so far I have not figured out how to get rid of it permanently.


 
Categories: IT Pro

The first thing I do when getting to a new public XP machine is to run a few batch files to stop a whole bunch of processes that are not needed while I do my work.

This only works if I am an administrator, but sadly enough on most public machines this is still the case.

Also most machines run DeepFreeze, so any changes I make are undone at the next reboot.

The batch files I am using are:
  • FixRegistry.cmd fixes some things in the registry, which makes it easier to execute the other batch files.
  • saj.cmd (stop-all-junk): stops unneeded services and kills many common unwanted applications
  • sas.cmd (stop-all-services): stops additional services that are not really needed.
  • sarj.cmd (stop-all-root-junk): Stops some processes that I can not stop as a normal admin, I need to run it with this special command:
    psexec.exe -s C:\bin\sarj.cmd
This runs the batch as the "System" user, make sure you use the full path to the batch file.

You can download the batch files from my download page.

If you don't already have the SysInternals PsTools package, you can download it here
 
Categories: IT Pro | Work on the road

I use a whole bunch of the Sysinternals tools on my USB stick when checking computers for viruses and fixing other problems. On every new computer, each of the tools first pops up the Microsoft License Agreement box which you have to agree to. It is just one click but it gets annoying over time.

So I use a small batch file "FixReg.cmd" which among other things has the following lines:

reg.exe ADD HKCU\Software\Sysinternals\PsKill /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD HKCU\Software\Sysinternals\PsList /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD HKCU\Software\Sysinternals\PsInfo /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD HKCU\Software\Sysinternals\PSexec /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD "HKCU\Software\Sysinternals\Process Explorer" /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD "HKCU\Software\Sysinternals\Process Monitor"  /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD "HKCU\Software\Sysinternals\Autoruns"         /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD "HKCU\Software\Sysinternals\TCPView"          /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD "HKCU\Software\Sysinternals\RootkitRevealer"  /v EulaAccepted /t REG_DWORD /d 1 /f 

I run this on every new computer before I do anything else. After all I have already read and agreed to the license terms more than once. As you can see from the HKCU registry hive, each user has to agree separately.

When using the psexec.exe tool with the -s switch to run a command prompt under the almighty System account, you first have to agree to the License Agreement, which is fine. But then when you want to work with additional Sysinternals tools within the System console, you have a problem. When starting pskill.exe for example, the program tries to pop up the License Agreement dialog box. This does not work because the system account session does not have a desktop and this point and the program hangs because nobody can ever agree to the License Agreement.

To fix this, add a few more lines to the FixReg.cmd batch:

reg.exe ADD HKU\.DEFAULT\Software\Sysinternals\PSexec /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD HKU\.DEFAULT\Software\Sysinternals\PsKill /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD HKU\.DEFAULT\Software\Sysinternals\PsList /v EulaAccepted /t REG_DWORD /d 1 /f
reg.exe ADD HKU\.DEFAULT\Software\Sysinternals\PsInfo /v EulaAccepted /t REG_DWORD /d 1 /f

The System account looks in the HKEY_USERS\.DEFAULT section of the registry for any settings it should use.

Now you can kill processes that you couldn't kill as normal administrator:

psexec.exe -s cmd.exe 
pskill.exe badprogram.exe 
exit

 
Categories: IT Pro | Tools

Two issues I had to fight with today which took longer to solve than I originally thought.

First I looked into a problem I had since the last new version of my travelog website. When I add a new entry for the day, the date and time is send along with the other data to the server with an http post. But every time I save a entry the time is reduced by 7 hours. So after a few edits even the day itself is wrong.
I can explain the 7 hours, that the time difference between where I am (Peru) and the server (Germany), but where does it come from?

I looked at the database server and it has the time as specified in the web form. So the issue is getting the data back from the server. I checked my JavaScript which pulls the data via AJAX from the server and puts it onto the form, that looked fine too. Then I used Firebug to look at the JSON data that comes from the web server. The time there is seven hours less than whats in the database! I use ASP.NET web methods to return an object serialized as JSON.

The JSON serializer figures out, the client is 7 hours behind the server and takes those seven hours off each DateTime value. How does the server even know about those seven hours. I first thought about the http request headers but there is no time zone information in there. Well I still don´t know how it knows.

To fix the problem I send the timezoneOffset in the browser available through the JavaScript getTimezoneOffset() method when making a request. On the server I find out the time zone offset of the server and then fix all DateTime fields before sending them down. Not nice but it works.

  // get a local time zone info
  TimeZoneInfo tz = TimeZoneInfo.Local;

  // get it in hours
  int offset = tz.BaseUtcOffset.Hours;

  // add one hour if we are in daylight savings
  if (tz.IsDaylightSavingTime(DateTime.Now))
  {
      offset++;
  }

The second problem I had was with a very simple site, I added an aspx page to download zip files from the site and log the downloads. It worked fine locally but on the live server the line:

if (System.IO.File.Exists(fileName)
which makes sure the file exists before it is trying to send it returned false. I triple checked that the file was there and it was. I checked NTFS permissions and IIS settings, all fine. I could download the file directly via http from the same location. I ran ProcessMonitor on the server and the web process didn't even try to open the file. What is going on here?

I finally removed the line above and let the Response.TransmitFile method throw the error, now it told me what was going on. I had set the site to Medium Trust because it only performed very basic operations. In Medium Trust your application is not allowed to access the file system.

So in this case System.IO.File.Exists returns a false, but shouldn´t it really throw an exception? I am not sure, but it made it harder to debug.
 
Categories: ASP.Net

October 9, 2009
@ 02:52 AM

When travelling around the world I plug in my USB devices in countless public computers.

Many viruses use the autorun.inf file to spread themselves around. When plugging in a USB device into an infected computer the virus copies itself into a hidden location, often the Recycler directory and adds a autorun.inf file in the root of the device to automatically start itself on the next computer.

An easy way to notice whether you may have a virus on your device is to create your own autorun.inf and assign an icon to the device.

Create a text file with the name autorun.inf and place it into the root of your device. The content should look like this:

[autorun]
icon=panama.ico

Now place the icon file "panama.ico" into the root of the device as well. The next time you plug in your device the Windows dialog will look like this:

Windows New Device dialog

Notice the icon in the top left corner. If you do not see your icon, you know something has overwritten your autorun.inf, most likely a virus.

What to do in this case? Check my post Manually finding and removing malware

I prepared some icon files in case you do not have any:

icons

icons.zip (27.17 KB)
 
Categories: Work on the road

On TweeNet to access a band page, the old url is


www.twee.net/bands/band.asp?key=smiths

You have to know the internal name of the band as well.

Now you can get to the same page using

www.twee.net/band/The Smiths

The site runs under ASP.Net 3.5 SP1 on IIS6, so I have all the routing goodness of that ASP.Net version.
To get this working, I first had to add the routing assemblies and modules to my web.config

<assemblies>
        <add assembly="System.Web.Abstractions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
        <add assembly="System.Web.Routing, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
      
<httpmodules>
      <add type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, 
             Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" name="UrlRoutingModule" />
</httpmodules>


These are additions to the already existing assemblies and modules which are not listed here.

Next I implemented a routing handler:

using System;
using System.Web;
using System.Web.Compilation;
using System.Web.Routing;
using System.Web.UI;

namespace TweeNet.PublicSite.RouteHandlers
{
    public class BandRouteHandler : IRouteHandler
    {
        public BandRouteHandler(string virtualPath)
        {
            this.VirtualPath = virtualPath;
        }

        public string VirtualPath { get; private set; }

        public IHttpHandler GetHttpHandler(RequestContext
              requestContext)
        {

            foreach (var urlParm in requestContext.RouteData.Values)
            {
                requestContext.HttpContext.Items[urlParm.Key] = urlParm.Value;
            }

            var page = BuildManager.CreateInstanceFromVirtualPath
                 (VirtualPath, typeof(Page)) as IHttpHandler;
            return page;
        }
    }
}

then in Global.asax I added the route:
    protected void Application_Start(Object sender, EventArgs e)
    {
            RegisterRoutes(RouteTable.Routes);
    }
    
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.Add(new System.Web.Routing.Route
        (
             "band/{bandname}"
             , new TweeNet.PublicSite.RouteHandlers.BandRouteHandler("~/bands/band.aspx")
        ));
    }

In the code behind of the band.aspx page I added some code:
    if (HttpContext.Current.Items["bandname"] != null)
    {
        string userTerm = HttpContext.Current.Items["bandname"].ToString();

        // now use that term to find the band data in the database and display the page content
    }


The last problem was that IIS6 doesn't pass something like "The Smiths" to the ASP.net pipeline. Steve Sanderson's blog post describes some options how to deal with this 

But in my situation the best option was the following:
As a /band directory did not existed yet, I created that empty folder in the filesystem.

Then in IIS I selected it and created an application for it. I now added a wildcard application mapping to the asp.net runtime. So all requests under /band/ will go to asp.net.

All the other static files on the site are not affected. I then removed the application from '/band/'. IIS still keeps the wildcard mapping even if the folder is no longer an application and you can not see that mapping in the IIS Manager GUI.


 
Categories: ASP.Net