|
|
NOTES DEVELOPMENT
Take Advantage of the C++ API
Create a useful API program from scratch that can go where Formula language and LotusScript can't.
You're working on a solution to a challenging problem and find yourself up against the wall. You just can't implement a practical workaround -- except for that one option which, seemingly, calls for overcoming an even bigger obstacle -- using the Notes C++ API.
In the July 1999 issue of Lotus Notes & Domino Advisor, Henry Newberry and Rocky Oliver introduced readers to the C API. Subsequent articles showed how to create some great utilities for everyday use. As Henry and Rocky stated, the Notes API isn't for the faint of heart. But by stretching yourself a little bit, you can add more functionality to your applications and get into places that Formula language and LotusScript just can't reach. The C++ API not only offers more robust functionality, it does so with improved performance, because API programs run outside the overhead of the Notes client.
This article explains how to set up your compiler and run a simple API program. It then demonstrates a practical use of the C++ API by building an executable program and a DLL that you may find useful around your Notes shop.
This article assumes you have written at least very basic C/C++ programs and are familiar with its syntax. Even if you aren't familiar with C/C++, we hope the techniques and the functionality highlighted in this article encourage you to learn a little C++ just for the sake of extending your Notes applications and maybe even writing the next cool third party Notes tool.
The C++ API
While the C API acts directly on the Notes program files, the C++ API has an additional degree of separation because the C++ API is really a wrapper for the C API. The C++ API makes calls to a C API DLL that, in turn, acts on the Notes program files. The advantage to this separation is that the C++ API is easier to use because, similar to LotusScript, the C++ API is an object-oriented interface to Notes. Looking at the architecture of the C++ API (figure 1), it doesn't look much different from a LotusScript poster. This makes the C++ API more intuitive to use for the experienced LotusScripter.

Figure 1: The architecture of the C++ API -- This looks much like the R5 back end classes.
There are a couple noticeable differences in the architecture. One is the object array classes, which really bring home the object-oriented abstraction of the C++ API. The other is the LN data types. While LotusScript has a string, integer, and other data types transparently built in, with the Notes API you must use types like LNString, LNText and LNNumber. If you try passing a vanilla C++ type int into an API method that's expecting an LNNumber, you'll get an error.
Creating that first API program can be intimidating. That's why Lotus has done it for you. Code libraries, sample programs, and documentation have been put into the C++ API Toolkit, which you can find at http://www.lotus.com/techzone. There are two downloads for each release -- one has the API libraries and sample programs; the other is an invaluable Notes database with a user's guide for using the API and a reference guide for the API classes.
If this is your first C++ API program, see the sidebar, "Setting Up and Running a C++ API Program," below for step-by-step instructions on compiling and running an example program. Otherwise, read on to see how we overcame a challenging problem with the C++ API.
The scenario
The Computer Services group at Acme Inc. has a database application, aptly named Computer Services, that lets the company track where each of Acme's 10,000 employees is located and what kind of computer and peripherals each employee has. The Computer Services application gets updated every night with information on personnel location moves and new hires. An agent, which does the update, goes into the Acme Directory database and searches for changed documents (figure 2). The agent then matches up the changed documents with corresponding documents in the Computer Services database and updates them accordingly. To use a reliable key, the Universal Note ID (UNID) of the Acme Directory person document is stored in its sister document in the Computer Services database. Pretty straightforward, so far (listing 1).

Figure 2: The two databases in our scenario -- The Computer Services database is updated nightly with changes in the Acme Directory.
The problem
The problem is when people at Acme Inc. are terminated. Company policy dictates that when an employees leaves, his person documents in the Acme Directory are immediately deleted; Acme would just like to forget about them as soon as possible, and since there are no rehires at Acme, the 'Allow Soft Deletions' database property hasn't been selected.
Listing 1: Code to update Computer Services database with changes from Acme Directory -- This code is straightforward, but what if someone leaves the company?
Options
Option Public
Option Declare
Declarations
'customize these settings for your use
Const DBSOURCE = "AcmeDirectory.nsf"
Const VIEWTARGET = "PeopleByADUNID"
Const SOURCEUNID = "AcmeDirectoryUNID"
Initialize
Sub Initialize
Dim session As New notessession
Dim dbTrgt As notesdatabase
Dim dbSrc As New Notesdatabase("","")
Dim view As notesview
Dim agent As NotesAgent
Dim item As NotesItem
Dim varLastRun As Variant
Dim collection As notesdocumentcollection
Dim docTrgt As notesdocument
Dim docSrc As notesdocument
Dim intCount As Integer
Dim strPath As String
Dim strServer As String
Set agent = session.CurrentAgent
VarLastRun = agent.LastRun
Dim dt As New notesdatetime(varLastRun)
Set dbTrgt = session.CurrentDatabase
Set view = dbTrgt.GetView(VIEWTARGET)
StrServer = dbTrgt.Server
'to use this, dbs must be in same directory
strPath = Left(dbTrgt.FilePath,_
Instr(dbTrgt.FilePath,dbTrgt.FileName)-2)+"\"
Call dbSrc.Open(strServer,strPath+_
DBSOURCE)
'get all docs since the agent last ran
Set collection =_
dbSrc.Search("Form=""Person""", dt, 0)
' for each document in the collection
For intCount=1 To collection.count
Set docSrc=collection.GetNthdocument(intCount)
'get the corresponding person doc by UNID
Set docTrgt=_
view.GetDocumentByKey(docSrc.UniversalID)
'if there is a corresponding doc
If Not docTrgt Is Nothing Then
'check to see if the person has moved
'(even if not, the whole doc will be updated)
If docSrc.Location(0)<>docTrgt.Location(0) Then
docTrgt.ChangeStatus="Location Change"
docTrgt.LocationOld=docTrgt.Location(0)
End If
'update the changes
Call docSrc.CopyAllItems(docTrgt,True)
Call docTrgt.Save(True,True)
Else
'there isn't a corresponding doc so
'create one and store the UNID of the
'corresponding AcmeDirectory doc in it
Set docTrgt=dbTrgt.CreateDocument()
Call docSrc.CopyAllItems(docTrgt,True)
docTrgt.ChangeStatus="New Employee"
Set item=New NotesItem(docTrgt, SOURCEUNID,_
docSrc.UniversalID)
item.IsSummary=True
Call docTrgt.Save(True,True)
End If
Next
End Sub
For the Computer Services application to know that a person has been terminated, it needs to know that a person document has been deleted from the Acme Directory. There are a few ways to do this. Every night an agent could go through each person document in the Computer Services database and attempt a look up to its sister document in the Acme Directory. When a look up failed, you would know that a document had been deleted, meaning that person had been terminated, so the document in the Computer Services database would be marked as "Terminated."
However, there is no view by UNID in the Acme Directory, and to have one put into such an important, heavily-used database takes a directive from the CIO. A customized replica is not an option either. If you used the Employee ID as a key instead of the UNID, it would be available for a database search. But it would take 10,000 separate searches to find all the possible terminations and your server just can't handle that load.
Another alternative is a data transfer tool such as Lotus Enterprise Integrator (Notes Pump), which can get all documents including deletion stubs. But they start at about US$8,000 dollars; and since your department's budget is all overhead expense to your company, that's $8,000 more than your in-house solution will cost.
The solution
When a document is deleted, it leaves behind a deletion stub. This is so when replication occurs, the other replica knows to delete its corresponding document. Using Notespeek, a utility that you can download right from the Lotus Knowledge Base, you can see what's left when a document is deleted and becomes a stub (figure 3). As expected, the document UNID is still in there along with the date of last modification, and, in this case, deletion. If you can access these stubs, there will be a much smaller number of documents to look through, and you can then efficiently update the Computer Services database.

Figure 3: The contents of a deletion stub viewed with Notespeek -- The trick is to get to the information programmatically with the code.
So, in a nutshell, you need to run an agent to get all the deletion stubs since the last update, see if any of their UNIDs match the UNIDs stored in the documents in the Computer Services database, and mark the related Computer Services documents "Terminated."
We'll show you the code and then go into some detail about where the LotusScript classes and API classes diverge (listing 2). To keep the code generic, we refer to Acme Directory as the source database (the source of the deletion stubs), and Computer Services as the target database (the one that contains the sister documents for which we are searching).
Listing 2: The code to get the deletion stubs from the Acme Directory and update the Computer Services database with Terminations -- Besides some changes in syntax, this isn't much different than some LotusScript code.
/*
Purpose:
This function checks all deletion stubs in the
Src Database. For each stub, it looks for a
document in the Target View of the Target
Database for documents with the Source document's UNID.
Parameters:
SrcDBName
The path of the Source database, relative to the
Data directory if a Server Name is specified.
TrgtDBName:
The path of the Target database, relative to the
Data directory if a ServerName is specified.
TrgtViewName:
The lookup view in the Target database, sorted
by the Source Document's UNID.
LastRunTime:
The last run time of this program, which serves as the cutoff date for checking stubs.
SrvrName:
The name of the server on which the databases
reside.
*/
#include <lncppapi.h>
#include <iostream.h>
#define ERR_BUF_SIZE 512
//argc is the number of arguments passed
//argv is the array of objects passed
void main(int argc, char *argv[])
{
char * SrcDBPath=NULL;
char * TrgtDBPath=NULL;
char * TrgtViewName=NULL;
char * LastRunTime=NULL;
char * SrvrName=NULL;
LNNotesSession Session;
LNDatabase dbSrc;
LNDatabase dbTrgt;
LNViewFolder viewTrgt;
LNNoteArray arrayNotes;
LNNote noteSrc;
UNID * ptr_UNID=NULL;
LNUniversalID unidNote;
LNString strUNID;
LNVFFindOptions findOptions;
LNVFNavigator navEntries;
LNVFEntry entryTrgt;
LNDocument docTrgt;
LNText textStatus;
LNINT i;
LNINT count;
//Verify that the proper number of
//parameters were passed.
if (argc < 5 || argc > 6)
{
//wrong number of arguments
//remind them of the syntax
char CommandBuf[10];
cout <<"\nFunction Syntax:\n\t"
<< argv[0]
<<" <Src db> <Trgt db> <Trgt view> "
<<" <last run time> \n"
<< endl
<< "Hit Return To Exit";
cin.getline(CommandBuf, 10);
//get out of the program
return;
}
// Get the parameter values.
SrcDBPath = argv[1];
TrgtDBPath = argv[2];
TrgtViewName = argv[3];
LastRunTime = argv[4];
if (argc == 6) SrvrName = argv[5];
// Make the error handler throw all errors
// encountered during execution.
LNSetThrowAllErrors(TRUE);
try
{
// Initialize the C++ API session.
Session.Init(argc, argv);
// Get the Src and Trgt databases.
Session.GetDatabase(SrcDBPath,&dbSrc,SrvrName);
Session.GetDatabase(TrgtDBPath,&dbTrgt,SrvrName);
// Open the databases so they can be accessed.
dbSrc.Open();
dbTrgt.Open();
//Get and open the lookup view sorted by UNID of
// Src document.
dbTrgt.GetViewFolder(TrgtViewName, &viewTrgt);
viewTrgt.Open();
// Get all documents modified since LastRunTime.
dbSrc.GetModifiedNotes(LastRunTime, &arrayNotes);
for (i=0; i < arrayNotes.GetCount(); i++)
{
// Get a pointer to the current note
// in the array.
noteSrc = arrayNotes[i];
//Check whether the note is a deletion stub.
//before continuing.
if (noteSrc.IsDeleted())
{
// Get a pointer to the UNID of the current
//deletion stub.
ptr_UNID = noteSrc.GetUniversalID();
// Create a LNUniversalID object with the
//pointer and get the UNID as a string.
unidNote = LNUniversalID (ptr_UNID);
strUNID = unidNote.GetText();
// Search the view for all the document
//storing this UNID and update ChangeStatus.
viewTrgt.Find(strUNID, &entryTrgt,&count,
&navEntries);
if (count>0)
{
entryTrgt.GetDocument(&docTrgt);
docTrgt.Open();
//get change status item
docTrgt.GetItem("ChangeStatus", &textStatus);
//set text
textStatus.SetValue("Terminated");
//save the doc and the text change
//will save with it
docTrgt.Save();
docTrgt.Close();
}
}
}
}
//If an error occurred display the error message.
catch (LNSTATUS Lnerror)
{
char CommandBuf[10];
char ErrorBuf[ERR_BUF_SIZE];
LNGetErrorMessage(Lnerror,ErrorBuf,ERR_BUF_SIZE);
cout << "Error: " << ErrorBuf << endl
<< "Hit Return To Exit";
cin.getline(CommandBuf, 10);
}
// Close the view and databases.
viewTrgt.Close();
dbSrc.Close();
dbTrgt.Close();
// Terminate the API session.
Session.Term();
}
API bug and workaround
The best way to search for deletion stubs from a database is to use db.GetModifiedNotes( ) with a third parameter LNNOTETYPE_NOTIFY_DELETION, which should only retrieve deletion stubs since the LastRunDate -- but it doesn't work properly. At the Spring 1999 Lotus Notes & Domino Advisor DevCon, the Lotus representative handing out the API architecture posters told us that it wasn't working because of a bug. The workaround is to leave off the third parameter and get an array of all notes modified since the LastRunDate. You then need to loop through the array of notes and check the IsDeleted( ) method to determine whether it is a deletion stub and then update the sister document.
Just like in LotusScript, we started a session, opened some databases, performed a search and a view look up, and updated some documents. However, a few of the objects don't directly correspond to LotusScript classes.
LNNote
Behind the scenes, Lotus Notes treats every object in a database as a note (hence the product name). LNNote is a superclass that contains methods that you can use on all database objects, such as documents, forms, and views. IsDeleted is one of the properties of a Note and if its value is True, it's a deletion stub.
LNVFNavigator
The LNViewFolder.Find( ) method populates an LNVFNavigator object. LNVFNavigator is similar to a document collection in that it contains a subset of the documents in the view (those retrieved by the Find( ) method). However, it differs from a document collection in that the subset of documents is treated as though it were its own view. Some methods available on the LNVFNavigator object include GotoFirst( ), GotoNext( ), GotoNextMain( ), and GotoNextUnread( ). The LNVFNavigator object is an array of LNVFEntry objects.
LNVFEntry
This is analogous to LotusScript's new NotesViewEntry class in R5. It represents a row in a view or folder and provides access to the column values displayed in the row. Its GetDocument( ) method can also be used to obtain the underlying document.
LNUNIVERSALID and UNID
Once you have an LNNote object, you can use the GetUniversalID( ) method, but it returns a pointer to a UNID object, which isn't described anywhere in the documentation. The UNID object, even when dereferenced with an asterisk, is the eight-digit memory address of a pointer. To obtain the 32-digit hexadecimal UNID as a string, you must create an LNUniversalID object using the UNID object. You can then use the GetText( ) method of the LNUniversalID object to return the string needed for the lookup in the target database.
LNText
To change or append the value of an item in LotusScript, you could use Item.AppendToTextList(), and when the document is saved, the change in the item is also saved. In the API there is a bit more of a jump that has to do with the fact that LNText is derived from the LNItem class. An LNText object is returned from the LNDocument.GetItem() method. By setting LNText with SetValue and then saving the document, the change to the item is also saved.
Running the code from the command line
To try this code, open a text editor such as WordPad, type in the code, and save the file with a .cpp extension. You can then create another project and compile it following the same instructions from the sidebar. We called our project, GetStubsAndTerminate. You can make you own target and source databases or download ours at http://www.advisor .com/ wHome.nsf/w/MLNfiles, file: judgc05.zip. You can then populate the databases with related documents, delete some documents from the source database and run the program from DOS in your Notes directory. At the command line, enter the program name and arguments for the source database file name, target database file name, last run time, the name of the view in the target database by which the SOURCE UNIDs are categorized, and the server name which is optional (figure 4). A white space character defines a separation between the arguments, so use underscores in place of spaces within a single argument or enclose each argument in double quotes.
Figure 4: Calling the GetStubsAndTerminate executable from the command line -- Test the code this way before calling it from script.
When you run this code and your databases have some level of access control, the DOS prompt box displays the user name of the ID specified in your Notes.ini file. This is because you're starting a new process with a thread in Notes (if the program is run on a server whose ID has no password, there will be no prompt). After entering your password and hitting Return, your program should crunch for a couple of seconds and the sister docs of the documents you deleted in the source database should be marked "Terminated."
Running the code from LotusScript
Once you check that the code works from the command line, you can use the more efficient and less mistake-prone method of calling it with LotusScript. Calling a Notes API program from Notes runs two threads of Notes concurrently: one thread being your Notes client and the other initialized by the API program. This is not recommended by Lotus as stated in the documentation, but it works (listing 3).
Listing 3: Script to call the GetStubsAndTerminate program -- Since this runs two threads of Notes concurrently, only use it to test your code.
Options
Option Public
Option Declare
Declarations
'customize these settings for your use
Const DBSOURCE= "AcmeDirectory.nsf"
Const VIEWTARGET="PeopleByADUNID"
Initialize
Sub Initialize
Dim session As New notessession
Dim dbTrgt As notesdatabase
Dim view As notesview
Dim agent As NotesAgent
Dim varLastRun As Variant
Dim strPath As String
Dim strServer As String
Dim strLastRun As String
Dim intSuccessfulStart As Integer
Dim strArguments As String
Set dbTrgt=session.CurrentDatabase
Set agent=session.CurrentAgent
varLastRun=agent.LastRun
strLastRun=Str(varLastRun)
'for testing, you can roll back the time
strLastRun="09/10/2000 12:01:01 AM"
strServer=dbTrgt.Server
'to use this, dbs must be in same directory
strPath=Left(dbTrgt.FilePath,_
Instr(dbTrgt.FilePath,dbTrgt.FileName)-2)+"\"
'the name of the exe and all its arguments
'needs to be in one string expression.
strArguments=|"GetStubsAndTerminate" "| & _
strPATH+DBSOURCE & |" "| & dbTrgt.FilePath &_
|" "| & VIEWTARGET & |" "| & strLastRun &_
|" "| & strServer & |"|
'the return value for shell is OS specific
'and only lets us know that the program started OK
intSuccessfulStart= Shell(strArguments, 1)
End Sub
This executable program works fine, but again, it's not recommended by Lotus. Also, this procedure should be part of a scheduled agent and chances are your administrator isn't going to let it near his server in its present form.
Running the API code as a DLL function
The next step is to make a few simple changes to the C++ code and compile it into a DLL. Then we can declare it and call it in our script, just like Henry and Rocky showed you in their articles previously mentioned. This means that instead of GetStubsAndTerminate being its own executable program, it will be a function within a DLL we will call 'StubMaintenance' (listing 4). We don't know what else we'll do with deletion stubs, but if we think of something, we can add a function to our StubMaintenance DLL. Using MS Visual Studio and Windows, our compiler procedure and settings didn't change except that out project was a Win32 Dynamic-Link Library instead of a Win32 Console Application. Listing 5 shows the code to call the DLL.
Listing 4: The StubMaintenance DLL -- GetStubsAndTerminate has become a function within the DLL.
/*
Purpose:
This dll performs maintenance related to deletion stubs. The only function, GetStubsAndTerminate, checks all deletion stubs in the Source Database. For each stub, it looks for a document in the Target View of the Target Database for documents with the source document's UNID.
*/
#include <lncppapi.h>
#include <iostream.h>
#define ERR_BUF_SIZE 512
//this is the Linkage Directive
//it allows the code to be called by LotusScript
extern "C"
{
extern __declspec( dllexport )
int GetStubsAndTerminate(char * SrcDBPath,
char * TrgtDBPath, char * TrgtViewName,
char * LastRunTime, char * SrvrName);
}
//this is how we will declare the function in LS
//except we will use string instead of char *
int GetStubsAndTerminate(char * SrcDBPath,
char * TrgtDBPath, char * TrgtViewName,
char * LastRunTime, char * SrvrName)
{
LNNotesSession Session;
Session.Init ();
LNDatabase dbSrc;
LNDatabase dbTrgt;
LNViewFolder viewTrgt;
LNNoteArray arrayNotes;
LNNote noteSrc;
UNID * ptr_UNID=NULL;
LNUniversalID unidNote;
LNString strUNID;
LNVFFindOptions findOptions;
LNVFNavigator navEntries;
LNVFEntry entryTrgt;
LNDocument docTrgt;
LNText textStatus;
LNINT i;
LNINT count;
// Make the error handler throw all errors
// encountered during execution.
LNSetThrowAllErrors(TRUE);
try
{
// Get the Src and Trgt databases.
Session.GetDatabase(SrcDBPath,&dbSrc,SrvrName);
Session.GetDatabase(TrgtDBPath,&dbTrgt,SrvrName);
// Open the databases so they can be accessed.
dbSrc.Open();
dbTrgt.Open();
//Get and open the lookup view sorted by UNID of
// Src document.
dbTrgt.GetViewFolder(TrgtViewName, &viewTrgt);
viewTrgt.Open();
// Get all documents modified since LastRunTime.
dbSrc.GetModifiedNotes(LastRunTime, &arrayNotes);
for (i=0; i < arrayNotes.GetCount(); i++)
{
// Get a pointer to the current note
// in the array.
noteSrc = arrayNotes[i];
//Check whether the note is a deletion stub.
//before continuing.
if (noteSrc.IsDeleted())
{
// Get a pointer to the UNID of the current
//deletion stub.
ptr_UNID = noteSrc.GetUniversalID();
// Create a LNUniversalID object with the
//pointer and get the UNID as a string.
unidNote = LNUniversalID (ptr_UNID);
strUNID = unidNote.GetText();
// Search the view for all the document
//storing this UNID and update ChangeStatus.
viewTrgt.Find(strUNID, findOptions,
&entryTrgt,&count,&navEntries);
if (count>0)
{
entryTrgt.GetDocument(&docTrgt);
docTrgt.Open();
//get change status item
docTrgt.GetItem("ChangeStatus", &textStatus);
//set text
textStatus.SetValue("Terminated");
//save the doc and the text change
//will save with it
docTrgt.Save();
docTrgt.Close();
}
}
}
// Close the view and databases.
viewTrgt.Close();
dbSrc.Close();
dbTrgt.Close();
// Terminate the API session.
Session.Term();
// Exit the function returning a success code
// of 1 back to the LS call.
return (1);
}
//If an error occurred, return 0.
//Not referencing the Lnerror will give a
//warning at compile time, but Catch
//expects it to be declared and we
//don't need to use it.
catch (LNSTATUS Lnerror)
{
// Close the view and databases.
viewTrgt.Close();
dbSrc.Close();
dbTrgt.Close();
// Terminate the API session.
Session.Term();
// Exit the function returning a failure code
// of 0 back to the LS call.
return (0);
}
}
Listing 5: LotusScript code to call the GetStubsAndTerminate function inside the StubsMaintenance DLL -- This time, it's a function call; not starting another program.
Options
Option Public
Option Declare
Declarations
'declare the function and the dll where it
'is located
Declare Function GetStubsAndTerminate _
Lib "StubMaintenance.dll" _
(Byval SourceDBPath _
As String, Byval TargetDBPath As String, _
Byval TargetViewName As String, _
Byval LastRunTime As String,_
Byval ServerName As String) As Integer
'customize these settings for your use
Const DBSOURCE= "AcmeDirectory.nsf"
Const VIEWTARGET="PeopleByADUNID"
Initialize
Sub Initialize
Dim session As New notessession
Dim dbTrgt As notesdatabase
Dim view As notesview
Dim agent As NotesAgent
Dim varLastRun As Variant
Dim strPath As String
Dim strServer As String
Dim strLastRun As String
Dim intSuccess As Integer
Set dbTrgt=session.CurrentDatabase
Set agent=session.CurrentAgent
varLastRun=agent.LastRun
'strLastRun=Str(varLastRun)
'for testing you can roll back the time
strLastRun="09/10/2000 12:01:01 AM"
strServer=dbTrgt.Server
'to use this, dbs must be in same directory
strPath=Left(dbTrgt.FilePath,_
Instr(dbTrgt.FilePath,dbTrgt.FileName)-2)+"\"
'this time the return code let's us know that
'the dll call went successfully to completion
intSuccess=GetStubsAndTerminate(strPATH+DBSOURCE,_
dbTrgt.FilePath,VIEWTARGET,strLastRun,strServer)
End Sub
Notice when you execute this code, there's no DOS prompt because you're working within the same process in Notes. And it's thread safe, since the LotusScript code waits for the DLL to return before continuing to execute.
Distributing the code
After you have your code working and tested, you can distribute it to any user by placing the C API lcppn21.dll (that's for the MS Visual Studio compiler -- check the documentation for other compilers) and the DLL you made into the user's Notes directory. This could be as simple as attaching the files to a Notes document and having a script unload them onto their machines. Since this particular code is most effective as part of a scheduled agent, you can also place the files in the Domino directory of a server. This assumes a default and registered install of Notes; otherwise you might need some additions to the user's environment settings.
Caveats
Even deletion stubs are eventually deleted. If you go to the Replication Settings area of the database properties (figure 5), you'll see a check box labeled, "Remove documents not modified in the last:". This setting not only controls how often unmodified documents are deleted, it also determines how often deletion stubs are purged, regardless of whether or not the box is checked. See Lotus Knowledge Base Technote 158404 for details. The deletion stub code only works if it's run while the deletion stub is still in the database, so schedule the run interval accordingly. If the above setting is '0', for example, the deletion stubs are purged as soon as they are created. In that case, you couldn't use the code at all.
Figure 5: Settings to control deletion stub purge -- The code must be run while the deletion stub is still in the database.
What else can you do?
This article just scratches the surface of the C++ API. What else can you do with the C++ API? Well, here's a list straight from the Toolkit
documentation:
- Create, copy, replicate, and delete databases
- Get and set many database design properties
- Create, copy, and delete database help documents
- Copy and delete database icons
- Create, copy, modify, and delete folders, views, forms, subforms, shared fields, agents, actions, formulas, LotusScript, and simple actions
- Manipulate folder contents and move folders
- Read and modify design properties for views, folders, agents, actions, forms, and subforms
- Work with rich text
Did it say you can create views? Yes, it sure did.
Brad Balassaitis is a senior developer for the e-business consulting division of Palarco, Inc. He is an R4 and R5 PCLP application developer with experience in projects ranging from workflow to Web integration. balassaitis@yahoo.com.
W. Colin Judge is dual principally certified in R4 and R5. mncjudge@altavista.com.
Brad and Colin welcome comments on this article and are interested in hearing from other developers about how they have used the C++ API.
ARTICLE INFO
FREE ACCESS
Keyword Tags: Application Development, Business Software, C Language, C++ API, C++ Language, Database, Database Development, Database Management, Development, E-Business, IBM, IBM Lotus, Lotus, Lotus Formula Language, Lotus Notes, Lotusscript, LotusScript, Microsoft Visual Studio, Microsoft Windows, Tech: Development
|
|