Sometimes I write code that I think I need but never end up using. This was the case with the parseCommandLineString() function that I wrote in Visual Basic .NET. I needed a function that would take a command line string that included arguments and parse it in the same way that Environment.ParseCommandLineArgs() does. Why? Because System.Diagnostics.ProcessStartInfo uses two properties that separate the executable file name from the arguments. Why Microsoft left this functionality out of the framework is beyond me. Anyway, there is a method build into the Windows API that can parse arguments from a command line string: CommandLineToArgv(). Unfortunately, calling it in VB .NET requires Marshalling and I couldn’t find a good example online. Here’s my code:
Private Declare Function CommandLineToArgv Lib "shell32.dll" Alias "CommandLineToArgvW" (ByVal lpCmdLine As String, ByRef pNumArgs As Integer) As Long
'''
''' Summary: Parse the command line string so that it can be used with System.Diagnostics.Process. I chose to use the Windows API here to ensure that the command line parsing is consistent with how Windows handles it.
'''
''' Parameter command: The string that should be parsed
''' Returns: An array of command line arguments similar to what Environment.GetCommandLineArgs() produces.
''' It sure would be nice if the framework had a method for doing this. It becomes a drawback of using System.Diagnostics.Process, which requires arguments to be separated from the executable.
Private Function parseCommandLineString(ByVal command As String) As String()
Dim numargs As Integer
Dim t As Integer
Dim ptrCommand As IntPtr = Marshal.StringToHGlobalUni(command) 'Marshal the string to a pointer
Dim ptrSplitArgs As IntPtr = CommandLineToArgv(ptrCommand, numargs) 'Pass the pointer to CommandLineToArgv for parsing, retrieve the pointer of the result.
If ptrSplitArgs = IntPtr.Zero Then Throw New System.ComponentModel.Win32Exception 'Is it a valid pointer? Throw an exception if it isn't.
Dim splitargs(numargs - 1) As String
For t = 0 To numargs - 1
splitargs(t) = Marshal.PtrToStringUni(Marshal.ReadIntPtr(ptrCommand, t * IntPtr.Size)).Trim 'Iterate through the arguments and add them to an array.
Next
Marshal.FreeHGlobal(ptrCommand)
Marshal.FreeHGlobal(ptrSplitArgs)
Return splitargs
End Function
UPDATE: I discovered that there is a working NETDOM.EXE for Windows 7. Here’s what you need to do (on a Windows 7 machine) to get it:
NETDOM should be located in your SYSTEM32 folder. If would rather use Powershell to join the domain, since it’s included with the Windows 7 RTM, then please continue reading. I apologize for any confusion.
(Begin Original Post)
Now that the title of this post has your attention, I can tell you that Windows 7 isn’t really missing this important tool that joins a machine to an Active Directory Domain in an automated fashion. Instead, this command-line utility has been superseded by a new command that’s included in Microsoft’s love-it-or-hate-it command line shell: Windows Powershell. Why? Well, Powershell is certainly more powerful than the standard command prompt. But more importantly, Windows 7 is the first version to include Windows Powershell in the RTM build. With Powershell built into Windows 7, perhaps Microsoft saw no reason to continue including and supporting our old pal, NETDOM.
When you’re finished grieving over the loss of our beloved NETDOM, which has joined countless computers to countless Windows Domains (or far inferior Workgroups), it’s time to roll up your sleeves and start working with the successor command: Add-Computer. This command will only run in a Windows Powershell command prompt. The good news, however, is that you can easily run Add-Computer inside Powershell through a normal command prompt (or batch file). To do so, open a command prompt (with elevated privileges) and run this command:
powershell Add-Computer -DomainName "YOURDOMAIN"
See? That wasn’t so bad now was it? If you don’t mind entering credentials to join the domain on every single computer, that’s all you need. But unfortunately, some of us need to automate the process of joining the domain. For that, it gets more complex, and we’ll need a bit more Powershell to make it work.
From a command prompt, you can get more detailed usage instructions for Add-Computer by using this command:
powershell Add-Computer -?
In the syntax section, you’ll find syntax switches that can be used to specify the domain name, OU path, and credentials. For a more details and examples on Add-Computer, you can also use this command:
powershell get-help Add-Computer -detailed
The first thing you should notice is that, unlike NETDOM, there aren’t syntax switches to specify the username and password. Instead, there is a switch called “-Credential” that takes in a PSCredential object. Therefore, we need to create a PSCredential object with the credentials that will be used to join the computer to the domain before we can actually use the Add-Computer command in an automated way. To do this, we’ll need to create a Powershell script.
If you have never used Powershell before, you’ll probably say to yourself, “PSCredential object? What is that!?” I’ll give you this very brief explanation: PSCredential is an object that can securely store Windows credentials. Furthermore, Powershell is more like full-blown Object-Oriented scripting language than a shell language. Like DOS, it has a command prompt. However, the differences usually end there. Anyway, this article isn’t about Powershell, but if you want to know more about it, start Googling. Or you can just continue on to get the Powershell script.
The Powershell script needed to join the domain contains only two commands. Create a new text file named “joinDomain.ps1″ and put the following powershell code into it:
$credential = New-Object System.Management.Automation.PsCredential("MY.DOMAIN.COM\user", (ConvertTo-SecureString "mypassword" -AsPlainText -Force))
Add-Computer -DomainName "MY.DOMAIN.COM" -Credential $credential -OUPath ("OU=Computers,DC=MY,DC=DOMAIN,DC=COM")
The first line of the script creates a new System.Management.Automation.PsCredential object. PsCredential takes in two parameters: a string containing a username and a secure string containing the password. You should change “MY.DOMAIN.COM\user” to the user that will join the computer to the domain. Change “mypassword” to the password of that account.
The second line is the Add-Computer command. “MY.DOMAIN.COM” should be changed to the domain that the computer is joining. Change OUPath to the OU String that points to the OU container that the computer object should be placed in.
To run the Powershell script above, you need to open an elevated command prompt. To run it, type powershell ./joinDomain.ps1 and press enter. In many cases, you will find that you’re not allowed to run the script, despite running the command as an administrator:
>powershell ./joinDomain.ps1
File joinDomain.ps1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details.
The funny part about Powershell is that, by default, it is configured to only allow the execution of signed scripts. This is a security feature so that unauthorized or malicious scripts that could compromise the system can’t be executed. After all, Powershell is quite power-ful. Unfortunately, this really tends to confuse and frustrate people. To get around this, you can temporarily change the execution policy, and then change it back:
powershell Set-ExecutionPolicy Unrestricted powershell ./joinDomain.ps1 powershell Set-ExecutionPolicy Restricted
You can also change the execution policy to allow only signed scripts and scripts created by you. For more information about the Powershell execution policy, check out this article.
Now that you are able to automate a domain join with Powershell instead of NETDOM, there is one final thing that I want to mention. In the script above, the password String was converted to a SecureString by using the “-AsPlainText -Force” arguments. Using SecureString in this way is generally discouraged as it defeats the whole purpose of having a secure string. Furthermore, having account credentials in plain text with in the script is insecure and generally a bad idea. I’m guessing that this is the reason why Microsoft left out the “/userD” and “/passwordD” parameters from the NETDOM command and made it more slightly difficult to include the credentials in plain text. You should limit the rights of the account you’re using to automatically join the domain so that it cannot be used to delete Active Directory objects, access network shares, etc. You should also consider other methods of storing the credentials. This article has an alternative method for storing credentials used in Powershell that may meet your needs.
Since I maintain the Windows XP Image for our lab machines, I was tasked with doing the same thing with Windows 7. When I rewrote our imaging tools last Spring, I created a WinPE 2.0 image that included the WMI package so that the imaging application could perform WMI queries in PE. I used WMI only to get basic information about the machine (Such as the Manufacturer, Model, Serial Number, and Disk Information).
While upgrading the WinPE image from 2.0 to 3.0 in anticipation of deploying Windows 7, I discovered that the Windows PE 3.0 base image (the same one that comes with the Windows AIK) was missing Win32_DiskPartition. I use this class to retrieve the number of partitions on the system disk so that I can make sure that each partitition (and its volume) has been assigned a drive letter so that I can search each drive for a preexisting configuration file. But why would Microsoft remove this class!? On a standard Windows 7 machine, the command “WMIC.EXE PARTITION” returned a list of partitions on the system, confirming that I wasn’t losing my mind. However, the command returned nothing when I tried it in Windows PE 3.0.
After lots of searching, I finally came across a post on Microsoft Technet where another developer ran into the same issue. Fortunately, he found a simple, but somewhat obscure, solution:
Using the Windows 7 wbem folder in your WinPE 3.0 image will make it several Megabytes larger, but at least you will be able to get to the missing WMI class(es) that you need!
Update 3/11/2010: After upgrading to the latest version of the Microsoft WAIK, I was unable to overwrite existing files in the WBEM folder (Access Denied). For some reason, the permissions are different on this folder now. Here is the workaround:
Once the permissions are updated, you will be able to overwrite files in the WBEM folder.
There are some software packages that introduce a lot of interesting complexities that SCCM 2007 gets confused by. One of the applications that I was trying to test and deploy was giving me a particularly interesting problem: all of the clients downloaded the package, but when they checked the content, it did not match the source.
The error, which had an ID of 10057, was found by going to System Status -> Advertisement Status -> <advertisement name> -> Show Messages on the actions pane:
The program for advertisement “SIT00001 has failed because download of the content “SIT00029″ – “Per-system unattended” has failed. The download failed because the content downloaded to the client does not match the content specified in the content source.
Possible causes: The content on the distribution point has been manually modified, or a local administrator on the computer has modified the content in the computer’s hash. Solution: Refresh the content on the distribution point and retry the download.
The solution seemed obvious: update the distribution points. But multiple tries, including recreating the package and advertisement completely, did not fix the problem. Finally, I stumbled upon a forum post that helped me narrow the problem down to one of two scenarios:
Binary Differential Replication – If this is enabled in the package configuration, some packages seem to fail. I’m assuming that they can’t handle this kind of replication and several of the files become corrupt, creating a hash mismatch. This can be turned off by opening up the package properties, going to the Data Source tab, and unchecking Enable binary differential replication. This wasn’t my problem because I hadn’t enabled binary differential replication.
Hidden Files – Apparently, if the package source contains hidden files, SCCM may not calculate the correct hash for the package and clients could encounter an error. I found a quick way to check this using the command line:
The package finally verified properly and the advertisement completed.
If you’re planning on using System Center Configuration Manager 2007 for its ability to distribute software over the internet and throttle large file transfers to avoid saturating the network, you may end up spending a lot of time scratching your head when packages start downloading to a client and then suddenly stop.
Such was the scenario I experienced while testing BITS with one of the applications we plan to deploy to clients. The client machine would begin the transfer and start the download… and then stop. Even worse, the issue never resolved itself, even when the machine was left running overnight. Since the issue was less-than-obvious (hint: It was not SCCM that was misconfigured) I decided to list a few tips in the BITS troubleshooting process that may be of use to others. (more…)
While designing and testing a deployment process for Windows Vista using System Center Configuration Manager 2007 I ran into a seemingly obscure problem: Vista refused to use drive letter C as the OS Volume and instead chose D as the system drive letter. The result is that the root of the system drive was D:\ instead of C:\, which is something that legacy applications are not fond of.
This would seem like an easy problem to resolve. Surely, it is caused by the way the disk is partitioned or perhaps the drive letter that SCCM applies the image to is incorrect. Perhaps it’s a registry setting in the image file that needs to be modified offline. I experimented with all of these things, with no luck. Finally, I came across a technet blog entry that I had missed with previous Google search queries:
Several people have tried to use the install.wim from the Windows Vista installation media in an Install an existing image package task sequence. They are surprised to discover that, upon completion, the operating system is on the D: drive instead of the C: drive. The short explanation for why this happens is that the operating system volume for the images in install.wim is D:. In other words, when the image was captured, the reference machine had the operating system on volume D:. Why this is the case for the install.wim that ships on the Windows Vista installation media is beyond the scope of this blog.
So essentially, you can’t use the install.wim image from Vista in SCCM if you want to use C for the system drive letter. That would have been nice to know….
Some time ago, I devised a scheme for backing up my critical data that was both simplistic and inefficient. Every other morning at 4:00am I would create an image of my hard drive using DriveImageXML and then copy it to my NAS using the Windows Task Scheduler. This worked great for a while. However, as my hard drive contents grew it began to take too long to do these backups. Moving into an apartment that required me to use a wireless network was the final nail in the coffin and I finally discontinued this practice.

Since then, I have tried several different free backup solutions. None of them worked quite how I liked, for various reasons. Additionally, many of the ones that promised “set it and forget it” features rarely worked as advertised. In fact, after a month of using AceBackup I discovered my automated backups weren’t working for some reason. When a backup application has failed once, it’s hard to trust it again with such a critical task. I’ve heard a lot of great things about Jungle Disk but was reluctant to start paying monthly fees for a backup service that I ought to be able to handle myself.
Then, I stumbled upon the open source utility Areca Backup while looking for an acceptable backup solution for my work machine. Had I noticed at first that it was written in Java, I probably wouldn’t have tried it. However, I went ahead and installed it and I must say that this is the best backup utility that I have come across, at least for my needs.
Getting e-mails after backups is also a nice feature. Custom e-mail notifications can be set up for each backup. I setup mine to tell me in the subject of the e-mail if the backup was successful (1) or not (0). That way, I can filter successful backups that I don’t need to see to a reports folder while still getting the message when a backup fails.
The user interface of Areca Backup is surprisingly intuitive. I was able to create a compressed, encrypted, network-based backup with little trouble at all. There are several storage modes and backup types and backups are very fast. The coolest part about this software is the logical view screen. It allows the user to peek into their backups at files and restore any version of a file that they please. To recover, simply right click and select Recover… and then point to the folder where you want the recovered file(s) placed. Another incredibly useful feature is the ability to search within your archives for files.
The only part of this backup utility that seems half-baked to me is the scheduled backups feature. Areca does not run as a service (which can be good and bad) so it is up to the user to create a Windows Scheduled Task to automate archiving. Fortunately, the Areca has a feature that will automatically create the batch file with backup commands so that the user is only required to point their scheduled task to execute a file instead of manually entering the command line parameters. I can live with that.
Overall, this seems like a great open source project and I am impressed with how the software has worked so far. My plan is to start using this at home for backups both to my NAS and an offsite FTP/SSH server. Check it out.
For deploying Windows XP to computers in our lab environment, we use 2GB 200x USB drives manufactured by Apacer. The reason for using these drives is simple: They are much faster and more reliable than DVD media. For Vista, we need to upgrade to 8GB drives (4GB would probably work for now, but having some padding is nice). The 8GB version of thet Apacer drive is very expensive (some retailers have it listed at over $100). Since we will eventually need to buy a lot of 8GB drives, I decided to look for some chreaper alternatives that have similar performance. I came across the Super Talent Pico Drive, which has fairly good performance reviews when compared to the Apacer drive. At $26, it’s a bargain. I decided to order one for testing.
I was shocked at how small this drive was. I would be reluctant to put most USB drives on my keyring because they are too bulky but this drive is really perfect accessory to your keys. The USB connector folds out of the casing. The one thing I dislike about the connector is that it allows you to put the key in to a USB slot backwards, so you have to pay attention when plugging it in.
In terms of performance, it is plenty fast for what we need. It is definitely faster than DVD media and may even have faster reads than the Apacer models we have used. If I have time someday I will do a side-by-side comparison. Writes to drive, while not blazing, are definitely fast enough for our needs since we only need to read data from the drive during our build process.
The verdict is still out for the sturdiness of this device. The USB connector is constructed of plastic that seems to scratch easily. I’m not sure if this will cause problems down the road. When the connector is folded into its case it seems to be fairly safe from unintentional damage.
Overall, I am really impressed with this USB drive. Having a USB drive with both gobs of space and great performance is spectacular, especially at this price. Super Talent has several other models that we may look at in the future.
I originally wrote this report last Fall due to ongoing issues with ResCom at the Mont Alto campus. This document was distributed around campus and to staff members of Housing and Food Services .