I believe this qualifies as “nifty”

I was playing around in AutoIt because I was doing some R&D on a tool that could position and resize a window give the proper disposition values and window title.  WinMove() to the rescue!  I dunno how I missed this lil gem.  I can only imagine that I overlooked it because of the name.  I was looking for something like “WinSize” or “WinResize” or “WinSetSize” or something of that nature.  I thought WinMove simply moved a window, and it can!  But it can also be used to resize and/or position a window using a window handle or title (see the documentation).  The tool I intended to build needed to be able to look for a window who’s title contained a specified string, then position and size that window using values I provide.

Below is a rough example of how to resize a notepad window:

#include<Array.au3>
 
;Window state constants
Const $WINSTATE_WINDOWEXISTS = 1
Const $WINSTATE_VISIBLE = 2
Const $WINSTATE_ENABLED = 4
Const $WINSTATE_ACTIVE = 8
Const $WINSTATE_MINIMIZED = 16
Const $WINSTATE_MAXIMIZED = 32
 
;Position
Dim $nPosX = Default
Dim $nPosY = Default
 
;Size
Dim $nWidth = 300
Dim $nHeight = 200
 
Dim $sWindowTitle = "Notepad"
Dim $i = 0
Dim $varState
Dim $arrWindows = WinList()
_ArrayDisplay($arrWindows)	;Just to show a list of all windows and handle IDs.
 
;Look for any window title with "Notepad" in it, get the window state by HWND and then move/size by HWND.
If IsArray($arrWindows) And $arrWindows[0][0] > 0 Then
	For $i = 1 To $arrWindows[0][0]
		If StringInStr($arrWindows[$i][0], $sWindowTitle) > 0 Then
			$varState = WinGetState($arrWindows[$i][1])
			If BitAND($varState, $WINSTATE_WINDOWEXISTS) And BitAND($varState, $WINSTATE_VISIBLE) Then
				WinMove($arrWindows[$i][1], "", $nPosX, $nPosY, $nWidth, $nHeight)
			EndIf
		EndIf
	Next
EndIf
 
Exit

Ultimately, I wrapped all this up in a UDF that I could use in my project. It is important to know that this won’t work on minimized windows, BUT…. it WILL work on hidden windows.

Note: The “Default” keyword can be used instead of integer values when defining position, which will cause the position to be placed at the Windows default.

Checking Group Membership in VBScript

Several of the logon scripts I develop for clients perform things like drive-mapping based whether or not the user or computer are a member of a particular security group.  As such, I needed a function to check group membership.  I found several different examples and ultimately settled on my own implementation after some trial and error.  The following is the actual VBScript function I use on our logon scripts [we attach them to a group policy object (GPO)]:

'********************************************************************
'*
'* Function IsGroupMember()
'* Purpose : Determines if the specified object is a member of the
'*               specified group.
'* Input ..: UserOrComputerDN - [String] The distinguished name of
'*                               the user or computer to check.
'*           GroupName        - [String] The name of the group to
'*                               check membership of.
'* Output .: True  - The user is a member.
'*           False - The user is not a member, the group does not
'*                   exist, or the user does not exist.
'*
'********************************************************************
Function IsGroupMember(ByVal UserOrComputerDN, ByVal GroupName)
	On Error Resume Next
	Dim iRet
	Dim objUserOrComputer
	Dim objGroup
	Dim sGroup
	Dim colGroups
	iRet = False
 
	'Bind user or computer AD object.
	Set objUserOrComputer = GetObject("LDAP://" & UserOrComputerDN)
	If ((Err.Number = 0) And (IsObject(objUserOrComputer))) Then
		'Get the collection of groups the object is a member of.
		colGroups = objUserOrComputer.MemberOf
		If (Not IsEmpty(colGroups)) Then
			If (TypeName(colGroups) = "String") Then
				'User is a member of only one group.
				Set objGroup = GetObject("LDAP://" & colGroups)
				If ((Err.Number = 0) And (IsObject(objGroup))) Then
					If (LCase(objGroup.CN) = LCase(GroupName)) Then
						iRet = True
					End If
				End If
			Else
				'Object is a member of multiple groups.
				'Iterate through the groups the user is a member of and see
				'if any of them match the specified group.
				For Each sGroup In colGroups
					Set objGroup = GetObject("LDAP://" & sGroup)
					If ((Err.Number = 0) And (IsObject(objGroup))) Then
						If (LCase(objGroup.CN) = LCase(GroupName)) Then
							iRet = True
							Exit For
						End If
					End If
				Next
			End If
		End If
	End If
 
	'Dispose objects and return.
	Set objGroup = Nothing
	Set objUserOrComputer = Nothing
	IsGroupMember = iRet
End Function

So you could use the above function like this:

Dim userDN
Dim securityGroup
securityGroup = "sStaff"
userDN = "CN=John Doe,OU=My Division,DC=fabrikam,DC=com"
If IsGroupMember(userDN, securityGroup) Then
   MsgBox "User is a member of " & securityGroup
Else
   MsgBox "User is not a member"
End If

Decompiling .NET Assemblies

Today I found a need to take a look at the code inside of a DLL (.NET Class Library Assembly).  I remembered the trusty Microsoft ILDASM tool that comes with the .NET Framework and started there.  For those of you .NET developers out there who aren’t familiar with this tool, you should know that it is your friend. ILDASM (Intermediate Language Dis-ASseMbler) basically lets you view the MSIL code (Microsoft Intermediate Language) of the assembly, which the .NET CLR (Common Language Runtime) actually executes.  MSIL is not directly executable, and isn’t something you would want to attempt to program in (that’s why its an intermediate language), but it can provide much insight as to how an assembly is written.

But then I stumbled on this article which talks about .NET Relector, which is like the greatest thing since Red Bull ™.  Reflector essentially decompiles a .NET Assembly into your choice of MSIL, C#, VB, Delphi, MC++, or Chrome.  It is a beautiful thing.

Important note: both tools decompile for “view only”.  It doesn’t decompile back to original source files.  I suppose if you really want to decompile, edit, then recompile, you’d probably have a lot of copy-and-pasting to do.

Our Server is FAIL. It has a bad.

So I get into work today only to find our primary server (PDC, AD, DHCP, DNS, Exchange, and File Services) took a dump. The mirror broke, but it was still functional, which means an immediate backup of the data was in progress and I can’t touch anything until after the mirror is rebuilt. I can see all kinds of questions and remarks whirling around in your head….. but don’t get me started…..

I am not the “powers that be” so we’ll just leave it at that. Luckily, I do the majority of my work on my local machine, and I only push archives, version releases, and/or “checkpoints” to the server. I handle backups of my workstation myself…. and I do it on a regular and automated basis.

Anyhoo….

For any of you .NET developers, I discovered a SWEET control you can use in your WinForms applications called “C# List View” (or Glacial ListView) which allows for embedded controls within the rows, color-coding, etc.  Very cool.  I’m implementing it in a couple of proprietary projects I’m working on, and I gotta say… it’s been a boon.  Being able to produce a grid view with progress bars in it (dynamically) is f**kin sweeeeeeet.

Speaking of which…. time for me to get back to work!  The Red Bull is coursing through me, can’t let it go to waste!

Get the RootDSE in C#

As you may already know, you can use the following to the domain name of the currently logged in user:

Environment.UserDomainName();

But what if you want the whole (fully qualified) domain name or root DSE?  Well, that is a little trickier.  You start by importing the usual namespaces:

using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;

And then you can use the following method to return an FQDN string (“mydomain”, or “mydomain.local”, or “subdomain.mydomain.local” for example, instead of “DC=mydomain” or “DC=mydomain,DC=local”):

public string GetRootDSE()
{
   string sRootDSE = string.Empty;
   using (DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE"))
   {
      string sRootDSE_DN = rootDSE.Properties["defaultNamingContext"].Value.ToString();
      if (sRootDSE_DN.Contains(","))
      {
         string[] arrSubStrings = sRootDSE_DN.Split(',');
         foreach (string item in arrSubStrings)
         {
           string[] arrParts = item.Split('=');
           if (string.IsNullOrEmpty(sRootDSE))
           {
              sRootDSE = arrParts[1];
           }
           else
           {
              sRootDSE = string.Concat(sRootDSE, ".", arrParts[1]);
           }
         }
      }
   }
   return sRootDSE;
}

So then the following would be true:

string myDomain = GetRootDSE();
// myDomain == "cyrusbuilt.net"

Good times :-)

Weirdness….

Ok, so apparently a few of you noticed some weirdness on with CyrusBuilt.Net. Indeed, I did temporarily have some issues. Sorry about that! I committed the rookie mistake of updating a WordPress plugin without backing up my current plugins directory first….

As it turns out, there is an incompatibility between the Safitech 1.7 WP theme and the WP CodeBox plugin by Eric Bess that I normally use to display the pretty code boxes in my posts.  I was able to fix the problem myself in the previous version of WP CodeBox, but not in Version 1.4.  The problem seems to be with the way Safitech and WP CodeBox uses jQuery.  After messing around with it for a while, I decided it wasn’t worth the trouble any more and I switched to the WP Syntax by Ryan McGeary instead.  Both plugins use the GeSHi syntax highlighter, however WP CodeBox is more advanced in that it provides a collapsible codebox and several other settings, which WP Syntax lacks.  However, WP Synatx works.

As far as I can tell, WP Syntax doesn’t use jQuery at all, and therefore has no compatibility issues with Safitech.  I don’t know which one (Safitech or WP CodeBox) is not quite using jQuery right, but one of them must be doing something that isn’t totally supported.  I’m not totally keen on PHP or JavaScript since my experience is somewhat limited, but I couldn’t find the problem by looking into the source code of either one of them.

Either way, the problem (in my case) is resolved now.

Grrrrrr…..

Get Active Directory Object Category in C#

This is another one of those “out of necessity” scenarios.  I’m currently building an Active Directory-aware application that impliments an Active Directory helper class I wrote to provide methods for working with AD.  A lot of my work was based on this article.  One of the methods I added to my class I decided to share here because I was unable to find a similar solution through Googling.

So the problem I had was, I needed to be able to get the category of an object in AD.  To do this in C#, we first have to import the AD namespaces:

using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;

Then we create an object category enumeration to define what categories to support:

public enum ObjectCategory
{
   User,
   Group,
   Computer,
   Unknown
}

And now we define the method:

public ObjectCategory GetObjectCategory(string ObjectName, string FQDN)
{
   string sDistinguishedName = string.Empty;
   string sConnectPrefix = "LDAP://" + FQDN;
   DirectoryEntry entry = new DirectoryEntry(sConnectPrefix);
   DirectorySearcher mySearcher = new DirectorySearcher(entry);
   string[] sSearches = new string[] {"(&(objectClass=user)(objectCategory=person)(|(cn=" + ObjectName + ")(sAMAccountName=" + ObjectName + ")))"
                                    , "(&(objectClass=group)(objectCategory=group)(|(cn=" + ObjectName + ")(dn=" + ObjectName + ")))"
                                    , "(&(objectClass=computer)(objectCategory=computer)(|(cn=" + ObjectName + ")(dn=" + ObjectName + ")))"};
    foreach (string s in sSearches)
    {
        mySearcher.Filter = s;
        SearchResult result = mySearcher.FindOne();
        if (result == null)
        {
           continue;
        }
        else
        {
           if (s.Contains("objectCategory=person"))
           {
              return ObjectClass.User;
           }
           if (s.Contains("objectCategory=group"))
           {
              return ObjectClass.Group;
           }
           if (s.Contains("objectCategory=computer"))
           {
              return ObjectClass.Computer;
           }
        }
     }
   return ObjectClass.Unknown;
}

Now we can use this like so:

if (GetObjectCategory("John Doe", "Fabrikam.com") == ObjectCategory.User)
{
   Console.WriteLine("John Doe is a User!");
}
else
{
   Console.WriteLine("John Doe is NOT a User!!");
}

I will eventually release a class library project (VS2008/.NET Framework 2.0) which contains my entire helper class and custom exceptions after I complete the application I’m currently working (this could be a lil while).

Happy coding!

Check to See if PowerShell is Installed in NSIS

I recently built a graphical application entirely in Windows PowerShell.  While this may sound like a daunting task, it really wasn’t.  The GUI itself is constructed using the same .NET classes that you would use in C# or VB.  You can cut down your dev time by using a form designer like Sapien’s PrimalForms.  Why build an entire app in PowerShell?

Well, to be honest, part of my motivation was just to see if I could. The other was that my initial script was heavily dependent on Quest’s ActiveRoles Management PowerShell Snapin.  Sure, I could have built the application in C# or VB and then imported the PowerShell namespace for some of this, if I really wanted to, but I’m still new to PowerShell and I saw no reason to port most of my code over when I would still be dependent on PowerShell and QARMS anyway.

So naturally, when I went to build an installer for this application, I had to consider that I couldn’t count on the end user having QARMS installed, nor could I assume that every user (especially prior to Windows Vista) would have PowerShell 1.0 or higher installed.

I usually build on my installers in the wonderful Nullsoft Scriptable Install System.  It is VERY powerful, extensible, and open source.  Plus it was originally developed by Nullsoft (of WinAMP fame).  Since PowerShell is the primary external dependency for my application, one of the first things I would need to do is to check to make sure PowerShell is installed.  After googling around for a simple solution and not finding one, I decided to just write a function to do the check myself.  As I expected, everything I needed to know was in the Registry.  So I just look for the appropriate value and return either a “1″ (True) or a “0″ (False).

Like so:

Function IsPowerShellInstalled
  Push $0
  ReadRegDWORD $0 HKEY_LOCAL_MACHINE "SOFTWARE\Microsoft\PowerShell\1" "Install"
  StrCmp $0 "" psNotInstalled psInstalled
  psNotInstalled:
    Push 0
    Goto psCheckDone
  psInstalled:
    Push 1
  psCheckDone:
    Return
FunctionEnd

And here is how you would use it:

Section "Windows PowerShell 1.0" SEC02
  DetailPrint "Checking to see if PowerShell is already installed..."
  Call IsPowerShellInstalled
  Pop $0
  StrCmp $0 0 _psNotInstalled _psInstalled
  _psNotInstalled:
    SetOutPath "$TEMP"
    SetOverwrite ifnewer
    DetailPrint "Installing Windows PowerShell..."
    File "WindowsXP-KB926139-v2-x86-ENU.exe"
    ExecWait '$TEMP\WindowsXP-KB926139-v2-x86-ENU.exe'
    Goto EndPSInstalled
  _psInstalled:
    DetailPrint "PowerShell is installed.  Skipping installation..."
  EndPSInstalled:
SectionEnd

This, of course, would need to be modified if you need to install a different version of PowerShell (64-bit, for example) or if you need to check for PowerShell v2.0 instead of 1.0, but you get the jist of it.

Setting User Permissions on a Folder in PowerShell (Even if it’s a UNC Path)

Ever needed to assign permissions to a remote directory?  This is something I’ve been trying to do for QUITE some time.  Luckily, I’ve recently embraced…. and subsequently fell in love with…. Windows PowerShell.  I’ve read some people state that it is a language that is hideous to look at, but I disagree.  While typing lengthy piped commands at the shell prompt can be kind of ugly, actually writing a script using a proper editor or IDE is very similar to the C# language.  And what a powerful language it is!!!  Commands can be piped into other commands, like in BaSH.  The difference is that PowerShell outputs data as objects instead of text.  Also, PowerShell embraces WMI, COM, all the pre-existing CLI commands and utilities found in Windows already, and more importantly: The .NET Framework.

If that wasn’t good enough, there are a boatload of Snappins and Extensions available and you can even write your own.  As a result, there isn’t much you can’t do with PowerShell.

So, after doing a little research, I found a way to set the security (permissions) on a directory.  Coincidentally, this method works on both a local directory and a UNC path.  The following function sets the specified rights, for the specified user, on the specified path (and all child folders and files within it).

## FUNCTION ##############################################################################
## Name ........: Set-UserAccess
## Description .: Sets/Modifies user permissions on a given directory (and all files/folders
##                within it.
## Syntax ......: Set-UserAccess -Path "C:\some\folder\name" -User "MyDomain\MyUserName" -Permission "Modify"
## Parameters ..: Path       - The full path to the directory to set permissions on (UNC paths are supported).
##                User       - The name of the user to grant permissions for (ie. "MyUserName" or "MyDomain\MyUserName").
##                Permission - The permssion to set ("Read", "Write", "Modify", "FullControl").
## Return Value : Success - $true
##                Failure - $false
## Author ......: Chris Brunner
## Modified ....:
## Remarks .....:
## Related .....:
##########################################################################################
function Set-UserAccess {
	param (
		[String]$Path,
		[String]$User,
		[String]$Permission
	)
	if (Test-Path -Path $Path -PathType Container) {
		## Get the current ACL.
		$acl = Get-Acl -Path $Path
 
		## Setup the access rule.
		$allInherit = [System.Security.AccessControl.InheritanceFlags]"ContainerInherit", "ObjectInherit"
		$allPropagation = [System.Security.AccessControl.PropagationFlags]"None"
		$AR = New-Object System.Security.AccessControl.FileSystemAccessRule($User, $Permission, $allInherit, $allPropagation, "Allow")
 
		## Check if Access already exists.
		if ($acl.Access | Where { $_.IdentityReference -eq $User}) {
			$accessModification = New-Object System.Security.AccessControl.AccessControlModification
			$accessModification.value__ = 2
			$modification = $false
			$acl.ModifyAccessRule($accessModification, $AR, [ref]$modification) | Out-Null
		} else {
			$acl.AddAccessRule($AR)
		}
		Set-Acl -AclObject $acl -Path $Path
		Return $true
	} else {
		Return $false
	}
}

The following is an example of how you could use it:

#Create a folder in a remote share.
$path = "\\myserver\UserData\john.doe"
New-Item -Path $path -ItemType 'directory'
#Set the permissions.
if (Set-UserAccess -Path $path -User "mydomain\john.doe" -Permission "Modify") {
   Write-Host "User permissions set!"
} else {
   Write-Host "Set user permissions failed!!!"
}

That’s it!  Enjoy!

Outlook Anywhere via GPO/Logon Script

This is something I beat my head against the wall over for 2 weeks….. and I nearly gave up.

The Problem:

Ok, so I had client that recently migrated Active Directory for…… from one domain to other and from Microsoft Windows Server 2003 32bit (on the primary DC) to Microsoft Windows Server 2008 64bit.  This was not an easy task, to say the least.  Nonetheless, we completed our objective and then began takling the Exchange server migration.  The mail server was also migrated to Server 2008 and Exchange 2007.  This of course means that all the user workstations and such had to migrated to the new domain and be able to talk to the new mail server.  I’ll spare all the intricacies and details and focus on the main problem I just solved:

There is a mixture of both Outlook 2003 and Outlook 2007…. and we HAD to enable Outlook Anywhere (RPC over HTTPS) per the customer’s request.  Now at first, this doesn’t seem like too big of a deal.  And when it comes to Outlook 2007, it’s really fairly straight forward.  There is several articles, including Microsoft Knowledge Base articles about configuring Outlook Anywhere via GPO using the appropriate administrative template and the Office Administration Templates for Group Policy.  However, there IS NO administrative template for Outlook Anywhere (RPC over HTTPS) for Outlook 2003…… or at least not one that I could find anywhere.  Microsoft does not appear to have made one.  In short, you can configure all the necessary settings via GPO when dealing with Outlook 2007 clients, but not for Outlook 2003.

The Solution:

I set out to solve this problem via GPO but there just isn’t a way to do this via GPO, except to define a logon script.  After much research and digging I was able to find a few registry that pertained to  RPC over HTTPS.  Eventually I spent spent some time using Regmon to capture registry changes made by Outlook.  I discovered that all the values of interest are stored here:

HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\13dbb0c8aa05101a9bb000aa002fc45a

You’ll notice the root is HKEY_CURRENT_USER, which means these settings are applied on a per-user basis.  If the user’s local Outlook profile has not yet been set up, then this key will not exist.  The idea here was to configure one user, then export the necessary registry values and create a script that could import them for everyone else.

This was a tricky task because all the values that needed changing were not given identifiable names like “AuthenticationType”.  They are given names like “00036627″ instead.  Almost all the values were of type REG_BINARY (hex) too, so everything is basically encoded.

So here are the keys that you will need to change:
00036623  (REG_BINARY) = Enables/Disables the “Connect using HTTP” box.
001f6622   (REG_BINARY) = Sets the address for the first text box labelled as https://
001f6625  (REG_BINARY) = Sets the address for the second text box labelled as “Principal name for proxy server”
00036627  (REG_BINARY) = Sets the the authentication type.  (01000000 = Basic, 02000000 = NTLM)
00036601 (REG_BINARY) = Sets cached Exchange mode.   (84010000 = Enabled.   84050000 = Enabled with public folders/favorites.  04000000 = Disabled.)
001e6608 (REG_SZ) = Stores the TCP/IP address, the NetBIOS computer name, or the DNS FQDN used to create the initial profile.
001e6602 (REG_SZ) = Stores the NetBIOS computer name where the mailbox is located.

I CANNOT STRESS THIS ENOUGH:
It is imperative to manually configure a client first…. then export the keys to get the correct values.
Once you have the right values, you can import a .reg file using a script deployed via GPO or you can just code your script to write the values directly to the registry.  This way you only have to configure one client and then the rest of your clients can be configured automatically.

Hope this helps!