Friday, 20 February 2015

Outlook Profiles: Prevent the account setup wizard from being populated by the AD

Hi Folks,

Consider the following scenario:

Users in a cross-forest environment are migrated from one exchange server in Domain A and must connect to the new exchange server in domain B with their new credentials. Their AD attributes gets changed in the migration process to contain their new email address from domain B, however user’s workstation are still in logged-in domain A. The users create a new mail profile in Control Panel -> Mail and the auto-discover account setup wizard shows up pre-populated with their email address from the domain and the password fields are hidden.

The problem is:  The users does not enter their new password or change the email address and click next, the auto-discover tries to authenticate with their windows credentials from Domain A to domain B and authentication is failing. A credentials prompt shows up containing the wrong email address (legacy from domain A) and the confused users enters is old credentials. Since there is no trust being the domain A and B, authentication fails over and over again until their account gets locked.

The best solution would be to show up the autodiscover window without pre-populated fields or pre-populated email address with the password field visible EVEN if the user is connected to a domain. Well that's possible using a registry key, here's how (where %v% is the outlook version):

Key: HKEY_CURRENT_USER\Software\Microsoft\Office\%v%\Outlook\Preferences
DWORD: ExchangeAddressDetect

Deploying this key by GPO or with a custom script will disable the active directory lookup in the wizard. See this technet for more information : 

I hope this is as helpful to you as it was to me,

Friday, 14 November 2014

Silently Configure an exchange 2013 profile : VBScript and ProfMan.dll


There's multiple enterprises right now migrating to Exchange 2013 from (2003,2007,2010). When such a migration occurs, the users must configure their Messaging Email profile to point to the new server. A simple script deployed by GPO within the domain can silently configure this on logon and make it transparent for a user.

There's multiple options at hand to do so, but it would be wise to buy a commercial product like Redemption that comes with a profile manipulation dll named ProfMan instead of trying to achieve this with the Outlook 2010 MAPI headers and C++

I'm aware that there's other tools available. However, Redemption is cheap and offer much more control over what you're doing that other out-of-the-box tools.
Here's a simple example on how to do a cross-platform ( Windows XP,Vista,7,8 (x86,x64)) and cross-client (Outlook 2003,2007,2010,2013) profile configuration script.

This could be done easily with .NET but in this case, Windows XP users might not have .NET installed so VBScript is used.

First step here is to declare a couple of Global Constants

Options Explicit

Const HKEY_LOCAL_MACHINE = &H80000002
Const PR_ROH_FLAGS = &H66230003
Const PR_ROH_FLAGS_HTTP_SLOW = &H00000021
Const PR_ROH_PROXY_SERVER = &H6622001F
Const PR_PST_PATH = &H6700001E
Const PR_DISPLAY_NAME = &H3001001E

Const EXCH2013_PROFILE_NAME = "2013 profile"
Const EXCH2013_SERVER = "exchange.mydomain"
Const EXCH2013_PROXY  = "exchange.mydomain"
Const PROFILE_AUTH_SCHEME   = "Negociate" 'Basic, Ntlm, Negociate

Dim olBitness, osBitness
Dim m_objNetwork, m_objShell

Call Main

Okay, once we've got that going we need to retrieve the user version of Outlook and check the OSBitness and Outlook bitness. If the script is not in the correct bitness, the script will restart itself with the correct cscript executable.

Sub CheckOutlookBitness
Dim processShell, processEnv, system_architecture
 Set processShell = CreateObject("WScript.Shell")
 Set processEnv = processShell.Environment("Process")
 osBitness = processEnv("PROCESSOR_ARCHITECTURE")
 If osBitness = "x86" Then
  system_architecture = processEnv("PROCESSOR_ARCHITEW6432")
  If system_architecture = "AMD64" Then
   osBitness = "x64"
   osBitness = "x86"
  End If
  osBitness = "x64"
 End If
 Set processEnv = Nothing
 Dim outlookVersion : outlookVersion = GetOfficeVersion
 Dim strOutlookVersion : strOutlookVersion = outlookVersion & ".0"
 If outlookVersion = -1 Then
  Call Toolbox.DisplayMessage( Messages.OutlookVersion, False )
  WScript.Quit -1
 End If
 If osBitness = "x86" Then
  olBitness = "x86"
 ElseIf CInt(outlookVersion) <= 12 Then
  olBitness = "x86"
 ElseIf osBitness = "x64" Then
  'Create outlook to get version, and retrieve registry keys  
  Dim strBitKeyPath : strBitKeyPath = "SOFTWARE\Microsoft\Office\" & strOutlookVersion & "\Outlook"
  'Retreive bitness
  Dim olReg : Set olReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
  Dim arrValues, strValue, arrTypes, intValue
  olReg.GetStringValue HKEY_LOCAL_MACHINE, strBitKeyPath, "Bitness", strValue
  olBitness = strValue
 End If
 Set processShell = Nothing
 'Check bitness
 Dim restartCS : restartCS = False
 If osBitness = "x86" Then
   restartCS = False
 ElseIf olBitness = "x86" Then
  If Not LCase(Right(WScript.FullName, 20)) = "syswow64\cscript.exe" Then
   restartCS = True
  End If
 ElseIf olBitness = "x64" Then
  If LCase(Right(WScript.FullName, 20)) = "syswow64\cscript.exe" Then
   restartCS = True
  End If
  WSCript.Echo "A version of outlook was not found"
  WScript.Quit -1
 End If  
 'Force script execution in cscript.exe in the correct bitness
 Dim arg, Str
 If restartCS Then
   For Each arg In WScript.Arguments
    If InStr(arg, " ") Then arg = """" & arg & """"
    Str = Str & " " & arg
   'Outlook 32 bit on 64 bit OS
   If osBitness = "x64" And olBitness = "x86" Then
    CreateObject("WScript.Shell").Run _
    "%SystemRoot%\syswow64\cscript.exe //nologo """ & _
    WScript.ScriptFullName & _
    """ " & Str
    CreateObject("WScript.Shell").Run _
    "cscript.exe //nologo """ & _
    WScript.ScriptFullName & _
    """ " & Str
   End If
 End If
End Sub
Function GetOfficeVersion
  On Error Resume Next
  GetOfficeVersion = -1
  Dim sValue
  sValue = m_objShell.RegRead("HKCR\Outlook.Application\CurVer\")
  If Err.Number <> 0 Then
   Exit Function
  End If
  Select Case sValue
   Case "Outlook.Application.15" : GetOfficeVersion = 15
   Case "Outlook.Application.14" : GetOfficeVersion = 14
   Case "Outlook.Application.12" : GetOfficeVersion = 12
   Case "Outlook.Application.11" : GetOfficeVersion = 11
   Case "Outlook.Application.10" : GetOfficeVersion = 10
   Case "Outlook.Application.9"  : GetOfficeVersion = 9
   Case "Outlook.Application.8"  : GetOfficeVersion = 8
  End Select
  On Error Goto 0
End Function

Now that we have the bitness in hand and the script is started in the correct cscript, we must register the ProfMan COM+ dll in order to use it. Notice the sleep here, this is required. The dll might not be totally registered once we try to create the object later on if there's no sleep. 5 seconds looked to work for me on most computers.

Sub RegisterProfman
 scriptPath = Left(WScript.ScriptFullName, (Len(WScript.ScriptFullName) - Len(WScript.ScriptName)))
 'Register profman
 If osBitness = "x64" And olBitness = "x86" Then
  CreateObject("WScript.Shell").Run _
  "%SystemRoot%\syswow64\regsvr32.exe """ & scriptPath & "ProfMan.dll""" & " /s"
 ElseIf olBitness = "x64" Then
  CreateObject("WScript.Shell").Run _
  "regsvr32.exe """ & scriptPath & "ProfMan64.dll""" & " /s"
  CreateObject("WScript.Shell").Run _
  "regsvr32.exe """ & scriptPath & "ProfMan.dll""" & " /s"
 End If 
 WScript.Sleep 5000
End Sub

Once the dll is registered, we can now create the profile. The user email address must be retrieved some way or the other in order to do so. We will assume in this case that the target exchange is in the same AD forest and that the user and that he logs on Windows with is active directory account. The code below is the equivalent of doing "manual configuration" in the account configuration wizard. When not going through the auto-discover, you must set a proxy address for Exchange 2013. This is usually the same address as the exchange itself. The authentication scheme differs depending on configuration, it's usually set at Negociate.

Function CreateProfile
 CreateProfile = False
 Dim strUsername, strServername
 'Get user name and server name from INI
 strServername = EXCH2013_SERVER
 strUsername = m_objNetwork.Username
 Dim m_objProfMan, newProfile, services, service, exService, properties, profSec, profile

        'Create the ProfMan object
        Set m_objProfMan = CreateObject("Profman.Profiles")
 'Check if the profile is already existent
 For profile = m_objProfMan.Count To 1 Step -1 
                If m_objProfMan.Item(profile).Name = EXCH2013_PROFILE_NAME Then
   Exit Function
  End If
 'Does not exists, create new
 Set newProfile = m_objProfMan.Add(EXCH2013_PROFILE_NAME)
 'Add exchange service
 Set exService = newProfile.Services.Add("MSEMS", "Microsoft Exchange", False)
 'Configure auth scheme and proxy server
 Dim strProxyServer : strProxyServer = EXCH2013_PROXY
 Dim strAuthScheme  : strAuthScheme  = PROFILE_AUTH_SCHEME
 If Not strProxyServer = "_" Then
  newProfile.GlobalProfSect.Item(PR_ROH_PROXY_SERVER) = strProxyServer
  newProfile.GlobalProfSect.Item(PR_ROH_FLAGS) = PR_ROH_FLAGS_HTTP_SLOW
  If Not strAuthScheme = "_" Then
   If LCase(strAuthScheme) = "basic" Then
    newProfile.GlobalProfSect.Item(PR_ROH_PROXY_AUTH_SCHEME) = &H00000001
   ElseIf LCase(strAuthScheme) = "ntlm" Then
    newProfile.GlobalProfSect.Item(PR_ROH_PROXY_AUTH_SCHEME) = &H00000002
   ElseIf LCase(strAuthScheme) = "negotiate" Then
    newProfile.GlobalProfSect.Item(PR_ROH_PROXY_AUTH_SCHEME) = &H00000010
                           Exit Function
   End If
   newProfile.GlobalProfSect.Item(PR_ROH_PROXY_AUTH_SCHEME) = &H00000001
  End If
 End If
 'Confiure EX Service
 Set properties = CreateObject("ProfMan.PropertyBag")
 properties.Add PR_PROFILE_UNRESOLVED_NAME, strUsername
 properties.Add PR_PROFILE_UNRESOLVED_SERVER, strServername
 exService.Configure 0,, properties

        'Configure cache mode ON
        newProfile.GlobalProfSect.Item(PR_PROFILE_CONFIG_FLAGS) = PR_OST_CACHE_PRIVATE

        'Set as default
        newProfile.Default = True
        CreateProfile = True
 'Close memory references
        Set exService = Nothing
 Set newProfile = Nothing
        Set m_objProfMan = Nothing
End Function

The profile is now configured, yay! Credentials will be asked when the user launch outlook with is new profile. You can integrate all these functions in the main, which will look like this.

Sub Main
On Error Resume Next
Set m_objNetwork = CreateObject("WScript.Network")
Set m_objShell = CreateObject("WScript.Shell")

'Check bitness
Call CheckOutlookBitness
If Err.Number <> 0 Then
   WScript.Quit -1
End If

'Register ProfMan
Call RegisterProfman
If Err.Number <> 0 Then
    WScript.Quit -1
End If

'Create profile
Dim bStatus
bStatus = CreateProfile
If Err.Number <> 0 Or Not bStatus Then
     WScript.Quit -1
End If
End Sub

If this is method is interesting in the project or contract you're working on I suggest purchasing a copy of Redemption at You will also be provided with the profman library.

Good luck, have fun

Thursday, 6 November 2014

Fresh Start: Increasing my blog diversity

Good evening Microsoft pilgrims,

It's been a while already since I updated this blog. I had some free time today I decided to check out the last couple of months viewers statistics. The good news is that it's getting more and more viewed, the bad news is that there's still alot to be done to get a respectable audience.

To achieve my audience goals I must update my site more often. However, I've been working in a exchange migration project in the last few months which explains why the SharePoint/.NET content is lacking. At first, when I started the blog, I wanted to focus on .NET exclusively but since I get most of my ideas on what to write about from solutions or problems resolution that I come up with at work it's hard to update this blog when I'm not doing .NET

Thus, this point forward I'm going to expend my range of topics and you will see more frequent posts (I hope so) in the next couple of weeks including some nice MAPI/Outlook code examples that I find the internet is lacking.


Tuesday, 23 September 2014

My load-testing program : Current status


I just released a new version of my testing program:  Web Services Ultimate Load Tester, which is available at :

Since the last time I talked about it back in June, here's the new features and corrected bugs:


  • The program can now pass IEnumerable<t> or Arrays as parameters
  • Added support for some ASMX web services
  • Added the new output window in the GUI project
  • Rest API's (JSON) are now supported in addition to SOAP (ASMX,WCF)
  • The program has been renamed Web Ultimate Load Tester since it's supporting WCF,ASMX and REST services now.
  • UI improvements
  • Added a reset button to switch between endpoints in the same session
  • The stop button can now be used to switch CSV files and keep the same endpoint

Bug fixes

  • Fixed a bug where the program would freeze if more threads than CSV file rows where entered.
  • Fixed multiple bugs where nested objects would not behave correctly
  • Fixed a flickering issue with the progress bar when multithreading inside a the console application.
  • Fixed a bug where an exception thrown would not increment the progress bar
  • Fixed a bug where the program would fail if the log file was not created
  • The logger is now logging exception stack and inner exception message if exists
  • Corrected annoying UI glitches
  • Fixed a critical issue where object parameters would not build correctly.

This small project is stating to be pretty neat. I use it myself alot to load bulk data, stress tests application or just do a quick unit test and I encourage everyone to try it out. If you see any bugs, please report it as an issue in the codeplex website.

Thursday, 12 June 2014

WCF Load Testing: Introducing my new homebrew program


I recently had to load test a WCF service using a CSV file. I looked around on the net to find some kind of f tool that would allow me to easily use my CSV file as an input parameter and hit the service for instance 1 hour or when the calls were done.

Turns out there's about nothing standalone, easy to configure, dynamic and that accepts a CSV file as input parameter.. so I decided to code it myself. So here it is in "alpha" stage version

Everything is open-sourced and on codeplex at
I did some tests in the limited environments I have here so expect some bugs.

The user interface:

This looks simple but took me quite some time to do already and is what I'm qualifing myself as pretty neat. You don't need anything except .NET 4.5 and it's shipped with an installer. The program will load the contract, compile and load the compiled assembly dynamically. There's also a console application as a bonus in the package, if you're like me and prefer to do things like that in mode console you can.

The CSV file basically just states the method call and parameters you want the program to make. You can find more on the documentation on the codeplex site.

Those who whishes to raise issues can do so on the codeplex site. I will not include contributors for now. If you have a request about a feature for 1.0, post a comment in here.

What I'm thinking for 1.0 will be to add more supported bindings, more supported service types like ASMX and may be to rework the "editable binding" feature.