<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Notes Log &#187; Lotus Notes</title>
	<atom:link href="http://noteslog.com/category/notes/feed/" rel="self" type="application/rss+xml" />
	<link>http://noteslog.com</link>
	<description></description>
	<lastBuildDate>Sat, 15 May 2010 13:31:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>Computed When Composed</title>
		<link>http://noteslog.com/post/computed-when-composed/</link>
		<comments>http://noteslog.com/post/computed-when-composed/#comments</comments>
		<pubDate>Mon, 25 Sep 2006 19:11:59 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=39</guid>
		<description><![CDATA[Computed When Composed fields hide a subtlety that may cause troubles. Any CWC field is computed the first time it is added to a document in memory. This certainly happens when the document is composed, but it also happens when an existing document is opened with a form with such a new field.]]></description>
			<content:encoded><![CDATA[<p>Computed When Composed fields hide a subtlety that may cause troubles. Any CWC field is computed the first time it is added to a document in memory. This certainly happens when the document is composed, but it also happens when an existing document is opened with a form with such a new field.</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/computed-when-composed/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>@Author and @Created</title>
		<link>http://noteslog.com/post/author-and-created/</link>
		<comments>http://noteslog.com/post/author-and-created/#comments</comments>
		<pubDate>Mon, 25 Sep 2006 18:25:50 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=38</guid>
		<description><![CDATA[A common mistake in Notes applications is having documents that rely on the @Author and @Created functions to know who created any document and when. These are system functions and for this reason they are unreliable in the context of the application domain. In fact they merely tell who created the Notes document and when, [...]]]></description>
			<content:encoded><![CDATA[<p>A common mistake in Notes applications is having documents that rely on the @Author and @Created functions to know who created any document and when. These are system functions and for this reason they are unreliable in the context of the application domain. In fact they merely tell who created the Notes document and when, but they don&#8217;t say anything about the domain document represented by the Notes document. </p>
<p>It could be argued that if the author and created fields need to hold exactly the values relative to the Notes document, not the domain document, then the @Author and @Created functions do the job. But this is a mistake as well, because their values may change without notice: for example the creation date changes when a document is cut from and pasted again into the database.</p>
<p>So, if a piece of information makes any sense for a document it needs to stay in its own field in the form. </p>
<p>Notes main flexibility is that it&#8217;s possible to add new fields at any time. A natural way of adding such two fields to a form is by means of Computed When Composed fields, whose formulas could be @Author and @Created respectively. </p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/author-and-created/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Accessing an Oracle table</title>
		<link>http://noteslog.com/post/accessing-an-oracle-table/</link>
		<comments>http://noteslog.com/post/accessing-an-oracle-table/#comments</comments>
		<pubDate>Wed, 07 Jun 2006 22:20:21 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=26</guid>
		<description><![CDATA[A Lotus Connector is a powerful means for accessing an Oracle table. To have it work properly, the machine from which the Lotus code is being executed (wether a Domino server for Web operations or a Notes client) needs a working copy of the Oracle client software. The Lotus Conector will simply use some client [...]]]></description>
			<content:encoded><![CDATA[<p>A Lotus Connector is a powerful means for accessing an Oracle table. To have it work properly, the machine from which the Lotus code is being executed (wether a Domino server for Web operations or a Notes client) needs a working copy of the Oracle client software. The Lotus Conector will simply use some client libraries for accessing the networked database, so the client needs not be running before executing the Lotus code, nor it needs any custom configurations for accessing the database, like any changes to the <code>tnsnames.ora</code> file.</p>
<p>In fact, all of the conection data that would usually go into the <code>tnsnames.ora</code> configuration file, can be much more conveniently stored inside a LotusScript library, like the following</p>
<p>'Connection_Oracle:  

Option Public 
Option Declare  

Const connection_Host     = "172.18.32.51" 
Const connection_Port     = "1525" 
Const connection_SID      = "extprd" 
Const connection_UserId   = "Notes Log" 
Const connection_Password = "Notes Log"  

Dim connection_Server As String  
Sub Initialize
	connection_Server = {} _
	& {(DESCRIPTION=} _
	& {(ADDRESS_LIST=} _
	& {(ADDRESS=} _
	& {(PROTOCOL=TCP)(Host=} & connection_Host & {)(Port=} & connection_Port & {)} _
	& {)} _
	& {)} _
	& {(CONNECT_DATA=} _
	& {(SID=} & connection_SID & {)} _
	& {)} _
	& {)} 
End Sub</p>
<pre class="hl"><span class="slc"></span></pre>
<h3>Booking a company ID</h3>
<p>This is a very simple use of the Lotus Connector clockwork.<br />
There is a SAP application where companies are added to a database using a SAP data entry screen. For some reason, the time at which all the needed data is available, is too far in the future (;-), and some guy needs the company ID as soon as possible, well before the SAP application will provide one.</p>
<p>Behind the scenes, the SAP application returns as a company ID the record ID of that company into the companies Oracle table. Finally that record ID is a value of an Oracle sequence, which is incremented whenever a new record is added to the companies table. So it is possible to book a company ID, simply by incrementing the sequence. This means that no record is added to the database, but that ID is unique and will never be used for any other record.</p>
<p>'BookCompanyID:  

Option Public 
Option Declare  

Uselsx "*LSXLC"  
Use "Connection Oracle"  

Function BookCompanyID As Long
	On Error Goto HandleError

	Dim session As New LCSession
	session.ClearStatus
	Goto Begin

HandleError:
	Dim errmsg As String
	errmsg = "Error " & Err & Chr$( 10 ) _
	& Getthreadinfo( 1 ) & ":" & Erl & Chr$( 10 )

	If session.Status <> LCSUCCESS Then
		errmsg = errmsg & session.GetStatusText
	Else
		errmsg = errmsg & Error$
	End If
	Error Err, errmsg  

Begin:
	Dim sqlNextval As String
	sqlNextval = { SELECT SQ_COMPANIES.NEXTVAL FROM DUAL }

	Dim connection As New LCConnection( "oracle8" )
	connection.Server = connection_Server
	connection.UserId = connection_UserId
	connection.Password = connection_Password      
	
	'-------------------------------------------------------------------------------------------     connection.Connect
	Dim fields As New LCFieldList
	Call connection.Execute( sqlNextval, fields )
	Dim nextVal As LCField
	Set nextVal = fields.Lookup( "NEXTVAL" )
	Call connection.Fetch( fields )
	BookCompanyID = nextVal.Value( 0 )

Done:
	connection.Disconnect     
	'-------------------------------------------------------------------------------------------  

End Function</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/accessing-an-oracle-table/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CRC32</title>
		<link>http://noteslog.com/post/crc32/</link>
		<comments>http://noteslog.com/post/crc32/#comments</comments>
		<pubDate>Tue, 06 Jun 2006 21:09:00 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=23</guid>
		<description><![CDATA[A CRC is a number which is supposed to change accordingly to the string its computed upon. So it can be used to detect if the user made changes to a chosen subset of the fields of a document. When saving the document, just join the text values of the fields in an ordered manner, [...]]]></description>
			<content:encoded><![CDATA[<p>A CRC is a number which is supposed to change accordingly to the string its computed upon. So it can be used to detect if the user made changes to a chosen subset of the fields of a document.</p>
<p>When saving the document, just join the text values of the fields in an ordered manner, then get the CRC of that string and store it as a new field value. When checking the document for changes to those fields, make the string again and get its CRC. If the saved CRC and the one just computed are equal, then you should be reasonably sure that those field values have not changed, but if the CRC does change, then you can be completely sure that those field have changed as well!</p>
<p>A CRC32 class comes with the java.util.zip package, and LS2J can be used to access and use it.</p>
<p>Here is the code for a Java library, name it <code>CRC32 Java</code></p>
<pre>import java.util.zip.CRC32;

public class StringCRC32 {

    public static String StringCRC32 ( String s )
    {
        byte[] b = s.getBytes();

        CRC32 crc = new CRC32();

        for ( int i=0; i<b.length; i++ )
        {
            crc.update( b[i] );
        }

        return Long.toString( crc.getValue() );
    }

}</pre>
<p>And here is a LS2J wrapper, just for making it simpler to use in LotusScript. It&#8217;s a LotusScript library and you should name it <code>CRC32</code></p>
<p>'CRC32:

Option Public
Option Declare

Use "CRC32 Java"
Uselsx "*javacon"

Function StringCRC32( aString As String ) As String
    Dim jSession As JavaSession
    Set jSession = New JavaSession
    Dim jClass As JavaClass
    Set jClass = jSession.GetClass( "StringCRC32" )
    StringCRC32 = jClass.StringCRC32( aString )
End Function</p>
<p>Â </p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/crc32/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hack 2 needed</title>
		<link>http://noteslog.com/post/hacking-2-needed/</link>
		<comments>http://noteslog.com/post/hacking-2-needed/#comments</comments>
		<pubDate>Sun, 09 Apr 2006 09:13:52 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=20</guid>
		<description><![CDATA[I&#8217;d like I could use parallel assignment in LotusScript. It&#8217;s the britghtest single programming concept I discovered in the past year, studying Ruby and other scripting languages. It&#8217;s fairly symmetrical to the argument passing concept and it really enhances code readability.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;d like I could use parallel assignment in LotusScript. It&#8217;s the britghtest single programming concept I discovered in the past year, studying Ruby and other scripting languages. </p>
<p>It&#8217;s fairly symmetrical to the argument passing concept and it really enhances code readability.</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/hacking-2-needed/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OLE constants</title>
		<link>http://noteslog.com/post/ole-constants/</link>
		<comments>http://noteslog.com/post/ole-constants/#comments</comments>
		<pubDate>Sat, 11 Mar 2006 16:14:32 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=18</guid>
		<description><![CDATA[Microsoft Office products like Word, Excel and Project can be accessed by LotusScript code running on a Windows machine through the OLE interface made available by the CreateObject function: Dim pjApp As Variant Set pjApp = CreateObject( "MSProject.Application" ) A handle to an OLE root object is a simple and mighty link to (almost) every [...]]]></description>
			<content:encoded><![CDATA[<p>Microsoft Office products like Word, Excel and Project can be accessed by LotusScript code running on a Windows machine through the OLE interface made available by the <code>CreateObject</code> function:</p>
<p>Dim pjApp As Variant
Set pjApp = CreateObject( "MSProject.Application" )</p>
<p>A handle to an OLE root object is a simple and mighty link to (almost) every property and method of the interfaced application.</p>
<p>Certainly not reachable are the constants, and I cannot guess any reason for this. MS Project 2003 has some 3,800 constants defined. All of them are well described in the online manual, but none of them is ever linked to any of the objects reachable from the root object. They are available from insibe the VBA macros, simply because <code>MSProject</code> is the object library and the VBA runtime generates an object for it. So the constant <code>pjTimescaleDays</code>, which is a member of the enumeration <code>PjTimescaleUnit</code>, is defined by the path</p>
<blockquote><p>MSProject.PjTimescaleUnit.pjTimescaleDays</p></blockquote>
<p>In fact VBA programmers are more lucky, because the VBA runtime makes an alias for each constant so that the last identificator is enough.</p>
<p>In my programs I could put <code>Const pjTimescaleDays = 4</code>, it&#8217;s just a matter of copy and paste from the Object Browser integrated in the VBA IDE. But this procedure can quickly become annoying, because the whole corpus of help articles and examples heavily rely on the VBA runtime aliasing feature. And if a Notes agent needs access to just a 10% of all of the available constants, then it&#8217;s really cumbersome to manually build the 400 lines of constants. And they are constant in a subtle way, i.e. until the product vendor suddenly change them.</p>
<p>To access constants defined in OLE applications the VBA Object Browser uses the Tlbinf32.dll library. Luckily enough this library is just another OLE application which can be directly used in LotusScript code.<br />
It officially ships with Visual Studio, but if you haven&#8217;t got it already (it sould be in the windows \system32 folder), you can <a href="http://www.afreedll.com/dll/download/2276/TLBINF32.DLL" target="_blank">download the Tlbinf32.dll library</a> from afreedll.com.</p>
<blockquote style="font-size: smaller"><p><a href="http://msdn.microsoft.com/msdnmag/issues/1200/TypeLib/default.aspx">a good article about the Tlbinf32.dll library</a><br />
<a href="http://download.microsoft.com/download/vstudio60pro/doc/1/win98/en-us/tlbinf32.exe">a useful manual for the Tlbinf32.dll library</a></p></blockquote>
<h3>Example</h3>
<p>To show how you can load OLE constants and use them to effectively simplify and make your code selfdocumented, here is a simple agent for importing MS Project tasks into Notes documents.</p>
<p>'Import tasks from MSProject:

Option Public
Option Declare

Use "DoubleLists"
Use "RegistryAccess"

' replace this comment with code in Block 1
' replace this comment with code in Block 2
' replace this comment with code in Block 3</p>
<p><strong>Block 1: How to load OLE constants</strong></p>
<p>Dim pjConst List As Long

Function GetOLEConstant( typeLibInfo As Variant, Byval enumName As String, Byval itemName As String ) As Long
%INCLUDE "error_handling"

    GetOLEConstant = typeLibInfo.TypeInfos.NamedItem( enumName ).GetMember( itemName ).Value

End Function

Sub Load_pjConst
%INCLUDE "error_handling"

    Dim tli As Variant '= Object
    Set tli = CreateObject( "TLI.TLIApplication" )

    Dim MSPRJ_OLB As Variant '= Object
    Set MSPRJ_OLB = tli.TypeLibInfoFromFile( GetPathToMSPRJ_OLB )

    Dim MSO_DLL As Variant '= Object
    Set MSO_DLL = tli.TypeLibInfoFromFile( GetPathToMSO_DLL )

    Dim pjConstLibrary List As Variant '= List As Object
    Set List2( pjConstLibrary, "PjField"            , "pjTaskID" )              = MSPRJ_OLB
    Set List2( pjConstLibrary, "PjField"            , "pjTaskName" )            = MSPRJ_OLB
    Set List2( pjConstLibrary, "PjField"            , "pjTaskSummary" )         = MSPRJ_OLB
    Set List2( pjConstLibrary, "PjField"            , "pjTaskMilestone" )       = MSPRJ_OLB
    Set List2( pjConstLibrary, "PjTimescaleUnit"    , "pjTimescaleDays" )       = MSPRJ_OLB
    Set List2( pjConstLibrary, "MsoDocProperties"   , "msoPropertyTypeString" ) = MSO_DLL

    Forall enumLib In pjConstLibrary

        Dim enumName As String
        enumName = Listtag( enumLib )

        Forall itemLib In enumLib

            Dim itemName As String
            itemName = Listtag( itemLib )

            pjConst( itemName ) = GetOLEConstant( itemLib, enumName, itemName )

        End Forall
    End Forall

End Sub</p>
<p><strong>Block 2: How to find the path to the object libraries</strong></p>
<p>Function GetPathToMSPRJ_OLB
%INCLUDE "error_handling"

    Dim clsid As String
    clsid = RegQueryValue( "HKEY_CLASSES_ROOT", "MSProject.ApplicationCLSID", "" )

    Dim appPath As String
    appPath = RegQueryValue( "HKEY_CLASSES_ROOT", "CLSID" & clsid & "LocalServer32", "" )

    GetPathToMSPRJ_OLB = Strleftback( appPath, "" ) & "MSPRJ.OLB"

End Function

Function GetPathToMSO_DLL
%INCLUDE "error_handling"

    Dim clsid As String
    clsid = RegQueryValue( "HKEY_CLASSES_ROOT", "OfficeCompatible.ApplicationCLSID", "" )

    Dim appPath As String
    appPath = RegQueryValue( "HKEY_CLASSES_ROOT", "CLSID" & clsid & "InprocServer32", "" )

    GetPathToMSO_DLL = appPath

End Function</p>
<p><strong>Block 3: How to import many Project fields at once</strong></p>
<p>Sub Initialize
%INCLUDE "error_handling"

    Dim s As New NotesSession
    Dim db As NotesDatabase
    Set db = s.CurrentDatabase

    Dim pjApp As Variant '= Object
    Set pjApp = CreateObject( "MSProject.Application" )

    Call pjApp.FileOpen( "c:sample.mpp" )
    pjApp.Visible = False

    Dim pjProject As Variant '= Object
    Set pjProject = pjApp.ActiveProject

    Dim pjTaskCollection As Variant '= Object
    Set pjTaskCollection = pjProject.Tasks

    Call Load_pjConst

    Dim fieldIDList List As Long
    fieldIDList( "LineNumber" )   = pjConst( "pjTaskID" )
    fieldIDList( "Title" )        = pjConst( "pjTaskName" )
    fieldIDList( "IsSummary" )    = pjConst( "pjTaskSummary" )
    fieldIDList( "IsMilestone" )  = pjConst( "pjTaskMilestone" )

    Forall pjTask In pjTaskCollection
        If Not( pjTask Is Nothing ) Then
            Dim d As New NotesDocument( db )
            d.Form = "Task"

            Forall fieldID In fieldIDList
                Dim fieldName As String
                fieldName = Listtag( fieldID )
                Dim fieldValue As Variant
                fieldValue = pjTask.GetField( fieldID )
                Call d.ReplaceItemValue( fieldName, fieldValue )
            End Forall

            Call d.Save( True, True, True )
        End If
    End Forall

End Sub</p>
<p><strong>Installing the Tlbinf32.dll library</strong></p>
<p>Before using its services, the Tlbinf32.dll library needs to be properly installed. This is done by putting its file in the windows \system32 folder and then by registering it. This means that <em>the user has to have administrator rights to their PC</em>. <a href="/?p=16">As usual</a>, you can implement the auto install feature for a needed file in a Notes database in three steps:</p>
<ol>
<li>Attach the file to the About Database document and hide its paragraph</li>
<li>Add the @Command( [RunAgent]; &#8220;(Install Tlbinf32.dll)&#8221; ) to the Post Open event of the Database Script</li>
<li>Add the following agent to the database and name it (Install Tlbinf32.dll)</li>
</ol>
<p>'Install tlbinf32.dll:

Option Public
Option Declare

Use "RegistryAccess"

Sub Initialize
%INCLUDE "error_handling"

    Dim tli As Variant
    On Error Resume Next
    Set tli = CreateObject( "TLI.TLIApplication" )
    On Error Goto HandleError
    If Err = 0 Then
        Set tli = Nothing
        Exit Sub
    End If

    Dim instalar As String
    instalar = "tlbinf32.dll"

    Dim s As New NotesSession
    Dim db As NotesDatabase
    Set db = s.CurrentDatabase

    Dim d As notesdocument
    Set d = GetHelpAboutDocument( db, instalar )
    If d Is Nothing Then
        Msgbox "The library " & instalar & " has not been installed" & Chr( 10 ) _
        & "The library could not be found in the database" & Chr( 10 ) _
        & "Please notify your admin"
        Exit Sub
    End If

    Dim systemRoot As String
    systemRoot = RegQueryValue( "HKEY_LOCAL_MACHINE", "SOFTWAREMicrosoftWindows NTCurrentVersion", "SystemRoot" )

    Dim path As String
    path = systemRoot & "system32" & instalar

    Call ExtractAttachment( d, instalar, path )
    If Dir( path ) = "" Then
        Msgbox "The library " & instalar & " has not been installed" & Chr( 10 ) _
        & "The library could not be put in the folder " & path & Chr( 10 ) _
        & "Please notify your admin"
        Exit Sub
    End If

    If Shell( "regsvr32 /s " & instalar ) <> 33 Then
        Msgbox "The library " & instalar & " has not been installed" & Chr( 10 ) _
        & "The library could not be registered" & Chr( 10 ) _
        & "Please notify your admin"
        Exit Sub
    End If

    Msgbox "The library " & instalar & " has been installed"

    ' HKEY_CLASSES_ROOTCLSID{8B217746-717D-11CE-AB5B-D41203C10000}InprocServer32

End Sub

Function GetHelpAboutDocument( db As NotesDatabase, filename As String ) As NotesDocument
%INCLUDE "error_handling"

    Dim nc As NotesNoteCollection
    Set nc = db.CreateNoteCollection( False )
    nc.SelectHelpAbout = True
    Call nc.BuildCollection
    Dim nid As String
    nid = nc.GetFirstNoteId

    If nid <> "" Then
        Set GetHelpAboutDocument = db.GetDocumentByID( nid )
    Else
        Set GetHelpAboutDocument = Nothing
    End If
End Function

Sub ExtractAttachment( d As NotesDocument, filename As String, path As String )
%INCLUDE "error_handling"

    If Not d.HasEmbedded Then Exit Sub

    Dim embedded As NotesEmbeddedObject
    Set embedded = d.GetAttachment( filename )
    If embedded Is Nothing Then Exit Sub

    Call embedded.ExtractFile( path )
End Sub</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/ole-constants/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Double Lists</title>
		<link>http://noteslog.com/post/double-lists/</link>
		<comments>http://noteslog.com/post/double-lists/#comments</comments>
		<pubDate>Sun, 05 Mar 2006 21:44:05 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=17</guid>
		<description><![CDATA[I use many lists in my programs. They are handy for keeping it tidy and configurable. I surely could put a constant in its own identificator and sometimes I do. But what if I need two or more similar constants? Lists are definitely the best choice for holding column numbers and for exchanging fields between [...]]]></description>
			<content:encoded><![CDATA[<p>I use many lists in my programs. They are handy for keeping it tidy and configurable. I surely could put a constant in its own identificator and sometimes I do. But what if I need two or more similar constants? Lists are definitely the best choice for holding column numbers and for exchanging fields between Office and Notes documents.</p>
<p>Sometimes double lists would be better than normal lists. Example: an agent for importing some fields from an Excel book with many sheets into a Notes document using a double list for declaring the import structure. It should work this way:</p>
<pre>Dim xlImport List2 As String
xlImport( "Sheet1", "report" ) = "Title"
xlImport( "Sheet2", "date3" ) = "Date"
xlImport( "Sheet1", "dept" ) = "Department"</pre>
<p>With such a structure one could define the import as a simple forall loop:</p>
<pre>Dim xlSheet As Variant
Dim xlRange As Variant
Forall f In xlImport
  xlSheet = xlBook.Worksheets( Listtag( f, 1 ) )
  xlRange = xlSheet.Range( Listtag( f, 2 ) )
  Call notesDocument.ReplaceItemValue( f, xlRange.Value )
End Forall</pre>
<p>Unfortunately double lists don&#8217;t exist in LotusScript, so there is no chance for that code to run in a Notes system.</p>
<p>Here is my implementation of double lists, in its own &#8220;DoubleLists&#8221; script library. Its use is very similar to the one described earlier, but this is working code.</p>
<p>Dim xlImport List As Variant '= List As String
List2( xlImport, "Sheet1", "report" ) = "Title"
List2( xlImport, "Sheet2", "date3" ) = "Date"
List2( xlImport, "Sheet1", "dept" ) = "Department"

Forall sheet In xlImport
  xlSheet = xlBook.Worksheets( Listtag( sheet ) )
  Forall f In sheet
    xlRange = xlSheet.Range( Listtag( f ) )
    Call notesDocument.ReplaceItemValue( f, xlRange.Value )
  End Forall
End ForallÂ </p>
<p>The important thing here is the flexibility of this approach, something similar to the Prolog programming language. The import machine is known and fixed, but by means of a simple set of declarations it can import any book. And adding a new field to import is just a matter of copy and paste a declaration and adjust it, as well as stopping the import of a field is just a matter of commenting out a declaration. And the program gets self documented. These are the reasons why I use lists and double lists as much as possible.</p>
<p>'DoubleLists:

Option Public
Option Declare

Use "LsConst.lss"

Property Set List2( aList As Variant, tag1 As String, tag2 As String ) As Variant
%INCLUDE "error_handling"

    If Iselement( aList( tag1 ) ) Then
        Dim aux1 As Variant '= List
        aux1 = aList( tag1 )
        Select Case Datatype( List2 )
        Case V_DISPATCH, V_ERROR, V_IUNKNOWN, V_LSOBJ, V_PRODOBJ
            Set aux1( tag2 ) = List2
        Case Else
            aux1( tag2 ) = List2
        End Select
        aList( tag1 ) = aux1
    Else
        Dim aux2 List As Variant
        Select Case Datatype( List2 )
        Case V_DISPATCH, V_ERROR, V_IUNKNOWN, V_LSOBJ, V_PRODOBJ
            Set aux2( tag2 ) = List2
        Case Else
            aux2( tag2 ) = List2
        End Select
        aList( tag1 ) = aux2
    End If
End Property

Property Get List2( aList As Variant, tag1 As String, tag2 As String ) As Variant
%INCLUDE "error_handling"

    If Iselement( aList( tag1 ) ) Then
        Dim aux As Variant
        aux = aList( tag1 )
        If Iselement( aux( tag2 ) ) Then
            Select Case Datatype( aux( tag2 ) )
            Case V_DISPATCH, V_ERROR, V_IUNKNOWN, V_LSOBJ, V_PRODOBJ
                Set List2 = aux( tag2 )
            Case Else
                List2 = aux( tag2 )
            End Select
        Else
            List2 = Null
        End If
    Else
        List2 = Null
    End If
End Property</p>
<p>Â </p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/double-lists/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Auto Install</title>
		<link>http://noteslog.com/post/auto-install/</link>
		<comments>http://noteslog.com/post/auto-install/#comments</comments>
		<pubDate>Sun, 26 Feb 2006 00:00:13 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=16</guid>
		<description><![CDATA[The error_handling.lss file I described in a previous post needs to be available on any machine where the script using it will execute. This is due to a weird behavior of the Notes engine when processing the %Include instruction: The inclusion is done at execution time if executing on a client and at compilation time [...]]]></description>
			<content:encoded><![CDATA[<p>The <code>error_handling.lss</code> file I described in a previous post needs to be available on any machine where the script using it will execute. This is due to a weird behavior of the Notes engine when processing the %Include instruction: The inclusion is done at execution time if executing on a client and at compilation time if executing on a server.</p>
<p>It is certainly possible to distribute a file to all the users, explaining in which folder to copy it, but it is much simpler to have the same database that use it to install it whenever needed.</p>
<p>The <code>HelpAbout</code> document is a good place for storing a file. After attaching it, a HideWhen formula will prevent it from showing up to the user. Then a simple <code>(Install error_handling.lss)</code> agent will extract the file to the proper folder (if the folder doesn&#8217;t already have one). And the formula <code>@Command( [RunAgent]; "(Install error_handling.lss)" )</code> in the <code>PostOpen</code> event of the <code>Database Script</code> library will run the agent each time the user opens the database.</p>
<p>Here is the code for the agent:</p>
<p>'Install error_handling.lss:

Option Public
Option Declare

Use "RegistryAccess"

Sub Initialize
    On Error Goto HandleError
    Goto EnterProc

HandleError:
    Error Err, Getthreadinfo( 1 ) & " : " & Erl & Chr$( 10 ) & Error$

EnterProc:

    Dim install As String
    install = "error_handling.lss"

    Dim notesFolder As String
    notesFolder = RegQueryValue( "HKEY_LOCAL_MACHINE", "SoftwareLotusNotes", "Path" )
    Dim path As String
    path = notesFolder & install
    If Dir$( path ) <> "" Then
        ' exit on library already installed
        Exit Sub
    End If

    Dim s As New NotesSession
    Dim db As NotesDatabase
    Set db = s.CurrentDatabase

    Dim d As notesdocument
    Set d = GetHelpAboutDocument( db )
    If d Is Nothing Then
        ' exit on library not available in the database
        Msgbox "The library " & install & " must be installed" & Chr( 10 ) _
        & "It's not in the database" & Chr( 10 ) _
        & "Contact the administrator of this database"
        Exit Sub
    End If

    Call ExtractAttachment( d, install, path )
    If Dir( path ) = "" Then
        ' exit on file not created
        Msgbox "The library " & install & " must be installed" & Chr( 10 ) _
        & "The file " & path & " couldn't be created" & Chr( 10 ) _
        & "Contact the administrator of this database"
        Exit Sub
    End If
    Print "Installed library " & install

    install = "error_handling_ui.lss"
    path = notesFolder & install
    Call ExtractAttachment( d, install, path )
    If Dir( path ) = "" Then
        ' exit on file not created
        Msgbox "The library " & install & " must be installed" & Chr( 10 ) _
        & "The file " & path & " couldn't be created" & Chr( 10 ) _
        & "Contact the administrator of this database"
        Exit Sub
    End If
    Print "Installed library " & install
End Sub

Function GetHelpAboutDocument( db As NotesDatabase ) As NotesDocument
    On Error Goto HandleError
    Goto EnterProc

HandleError:
    Error Err, Getthreadinfo( 1 ) & " : " & Erl & Chr$( 10 ) & Error$

EnterProc:

    Dim nc As NotesNoteCollection
    Set nc = db.CreateNoteCollection( False )
    nc.SelectHelpAbout = True
    Call nc.BuildCollection
    Dim nid As String
    nid = nc.GetFirstNoteId

    If nid <> "" Then
        Set GetHelpAboutDocument = db.GetDocumentByID( nid )
    Else
        Set GetHelpAboutDocument = Nothing
    End If
End Function

Sub ExtractAttachment( d As NotesDocument, filename As String, path As String )
    On Error Goto HandleError
    Goto EnterProc

HandleError:
    Error Err, Getthreadinfo( 1 ) & " : " & Erl & Chr$( 10 ) & Error$

EnterProc:

    If Not d.HasEmbedded Then Exit Sub

    Dim embedded As NotesEmbeddedObject
    Set embedded = d.GetAttachment( filename )
    If embedded Is Nothing Then Exit Sub

    Call embedded.ExtractFile( path )
End Sub</p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/auto-install/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Hack 1 needed</title>
		<link>http://noteslog.com/post/hacking-1-needed/</link>
		<comments>http://noteslog.com/post/hacking-1-needed/#comments</comments>
		<pubDate>Sun, 25 Dec 2005 21:14:11 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=15</guid>
		<description><![CDATA[In my apps I use many embedded views that show a single category. They are handy, but lack powerful features exposed views have. Sorting by a different column is one of them. Clearly it&#8217;s not a problem for embedded views as it is for categorized views. Notes cannot properly sort a categorized view by a [...]]]></description>
			<content:encoded><![CDATA[<p>In my apps I use many embedded views that show a single category. They are handy, but lack powerful features exposed views have. Sorting by a different column is one of them. </p>
<p>Clearly it&#8217;s not a problem for embedded views as it is for categorized views. Notes cannot properly sort a categorized view by a different column, because the feature is <a href="http://www-1.ibm.com/support/docview.wss?uid=sim2303d20bac5e6781c85256cc200654007" target="_blank">poorly implemented</a> in that case. </p>
<p>So the hack should allow for a sorting that left the categorization in place. For backward compatibility, it could be a switchable feature, and the switch could be a special starting for the view comment, like &#8220;[1]&#8220;. </p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/hacking-1-needed/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>RegQueryValue</title>
		<link>http://noteslog.com/post/regqueryvalue/</link>
		<comments>http://noteslog.com/post/regqueryvalue/#comments</comments>
		<pubDate>Fri, 09 Dec 2005 23:33:52 +0000</pubDate>
		<dc:creator>Andrea Ercolino</dc:creator>
				<category><![CDATA[Lotus Notes]]></category>

		<guid isPermaLink="false">http://noteslog.com/?p=14</guid>
		<description><![CDATA[@RegQueryValue Queries the Windows registry for a specified value. (from @RegQueryValue, Notes Designer Help database) Although this function can be used in LotusScript by means of an Evaluate, it doesn&#8217;t work at all forÂ the registry (Default) values. So here is aÂ LotusScript RegQueryValue full blown function, in its own RegistryAccess library. 'RegistryAccess: Option Public Option Declare [...]]]></description>
			<content:encoded><![CDATA[<p><em>@RegQueryValue<br />
Queries the Windows registry for a specified value.</em><br />
(from <a href="http://www-12.lotus.com/ldd/doc/domino_notes/6.5.1/help65_designer.nsf/f4b82fbb75e942a6852566ac0037f284/c0e655d4a5eab24385256e000049c840?OpenDocument">@RegQueryValue</a>, Notes Designer Help database)</p>
<p>Although this function can be used in LotusScript by means of an <code>Evaluate</code>, it doesn&#8217;t work at all forÂ the registry <code>(Default)</code> values. So here is aÂ LotusScript RegQueryValue full blown function, in its own RegistryAccess library.</p>
<p>'RegistryAccess:

Option Public
Option Declare

' http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/registry_functions.asp
' http://www.windowsdevcenter.com/lpt/a/4923

Type FILETIME
    dwLowDateTime As Long
    dwHighDateTime As Long
End Type

Public Type SECURITY_ATTRIBUTES
    nLength As Long
    lpSecurityDescriptor As Long
    bInheritHandle As Long
End Type

%REM
LONG RegOpenKeyEx(
  HKEY hKey,
  LPCTSTR lpSubKey,
  DWORD ulOptions,
  REGSAM samDesired,
  PHKEY phkResult
);
%END REM
Declare Function RegOpenKeyEx _
Lib "advapi32.dll" _
Alias "RegOpenKeyExA" _
( Byval hKey As Long _
, Byval lpSubKey As String _
, Byval ulOptions As Long _
, Byval samDesired As Long _
, phkResult As Long ) _
As Long

%REM
LONG RegCloseKey(
  HKEY hKey
);
%END REM
Declare Function RegCloseKey _
Lib "advapi32.dll" _
( Byval hKey As Long ) _
As Long

%REM
LONG RegQueryValueEx(
  HKEY hKey,
  LPCTSTR lpValueName,
  LPDWORD lpReserved,
  LPDWORD lpType,
  LPBYTE lpData,
  LPDWORD lpcbData
);
%END REM
Declare Function RegQueryValueEx _
Lib "advapi32.dll" _
Alias "RegQueryValueExA" _
( Byval hKey As Long _
, Byval lpValueName As String _
, Byval lpReserved As Long _
, lpType As Long _
, Byval lpData As String _
, lpcbData As Long ) _
As Long

%REM
LONG RegEnumKeyEx(
  HKEY hKey,
  DWORD dwIndex,
  LPTSTR lpName,
  LPDWORD lpcName,
  LPDWORD lpReserved,
  LPTSTR lpClass,
  LPDWORD lpcClass,
  PFILETIME lpftLastWriteTime
);
%END REM
Declare Function RegEnumKeyEx _
Lib "advapi32.dll" _
Alias "RegEnumKeyExA" _
( Byval hKey As Long _
, Byval dwIndex As Long _
, Byval lpname As String _
, lpcbName As Long _
, Byval lpReserved As Long _
, Byval lpClass As String _
, lpcbClass As Long _
, lpftLastWriteTime As FILETIME ) _
As Long

%REM
LONG RegSetValueEx(
  HKEY hKey,
  LPCTSTR lpValueName,
  DWORD Reserved,
  DWORD dwType,
  const BYTE* lpData,
  DWORD cbData
);
%END REM
Declare Function RegSetValueEx _
Lib "advapi32.dll" _
Alias "RegSetValueExA" _
( Byval hKey As Long _
, Byval lpValueName As String _
, Byval Reserved As Long _
, Byval dwType As Long _
, Byval lpData As String _
, Byval cbData As Long ) _
As Long

%REM
LONG RegCreateKeyEx(
  HKEY hKey,
  LPCTSTR lpSubKey,
  DWORD Reserved,
  LPTSTR lpClass,
  DWORD dwOptions,
  REGSAM samDesired,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  PHKEY phkResult,
  LPDWORD lpdwDisposition
);
%END REM
Declare Function RegCreateKeyEx _
Lib "advapi32.dll" _
Alias "RegCreateKeyExA" _
( Byval hKey As Long _
, Byval lpSubKey As String _
, Byval Reserved As Long _
, Byval lpClass As String _
, Byval dwOptions As Long _
, Byval samDesired As Long _
, lpSecurityAttributes As SECURITY_ATTRIBUTES _
, phkResult As Long _
, lpdwDisposition As Long ) _
As Long

%REM
LONG RegQueryInfoKey(
  HKEY hKey,
  LPTSTR lpClass,
  LPDWORD lpcClass,
  LPDWORD lpReserved,
  LPDWORD lpcSubKeys,
  LPDWORD lpcMaxSubKeyLen,
  LPDWORD lpcMaxClassLen,
  LPDWORD lpcValues,
  LPDWORD lpcMaxValueNameLen,
  LPDWORD lpcMaxValueLen,
  LPDWORD lpcbSecurityDescriptor,
  PFILETIME lpftLastWriteTime
);
%END REM
Declare Function RegQueryInfoKey _
Lib "advapi32.dll" _
Alias "RegQueryInfoKeyA" _
( Byval hKey As Long _
, Byval lpClass As String _
, lpcbClass As Long _
, Byval lpReserved As Long _
, lpcSubKeys As Long _
, lpcbMaxSubKeyLen As Long _
, lpcbMaxClassLen As Long _
, lpcValues As Long _
, lpcbMaxValueNameLen As Long _
, lpcbMaxValueLen As Long _
, lpcbSecurityDescriptor As Long _
, lpftLastWriteTime As FILETIME ) _
As Long

Declare Function SHDeleteKey _
Lib "shlwapi.dll" _
Alias "SHDeleteKeyA" _
( Byval hKey As Long _
, Byval pszSubKey As String ) _
As Long

'-- Constant Definitions for WIN32API
Dim regKey List As Long
Const HKEY_CLASSES_ROOT = &H80000000
Const HKEY_CURRENT_CONFIG = &H80000005
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_USERS = &H80000003
Const HKEY_PERFORMANCE_DATA = &H80000004

'Dim regError List As Long
Const ERROR_SUCCESS = 0&
Const ERROR_FILE_NOT_FOUND = 2&                  ' Registry path does not exist
Const ERROR_ACCESS_DENIED = 5&                   ' Requested permissions not available
Const ERROR_INVALID_HANDLE = 6&                  ' Invalid handle or top-level key
Const ERROR_BAD_NETPATH = 53&                    ' Network path not found
Const ERROR_INVALID_PARAMETER = 87&              ' Bad parameter to a Win32 API function
Const ERROR_CALL_NOT_IMPLEMENTED = 120&          ' Function valid only in WinNT/2000?XP
Const ERROR_INSUFFICIENT_BUFFER = 122&           ' Buffer too small to hold data
Const ERROR_BAD_PATHNAME = 161&                  ' Registry path does not exist
Const ERROR_NO_MORE_ITEMS = 259&                 ' Invalid enumerated value
Const ERROR_BADDB = 1009&                        ' Corrupted registry
Const ERROR_BADKEY = 1010&                       ' Invalid registry key
Const ERROR_CANTOPEN = 1011&                     ' Cannot open registry key
Const ERROR_CANTREAD = 1012&                     ' Cannot read from registry key
Const ERROR_CANTWRITE = 1013&                    ' Cannot write to registry key
Const ERROR_REGISTRY_RECOVERED = 1014&           ' Recovery of part of registry successful
Const ERROR_REGISTRY_CORRUPT = 1015&             ' Corrupted registry
Const ERROR_REGISTRY_IO_FAILED = 1016&           ' Input/output operation failed
Const ERROR_NOT_REGISTRY_FILE = 1017&            ' Input file not in registry file format
Const ERROR_KEY_DELETED = 1018&                  ' Key already deleted
Const ERROR_KEY_HAS_CHILDREN = 1020&             ' Key has subkeys & cannot be deleted

Const SYNCHRONIZE = &H100000

'Dim regStandard List As Long
Const STANDARD_RIGHTS_READ = &H20000
Const STANDARD_RIGHTS_WRITE = &H20000
Const STANDARD_RIGHTS_EXECUTE = &H20000
Const STANDARD_RIGHTS_REQUIRED = &HF0000
Const STANDARD_RIGHTS_ALL = &H1F0000

'Dim regAction List As Long
Const KEY_CREATE_LINK = &H20
Const KEY_CREATE_SUB_KEY = &H4
Const KEY_ENUMERATE_SUB_KEYS = &H8
Const KEY_NOTIFY = &H10
Const KEY_QUERY_VALUE = &H1
Const KEY_READ = ((STANDARD_RIGHTS_READ Or KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY) And (Not SYNCHRONIZE))
Const KEY_SET_VALUE = &H2
Const KEY_WRITE = ((STANDARD_RIGHTS_WRITE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY) And (Not SYNCHRONIZE))
Const KEY_EXECUTE = (KEY_READ)
Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or KEY_QUERY_VALUE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY Or KEY_CREATE_LINK) And (Not SYNCHRONIZE))

Const REG_OPTION_BACKUP_RESTORE = 4
Const REG_OPTION_NON_VOLATILE = 0
Const REG_OPTION_VOLATILE = 1
Const REG_CREATED_NEW_KEY = &H1                  ' A new key was created
Const REG_OPENED_EXISTING_KEY = &H2              ' An existing key was opened

' Reg Data Types...
Const RRF_RT_ANY = &H0000FFFF
Const REG_NONE = 0                               ' No value type
Const REG_SZ = 1                                 ' Unicode nul terminated string
Const REG_EXPAND_SZ = 2                          ' Unicode nul terminated string
Const REG_BINARY = 3                             ' Free form binary
Const REG_DWORD = 4                              ' 32-bit number
Const REG_DWORD_LITTLE_ENDIAN = 4                ' 32-bit number (same as REG_DWORD)
Const REG_DWORD_BIG_ENDIAN = 5                   ' 32-bit number
Const REG_LINK = 6                               ' Symbolic Link (unicode)
Const REG_MULTI_SZ = 7                           ' Multiple Unicode strings

Function RegQueryValue( keyName As String, subKeyName As String, valueName As String ) As String
%INCLUDE "error_handling"

    Dim sKeyType As Long ' to return the key type
    Dim lpHKey As Long ' return handle to opened key
    Dim lpcbData As Long ' length of data in returned string

    Dim MainKey As Long
    MainKey = regKey( keyName )
    ' Open key
    If RegOpenKeyEx( MainKey, SubKeyName, 0&, KEY_READ, lpHKey ) <> ERROR_SUCCESS Then
        RegQueryValue = ""
        Exit Function ' No key open, so leave
    End If

    Dim lBuffer As Long
    lBuffer = 0
    ' Ask for buffer size for this value
    Call RegQueryValueEx( lpHKey, valueName, 0, sKeyType, 0, lBuffer )

    ' Initialize buffer
    Dim sBuffer As String
    sBuffer = Space( lBuffer ) & Chr( 0 )
    lBuffer = Len( sBuffer )
    If RegQueryValueEx( lpHKey, valueName, 0, sKeyType, sBuffer, lBuffer ) <> ERROR_SUCCESS Then
        RegQueryValue = ""   'Value probably doesn't exist
        Exit Function
    End If

    ' Trim returned buffer to extract key name
    sBuffer = Left( sBuffer, lBuffer - 1 )
    RegQueryValue = sBuffer

    ' Always close opened keys
    Call RegCloseKey( lpHKey )
End Function

Sub Initialize
%INCLUDE "error_handling"

    regKey( "HKEY_CLASSES_ROOT" ) = HKEY_CLASSES_ROOT
    regKey( "HKEY_CURRENT_CONFIG" ) = HKEY_CURRENT_CONFIG
    regKey( "HKEY_CURRENT_USER" ) = HKEY_CURRENT_USER
    regKey( "HKEY_LOCAL_MACHINE" ) = HKEY_LOCAL_MACHINE
    regKey( "HKEY_USERS" ) = HKEY_USERS
    regKey( "HKEY_PERFORMANCE_DATA" ) = HKEY_PERFORMANCE_DATA
End Sub

Sub Terminate

End Sub</p>
<p>Â </p>
]]></content:encoded>
			<wfw:commentRss>http://noteslog.com/post/regqueryvalue/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
