Lunatic Sol

IT Tips

GetLocalAddressBook

2003-12-30 15:41:16 | LotusScript
個人アドレス帳のファイル名を names.nsf 以外にしている人はほぼいないでしょう。ですので、個人アドレス帳の NotesDatabase オブジェクトを取得したい場合は Dim db As New NotesDatabase("","names.nsf") でほとんど問題ありません。でも、Notes 上確実に個人アドレス帳を取得させたい場合はこの方法は適当ではありません。

方法としては NotesSession クラスの GetEnvironmentString メソッドで NAMES= をとる方法もあるのですが、NotesSession クラスの AddressBooks プロパティと IsPrivateAddressBook プロパティを使って取得する方法でファンクション化してみました。

Function GetLocalAddressBook As NotesDatabase
    Dim s As New NotesSession
    Dim books As Variant
    books = s.AddressBooks
    Forall b In books
        If b.IsPrivateAddressBook Then
            Set GetLocalAddressBook = b
            Exit Forall
        End If
    End Forall
End Function

これを実際に使ってみたい場合は以下のようなコーディングを Initialilze イベントなどに入れると OK です。

Sub Initialize
    Dim db As NotesDatabase
    Set db = GetLocalAddressBook
    Call db.Open(db.Server,db.FilePath)
End Sub

OSLoadString を LotusScript から Call してみよう

2003-12-17 15:53:58 | LotusScript
Notes C API に OSLoadString という関数があります。この関数は Notes C API 上戻されたエラーコードを元に割り当てられたメッセージを出力することができる関数です。LotusScript クラスだけを使用している場合には特に必要ありませんが、LotusScript で Notes C API 関数を直接 Call するようになると重要になってきます。ということで LotusScript で OSLoadString を Call する仕組みを作ってみました。今回は SxNotesError というクラスにしています。

  Dim sxErr As New SxNotesError(errorNumber&)

というような形でクラスオブジェクトを初期化して、GetErrMessage メソッドでエラーメッセージを取得してください。例えば errString$ = sxErr.GetErrMessage というような感じです。

Declare Function OSLoadString Lib "nnotes" (Byval handle As Integer, Byval errNum As Integer, Byval buffer As Lmbcs String, Byval textLen As Integer) As Integer
Const STS_REMOTE = &H4000
Const STS_DISPLAYED = &H8000

Class SxNotesError
    Private errNum As Long
    Public MaskOffErrCode As Integer

    Sub New(errorNum As Long)
        Me.errNum = errorNum

        '*** PROPERTIES ***
        MaskOffErrCode = GetMaskOffErrCode
    End Sub

    Private Function GetMaskOffErrCode As Integer
        If Me.errNum And STS_DISPLAYED Then
            Me.errNum = Me.errNum And (&HFFFF Xor STS_DISPLAYED)
        End If

        If Me.errNum And STS_REMOTE Then
            Me.errNum = Me.errNum And(&HFFFF Xor STS_REMOTE)
        End If
        GetMaskOffErrCode = Me.errNum
    End Function

    Function GetErrMessage As String
        Dim errLen As Integer
        Dim retBuffer As String
        retBuffer = String$(255,0)
        errLen = OSLoadString(0, Me.MaskOffErrCode, retBuffer, Len(retBuffer)-1)
        GetErrMessage = retBuffer
    End Function
End Class

LotusScript のボタンを作って (Declarations) に上のソースを全て入力し、Click イベントに以下のようなコードを入れるとエラーコードを入力してメッセージをチェックするシミュレーションができます。また別のテクニックとして lss ファイルにしてみよう の回で紹介した Tips を使って SxNotesError.lss というテキストファイルにして Notes プログラムディレクトリに保存し、(Declarations) には

  %INCLUDE "SxNotesError.lss"

と指定することで実現することもできます。

Sub Click(Source As Button)
    Dim sxErr As New SxNotesError(Clng(Inputbox$("エラーコード")))
    Msgbox sxErr.GetErrMessage
End Sub


■ 参考: OSLoadString</p>

Caldera OpenLinux 3.1.1 のアップデート

2003-12-17 14:11:30 | UNIX/Linux
Windows 2000 や XP の場合 [スタート] - [Windows Update] を選択することで最新のアップデートモジュールを見つけてインストールすることができる。Linux の場合は同じことができるのだろうか?

使用している Linux は Caldera OpenLinux Server 3.1.1 なので、このディストリビューション限定の方法になるが
[K メニュー] - [システム] - [Caldera システムアップデータ]
を選択することで最新版モジュール・パッチを検索してインストールすることができる。ただし、モジュールによってはエラーになるので選択を解除しながら進める必要があり、少し煩わしい (もしかしたら、自分の Linux 知識不足から来ている勘違いかもしれないが)。

当然 [K メニュー] なのでデスクトップに KDE を使っている場合のメニューになるが、GNOME でも同じような選択肢はあるのではないかと思う。(でも、OpenLinux のデフォルトは KDE なので大半の人は KDE を使っているのではないかなと思う。)

lss ファイルにしてみよう

2003-12-16 00:48:31 | LotusScript
数行のコードであれば Initialize イベントに直接コーディングしてしまえばいいですが、例えばエージェントの中で同じ処理を何度も使いたい場合、同じ内容を何箇所にも書くのは効率が悪いです。そんな場合はサブルーチンやファンクションを使いますね。さらに同じ処理をデータベース全体で使用したい場合はスクリプトライブラリ化してエージェントやアクションボタンから同じ DB であればどこからでも利用できるようになります。でも、異なる DB で同じ処理を使いたい場合どうすればいいでしょうか?

ヘルプファイルを見ると CONST (定数) ファイルとして lss が紹介 (lsxbeerr.lss など) されていますが、実はこのファイルの中に LotusScript コードを書くことで外部ファイル化することができ、Notes 上のどこからでもつかえるようになるんです。例えば以下のような Messagebox を実行するサブルーチンを mysample.lss というテキストファイルとして作成してみてください。ファイルは Notes プログラムディレクトリに置いてください。

Sub lssMessagebox
    Messagebox "Hello lss Messagebox !"
End Sub
そしてエージェントやアクションボタンなどの (Declarations) に %INCLUDE "mysample.lss" を書いて、Initialize イベントに Call lssMessagebox とだけ書いて実行してみてください。ボタンの場合であれば Click イベントでも OK です。Hello lss Messagebox ! と表示されましたよね?この方法を利用することで汎用的なコードを幅広く使いまわすことができるようになります。

ロケーション文書の NoteID

2003-12-15 00:00:10 | LotusScript
LotusScript でコーディングするとき、ロケーション文書の情報を参照したいときがあります。でも、毎回その ID を取得する方法を考えるのは面倒なのでファンクション化してみました。

Notes.ini の Location= というパラメータの2つめの引数は実はロケーション文書の Note ID なので、それを取得する仕組みです。今回はちょっと省略して接尾辞をつける方法で変数の型を決定させています。

Function GetLocationID As String
    Dim s As New NotesSession
    l$ = s.GetEnvironmentString("Location",True)
    IDBgn% = Instr(1, l$, ",") + 1
    IDEnd% = Instr(IDBgn%, l$, ",")
    GetLocationID = Trim(Mid$(l$, IDBgn%, IDEnd%-IDBgn%))
End Function

ローカルで実行した LotusScript からサーバーコンソールに Print する

2003-12-13 02:28:06 | LotusScript
Messagebox や Print は良く使うと思いますが、ローカルで実行したときはダイアログやステータスバーに記録されます。また、サーバーで実行したときはサーバーコンソールに記録されます。これは当然の動作ですが、クライアント上で実行した処理をサーバーコンソール上に記録することはできないでしょうか?

だいたいそんなこと考えなくても NotesLog クラスを使えばスクリプトの実行を記録することができるので Msgbox や Print にこだわる必要はありませんし、そもそもなぜサーバーコンソールに記録する必要があるのでしょう?という意見もあるかもしれません。でも、時にはコンソール上でログの時系列通りに記録してもらった方がトラブルシューティングに役に立つケースもあるかもしれませんよね。そんな「もしも」の時に使えるかもしれないちょっとした方法を考えてみました。RunOnServer メソッド (NotesDatabase クラス) を使います。

Sub Initialize
    Dim s As New NotesSession
    Dim db As NotesDatabase
    Dim agent As NotesAgent

    Set db = s.CurrentDatabase
    Set agent = db.GetAgent("printAgent")
    If agent.RunOnServer() <> 0 Then
Print "失敗: エージェントが実行できませんでした"
End Sub


printAgent エージェント

Sub Initialize
    Print "Local agent is executed !"
End Sub


上記の例は本当に単純に Print を実行するだけのエージェント printAgent (2つめのサンプル) を作成し、それを RunOnServer メソッドで Call する (最初のサンプルがローカルで実行するスクリプト) だけのものです。これでローカルエージェントの実行時にサーバーコンソールに Print を流すことができます。でも、printAgent の内容が Static になってしまいメリットがありません。

RunOnServer メソッドは引数に Note ID を String 型で受け取ることができるので、Notes 上で作業中の Note ID を RunOnServer メソッドに渡すことでその Note ID をベースにした情報をコンソールに Print することができます。以下の例では Note ID で取得した NotesDocument オブジェクトを使って UNID をコンソールに Print しています。こうすると少しは用途が出てくるでしょうか?

Sub Initialize
    Dim s As New NotesSession
    Dim db As NotesDatabase
    Dim agent As NotesAgent
    Dim ws As New NotesUIWorkspace
    Dim uidoc As NotesUIDocument
    Dim doc As NotesDocument

    Set db = s.CurrentDatabase
    Set agent = db.GetAgent("printAgent")
    Set uidoc = ws.CurrentDocument
    Set doc = uidoc.Document

    If agent.RunOnServer(doc.NoteID;) <> 0 Then
Print "失敗: エージェントが実行できませんでした"
End Sub


printAgent エージェント

Sub Initialize
    Dim s As New NotesSession
    Dim db As NotesDatabase
    Dim agent As NotesAgent
    Dim doc As NotesDocument
    Dim docId As String

    Set db = s.CurrentDatabase
    Set agent = s.CurrentAgent
    docId = agent.ParameterDocID
    Set doc = db.GetDocumentByID(docId)

    'ここに処理したい内容を書く

    Print "UNID=" + doc.UniversalID
End Sub

ノーツ R5 で Split

2003-12-12 14:30:09 | LotusScript
Notes 6 からは Split 関数というものが追加されました。この関数はスペースやカンマなどが含まれた文字列を任意のキーで文字列配列化することができます。これを R5 で使いたい場合、自分でコーディングする必要があります。結構良く使うのでファンクションを書いてみました。

ひとつのファンクションで解決させずに2つのファンクションを使うことで実現してみました。1つめの SxSplit() というのが R5 で Notes 6 の Split() と同じようなことを実現する仕組みです。Notes 6 の Split とほぼ同様ですが、引数が少ないです。最初に文字列、2つめの引数には区切り文字として認識させたいものを書きます。2つめの SxCountQuery は1つめの引数の文字列に、2つめの引数の文字が何回出現するかチェックしているものです。SxCountQuery() 単体でも汎用的に使えそうなので分けてみました。少しは便利に使えそうでしょうか?

Function SxSplit(Byval expression As String, delimiter As String) As Variant
    Dim count As Integer
    Dim i As Integer, p As Integer
    Dim length As Integer
    Dim tmpArray As Variant

    count = SxCountQuery(expression, delimiter)
    Redim tmpArray(count)
    i = 0
    length = Len(delimiter)
    p = Instr(expression, delimiter)
    Do While p > 0
        tmpArray(i) =Left(expression, p - 1)
        i = i + 1
        expression = Mid(expression, p + length)
        p = Instr(expression, delimiter)
    Loop
    tmpArray(i) = expression 'Add the leftover to the last array
    SxSplit = tmpArray
End Function

Function SxCountQuery(Byval expression As String, query As String) As Integer
    Dim count As Integer, p As Integer
    Dim length As Integer
    count = 0
    length = Len(query)
    p = Instr(expression, query)
    Do While p > 0
        count = count + 1
        expression = Mid(expression, p + length)
        p = Instr(expression, query)
    Loop
    SxCountQuery = count
End Function

今後このコードを改良して Notes 6 と同じ引数を持つ関数に変えて、ノーツ R5 でも Notes 6 でも使える Wrapper にしようと思います。

Disable Performance Counters

2003-12-11 13:34:39 | Windows
今回はちょっと仕事に関係する話

とあるサーバーアプリケーションを実行していると起動後数分でクラッシュしてしまう。Windows 2000 Server + SP3 上で実行したのはいいものの、まともに稼動してくれないので使い物にならない。

そのアプリケーション側にパラメータを設定することで問題は回避できて使えるようになるのでとりあえずは問題なくなっていたのだが、その根本の原因を調べないといけない状況になった。といっても、私自身がではなくて同僚がなのであるが...

でいろいろ調べたところ以下のレジストリにある

Disable Performance Counters

という DWORD 値が 1 になっている環境でのみ問題があるらしいとの結論に達した。

HKLM\\SYSTEM\\CurrentControlSet\\Services\\PerfProc\\Performance

まぁ、ここまではいいのであるが問題は何をインストールしたらこの DWORD 値が 1 となってしまうのかという点。私が使用しているテスト環境では DWORD 値が 1 になっていたし、他にも問題がおきている同僚の環境も 1 になっていた。しかしながらキーすらない環境もある。いったい全体何をインストールしたせいなのか。と思いインストールしているアプリケーションを考えてみる。

Ultr@VNC, Oracle, MS Visual Studio, MS Web Access Stress Tool, 秀丸, などなどきりがない。とりあえず、明日以降は regmon を常時かませてモニターするしかないかな...