Read fields in Lotus Notes Documents Through VBA - vba

I am trying to write VBA code that will read data from fields in a Lotus Notes document. Currently, I am able to read data using the FieldGetText method, however this only works when I have the document open. I need to be able to loop through documents without opening them, as there are several hundred. I need to be able to read these same fields from many documents, but cannot figure out how to loop through them. My code currently is:
Set LotusNotes = CreateObject("Notes.NotesUiWorkspace")
Set CurrentDoc = LotusNotes.CurrentDocument
While Not (CurrentDoc Is Nothing)
' Affectation of data
DueDate = CurrentDoc.FieldGetText("RevDueDate")
DueTime = CurrentDoc.FieldGetText("RevDueTime")
DateClosed = CurrentDoc.FieldGetText("DateClosed")
Wend
I understand that this uses a Front End object. I was able to use a Back End object that could loop through documents (without them being open), however the data (for the dates, specifically) did not match the field text from the documents. That code looked like this:
Set LotusNotes = CreateObject("Notes.NotesSession")
Set db = LotusNotes.GetDatabase("")
Set view = db.GetView(view_name)
view.AutoUpdate = False
Set columnview = view.AllEntries
Set doc = view.GetFirstDocument
While Not (doc Is Nothing)
revDate = doc.GetItemValueDateTimeArray("RevDueDate")
revDate = doc.RevDueDate
Set doc = view.GetNextDocument(doc)
Wend
Basically, I'm just wondering if it's possible to loop through multiple files using the NotesUIWorkspace class that I tried first, or if it's possible to somehow use FieldGetText in the NotesWorkspace class instead.
Any help is appreciated, thanks.

You're using the Notes "front-end" classes rooted at Notes.NotesUIWorkspace. These are OLE classes, which means that they need the Notes client to be running and they work on the open document. There are also back-end classes rooted at Notes.NotesSession. These are also OLE classes, so they still need the Notes client to be running but they can access other documents (and other databases, servers, etc.). There is also a set of back-end classes rooted at Lotus.NotesSession. Note the different prefix. These are COM classes, so they do not need the Notes client to be running - though it does have to be installed and configured, and your code will have to provide the user's password since the client won't prompt for it.
You can find documentation and examples for the NotesSession class here. Down near the bottom of the page, you'll find links to information about using them via OLE or COM. You can find a bunch of examples based on using the COM classes here, including one that traverses the documents in a view in database. Apart from the inital setup of the session, it would be the same if you use OLE.

It is possible, and also better to use the 'Back end' object Notes.NotesSession. So try this:
Set doc = view.GetFirstDocument
While Not (doc Is Nothing)
'Check if you have the field in the document
if doc.HasItem("RevDueDate") Then
revDate = doc.getFirstItem("RevDueDate").Text
End If
Set doc = view.GetNextDocument(doc)
Wend
It is also possible to use Notes.NotesUiWorkspaceobject by opening every document in the client, getting the fields data and closing the document but I would highly recommend NOT TO make it like this as there is high possibility that you will crash the Notes client, if you need to loop on more documents.

Related

Word automation failing on server, but dev station works great

I am automating a oft-used paper form by querying the user on a web page, then modifying a base Word document and feeding that modified doc file to the user's browser for hand-off to Word.
The code is Visual Basic, and I am using the Microsoft.Office.Interop module to manipulate the document by manipulating Word. Works fine on the development system (Visual Studio 2015) but not on the production server (IIS 8.5).
Both the Documents.Open() call and the doc.SaveAs() call fail with Message="Command failed" Source="Microsoft Word" HResult=0x800A1066
Things I've tried:
Added debugging out the whazoo: Single-stepping is not an option on the production machine, so I pinpointed the problem lines with debug output.
Googled and found that this problem has been reported as early as 2007, but no viable solutions were reported.
A couple sites mentioned timing issues, so I added several pauses and retries -- none helped.
Some mentioned privileging, so I tried changing file permissions & application pool users -- neither helped.
Enhanced my exception handling reports to show more details and include all inner exceptions. That yielded the magic number 800A1066 which led to many more google hits, but no answers.
Added fall-back code: if you can't open the main document, create a simple one. That's when I found the SaveAs() call also failing.
Dropped back to the development system several times to confirm that yes, the code does still work properly in the right environment.
Greatly condensed sample code does not include fallback logic. My Word document has a number of fields whose names match the XML tokens passed as parameters into this function. saveFields() is an array of those names.
Dim oWord As Word.Application
Dim oDoc As Word.Document
oWord = CreateObject("Word.Application")
oWord.Visible = True
oDoc = oWord.Documents.Open(docName)
Dim ev As String
For i = 0 To saveFields.Length - 1
Try
ev = dataXD.Elements(saveFields(i))(0).Value
Catch
ev = Nothing
End Try
If ev IsNot Nothing Then
Try
Dim field = oDoc.FormFields(saveFields(i))
If field IsNot Nothing Then
If field.Type = Word.WdFieldType.wdFieldFormTextInput Then
field.Result = ev
End If
End If
Catch e As Exception
ErrorOut("Caught exception! " & e.Message)
End Try
End If
Next
...
oDoc.SaveAs2(localDir & filename)
oDoc.Close()
oWord.Quit(0, 0, 0)
The code should generate a modified form (fields filled in with data from the parameters); instead it fails to open, and the fallback code fails to save the new document.
On my dev system the document gets modified as it should, and if I break at the right place and change the right variable, the fallback code runs and generates the alternate document successfully -- but on the production server both paths fail with the same error.
Barring any better answers here, my next steps are to examine and use OpenXML and/or DocX, but making a few changes to the existing code is far preferable to picking a new tool and starting over from scratch.
Unfortunately, Lex Li was absolutely correct, and of course, the link to the reason why is posted on a site my company considers off limits, thus never showed up in my google searches prior to coding this out.
None of the tools I tried were able to handle the form I was trying to automate either -- I needed to fill in named fields and check/uncheck checkboxes, abilities which seemed beyond (or terribly convoluted in) the tools I evaluated ...
Eventually I dug into the document.xml format myself; I developed a function to modify the XML to check a named checkbox, and manipulated the raw document.xml to replace text fields with *-delimited token names. This reduced all of the necessary changes to simple string manipulation -- the rest was trivial.
The tool is 100% home-grown, not dependent upon any non-System libraries and works 100% for this particular form. It is not a generic solution by any stretch, and I suspect the document.xml file will need manual changes if and when the document is ever revised.
But for this particular problem -- it is a solution.
This was the closest I got to a complicated part. This function will check (but not uncheck) a named checkbox from a document.xml if the given condition is true.
Private Shared Function markCheckbox(xmlString As String, cbName As String, checkValue As Boolean) As String
markCheckbox = xmlString
If checkValue Then ' Checkbox needs to be checked, proceed
Dim pos As Integer = markCheckbox.IndexOf("<w:ffData><w:name w:val=""" & cbName & """/>")
If pos > -1 Then ' We have a checkbox
Dim endPos As Integer = markCheckbox.IndexOf("</w:ffData>", pos+1)
Dim cbEnd As Integer = markCheckbox.IndexOf("</w:checkBox>", pos+1)
If endPos > cbEnd AndAlso cbEnd > -1 Then ' Have found the appropriate w:ffData element (pos -> endPos) and the included insert point (cbEnd)
markCheckbox = markCheckbox.Substring(0, cbEnd) & "<w:checked/>" & markCheckbox.Substring(cbEnd)
End If
' Any other logic conditions, return the original XML string unmangled.
End If
End If
End Function

How to get user-Information from domino-server via vba

I writing a macro for ms word, that starts when the document is loaded and fills a few fields with user information (tel,email etc.). I want this information to come form our domino-servers, as they are our primary database.
I have tried using notes objects in vba to establish a session on the server, but that requires that user to keep their notes-client open(as far as I know), so thats not an option.
Currently I am trying to get the information via ldap request(the domino-server runs the ldap-service), however I cant find information on how the code and search string has to look like.
The last site I tried was this one (http://www.selfadsi.org/bind.htm), using the "Bind using special credentials" variant.
But I am getting a protocol error.(the string proably needs to be adjusted for connecting to domino, I tried "LDAP://[Server-FQN]/dc=DE" hoping it would show me all users registered as C=DE, but it did not work.)
I have also searched through IBMs Support Site and Knowledge-Center, but could not find the info I need.
If anyone could provide me an example on how to connect to the domino ldap and pull the information for a user, I would be very grateful.
There are two different sets of Notes objects available to you in VBA. There are OLE objects, which you access this way:
Set Session = CreateObject("Notes.NotesSession")
And there are COM objects, which you access this way:
Set Session = CreateObject("Lotus.NotesSession")
The key differences between them are that OLE objects require that the Notes client must be running and they also include the "front-end" classes like Notes.NotesUIWorkspace that can actually drive the Notes client, while the COM objects do not require that the Notes client is running (though either it, or the Domino server, must be installed) and it does not provide the front-end classes.
If you want to stick with the LDAP approach, the first thing you should probably do is download an LDAP client. I like the SoftTerra LDAP browser, but I've also heard that a lot of people like the Apache Directory Studio. Browsing the tree is really the best way to figure out what queries will get you the info you need.
s mentioned in the commment on Richards answer, I also tried finding the LDAP-String using a LDAP-Browser. Doing that and some testing with ADODB Objects I was able to come up with the following code:
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"
Set objCommand = CreateObject("ADODB.Command")
Set objCommand.ActiveConnection = objConnection
objCommand.CommandText = "SELECT UID, mail FROM 'LDAP://[Server-FQN]:389 'WHERE ObjectClass='dominoPerson'"
Set objRecordSet = objCommand.Execute
While Not objRecordSet.EOF
varMail = objRecordSet.Fields("mail").Value
Debug.Print varMail(0)
objRecordSet.MoveNext
Wend
Which extracts the email-adress of all our users using anonymous access and prints the result into the debug window.
Now all I have to do is to adjust the WHERE-Statement to get the data I want per user. :-)

Access not exiting

Recently, my Access .mdb database started intermittently not allowing Access (both Access 2003 and 2007) to quit. If I quit (whether by pressing the X button or from the menu, then it closes the database and appears to exit Access, as well, but then it suddenly reappears (without any database open). The only way for me to exit at that point is from the task manager.
There are two significant changes that I did recently that might be related. 1) I started using the WinSCP .Net assembly to access an ftp server, which I had to install and register for COM from the instructions here. 2) I started using ODBC, first as a linked table, and then from VBA ADO code (see this). I doubt that this second change is causing this problem because I've had the problem both when I was using the linked tables and when with ADO.
This doesn't happen every time I open the database, and I haven't noticed a pattern. What could be causing this strange problem?
Edit - I found the root of the problem. By breaking my ftp download code at various points and seeing whether it will exit, I narrowed it down to the following:
Dim PDFFolders As Recordset
Set PDFFolders = CurrentDb.OpenRecordset("PDFFolders")
Dim syncOptions As TransferOptions
Set syncOptions = New TransferOptions
syncOptions.filemask = "*/*.pdf"
On Error Resume Next 'In case it doesn't exist
Do While Not PDFFolders.EOF
sess.SynchronizeDirectories SynchronizationMode_Local, info!RTFFolder, _
info!BasePDFFolder & "/" & PDFFolders!Name, False, , , _
syncOptions
PDFFolders.MoveNext
Loop
PDFFolders.Close
Set syncOptions = Nothing
Set PDFFolders = Nothing
On Error GoTo 0
If it runs the sess.SynchronizeDirectories statement, then access won't exit, otherwise it does. Looks to me like a bug win WinSCP.
I can do other things like downloading files, creating directories, etc. with no problem, but when it gets to this statement, it doesn't exit access afterwards.
Sticky instances of Access usually result from hanging object references.
If Access hangs the way you described, I would suspect a nasty circular reference.
To investigate on circular references, you basically have two options:
Inspect your code on circular dependencies - That might become tedious and not so easy. But if you know your code base deeply, you might have suspects where to look first.
Add logging to your code - In case you cannot spot the problem via inspection alone, you can consider adding consequent logging of object creation/deletion (through Class_Initialize/Class_Terminate). For larger code bases using classes heavily, this is a good investment to start with.
If your problem is with classes where you cannot control the code (as is your case), you might consider using that external classes only through wrapper classes where you can log creation/deletion. Of course in tricky cases, termination of the wrapper class does not mean termination of the wrapped class.
BTW, I strongly recommend to make sure to set every object reference explicitly to Nothing ASAP:
Set MyObj = GetMyObject()
' Proceed with coding here later
' First write the following line
Set MyObj = Nothing
Special thoughts have to be given in the case of local error handling to make sure to set the reference to Nothing in either case.
A good way to ensure this is using a With-block instead of an explicit variable (if the usage pattern allows to):
With GetMyObject()
' Use the object's members here
End With
With this pattern you save declaring the local variable and can be sure that the object reference does not survive the current method.
I still think it's a bug in WinSCP, but I found a workaround. I noticed that it only happened if I took the information from a table in the database, and not if I put in a hard-coded string. So I just added & vbNullString, which concatenates a blank string, which changes the data type from a Field to a String, and now it doesn't happen anymore.

lotus notes to VBA

I'm stuck with this problem for days
I have to read a specific mailbox in lotus notes and bring all the content into an excel spread sheet
but so far I have only been able to read the default inbox and have no way of switching to the other mailbox. I 'm really new to VBA can any one help me sort this out
here is the code I 'm using
Set NSession = CreateObject("Notes.NotesSession")
'get the name of the mailfile of the current user
DbLocation = NSession.GETENVIRONMENTSTRING("mail/mailbox", True)
'Get the notesdatabase for the mail.
Set NMailDb = NSession.GETDATABASE("mailboxer", DbLocation)
MsgBox (DbLocation)
I get an empty msgbox poping up
GetEnvironmentString() reads the notes.ini file. I'm not sure that's what you really want to be doing. Just from the syntax, I think you're using "mail/mailbox" as a placeholder for the actual path to the mailbox that you're looking for. E.g., you're really trying to read the mail from something like "mail/jsmith.nsf". (If I'm wrong, and you really do want to be reading the notes.ini file to get the location of the mail file, then your problem is that "mail/mailbox" is not a valid key for an ini file entry.)
My next assumption is that the Domino server where the mailbox lives is called "mailboxer", because that's what you're putting in the first argument of GetDatabase().
If I'm right about these things, then what what you need is
Set NMailDb = NSession.GETDATABASE("mailboxer", "mail/mailbox")
where "mail/mailbox" is replaced with the actual path to the mailbox that you are trying to open.
Some thoughts:
use Lotus.NotesSession if you don't have to interact with the Notes UI (Lotus.NotesSession is COM based, whereas Notes.NotesSession is OLE based)
make sure the user of the Notes client on the workstation running your VBA application has the rights require to open and read the mailbox
As D. Bugger stated, you need to be sure you have the Notes client installed on the same client machine your VB code will run, and you need to be sure the folder with the nnotes.exe file and the folder with the notes.ini file are in your environment path. (If not, you will get a COM error instantiating the Notes.NotesSession object.
If this helps, here is some starter code - not tested, but a rough guide... This walks through all documents in a Notes mailbox database, ignores anything except email documents (which have the form field = "Memo") and grabs some fields from each email.
Public Sub exportNotesMail(MailServer$, MailDBPath$)
Dim mailDb As Object, doc As Object, alldocs As Object, Session As Object
Set Session = CreateObject("Notes.NotesSession")
Set mailDb = Session.GETDATABASE(MailServer, MailDbPath$)
If mailDb.IsOpen = False Then mailDb.OPENMAIL
Set alldocs = mailDb.AllDocuments
Set doc = alldocs.GetFirstDocument
while not (doc is nothing)
If doc.GetItemValue("Form")(0) = "Memo" Then
thisSubject = doc.getItemValue("Subject")(0)
thisFrom = doc.getItemValue("From")(0)
' get more field values
' Export to Excel or wherever
End If
Set doc = alldocs.GetNextDocument(doc)
Next i
' done
End Sub
call exportNotesMail ("MyServer", "mail\myMailFile.nsf")

How can I connect to Lotus thru ODBC using VBA?

I'm interested in setting up an Access db to run a report automatically. To save myself the trouble of going to each client computer and setting up the appropriate DSNs, I'd like to set up the ODBC connections in the VB script itself if possible.
I've googled and checked this site and found some good starter code, but not enough to make all the error messages go away. Can someone complete the code below?
Sub SetupODBC(Str_Server as string, Str_Db as string)
'Str_Server=Name of Server
'Str_db=Name of Database
Dim C as ADODB.Connection
Set C = new ADODB.Connection
C.ConnectionString = ??
C.Open
Debug.print C.State
Exit Sub
Welcome to the board. ConnectionStrings is indeed your friend, but the issue you are having is that you don't have the driver:) Lotus Notes is not a relational database it is a document oriented database. Historically there has not been a way to Access it like it is a relational database for that reason. However IBM eventually got around to writing a sort of translator in the form of NotesSQL. If you follow the link and get the driver you should be able to use ODBC. It is worth noting that Notes exposes itself to COM. So if push comes to shove you can automate the client.
This site is your friend: http://www.connectionstrings.com/access
I didn't follow your question correctly at first. I see you want to create a link from Access to Lotus to report on Lotus Notes data. Well there are a few ways to do so.
I frequently use a method of exposing Lotus Notes data as XML, then accessing that XML from the remote system. You can easily create a Notes Page with the XML start tag, root element, and then insert an embedded view in between the root element. That embedded view then needs to display as HTML and contain columns that resolve to xml tags. For instance, each row of the view would look similar to this:
<Person><FirstName>Ken</FirstName><LastName>Pespisa</LastName></Person>
and your column formulas would be:
"<Person><FirstName>" + FirstName + "</FirstName>"
for the first name column, and for the last name column it would be this:
"<LastName> + LastName + </LastName></Person>"
Note that this assumes that your Notes server has the HTTP service turned on and you can reach the database via a browser.
However as mentioned by other answers, you can use other methods such as NotesSQL and COM. It sounds like you are putting this solution on many workstations, though, and NotesSQL would require you to install the driver on each workstation. The COM method would work without requiring any extra work at the users' desks so I'd favor that solution in this case.
Looks like a great site for my needs, even if it hasn't been updated in a year. But still no cigar. Now, I'm getting "Data source name not found and default driver not specified"
(Obviously, ServerNameGoesHere and DatabaseNameGoesHere are subsitutions)
Sub dbX()
Dim C As adodb.Connection
Set C = New adodb.Connection
C.Open _
"Driver={Lotus NotesSQL 3.01 (32-bit) ODBC DRIVER (*.nsf)};" & _
" Server=ServerNameGoesHere;" & _
" Database=DatabaseNameGoesHere.nsf;"
C.Close
End Sub