• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1931
  • Last Modified:

Creating an Outlook account via VBA

I'm trying to learn how to use the Outlook 2007 object to add a new e-mail account.
I've seen that this can be done without VBA using a PRF file, but I'm looking to learn more about the Outlook Object library and how it works.

So "simple" question: How do I use Outlook VBA to create an account (which maybe doesn't support a password, but oh well.)

--
More background: I've spent hours digging through the Outlook.Application object trying to figure out how to add an account.  I see that I can look at the existing ones through Outlook.Application.Session.Accounts, but I haven't found out how to add one.  I was hoping to simply take an existing one, store it in an account object and somehow append it, but the properties all seem to be read-only.

--
Any idea how to add an account to a particular "Store" (which I guess is the Outlook name for a folder like Personal Folders.
0
rspahitz
Asked:
rspahitz
  • 17
  • 10
  • 4
2 Solutions
 
Berkson WeinTech FreelancerCommented:
I don't believe there's a way to do this directly with VBA...  well not really.

I believe that the "Remption" addin (http://www.dimastr.com/redemption/) will allow you to do it, though I must admit that it's been a while since I've looked at the addin.  Give it a shot, it's free.

Another option is to directly create the registry keys for a profile.  That wouldn't change anything until Outlook is restarted, but it should work.
0
 
rspahitzAuthor Commented:
I like the idea of using the registry (although Automation would be much better), but I couldn't find any Outlook e-mail account information in the registry.  Do you have more information about that? (maybe I searched for the wrong thing.)

If I had more than 20 accounts, I would consider Redemption; also, I really wanted to learn the inner workings of Outlook, which I wouldn't get with that.
And I heard that some things can be done through the command line, but so far I only saw references to Exchange Server, which I'm not using.
0
 
OP_ZaharinCommented:
- i might be wrong but i have yet to found Microsoft documentation on creating new profile objects references. i guess for now we still have to use Outlook Profile File to create a new account pragramatically.

- in outlook 2007, the profile registry information can be found in the following key:

HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\
0
What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

 
rspahitzAuthor Commented:
Thanks OP...that's pretty well hidden...and encrypted.
Let me see how well I can handle the VBA to Add a registry entry...been a while since I've done it in VBA.
0
 
Berkson WeinTech FreelancerCommented:
Here's the registry functions that I've used:

Option Compare Database
Option Explicit

Declare Function RegCloseKey& Lib "advapi32.dll" (ByVal hKey&)
Declare Function RegOpenKeyExA& Lib "advapi32.dll" (ByVal hKey&, ByVal lpszSubKey$, dwOptions&, ByVal samDesired&, lpHKey&)
Declare Function RegQueryValueExA& Lib "advapi32.dll" (ByVal hKey&, ByVal lpszValueName$, ByVal lpdwRes&, lpdwType&, ByVal lpDataBuff$, nSize&)
Declare Function RegCreateKeyEx& Lib "advapi32.dll" Alias "RegCreateKeyExA" (ByVal hKey&, ByVal lpSubKey$, ByVal Reserved&, ByVal lpClass$, ByVal dwOptions&, ByVal samDesired&, lpSecurityAttributes As SECURITY_ATTRIBUTES, phkResult&, lpdwDisposition&)
Declare Function RegSetValueEx& Lib "advapi32.dll" Alias "RegSetValueExA" (ByVal hKey&, ByVal lpszValueName$, ByVal dwRes&, ByVal dwType&, ByVal lpDataBuff$, ByVal nSize&)



Type SECURITY_ATTRIBUTES
        nLength As Long
        lpSecurityDescriptor As Long
        bInheritHandle As Long
End Type

Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003

Public Enum RegistrySection
    HKCR = HKEY_CLASSES_ROOT
    HKCU = HKEY_CURRENT_USER
    HKLM = HKEY_LOCAL_MACHINE
    HKU = HKEY_USERS
End Enum



Const ERROR_SUCCESS = 0&
Const REG_SZ = 1&                          ' Unicode nul terminated string
Const REG_DWORD = 4&                       ' 32-bit number

Const KEY_QUERY_VALUE = &H1&
Const KEY_SET_VALUE = &H2&
Const KEY_CREATE_SUB_KEY = &H4&
Const KEY_ENUMERATE_SUB_KEYS = &H8&
Const KEY_NOTIFY = &H10&
Const KEY_CREATE_LINK = &H20&
Const READ_CONTROL = &H20000
Const WRITE_DAC = &H40000
Const WRITE_OWNER = &H80000
Const SYNCHRONIZE = &H100000
Const STANDARD_RIGHTS_REQUIRED = &HF0000
Const STANDARD_RIGHTS_READ = READ_CONTROL
Const STANDARD_RIGHTS_WRITE = READ_CONTROL
Const STANDARD_RIGHTS_EXECUTE = READ_CONTROL
Const KEY_READ = STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY
Const Key_Write = STANDARD_RIGHTS_WRITE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY
Const KEY_EXECUTE = KEY_READ



Sub RegistrySetValue(H_KEY As RegistrySection, RSubKey$, ValueName$, RegValue$)
    'H_KEY must be one of the Key Constants
    Dim lRtn&         'returned by registry functions, should be 0&
    Dim hKey&         'return handle to opened key
    Dim lpDisp&
    Dim Sec_Att As SECURITY_ATTRIBUTES
On Error GoTo HandleErr
    Sec_Att.nLength = 12&
    Sec_Att.lpSecurityDescriptor = 0&
    Sec_Att.bInheritHandle = False
    
        lRtn = RegCreateKeyEx(H_KEY, RSubKey, 0&, "", 0&, Key_Write, Sec_Att, hKey, lpDisp)
        If lRtn <> 0 Then
            Exit Sub       'No key open, so leave
        End If
        lRtn = RegSetValueEx(hKey, ValueName, 0&, REG_SZ, ByVal RegValue, CLng(Len(RegValue) + 1))
        lRtn = RegCloseKey(hKey)
ExitHere:
    Exit Sub

' Error handling block added by Error Handler Add-In. DO NOT EDIT this block of code.
' Automatic error handler last updated at 02-02-2005 13:54:12   'ErrorHandler:$$D=02-02-2005    'ErrorHandler:$$T=13:54:12
HandleErr:
    Select Case Err.Number
        Case Else
            MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Registry Functions.RegistrySetValue"    'ErrorHandler:$$N=Registry Functions.RegistrySetValue
    End Select
' End Error handling block.
End Sub

Function RegistryGetValue$(H_KEY As RegistrySection, SubKey$, value$)
   Dim sKeyType&       'returns the key type.  This function expects REG_SZ or REG_DWORD
   Dim ret&            'returned by registry functions, should be 0&
   Dim lpHKey&         'return handle to opened key
   Dim lpcbData&       'length of data in returned string
   Dim ReturnedString$ 'returned string value
    Dim fTempDbl!
On Error GoTo HandleErr
   If H_KEY >= &H80000000 And H_KEY <= &H80000006 Then
      ' Open key
      ret = RegOpenKeyExA(H_KEY, SubKey, 0&, KEY_READ, lpHKey)
      If ret <> ERROR_SUCCESS Then
         RegistryGetValue = ""
         Exit Function     'No key open, so leave
      End If
      
      ' Set up buffer for data to be returned in.
      ' Adjust next value for larger buffers.
      lpcbData = 255
      ReturnedString = Space$(lpcbData)

      ' Read key
      ret& = RegQueryValueExA(lpHKey, value, ByVal 0&, sKeyType, ReturnedString, lpcbData)
      If ret <> ERROR_SUCCESS Then
         RegistryGetValue = ""   'Key still open, so finish up
      Else
        If sKeyType = REG_DWORD Then
            fTempDbl = Asc(Mid$(ReturnedString, 1, 1)) + &H100& * Asc(Mid$(ReturnedString, 2, 1)) + &H10000 * Asc(Mid$(ReturnedString, 3, 1)) + &H1000000 * CDbl(Asc(Mid$(ReturnedString, 4, 1)))
            RegistryGetValue = Format$(fTempDbl, "000")
        Else
            RegistryGetValue = Left$(ReturnedString, lpcbData - 1)
        End If
    End If
    ' Always close opened keys.
    ret = RegCloseKey(lpHKey)
    End If
ExitHere:
    Exit Function

' Error handling block added by Error Handler Add-In. DO NOT EDIT this block of code.
' Automatic error handler last updated at 02-02-2005 13:54:12   'ErrorHandler:$$D=02-02-2005    'ErrorHandler:$$T=13:54:12
HandleErr:
    Select Case Err.Number
        Case Else
            MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Registry Functions.RegistryGetValue$"   'ErrorHandler:$$N=Registry Functions.RegistryGetValue$
    End Select
' End Error handling block.
End Function

Open in new window

0
 
Berkson WeinTech FreelancerCommented:
Oh, and I meant to mention:

You want to look here-
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\[[[[profile name]]]\9375CFF0413111d3B88A00104B2A6676

under that key, you'll see other keys starting at 00000001 and incrementing up from there.  The information isn't encrypted, it's just binary.  Open a value like "Account Name" under one of these keys and you'll see the ascii text that the binary translates to.

You should be able to add keys to add the email address to the profile.  I haven't done this before, but I'm certain that outlook only reads the registry at startup, so any changes that you do would require an outlook restart.
0
 
rspahitzAuthor Commented:
weinberk, is that entire path my subkey in the function call?

"HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676\00000007"

(Where 0000007 is the 7th entry/account in the group)

And then I have to set each value for all the settings (1 at a time):

ValueName$, RegValue$
"Account Name", "something"
"Email", "this@that.com"
"POP3 Server", "pop.domain.com"
etc.

for each account?
0
 
Berkson WeinTech FreelancerCommented:
The location is right.  I have something like 20 subkeys, but not 20 accounts.  I think this is due to a mix of exchange and pop accounts.

I'd create a pop account using Outlook and then try to look what's changed in the registry.
0
 
rspahitzAuthor Commented:
looks like I'll have to tweak the function to handle the data type...will try to get it running tonight or tomorrow...obviously don't want to mess up registry.
0
 
rspahitzAuthor Commented:
I can't seem to get the registry part working...for some reason I can't read the key.
Maybe I'm not setting it up right?

Both of these return error 2 when reading:

    Const cRegistryRootKey As String = "HKEY_CURRENT_USER"

    Const cRegistrySection As String = "" _
        & "Software" _
        & "\Microsoft" _
        & "\Windows NT" _
        & "\CurrentVersion" _
        & "\Windows Messaging Subsystem" _
        & "\Profiles" _
        & "\Outlook" _
        & "\9375CFF0413111d3B88A00104B2A6676"

    Debug.Print RegistryGetValue$(HK_Current_User, cRegistryRootKey & "\" & cRegistrySection & "\" & "NextAccountID", -1)
    Debug.Print RegistryGetValue$(HK_Current_User, cRegistrySection & "\" & "NextAccountID", -1)
0
 
Berkson WeinTech FreelancerCommented:
First, you're not doing it quite right...  :)

the thrid parameter of RegistryGetValue should be the value you're looking for, so in this case "NextAccountID"    The error code 2 is file not found - there is no key called "-1"  Change the 3rd parameter to "NextAccountID"

Try this, but note that it's returning a STRING, not the hexidecimal you're going to expect.  Convert the string that was returned (in my case 18) to decimal (12) to get the number you really want.
Sub doit()
    Const REGISTRY_BASE_LOCATION As String = "Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676"
    MsgBox RegistryGetValue(HKCU, REGISTRY_BASE_LOCATION, "NextAccountID")
End Sub

Open in new window

0
 
rspahitzAuthor Commented:
With that one, I get err 5 (and an empty message box.
Is it possible that Windows 7 has some permission issues to prevent me from accessing it from VBA without  a digital signature (or something like that)?
0
 
Berkson WeinTech FreelancerCommented:
Interesting, I too got that error in the vba editor within outlook.  

The code was originally tested from within the excel application.  I just tried again and now >I< am getting the error 5 too (permission denied).  

This worked earlier this AM.  I either left something out of the code I copied or there's something strange.

The permissions in the registry look good to me.

I'll check it out later today if I can make the time.
0
 
rspahitzAuthor Commented:
I guess I should dig around for other sources for working with the registry.  If I can get that to work, I think I can close this.
0
 
rspahitzAuthor Commented:
Not sure where it went wrong, but I was able to fix up the code so that I'm able to read/write keys.
The only problem I have so far is that I can't write DWORD keys, so I'm investigating.
After I write a few accounts, I'll restart Office and see if they're recognized.  If so, we're done.
0
 
Berkson WeinTech FreelancerCommented:
I'd be intersted in what you changed in the code...  It definitely worked and let me read the dword, but when you reported it not working, I used my pasted code again and it didn't.  
0
 
rspahitzAuthor Commented:
I almost had it and made a few more tweaks and messed things up...now I have to figure out where I went wrong. Darn it!
(I want to close this since it's essentially solved, but would like to keep it open until I can post something useful about it in my closing remarks.)
0
 
OP_ZaharinCommented:
- we defintely interested to know too if its finally work for u :)
0
 
rspahitzAuthor Commented:
OK....so close.  I figured out my mishap: I accidentally removed a required ByVal.

Now, the last outstanding part is that I still can't write DWORD values.  The first one returns error code 0, indicating a success, except that it writes a value of zero instead of the value passes.
All subsequent calls (for DWORD calls only!) result in error code 998 (which I couldn't identify.)

I modified the code and probably did something wrong since I don't understand what to send for DWORD values.  Here's what I have:


    If DataType = Type32Bit_Number Then
        lErrCode = RegSetValueEx(RegistryKey, RegistryName, 0&, DataType, ByVal CLng(Val(RegistryValue)), 4)
    Else
        lErrCode = RegSetValueEx(RegistryKey, RegistryName, 0&, DataType, ByVal RegistryValue, CLng(Len(RegistryValue) + 1))
    End If

I think the updated variable names are self-evident.
Any thoughts about why the 32-bit (DWORD) values are (1) writing zero only and (2) generating error 998?
0
 
rspahitzAuthor Commented:
Oh, just as a test, I left the partially created entry in the registry and restarted Outlook.  The new entry was created, but all the information came out in Korean!  Apparently I need a bit more info to get all the pieces just right.
Anyone know how Outlook generates a MiniUID? (I think that may fix it.)
0
 
rspahitzAuthor Commented:
Hmmm...also just noticed that Outlook created Unicode entries (with an extra 00 tacked onto each byte) whereas mine are without that.  Easy enough to fix by looping through each byte, but there's probably a better way to create a 2-byte Unicode entry rather than a 1-byte ASCII entry...any ideas, or maybe that's for another topic...

As it is, I'll create a new question with points for those who can help answer the DWORD and double-byte question.
0
 
OP_ZaharinCommented:
- i've read a PRF documentation which i think using UuidCreate() and UuidToString() but i can't recall that documentation to share it with you
0
 
rspahitzAuthor Commented:
FYI
Busy for a bit...will get back to this over the weekend.  Will have to create some related questions to solve the outstanding parts so that I can confirm that the above works.  then I'll close this...

1) How do I create a registry entry with DWORD
2) How do I create Mini UID?

Any suggestions as to which topics to add these into? Window Programming, maybe?  Where else?
0
 
Berkson WeinTech FreelancerCommented:
I'm pretty sure that
lRtn = RegSetValueEx(hKey, ValueName, 0&, REG_SZ, ByVal RegValue, CLng(Len(RegValue) + 1))
needs to have REG_SZ changed to REG_DWORD (which is set as a constant).

Then there's some playing you'll need to do.  Give this a read for a shove in the right direction:
http://www.experts-exchange.com/Programming/Languages/Visual_Basic/Q_11700898.html?sfQueryTermInfo=1+10+30+code+dword+set+vb

I don't know about the Mini UID
0
 
rspahitzAuthor Commented:
Thanks, W.

I updated the routine to pass the datatype then use it:


    If DataType = Type32Bit_Number Then
        lErrCode = RegSetValueEx(RegistryKey, RegistryName, 0&, DataType, ByVal CLng(Val(RegistryValue)), 4)
...

But that didn't seem to work.  Since DWORD/32-bit is 4 bytes, I hard-coded the length.  The problem seems to be that the value shows up in the registry as a hex # (0x000) and I'm passing it a long (which to me shouldn't be a problem but maybe it still needs to be a string?  All references I've seen are in C++ and not VB so the documents use the cryptic c-format that doesn't work in VB.
0
 
Berkson WeinTech FreelancerCommented:
So that I'm not wasting your time, I'll just say it, I don't know...

Maybe open another question for that?
0
 
OP_ZaharinCommented:
i second weinberk, maybe other expert have fresh idea on it.
0
 
rspahitzAuthor Commented:
Well, I never really got any solutions on my obstacles and I've waited long enough for this.

The best solution I got was to create a separate text file containing registry information (based on what I see when I export a registry entry) then import it into the registry.  I think that'll be good enough.
Meanwhile, some of the comments above got me in the right area to allow me to solve it.

Thanks!
0
 
Berkson WeinTech FreelancerCommented:
Make sure if you open another thread on the dword issue that you link it here so that future visitors can easily find it - plus we're interested!
0
 
rspahitzAuthor Commented:
FYI
I found anther registry entry that needs to be updated to create an account.  Here's the summary that I've found so far:

For this path:
HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\9375CFF0413111d3B88A00104B2A6676

there seem to be 5 keys (items with * should be reviewed/updated):
* NextAccountID - contains a number pointing to the next Account folder that will be created via Outlook
LastChangeVer - not sure; don't seem to need to touch this
{ED475419-B0D6-11D2-8C3B-00104B2A6676} - seems to point to the first two subfolders (00000001 and 00000002); don't need to touch
{ED475420-B0D6-11D2-8C3B-00104B2A6676} - seems to point to the next two subfolders; don't need to touch
*{ED475418-B0D6-11D2-8C3B-00104B2A6676} - seems to be a collection of all Outlook accounts, with 4 bytes for each, such as 03 00 00 00 (to represent account located in folder 00000003)

After this, each subfolder seems to be an 8-digit sequence number (although skipped entries seem to be fine as long as the above are synchronized.)

Each folder represents the settings for a particular account, which seem to be Outlook and Active Directory accounts, I think.  Depending on what type of account, you get different settings that were discussed previously in the question.

When I originally ran this, I didn't update the {ED475418... item so the account did not appear in Outlook when I restarted.  When I see these three things ({ED475418, NextAccountID, folder ########) updated correctly, the account seems to show up in Outlook.

So the only outstanding issues are how to create the Mini UID and the password via code (and maybe other IDs)  At this point, I will not be pursuing that any more since I went with the option to create a registry entry via code, then run it to import it, then manually update the missing parts (especially password) in Outlook.
--
I hope this helps the next person to find this.
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

  • 17
  • 10
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now