Categories
Archived Uncategorized

Disable IEESC on Windows 2008

The default configuration for Windows Server 2008 still has Internet Explorer Enhanced Security Configuration enabled. Considering most administrators probably disable this I wonder why it’s still there … nonetheless here is a script to disable Internet Explorer Enhanced Security Configuration (IEESC) from a script.

Disable IEESC on Windows 2008:

:: If required, backup the registry keys
:: This is always a good idea before making registry changes
REG EXPORT "HKEY_LOCAL_MACHINESOFTWAREMicrosoftActive SetupInstalled Components{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" "%TEMP%.HKEY_LOCAL_MACHINE.SOFTWARE.Microsoft.Active Setup.Installed Components.A509B1A7-37EF-4b3f-8CFC-4F3A74704073.reg"
REG EXPORT "HKEY_LOCAL_MACHINESOFTWAREMicrosoftActive SetupInstalled Components{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" "%TEMP%.HKEY_LOCAL_MACHINE.SOFTWARE.Microsoft.Active Setup.Installed Components.A509B1A8-37EF-4b3f-8CFC-4F3A74704073.reg"

REG ADD "HKEY_LOCAL_MACHINESOFTWAREMicrosoftActive SetupInstalled Components{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" /v "IsInstalled" /t REG_DWORD /d 0 /f
REG ADD "HKEY_LOCAL_MACHINESOFTWAREMicrosoftActive SetupInstalled Components{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" /v "IsInstalled" /t REG_DWORD /d 0 /f

Rundll32 iesetup.dll, IEHardenLMSettings
Rundll32 iesetup.dll, IEHardenUser
Rundll32 iesetup.dll, IEHardenAdmin

REG DELETE "HKEY_LOCAL_MACHINESOFTWAREMicrosoftActive SetupInstalled Components{A509B1A7-37EF-4b3f-8CFC-4F3A74704073}" /f /va
REG DELETE "HKEY_LOCAL_MACHINESOFTWAREMicrosoftActive SetupInstalled Components{A509B1A8-37EF-4b3f-8CFC-4F3A74704073}" /f /va

:: If you like you modify the registry to remove the warning that shows on first IE run
:: This registry change will also set the default home page to about:blank
REG DELETE "HKEY_CURRENT_USERSoftwareMicrosoftInternet ExplorerMain" /v "First Home Page" /f
REG ADD "HKEY_CURRENT_USERSoftwareMicrosoftInternet ExplorerMain" /v "Default_Page_URL" /t REG_SZ /d "about:blank" /f
REG ADD "HKEY_CURRENT_USERSoftwareMicrosoftInternet ExplorerMain" /v "Start Page" /t REG_SZ /d "about:blank" /f

Just another of those little Windows annoyances that can be removed easily if you like.

Categories
Archived Development Uncategorized

VB.NET – List active directory users – Re-write

Please note: This is a rewrite of an article I wrote way back in 2007. The original article has been updated with a link to this one.

As per usual I had a situation where I had to do some Active Directory stuff with VB.NET. I had to grab a list of all the users in our AD so that people could select a user and perform functions on them from within a webpage.
Another note: This article is VB.NET-specific. If you’d like to know how to do this using C# please see this post’s “partner” entitled “Follow Up – List active directory users – This time in C#”

Carrying on though, there are a number of ways you can do this but in the .NET Framework they all involve using the System.DirectoryServices namespace. A couple of quick things to be aware of before you try this yourself:

  • Before messing with your Active Directory make sure you know what you’re doing and have the permissions of your AD Administrators (if it’s not you!)
  • If you are going to use any of the following code from a .NET console application you’ll need to add a reference to System.DirectoryServices in addition to importing the namespace. I wrote a post a while ago about this entitled “Developer Dumbness” – it has the steps you need to add a reference to a .NET console application.

Anyway, the code below is a simple function that queries your Active Directory domain and retrieves all the user objects. This particular example filters out any user account whose “DisplayName” attribute meets the following criteria (the requirements for the app I had to write):

  • Has an email address
  • Does not contain the “$” symbol
  • Does not contain the words “Admin” or “admin”
  • Does not contain the words “Test” or “test”
  • Does not contain the words “Service” or “service”
  • Does not contain the word “System” or “system”

The example code below is a complete Windows console application that does all of the above. It’s important to note that if you’re using this in a production environment you’ll need to make sure the application containing the code has access to the Active Directory, usually using .NET impersonation – this is beyond the scope of this post.

List Active Directory Users in VB.NET:

Imports System
Imports System.Collections.Generic
Imports System.DirectoryServices

Module ListAdUsers

    Sub Main(ByVal ParamArray Args() As String)

        Console.Clear()
        Dim userList As List(Of String) = New List(Of String)

        Dim badEntries As Integer = 0
        Dim domainName As String = String.Empty

        If (Args.Length > 0) Then
            domainName = Args(0)
        Else
            Console.Write(String.Format("{0}Please enter your Active Directory domain name: ", vbCrLf))
            domainName = Console.ReadLine()
        End If

        Console.Write(String.Format("{0}Attempting to build user list for {1} ...{0}{0}", vbCrLf, domainName))

        Try
            If Not String.IsNullOrEmpty(domainName) Then
                Dim myDirectoryEntry As DirectoryEntry = New DirectoryEntry(String.Format("LDAP://{0}", domainName))
                Dim mySearcher As DirectorySearcher = New DirectorySearcher(myDirectoryEntry)
                Dim mySort As SortOption = New SortOption("sn", SortDirection.Ascending)
                mySearcher.Filter = ("(objectClass=user)")
                mySearcher.Sort = mySort
                For Each resEnt As SearchResult In mySearcher.FindAll()
                    Try
                        If Not String.IsNullOrEmpty(resEnt.Properties("Mail")(0).ToString()) _
                            AndAlso System.Text.RegularExpressions.Regex.IsMatch(resEnt.Properties("DisplayName")(0).ToString(), " |admin|test|service|system|[$]", System.Text.RegularExpressions.RegexOptions.IgnoreCase) Then
                            Dim space As Integer = resEnt.Properties("DisplayName")(0).ToString().IndexOf(" ")
                            Dim formattedName As String = String.Format("{0}{1}{2}", _
                                                                        resEnt.Properties("DisplayName")(0).ToString().Substring(space).PadRight(25), _
                                                                        resEnt.Properties("DisplayName")(0).ToString().Substring(0, space).PadRight(15), _
                                                                        resEnt.Properties("Mail")(0).ToString() _
                                                                        )
                            userList.Add(formattedName)
                        End If
                    Catch
                        badEntries = badEntries + 1
                    End Try
                Next
                If (userList.Count > 0) Then
                    Console.WriteLine(String.Format("=========== Listing of users in the {0} domain{1}", domainName, vbCrLf))
                    Console.WriteLine(String.Format("{0}{1}{2}{3}", "Surname".PadRight(25), "First Name".PadRight(15), "Email Address", vbCrLf))
                    For i = 0 To userList.Count - 1
                        Console.WriteLine(userList(i).ToString())
                    Next
                    Console.WriteLine(String.Format("{0}=========== {1} users found in the {2} domain", vbCrLf, userList.Count.ToString(), domainName))
                Else
                    Console.WriteLine(String.Format("{0}=========== 0 users found in the {1} domain", vbCrLf, userList.Count.ToString()))
                End If
                Console.WriteLine(String.Format("=========== {0} objects could not be read", badEntries.ToString()))
                Console.WriteLine("=========== End of Listing")
            Else
                Console.WriteLine("Please enter a domain name next time!")
            End If
        Catch ex As Exception
            ' in a production app you wouldn't show the user the exception details
            Console.Write(String.Format("A critical error occurred.{0}Details: {1}", vbCrLf, ex.Message.ToString()))
        End Try
    End Sub

End Module

There are a HEAP of attributes you can use instead of “sn” in the sample above. Personally I use ADSIEDIT.MSC to look at all the possible attributes but you’ll need domain administrative access to run that. Here are a couple of useful ones though …

  • company
  • department
  • description
  • displayName
  • mail
  • manager
  • name
  • givenName
  • sAMAccountName

Hope that helps someone! 🙂

Categories
Archived Development Uncategorized

List active directory users – C#

In this article I’m going to carry on with the follow-ups to posts I wrote in the past about various topics. Today’s topic is still on the subject of using .NET to list the users in Active Directory although this time we’re going to do it in C#. I wrote the first post and sample application using VB.NET back when I (perhaps) didn’t know as much as I know now … hopefully y’all won’t be too harsh on my n00b code from back then. 🙂

So, what’s different? Apart from the language change here is a summary of what’s changed between this version and the original.

  • All references that aren’t needed have been removed.
  • The results are now added to a generic list of strings (the original was just an ArrayList).
  • The domain name can now be passed as a parameter on the command line. If one isn’t provided the user will be prompted.
  • Regular expressions are now used to filter out user accounts that don’t match certain criteria (the original did some horribly repetitive case-sensitive matching).
  • The output is now formatted better (the original was just a plain list).
  • The account’s email address is now shown too.
  • A total count of the accounts that can’t be ‘read’ is shown at the end.
  • The app’s exception handling is a whole heap better (the original, being a sample, didn’t have any).
  • The app won’t run if a domain name isn’t provided at all – an error message is displayed.

Note that this version doesn’t do any validation of the domain name e.g. length, illegal characters etc although you could use regular expressions to validate it.

On the next page is the entire source for the application – it’s a single .cs file.

Categories
Archived Databases Uncategorized

Error 29506 with SQL 2005 Studio Express on Vista

If you need to install SQL Server 2005 Management Studio Express on Windows Vista you might receive an error like the screenshot below.

SSMSE installation error on Windows Vista

This is due to the new Vista feature called UAC. The fix is simple & there are 2 ways of implementing it.

  • Under ‘Accessories’ from ‘All Programs’, right-click the Command Prompt shortcut and select ‘Run as Administrator’. Accept the UAC warning when prompted.
  • From the command prompt change to the directory that holds the SSMSEE installation .MSI file.
  • Enter the command .SQLServer2005_SSMSEE_x64.msi if you’re running Vista x64 (64-bit) or .SQLServer2005_SSMSEE.msi if you’re running Vista x86 (32-bit)
  • Press enter and complete the installation as normal.

The second method is to disable UAC completely but I wouldn’t recommend it.

Categories
Archived Development Uncategorized

Using XPathNavigator to query XML files

Have you ever needed to query a specific key value in an XML file? Depending on the format of your XML file, here’s how to do it using XPathNavigator.

This is something I planned to write about ages ago but never got around to it.

Configuration

The System.Xml.XPath Namespace has some pretty useful stuff in it. This article is going to cover how to use both VB.NET and C# to extract a particular key value from an XML file. Let’s say you have a simple XML file called C:MyApplicationConfiguration.xml that stores configuration for your application. It looks like the sample below.

Configuration.xml:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<My.Application.Config>
  <Settings>
     <BackupServer>server01</BackupServer>
     <SmtpServer>server02.domain.local</SmtpServer>
     <SmtpPort>25</SmtpPort>
  </Settings>
</My.Application.Config>

The code – C#

First up, let’s look at the C# code to read the value of the "BackupServer" key from the file above.

Read a key using C#”:

XPathDocument document = new XPathDocument("C:\MyApplication\Configuration.xml");
XPathNavigator navigator = document.CreateNavigator();

XPathNavigator currentNode = navigator.SelectSingleNode("//My.Application.Config/Settings/BackupServer");
String backupServer = currentNode.InnerXml.ToString();

The code – VB.NET

Read a key using VB.NET:

Dim document As XPathDocument = New XPathDocument("C:MyApplicationConfiguration.xml")
Dim navigator As XPathNavigator = document.CreateNavigator()

Dim currentNode As XPathNavigator =
navigator.SelectSingleNode("//My.Application.Config/Settings/BackupServer")
Dim backupServer As String = currentNode.InnerXml.ToString()

That’s it! Pretty simple really. 🙂

Categories
Archived Development Uncategorized

C# – Self updating application …

Disclaimer: I wrote this code way back in 2007 and I reckon I’ve learnt a fair bit since then (not being a professional developer).  Go easy on me if you feel like commenting on the code itself.  🙂

This one might be interesting for people who have wondered about how to distribute the latest version of their application without having to tell people when an update is released.

There are built-in options that you can use with the Microsoft .NET platform such as ClickOnce. We use ClickOnce here at Trade Me to deploy some of our in-house applications and it works really well.

Recently, though, I thought “Why not see if I can write my own method of updating my application?” It turned out to be quite an interesting litte project! The application I was working on at the time is written in C# although the learning curve to go from VB.NET to C# or vice-versa isn’t too steep.

Where do we start?

To begin with I decided the update process should have the following steps.

  • Check my website for a text file containing version information.
  • Read the version information text file and compare the version information in it to the version of the application currently running.
  • If the version in the text file is the same or less than the version currently being run, do nothing other than show a message to the user.
  • If the version in the text file is greater than the version currently being run ask the user if they want to update to the later version.
  • Do the actual update.

Components

There are 2 components to this particular solution. The first part is the actual application being updated and the updater application. If you are going to follow along with any of the stuff in this article you’ll need to start a new solution with 2 projects. My main project is called “CountDown” and the updater application is called “AppStart” (for reasons not related to the updating stuff here). For the purposes of this article I’m going to assume you have a single project that needs the update functionality.

The main application

The first thing your main application will need to do is make use of the System.Net namespace by adding the following line to the top of the main namespace file.

Namespaces:

using System.Net;

When the user runs the application they can click a button that runs a method to do most of the steps above. The first thing the method does is try to resolve the IP address of my website (http://www.digitalformula.net resolves to 203.194.159.161).

Resolve IP Address:

try
{
  string RemoteDomain = "http://www.digitalformula.net";
  IPHostEntry inetServer = Dns.GetHostEntry(RemoteDomain.Replace("http://", String.Empty));
}
catch (Exception ex)
{
...
}
Categories
Archived Uncategorized

Exchange 2003 – Errors restoring using Recovery Storage Group

Nasty. That’s a good word to use when describing the process of restoring a Microsoft Exchange database. Sure, there are options provided that in theory make the process pretty easy but there are occasions when these options don’t do the job by themselves.

I had a situation recently where I was asked to restore a user’s mailbox that had been deleted. Their Active Directory account had also been deleted which means that the default restore options don’t work properly (they require that the user still exists when the restore is done). I’ll try and sum up what I had to do and the steps I took to get the mail back. Note that all this was done without the production copy of the database being taken down, changed or harmed in any way.

The backup software used for this was Symantec’s Backup Exec 11d – these steps aren’t specific this software and should work with any software capable of backing up and restoring an Exchange database. If you’re not familiar with using Recovery Storage Groups (RSG) with Backup Exec, here is a link to an article that describes how to do it. That article describes most of the base-level restore process but in my situation it didn’t work afterwards which meant some pretty in-depth troubleshooting.

The restore process completed successfully so I tried to mount the database I had configured within the RSG – no joy. The mount request spat out an error saying “An internal processing error has occurred”, accompanied by messages in the Application Eventlog saying:

Information Store (1588) Recovery Storage Group: Attempted to attach database '<path>Mailbox Store.edb' but it is a database restored from a backup set on which hard recovery was not started or did not complete successfully.

and

Error 0xfffffde0 starting database "Recovery Storage GroupMailbox Store" on the Microsoft Exchange Information Store.

and

Error 0xfffffde0 starting Storage Group /DC=local/DC=xxx/CN=Configuration/CN=Services/CN=Microsoft Exchange/CN=xxx/CN=Administrative Groups/CN=First Administrative Group/CN=Servers/CN=xxx/CN=InformationStore/CN=Recovery Storage Group on the Microsoft Exchange Information Store.
MDB failed to start.

Some Google trawling turned up Microsoft article entitled What to Do When an Exchange Store Won’t Mount. A quick read through there satisfied me that none of the conditions listed were causing the problem so I turned to the tried and sometimes trusted command-line program :: ESEUTIL (which is found in the C:Program FilesExchsrvrbin folder of a default Exchange Server 2003 installation).

ESEUTIL has a ton of options, the relevant ones in this case being /r for Recovery, /g for InteGrity and /p for RePair. Note that a lot of articles reference a file called Restore.env. It contains information about the log files that relate to the backup you’ve just restored and can be used with the eseutil.exe option /cc – in this situation the Restore.env file was empty which meant I couldn't use this option.

Important : Run eseutil.exe from the directory that contains the RESTORED database files, not the production ones!

So, I tried to run an integrity check :

Exchange Integrity Check:

eseutil.exe /g "Mailbox Store.edb"

However, I was told the database was in an incomplete state due to some of the log files not being replayed into the database yet and that I needed to run a database recover first (even though I was given the option to skip this if I wanted to, it wasn’t recommended). Note that r00 is site-specific and may need to be changed for your configuration. Run eseutil.exe from a command prompt to see the options.

Exchange Database Recovery:

eseutil.exe /r r00

The recover process failed too though. At this point I figured the database was simply in a state where it couldn’t be recovered and couldn’t be integrity-checked so I decided to run a repair :

Exchange Database Repair:

eseutil.exe /p "Mailbox Store.edb"

This started and completed fine. Great! For completeness' sake I decided to run an integrity check before trying to mount the database again:

Exchange Integrity Check:

eseutil.exe /g "Mailbox Store.edb"

This time it worked fine, even though towards the end of the process the eseutil.exe process ended up taking up quite a lot of memory and saying "Deleting unicode fixup table". I wouldn't recommend doing a Google search for that because all the results appear to be stories of doom and gloom with one guy even saying I’ve never been able to repair a database that returned that error. I guess he didn't do enough research or try hard enough because I eventually got it working just fine.

Ok so by this point the RSG copy of the database was mounted within Exchange System Manager and seemed to be working just fine (the database must be mounted before data can be extracted from it). If you’re paranoid about mounting an Exchange database with the same name as your production one, good – you should be. Mounting the database within the RSG, even though it has the same name as your production database, IS a safe process however.

After this it's usually a simple matter of using something like the Microsoft utility ExMerge to dive into the restored database and extract the desired mail into a PST file.

ExMerge

In this case however, running ExMerge highlighted a couple of problems. The list of mailboxes was complete, apart from the one I wanted to restore. This was due to the AD account being deleted as well. Assuming your backup is a complete database backup and also assuming the restore process worked ok, you should have a complete copy of the database available, including the deleted mailbox (as long as it was backed up prior to being deleted of course).

Looking through the ExMerge.log file I was able to find the GUID of the mailbox I wanted to restore and that ExMerge couldn't find a corresponding AD account for :: BCzD787CAmA0GAFDBF3X3EA7eDE – doesn’t look like a normal mailbox GUID does it? That's ok, you can convert it into a valid GUID.

First, navigate to http://www.asciitable.com/ – you'll need it in a minute. Now, open up a text editor and paste the GUID into it (I just used Notepad).

BCzD787CAmA0GAFDBF3X3EA7eDE

A valid hex value or couplet is always 2 characters long with each part being 0-9 or a-f … NOTHING else. You need to break out the couplets from the GUID value like the one above, find the ones that aren't valid hex and then convert them to valid hex values using http://www.asciitable.com. The one I was working with turned out as follows :

BC 7A D7 87 CA 6D A0 47 AF DB F3 58 3E A7 65 DE

Note that the ones I had to convert were ‘z’, ‘m’, ‘X’ and ‘e’ – you must make sure you separate the couplets properly by looking where the slashes appear. EVERYTHING HERE IS CASE-SENSITIVE!

I then used AD Users & Computers to create a temporary mailbox that I could change so that I wasn’t messing with any production mailboxes – if you change the GUID for a mailbox that is being used, a new mailbox with the new GUID will be created the next time the mailbox is accessed. Not good because then you’ll have to go through this whole restore process again. My temporary AD account was called “Restore Mailbox”.

The next part involves using ADSIEdit to change the msExchMailboxGuid property for the “Restore Mailbox” account so that it matches the one I converted above. You can start ADSIEdit by running adsiedit.msc (it comes with the Windows Support Tools which you should download and install if you haven't already).

Using ADSIEdit, navigate to Domain, open your domain (e.g. DC=YourDomain,DC=com), expand the users container (CN=Users) and select the “Restore Mailbox” account from the list (CN=Restore Mailbox). Open the properties of this object and scroll through the list until you find msExchMailboxGuid. Change the value to the completed and valid hex string you converted earlier by clicking the &quotlEdit" button and pasting the new value into the box provided. There is no need to restart any services for this change to take effect.

ADSIEdit

Next time you run ExMerge, "Restore Mailbox" (or whatever name you chose) should be an available mailbox that you can export to a PST file.

This is a fairly long process, especially considering the time eseutil.exe can take to run but it’s something you will probably have to do during your time as an Exchange Administrator.

Hope it helps someone!

Categories
Archived Development Uncategorized

Full-screen Windows app using VB.NET

Recently I had to write a Windows application that required the main form to run in full-screen mode. This means no title bar and with the window appearance above everything else, i.e. Start Button, taskbar, system tray and all other apps.

This requires an API call to the SetWindowPos function which you need to create an alias to before you can call it. The first part of the code, which should be placed in the declarations part of the form, looks like this :

SetWindowsPos alias:

Private Declare Function SetWindowPos Lib "user32.dll" Alias "SetWindowPos" (ByVal hWnd As IntPtr, ByVal hWndIntertAfter As IntPtr, ByVal X As Integer, ByVal Y As Integer, ByVal cx As Integer, ByVal cy As Integer, ByVal uFlags As Integer) As Boolean

You also need an alias to the API function called GetSystemMetrics, like this :

VB.NET – GetSystemMetrics alias:

Private Declare Function GetSystemMetrics Lib "user32.dll" Alias "GetSystemMetrics" (ByVal Which As Integer) As Integer
[/pre]

<p>
Following this you need to declare 4 constants :
</p>

Required constants:

Private Const SM_CXSCREEN As Integer = 0
Private Const SM_CYSCREEN As Integer = 1
Private Shared HWND_TOP As IntPtr = IntPtr.Zero
Private Const SWP_SHOWWINDOW As Integer = 64

Then 2 public properties :

Required property #1:

Public ReadOnly Property ScreenX() As Integer
    Get
        Return GetSystemMetrics(SM_CXSCREEN)
    End Get
End Property

-- and --

Required property #2:

Public ReadOnly Property ScreenY() As Integer
    Get
        Return GetSystemMetrics(SM_CYSCREEN)
    End Get
End Property

After this you need to write a simple sub routine to use the constants you declared above and that calls the SetWindowPos API function. The function's code looks like this :

The FullScreen() method:

Private Sub FullScreen()
    Me.WindowState = FormWindowState.Maximized
    Me.FormBorderStyle = FormBorderStyle.None
    Me.TopMost = True
    SetWindowPos(Me.Handle, HWND_TOP, 0, 0, ScreenX, ScreenY, SWP_SHOWWINDOW)
End Sub

Beyond that it's just a case of calling the FullScreen sub routine whenever you want the application to show in full-screen mode. Easy huh? 🙂