Macro to Restart Visual Studio Elevated

Kevin Dente ponders on Twitter:

Wonder if it would be possible to create a Visual Studio add-in or macro that re-launches VS elevated, with the current project, then exits

This is something I’ve been meaning to do for a while, as often I’ll open a Web Application in Visual Studio configured to run under IIS and be met with the following:

The Web Application Project is configured to use IIS. To access local IIS Web sites, you must run Visual Studio in the context of an administrator account..

Running anything elevated is easy, you just need to call ShellExecute (the .NET equivalent is System.Diagnostics.Process.Start) with the “RunAs” verb, so it’s simply a matter of starting Visual Studio’s devenv.exe passing the path to the current solution.

So here goes: copy and paste this into a Macro Module (ALT+F11) then wire up to a toolbar button as appropriate:

Sub ElevateVisualStudio()

    Dim slnPath As String = DTE.Solution.FullName
    DTE.Solution.Close(True)

    Dim startInfo As New System.Diagnostics.ProcessStartInfo(DTE.FullName, slnPath)
    startInfo.Verb = "RUNAS"
    System.Diagnostics.Process.Start(startInfo)

    DTE.Quit()

End Sub

Works On My Machine™, etc and only tested on Visual Studio 2008.

Using Windows 7’s “XP Mode” to run IE 6, IE 7 and IE8 side-by-side

Windows 7’s XP Mode is essentially a Windows XP virtual machine that runs in the new Windows 7 version of Virtual PC. This new edition of Virtual PC includes cool features such as seamless windows. The main reason I’m interested in this is to run older versions of Internet Explorer for testing purposes. IE8’s compatibility mode does a pretty good job of emulating IE7 but there are differences, so I investigated how to get “real” IE7 running under Windows 7’s XP Mode, without losing IE6.

Out of the box, the Virtual PC image is Windows XP SP3 with IE6. The trick to getting IE6 to appear as a seamless window like this is to launch the Virtual Windows XP virtual machine and in the VM, create a shortcut in the “All Users” Start Menu or desktop:

image

After a short delay, the shortcut will be duplicated into the Windows 7 host’s start menu:

image

When you launch this shortcut in Windows 7, the main Virtual PC window will close and the application will be launched and projected onto your desktop.

Creating a VM for IE7

You can’t install IE7 side-by-side with IE6 on the same machine, so to run IE7 at the same time as IE6 we will need to create a new VM. Unfortunately, if you run the VirtualWindowsXP.msi setup again you’ll get a message saying “Setup has detected that Virtual Windows XP is already installed”:

image

The workaround is to create a new instance of the VM manually using the supplied VHD as the base disk image. To do this do the following:

Open the Virtual Machines folder from the Start Menu and click Create virtual machine:

image

Specify a name and location for the virtual machine:

image

For memory I specified 256MB, which should me more than enough for just running IE7:

image

Here’s the important bit. Choose “Create a virtual hard disk using advanced options”

image

…and then choose a Differencing disk. This is the same way that the default Virtual Windows XP VM is set up:

image

If you want, you can change the default name and location (I didn’t bother):

For the parent virtual hard disk choose the original Virtual Windows XP virtual disk, which on my machine was at C:\Program Files\Virtual Windows XP\Virtual Windows XP.vhd:

image

Now start the new Virtual PC you just created from the Virtual Machines folder and complete Windows Setup as appropriate, choosing a computer name unique for your network and an Administrator password:

image

When that’s done:

  • Enable Integration Features from the Tools menu of the Virtual PC window
    • For credentials enter Administrator and the password you entered during setup.
    • I chose to Remember my credentials for convenience
  • download and install IE7 in the VM,

When IE7 has installed and rebooted, create a shortcut to IE7 in the “All Users” start menu which, after a short delay, will create a corresponding shortcut in the Windows 7 start menu of the host.

image

So here’s the end result, IE6, IE7 and IE8 all running on Windows 7:

image

Ahhh, 3 generations, all together – don’t they look adorable?

Fixing WebSVN “Unable to call svn command” error

WebSVN is a great PHP web-based interface to Subversion that we run on our IIS build server. But, after updating to a more recent build we started getting errors like the following when viewing diffs:

Unable to call svn command "svn --non-interactive --config-dir /tmp"

The source of the error is in the getFileContents function in svnlook.php, where it’s piping the output of svn cat to GNU Enscript (for syntax highlighting) and then parsing the output of that using sed. I guessed that maybe the regex that sed is using isn’t being escaped properly for the Windows command line and therefore failing.

But I noticed that all of this code was in an if($config->useEnscript) block. Just before that is an if($config->useGeshi) block that looks a lot simpler. Simpler because GeSHi is PHP-based syntax highlighter, rather than an external command whose output requires special parsing. So I went to our include/config.php and added the $config->useGeshi(); call (as detailed in the distconfig.php template config file), and it all started working happily again!

Unzipping/extracting MSI files

If, like me, you are constantly wanting to just extract the files from a Windows Installer MSI file quickly, then this is for you.

My ZIP utility of choice 7-Zip appears to support extracting MSI files but in fact extracts all the various weird and wonderful binary streams in the MSI rather than simply just the actual files. Thankfully I stumbled across a Windows Installer switch today after typing msiexec /? that does the job perfectly: the /a “administrative install” switch, e.g.:

msiexec /a foo.msi /qb TARGETDIR="C:\TEMP\Foo"

So, what I’ve done is packaged this up as a little registry tweak that conveniently lets you do this by simply right clicking a file like so:

image

Copy and paste the following into a *.reg file and double-click it:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\Msi.Package\shell\Extract MSI\command]
@="msiexec.exe /a \"%1\" /qb TARGETDIR=\"%1 Extracted\""

Hope that helps!

Fixing “Provider: Unspecified error” when querying LDAP with the ADsDSOObject OLEDB Provider

As part of our logon script we have a Windows Script Host script that was failing with “Provider: Unspecified error” (mmn, helpful) but only on some Windows XP machines.

The offending line of code looked like this:

rs = con.Execute("<LDAP://DC=example,DC=com>; (sAMAccountName="+ username +"); ADsPath; subTree")

After some trial and error the fix was this (see if you can spot the difference):

rs = con.Execute("<LDAP://DC=example,DC=com>;(sAMAccountName="+ username +");ADsPath;subTree")

Can you see what it is? No spaces after the semicolons!

Alternatively the SQL-like syntax also seems to be a bit more forgiving:

rs = con.Execute("SELECT ADsPath FROM 'LDAP://DC=example,DC=com' WHERE sAMAccountName='"+ username +"'")

It appears that with some later version of ADSI ADsDSOObject (or to give it its full title, the “OLE DB Provider for Microsoft Directory Services”) the query syntax strictness has been relaxed. In any case “Unspecifed error” appears to mean “Syntax error” in this case.

Getting a machine’s NetBIOS domain name in C#

I tried to find some mechanism to get the current machine’s NetBIOS domain name (the machine domain, not user domain), but couldn’t find anything in the usual places (e.g. System.Environment). If you want the fancy-schmancy Active Directory DNS domain then you can use Domain.GetComputerDomain().Name from System.DirectoryServices.ActiveDirectory, or another one that I stumbled across in Reflector was IPGlobalProperties.GetIPGlobalProperties().DomainName that lives in System.Net.NetworkInformation. But a simple way of getting the old-skool NetBIOS/LanManager-style machine domain name proved elusive.

Some googling suggested that WMI would provide the answer but I find WMI a little heavyweight, and not always reliable. The information is also probably in the registry somewhere, although I couldn’t find it after a cursory scan. The proper, supported way it would appear is to use the Network Management API. So my solution entailed P/Invoking to netapi32.dll.

If you’re after the same information I hope you find the code below useful. Once you’ve incorporated this in your project, just call the GetMachineNetBiosDomain method. It will return the machine’s Workgroup name if the machine is not domain-joined.

UPDATE: Now works on 64-bit thanks to update sent by Rp Brongers.

using System;
using System.Runtime.InteropServices;
using System.ComponentModel;

class NetUtil
{
    [DllImport("netapi32.dll", CharSet = CharSet.Auto)]
    static extern int NetWkstaGetInfo(string server,
        int level,
        out IntPtr info);

    [DllImport("netapi32.dll")]
    static extern int NetApiBufferFree(IntPtr pBuf);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    class WKSTA_INFO_100
    {
        public int wki100_platform_id;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string wki100_computername;
        [MarshalAs(UnmanagedType.LPWStr)]
        public string wki100_langroup;
        public int wki100_ver_major;
        public int wki100_ver_minor;
    }

    public static string GetMachineNetBiosDomain()
    {
        IntPtr pBuffer = IntPtr.Zero;

        WKSTA_INFO_100 info;
        int retval = NetWkstaGetInfo(null, 100, out pBuffer);
        if (retval != 0)
            throw new Win32Exception(retval);

        info = (WKSTA_INFO_100)Marshal.PtrToStructure(pBuffer, typeof(WKSTA_INFO_100));
        string domainName = info.wki100_langroup;
        NetApiBufferFree(pBuffer);
        return domainName;
    }
}

Disabling direct PDF viewing/opening from the web

This worries me (more information). It would appear that you could craft PDFs that could hijack someone’s machine simply by their opening the PDF. This is especially pernicious because Adobe Reader is so intent on integrating PDF into Internet Explorer using various Browser Helper Objects and ActiveX Controls that a “drive-by” hijack would be simple to set up once you’ve crafted the malicious PDF file.

A quick fix (that Works On My Machine™) that prevents the automatic opening/viewing of PDF files from the web is to change the HKEY_CLASSES_ROOT\.pdf\Content Type registry key from application/pdf to something like application/octet-stream. This means that you have to save the files first before opening them.

Some possible scriptable ways of automating this:

Method 1: A .REG file

Save the following into a file with a “.reg” file extension and running using regedit.exe /s filename.reg.

REGEDIT4
[HKEY_CLASSES_ROOT\.pdf]
"Content Type"="application/octet-stream"

Method 2: The REG command

Alternatively the following, using the reg.exe command will achieve the same result:

reg add HKCR\.pdf /v "Content Type" /d "application/octet-stream" /f

You obviously still need to be careful about PDFs, you just have the ability now to actively decide whether you want to open them.

Follow

Get every new post delivered to your Inbox.