Display GAL in Excel to get Alias or Email Adress - vba

I am trying to make use of information found in THIS POST.
I have 2 issues:
the following line hangs indefinitly. FIXED--- its just hidden and has no task bar item, simple search told me how to bring to to the front
strAddress = objWordApp.GetAddress(, strCode, False, 1, , , True, True).GetProperty("http://schemas.microsoft.com/mapi/proptag/0x3A00001E")
I need to return something I can use in a TO line, so an ALIAS or a full email address. I have tested this code in WORD and it works perfrectly (remvoing the references to word) except I cannot get the correct information I need. When I pull I get an exchange distinguished name.. I need to convert this somehow to an alias or email address
/o=corperation/ou=administration/cn=my.name
BACKGROUND: the code in the previously mentioned post displayed the OUTLOOK GAL so a user can search/select a contact from it. I want to use the GAL because it can handle the 200,000+ records and includes distrobution lists.
SOFTWARE: This has to function within the OFffice 2010 suite. I don't need any backwords compatibility and future proofing is a minimal concern at the moment.
END RESULT: I basically just want a user to be able to search for a recipient and have that address end up in a cell.
Any hints would be greatly appreciated.

Method One: Using the GetAddress function
Does the following code still hang indefinitely for you?
Set objWordApp = CreateObject("Word.Application")
InputBox "", "", objWordApp.GetAddress(, "<PR_EMAIL_ADDRESS>", False, 1, , , True, True)
Method Two: If you know the username grab it directly
You can maybe use the LDAP directly to get this information:
Public Function GetUserEmail(ByVal userName As String) As String
Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.CommandText = "SELECT mail FROM 'LDAP://DC=something,DC=co,DC=uk' WHERE objectCategory='user' AND sAMAccountName='" & userName & "'"
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
If Not objRecordSet.EOF Then
GetUserEmail = objRecordSet.Fields("mail").Value
Else
GetUserEmail = vbNull
End If
End Function
Method Three: Create your own searchable form
You could create your own UserForm to bring back a list of users from the LDAP. You could choose the fields you want to search on and then allow the user to click that item to grab the email address. It's a little messy, but it should load a bit faster, since it'll only search on a name more than 3 characters long.
In this example above I created a query which searches on the givenName or sn field of the LDAP:
Private Sub txtSearch_Change()
If Len(txtSearch) > 3 Then
queryString = txtSearch
Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.CommandText = "SELECT givenName, sn, mail FROM 'LDAP://DC=something,DC=co,DC=uk' WHERE objectCategory='user' AND (givenName = '*" & queryString & "*' Or sn = '*" & queryString & "*')"
Set objRecordset = objCommand.Execute
lvResults.ListItems.Clear
Do Until objRecordset.EOF
Set li = lvResults.ListItems.Add(, , objRecordset.Fields("givenName").Value)
li.SubItems(1) = objRecordset.Fields("sn").Value
If Not IsNull(objRecordset.Fields("mail")) Then
li.SubItems(2) = objRecordset.Fields("mail").Value
End If
objRecordset.MoveNext
Loop
End If
End Sub
Notes
Something to note, is you will need to change the LDAP string to your company domain controller. For example LDAP://DC=something,DC=co,DC=uk.
If you don't know this you can find it out by doing:
Set sysinfo = CreateObject("ADSystemInfo")
MsgBox sysinfo.userName
Note you only want to take the DC= parts.
A list of all attributes can be found here: http://www.computerperformance.co.uk/Logon/LDAP_attributes_active_directory.htm

Related

How to read all attribute values in a VBA LDAP query?

I'm doing some development work and need to see what data is stored in Active Directory in order to figure out which fields are being used and may contain information that will be useful. I have a method of pulling all of the mandatory property field names, and of pulling the data with LDAP if I know what the field names are, but what I can't figure out is how to do it dynamically. Here's my code:
Function LDAPdump()
Dim objSysInfo As Object
Dim objSchema As Object
Dim objRecordset As Object
Dim objConnection As Object
Dim objCommand As Object
Dim objUser As Object
Dim objLUser As Object
Dim dname As String
Dim DLoc As Integer
Dim ADaddress As String
Set objSysInfo = CreateObject("ADSystemInfo")
Set objSchema = GetObject("LDAP://schema/user")
Set objUser = GetObject("LDAP://" & objSysInfo.UserName)
dname = objUser.distinguishedName
DLoc = InStr(dname, "DC=")
ADaddress = Right(dname, Len(dname) - DLoc + 1)
Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand = CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Page Size") = 1
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
objCommand.CommandText = "SELECT * FROM 'LDAP://" & ADaddress & "' WHERE objectCategory='user' AND cn='" & objUser.CN & "'"
Set objRecordset = objCommand.Execute
If objRecordset.RecordCount = 0 Then
MsgBox "no data"
Exit Function
End If
Set objLUser = GetObject(objRecordset.Fields("ADsPath").Value)
'print all mandatory property field names
For Each strAttribute In objSchema.MandatoryProperties
Debug.Print strAttribute & ": " & objLUser(CStr(strAttribute)).Value
Next
End Function
The spot that's specifically giving me issues is pulling the objLUser attribute value. For example, when I run this code the first attribute that comes up is cn. If I specifically write Debug.Print strAttribute & ": " & objLUser.cn I get cn: Chaosbydesign back in the immediate window, but when I try to dynamically get it in the For loop, I get back "The directory property cannot be found in the cache." Looking at objUser or objLUser in the Locals window doesn't provide any hints to a solution because it just says <No Variables> when expanded, even though I can go into the immediate window and type in ? objLUser.cn/mail/displayname and get my common name, email and display name back.
I have a workaround for this that involves a bit of shenanigans with a text file and Excel to basically write out the Debug.Print line for every single attribute, so this problem itself isn't a show stopper, more just an annoyance that I'd like to figure out.

How to get email address with VBA based on windows login name?

How can I use VBA to get the email address of users on my network based on their Windows user name?
I have a log file that contains the UserName of everyone who launched this Access database. The username was generated from Environ("USERNAME") when they launched the database. I need to send an email to all recent users.
I don't need it to return the current users email address, and I don't need the code to send email.
I was wondering if it was possible using LDAP, but the example I have only works for the current logged in user. Is there another way?
Function GetEmailAddress(Optional strUserName As String = "") As String
' Get user's email address from LDAP
Dim sysInfo As Object
Dim oUser As Object
If strUserName = "" Then
' No name was passed in. Get it for the current user.
strUserName = Environ("USERNAME")
End If
' How I can I use strUserName to get the email address?
' The LDAP query below only works for the current logged in user.
Set sysInfo = CreateObject("ADSystemInfo")
Set oUser = GetObject("LDAP://" & sysInfo.UserName & "")
Debug.Print "Display Name: "; Tab(20); oUser.Get("DisplayName")
Debug.Print "Email Address: "; Tab(20); oUser.Get("mail")
Debug.Print "Computer Name: "; Tab(20); sysInfo.ComputerName
Debug.Print "Site Name: "; Tab(20); sysInfo.SiteName
Debug.Print "Domain DNS Name: "; Tab(20); sysInfo.DomainDNSName
GetEmailAddress = oUser.Get("mail")
Set sysInfo = Nothing
Set oUser = Nothing
End Function
Personally, I use the following function, though there are many possibilities.
Public Function GetMailAddress(strUsername As String) As String
Dim cmd As New ADODB.Command
Dim cn As New ADODB.Connection
Dim rs As ADODB.Recordset
cn.Open "Provider=ADsDSOObject;"
cmd.CommandText = "<LDAP://dc=MyCompany,dc=intranet>;(&(objectCategory=User)(CN=" & strUsername & "));mail;subtree"
cmd.ActiveConnection = cn
Set rs = cmd.Execute
GetMailAddress = rs.Fields(0).Value
End Function
You need to fill in the valid DC values for your LDAP server, of course.

How to limit the number of results in an LDAP query

I'm programming an Office 2010 Word template that needs to interact with the Active Directory, to retrieve some user info. The user filling out the template can give some input for the search that is used to retrieve the AD info.
I'm using the ADODB Recordset and Command to setup my query:
Public Function GetActiveDirectoryInfo(searchString As String) As Object
Dim adoCommand, adoConnection, strBase, strFilter, strAttributes
Dim strQuery, adoRecordset
'remove user input asteriks
searchString = Replace(searchString, "*", "")
' Setup ADO objects.
Set adoCommand = CreateObject("ADODB.Command")
Set adoConnection = CreateObject("ADODB.Connection")
adoConnection.Provider = "ADsDSOObject"
adoConnection.Open "Active Directory Provider"
Set adoCommand.ActiveConnection = adoConnection
strBase = "<LDAP://" & GLOBAL_LDAP_USER_OU & ">"
' Filter on user objects.
strFilter = "(&(objectCategory=person)(objectClass=user)(|(sn=" & searchString & "*)(cn=" & searchString & "*)))"
' Comma delimited list of attribute values to retrieve.
strAttributes = ACTIVE_DIRECTORY_FIELDS
' Construct the LDAP syntax query.
strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";OneLevel"
adoCommand.CommandText = strQuery
adoCommand.Properties("Page Size") = 10
adoCommand.Properties("Timeout") = 30
adoCommand.Properties("Cache Results") = False
'adoCommand.Properties("Maximum Rows") = 10 'error: read only
On Error GoTo err_NoConnection
' Run the query.
Set adoRecordset = adoCommand.Execute
Set foundItems = adoRecordset
Debug.Print "Found : " & foundItems.RecordCount & " records"
Exit Function
err_NoConnection:
'in case of error: return <nothing>
Debug.Print Err.description
Set GetActiveDirectoryInfo = Nothing
End Function
THis function is part of a User Class in MS Word.
My question: how to prevent the user from retrieving thousands of records? I have been reading about paging, but that seems more like a network-load-thing than actually limiting the search results that will eventually get back to me.
I've tried several things but with no results. For instance setting the MaxRecords or Maximum Rows properties. Both give an error (non existing property for the first, and read only error for the second).
ANy ideas anyone.
MS Office 2010 dutch
ADO Objects 6.0
Thanx!
Unfortunately ADO MaxRecord that limits the number of search results is not implemented for ADsDSObject. The link https://support.microsoft.com/kb/269361 details the workaround to solve the issue.

Querying Excel by multiple users - Need suggestion

I am seeking one suggestion on how to build an excel macro for below requirement. Request you to provide your valuable comments in EXCEL Only.
Scenario
I have one spreadsheet "Product Master" that contains all the product details.
(i.e. Product ID,Product Name,Product Type,Quantity etc etc)
I am designing a UserForm using excel VBA where anyone can fetch all the details of a product based on its Product ID. Now the product-master sheet where all the product details is present will get updated on a daily basis. And each user should be able to update any details in that sheet based on his requirement.
Questions/Doubts
How do I design my system? I mean where should I put my "Product-Master" spreadsheet so that it can be accessed by multiple users. What I am thinking is to put product-masster on a shared_drive so that all can access that sheet through VBA userform. I will provide excel VBA userform macro to everyone in my office & they will query that sheet present in shared drive. does this seem ok?
Does excel provide facility to Query data from sheet present in shared-drive & update it when required. And I want this to be queried by multiple users at a time.
I know there are other products/technologies that provides better solution than EXCEL. But I want the solution in EXCEL ONLY.
I would appreciate it if anyone can provide his/her valuable comments on this. Let me know in case you need any details.
Thanks you.
Here are some example functions getting data from/posting data to MS Access (took me awhile to dig these up, hah!). This uses a Reference to the Microsoft DAO 3.6 Object Library and will only work with legacy .mdb files, not accdb (because the mdb driver is 100x faster and doesn't have a memory leak.)
Const DBPath As String = "Full\Database\Path"
Function GET_ACCESS_DATA(DBPath, SQL) As Object
Dim dbConn As Object
Dim dbRS As Object
Dim SQL As String
On Error GoTo ErrorHandler
SQL = "Sql Query"
'Set up database connection string
Application.StatusBar = "Connecting to Database..."
'Open database connection
Set dbConn = OpenDatabase(DBPath)
'Run the query
Application.StatusBar = "Running Query..."
Set dbRS = dbConn.OpenRecordset(SQL, DAO.dbOpenForwardOnly, DAO.RecordsetOptionEnum.dbReadOnly)
'If no rows returned, display error message and exit
If dbRS.RecordCount = 0 Then
Application.StatusBar = "Running Query...Error"
MsgBox "There are no records for the selected criteria.", vbInformation, "Refresh Data"
Application.StatusBar = "REFRESHING DATA PLEASE WAIT.."
Exit Function
End If
'returns DAO Recordset with the data
Set GET_ACCESS_DATA = dbRS
'A recordset can either be looped through or pasted to a spreadsheet with the Worksheet.Range.CopyFromRecordset method
'Error trap here
End Function
Function POST_TO_ACCESS() As Boolean
POST_TO_ACCESS = False
errormod = "TRACKING"
On Error GoTo ERROR_TRAP:
'START CONTROLS
Application.StatusBar = "Formatting Data"
St_Timer = Timer 'start connection timer
Dim cn As DAO.Database
Set cn = DAO.OpenDatabase(DBPath)
En_Timer = Timer 'get connection time
'SetKey Parameters
UserNM = Replace(User_Name(), Chr(39), "")
CompNm = Environ("COMPUTERNAME")
Elapsed_Time = En_Timer - St_Timer
SQL = "INSERT INTO TBL_TRACKING " & _
"(UserNM) " & _
" VALUES ('" & UserNM & "')"
cn.Execute SQL
cn.Close
'END CONTROLS
Application.StatusBar = False
POST_TO_ACCESS = True
'error trap here
End Function
Function User_Name()
'This just gets the LDAP username of whoever is logged in. Useful for tracking. Not guarenteed to work for your Active Directory :)
Dim WshNetwork
Dim objAdoCon, objAdoCmd, objAdoRS
Dim objUser, objRootDSE
Dim strDomainDN, strUserName, strUserFullName
strUserFullName = ""
Set WshNetwork = CreateObject("WScript.Network")
strUserName = WshNetwork.UserName
Set objRootDSE = GetObject("LDAP://rootDSE")
strDomainDN = objRootDSE.Get("defaultNamingContext")
Set objAdoCon = CreateObject("ADODB.Connection")
objAdoCon.Open "Provider=ADsDSOObject;"
Set objAdoCmd = CreateObject("ADODB.Command")
Set objAdoCmd.ActiveConnection = objAdoCon
objAdoCmd.CommandText = _
"SELECT ADsPath FROM 'LDAP://" & strDomainDN & "' WHERE " & _
"objectCategory='person' AND objectClass='user' AND " & _
"sAMAccountName='" & strUserName & "'"
Set objAdoRS = objAdoCmd.Execute
If (Not objAdoRS.EOF) Then
Set objUser = GetObject(objAdoRS.Fields("ADsPath").Value)
objUser.GetInfoEx Array("displayName"), 0
strUserFullName = objUser.Get("displayName")
Set objUser = Nothing
User_Name = strUserFullName
Else
End If
Set objAdoRS = Nothing
Set objAdoCmd = Nothing
objAdoCon.Close
Set objAdoCon = Nothing
Set objRootDSE = Nothing
Set WshNetwork = Nothing
End Function

Invalid Use of Property in VB6 Error. How to fix this?

Private Sub aTbBar_Change()
Set con = New ADODB.Connection
With con
.CursorLocation = adUseClient
.ConnectionString = "Provider=Microsoft.jet.oledb.4.0;persist security info=false;data source=" & App.Path & "\Event_Participants.accde"
.Open
End With
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = con
.CursorType = adOpenDynamic
.Source = "select * from Participants"
.Open
'check from table if user and pwd matches
If rs.RecordCount <> 0 Then
rs.MoveFirst
While Not rs.EOF
If rs!Bar_Code_No = Val(Me.aTbBar) Then
Me.aTbName = rs!Full_Name
Me.aTbSection = rs!Section
Me.aTbArrtime = Time()
End If
rs.MoveNext
Wend
End If
.Close
Set rs = Nothing
End With
'save to the database
'check from table if user and pwd matches
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = con
.CursorType = adOpenDynamic
.LockType = adLockOptimistic
.Source = "select * from Participants"
.Open
If rs.RecordCount <> 0 Then
rs.MoveFirst
While Not rs.EOF
If rs!Bar_Code_No = Val(Me.aTbBar) Then
.Update
rs!Arr_Time = Me.aTbArrtime
End If
rs.MoveNext
Wend
End If
End With
rs.Close
Set rs = Nothing
End Sub
Invalid Use of Proper error always occur when I type in to that textbox name aTbBar
The error occurs at Me.aTbName = rs!Full_Name. Can you help me on this one. Sorry, im new in this forums and in VB. I really need help
The default property triggered for a TextBox is the Text property. So, if there is a TextBox with the name Text1, then this statement: Text1 = "Hello" would be equivalent to Text1.Text = "Hello". But I always prefer using the property name along with the control name, when accessing it(ie, Text1.Text = "Hello").
Anyway, test it out by using this line: Me.aTbArrtime.text = rs!Full_Name
Another thing that I have in mind is, if you have used some other component, say a custom made TextBox control (instead of the default one), and in the case of load failure, VB would replace the control(the custom made textbox) with a PictureBox in your forms. For checking that, click on the TextBox in the form and view it's properties. And see whether the control type is a TextBox. If it is a PictureBox, then double check whether your OCX or DLL for the custom made textbox is present in the project.
A small suggestion on your SQL code is that, you could have included the comparison in your query itself, instead of looping through all the records. For example:
.Source = "select * from Participants WHERE Bar_Code_No = " & Val(Me.aTbBar.Text) & " LIMIT 1"
This would return a single record if it matches the Bar_Code_No. After executing this query, you only need to check if it returns any record. If so, a match is found. Otherwise, no match is found. By this way, you can avoid looping, which might make your program Non-Responding if the number of records in the table Participants is enormously large !
Hope this would help you. Wish you good luck :)