VBScript to Remap your Outlook Profiles to a new server.

April 16, 2008 on 1:45 pm | In General Programming | 4 Comments

Recently recovered from an incident involving the slow and painful death of a MS Exchange Server. Right smack in the middle of the day, with some 800 mailboxes on that one host. We restored the mail stores to another Exchange Server, and ran a hack script to go and correct the homeMDB, homeMTA, and msExchMailServer attributes in the AD to point to the new host. That was the easy part. Then we had all these users with outlook profiles pointing to a dead server. Outlook may be resilient in picking the right server when you point it to the wrong server, but if the wrong server (to which is is pointed) is not available - as in if the server it’s pointed at is DEAD, then it doesn’t know to get the new info from the AD to point you to the correct one. This, of course, is exactly what happened to me an my ilk. We had some hundreds of users without profiles pointing to the right host. You’ve heard of Autoconfig for Outlook 2007, but that only works for NEW profiles, not existing ones suddenly pointing to a dead host. So what to do? Manually have all the users repoint their profiles by talking them through the Mail Control Panel widgets? NO! That’s expensive. Instead, write a script and deploy it as a login script.

Why a Login script? Well, because this script affects the HKEY_CURRENT_USER registry hive which only exists for a particular user when that user is logged on. At least for all intents and purposes that’s true. I think it may be possible to somehow enumerate all users on the HKEY_USERS hive, but I didn’t go that far. I apologize up front for not going the extra mile on this one, but hey, look! A free script.

NOTE: If you copy and paste it, and get a compile error, check for abberant the new-line characters caused by the squishing of my code by the text rendering on the page.


'------------------------------–
'Script RepointProfile.vbs
'Author: Dave Dolan
'Purpose: Repoints exchange profiles to use NewServer instead of OldServer
'------------------------------–
'(Editable Constants) Change OldServer and NewServer below as appropirate for the task
'----------------------------
CONST OldServer = "OldMailHostName"
CONST NewServer = "NewMailHostName"
'----------------------------
' Command Line Usage:
'------------------–
' RepointProfile.vbs [/print:true|false]
' -- (optional) print parameter when set to 'true' displays what it's doing, otherwise it prints nothing
' ---------------------------------------------------------–
' NOTE: VERY IMPORTANT: Run this in the context of the current user to change the profile settings,
'         since it affects HKEY_CURRENT_USER!
'----------------------------------------------------------------------
 
'--------- Edit NOTHING Below this line ----------------------
 
'if they have non-exchange profiles, they're gonna throw (harmless if skipped) errors. I care not.
on error resume next
 
CONST NetBiosValue = "001e6602"
CONST FQDNValue = "001e6608"
CONST xFiveHundredValue = "001e6612"
 
Const HKEY_CURRENT_USER = &H80000001
printParam = Wscript.Arguments.Named("print")
 
if len(printParam) >= 0 then
  if ucase(printParam) = "TRUE" then
     printValues = 1
  else
     printValues = 0
  end if
else
  printValues = 0
end if
 
computerName = "."
 
const BASE_KEY = "Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles"
 
set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & computerName & "\root\default:StdRegProv")
 
objReg.EnumKey HKEY_CURRENT_USER, BASE_KEY, arrSubKeys
 
for each subkey in arrSubKeys
 
if printValues = 1 then
  Wscript.Echo "Subkey = " & subkey
end if
 
subKeyPath = BASE_KEY & "\" & subkey & "\" & "13dbb0c8aa05101a9bb000aa002fc45a"
 
objReg.GetStringValue HKEY_CURRENT_USER, subKeyPath, NetBiosValue, nbServerName
objReg.GetStringValue HKEY_CURRENT_USER, subKeyPath, FQDNValue, fqdn
objReg.GetStringValue HKEY_CURRENT_USER, subKeyPath, xFiveHundredValue, X500
 
fqdn = ucase(fqdn)
 
if printValues = 1 then
Wscript.Echo "Current Values"
Wscript.Echo "------------–"
Wscript.Echo "Netbios Value: " & nbServerName
Wscript.Echo "FQDN Value: " & lcase(fqdn)
Wscript.Echo "X500 Value: " & X500
end if
 
if instr(nbServerName, OldServer) then
 
  nbServerName = Replace(ucase(nbServerName), OldServer, NewServer)
  fqdn = lcase(Replace(fqdn, OldServer, NewServer))
  x500 = Replace(X500, OldServer, NewServer)
 
  objReg.SetStringValue HKEY_CURRENT_USER, subKeyPath, NetBiosValue, nbServerName
  objReg.SetStringValue HKEY_CURRENT_USER, subKeyPath, FQDNValue, fqdn
  objReg.SetStringValue HKEY_CURRENT_USER, subKeyPath, xFiveHundredValue, X500
 
  if printValues = 1 then
   Wscript.Echo ""
   Wscript.Echo "New Values"
   Wscript.Echo "------------–"
   Wscript.Echo "Netbios Value: " & nbServerName
   Wscript.Echo "FQDN Value: " & fqdn
   Wscript.Echo "X500 Value: " & X500
  end if
 
end if
 
next
 
set objReg = nothing
 

Visual Studio 2008 Workflow designer BUG!! (feature?)

April 7, 2008 on 11:53 am | In .NET Coding | No Comments

I just got back from tearing my hair out over a frustrating bug I found in the workflow designer included with Visual Studio 2008. First let me start by saying that developing using sharepoint as a platform for workflow, especially with integration into Visual studio has been an overall pleasant experience. I have been able to work at this much faster and easier in 2008 than I ever had using the workflow extensions for VS 2005. Of course, sharepoint is my focus for a workflow host, so I’m happy with the options I have out of the box. That won’t be the case for many who use workflow for other, non-sharepointy things. You’ll have to wait for the rumored and afamed 2008 version of the extensions to be released in June of this year.

Now, about this bug. As you may or may not be aware, workflow serializes it’s state to a persistence provider when it’s ‘put to sleep.’ This state is basically saved a as state bag (hashtable) of data and pumped as a binary lump to a persistence provider. Each property of each item is known by a key consiting of, among other things, its property name. Now, since multiple activities, or even state variables, can possibly contain properties of the same name, it makes sense to add something else to the mix to identify one as being unique from the other. You might think they’d use some kind of a guid or something, and internally that’s involved somehow, however, there is a different, seemingly more accessible way that workflow foundation solves this problem. Activities that require continuations based on the serialization also require that you specify what’s known as a Correlation Token. This is a pairing of an arbitrary string with an owner activity. Event driven activities, for one example, that wish to consume sequential or related events will need to use the same Corellation Token if they are to act upon the same item. The example that I’m using (and so happens that I was cursing) is the CreateTask to OnTaskCreated pairing. If you want to handle the OnTaskCreated event for a particular CreateTask activity you have to make sure that you’re using the same Correlation token.

Easy enough right? The designer lets you, via the property panel, enter the name of the token, and select the owning activity. That’s fine, of course, unless you start getting to antzy about the order or layout of your activities and you cut one here and paste it there. After you’ve copy/pasted one to re-arrange it, you’ll notice upon inspection of the property grid that all seems to be intact. The correlation token is the same as it was, specifying the same name of the token, as well as the identical owner activity. When I run the workflow after doing this, I discoved something quite strange.

The OnTaskCreated handler wasn’t firing until the next one was created.

After much pain and suffering, and a support call to microsoft, which I paid for, but as of yet have got no response to it, I discovered something in the designer code that explained my problem. (This code appears in the [workflowname].designer.cs file, in the InitializeComponent method’s implementation block.)

The tokens look the same, as in they have the same attributes, but they are two separate instances of the CorrelationToken class. This is probably something that the designer overlooks, because it’s simply creating objects with the values to fulfill the properties I’ve set, instead of backtracking to see if some token or other is already there from before. So I corrected the assignment statements to use the same tokens for both instances, and it’s all better now.

see here:


       correlationtoken1.Name = "someStateTaskToken";
       correlationtoken1.OwnerActivityName = "someStateActivity";
       this.SendDirRejectionNotice.CorrelationToken = correlationtoken1;
...
 
        correlationtoken2.Name = "someStateTaskToken";
        correlationtoken2.OwnerActivityName = "someStateActivity";
        this.SendDirApprovalRequired.CorrelationToken = correlationtoken2;
 

should become this:


       correlationtoken1.Name = "someStateTaskToken";
       correlationtoken1.OwnerActivityName = "someStateActivity";
       this.SendDirRejectionNotice.CorrelationToken = correlationtoken1;
...
 
       this.SendDirApprovalRequired.CorrelationToken = correlationtoken1;
 

I just looked through the rest of the deisgner generated code for the same sort of constructs to make sure that I had no dups. Phew, what a train wreck! I had to correct it in about 7 places.

So, all things considered the experience I’ve had with the workflow and designer has been ok, but this is a glaring hole. You want to hear my theory as to why this happens, and why the event actually fires after the next task is created?

Sure, I’ll tell you. Since the idea of the tokens is to provide a handle on state before and after serialization, it’s probably referenced by it’s values after it’s deserialized, (i.e. when the code tries to hydrate the instance of the workflow, it pairs the tokens with their owners by the names of the instances.) This means that if you always persisted it, and re-hydrated it before the event fires, the duplicate token declaration ‘in code’ probably wouldn’t matter, because it would be ‘reconnecting them’ by their names. But the problem I experienced is becasue the first and second instances of the tokens are both instantiated right after my workflow starts, and they aren’t serialized yet, and this reconnection doesn’t happen yet… and somewhere inside the workflow’s in-the-box code these tokens are compared by reference or some other non-value hash for equality, instead of by value of their members, and of course, they don’t match that way, they’re separate instances!

I don’t know if fixing it like this is A) stable (in that it will survive any change I make in the designer), B) a good idea, which it probably isn’t. It’s a hack to fix a broken workflow for this one very specific issue, and I’m hoping to heck there is a hotfix for it. I wouldn’t recommend it beyond a build cycle or two. I don’t know when this code is regenerated by the designer, nor what causes it to create separate token instances (I do know that copy/paste is one!). I’ll post back if I find out.

I would classify that as a bug. Good luck folks! Love that workflow!

Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds. Valid XHTML and CSS. ^Top^