goo blog サービス終了のお知らせ 

Lunatic Sol

IT Tips

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>

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 にしようと思います。