Lotusscript call to @Formula API not working

What’s wrong with this code, I can’t find an error, and a simple formula like “abc”:“def” returns only " “:” ". I’m not sure if the MoveMemory() call works like this in R6+, as R6+ uses single byte addressing:

Const TYPE_TEXT_LIST = &H501

Const OS_TRANSLATE_NATIVE_TO_LMBCS = 0

Const OS_TRANSLATE_LMBCS_TO_NATIVE = 1

Declare Function NSFFormulaCompile Lib “nnotes.dll” _

(FormulaName As Any, Byval FormulaNameLength As Integer, _

Byval FormulaText As String, Byval FormulaTextLength As Integer, _

rethFormula As Long, retFormulaLength As Integer, retCompileError As Integer, _

retCompileErrorLine As Integer, retCompileErrorColumn As Integer, _

retCompileErrorOffset As Integer, retCompileErrorLength As Integer) As Integer

Declare Function NSFComputeStart Lib “nnotes.dll” (Byval _

Flags As Integer, Byval lpCompiledFormula As Long, rethCompute _

As Long) As Integer

Declare Function NSFComputeStop Lib “nnotes.dll” (Byval hCompute As Long) As Integer

Declare Function NSFComputeEvaluate Lib “nnotes.dll” _

(Byval hCompute As Long, Byval hNote As Long, rethResult As _

Long, retResultLength As Integer, retNoteMatchesFormula As _

Boolean, retNoteShouldBeDeleted As Boolean, retNoteModified As _

Boolean) As Integer

Declare Function OSLockObject Lib “NNOTES.DLL” Alias “OSLockObject” (Byval handle As Long) As Long

Declare Sub OSUnlockObject Lib “NNOTES.DLL” Alias “OSUnlockObject” (Byval handle As Long)

Declare Sub OSMemFree Lib “NNOTES.DLL” Alias “OSMemFree” (Byval handle As Long)

Declare Sub MoveMemory Lib “kernel32.dll” Alias “RtlMoveMemory” _

(Destination As Any, Source As Any, Byval Length As Long)

Declare Function ListGetNumEntries Lib “nnotes.dll” (vList As Long, Byval NoteItem As Boolean) As Integer

Declare Function ListGetText Lib “nnotes.dll” (pList As Long, Byval fPrefixDataType As Boolean, _

Byval EntryNumber As Integer, retTextPointer As Long, retTextLength As Integer) As Integer

Dim sOutBuffer As String

Declare Function OSTranslate Lib “nnotes.dll” (Byval TranslateMode As Integer, Byval InData As String, Byval InLength As Integer, Byval OutData As String, Byval OutLength As Integer ) As Integer

Sub Initialize

v=RunFormula({@Unique("abc":"def")})

End Sub

Function RunFormula(s As String) As Variant

Redim vResult(0 To 0) As String

vResult(0) = ""



Dim p_hNote As Long, p_sFormula As String

p_sFormula = s

p_hNote = 0



Dim iReturnValue As Integer

Dim hFormula As Long

Dim iFormulaLength As Integer

Dim iCompileError As Integer

Dim iCompileErrorLine As Integer

Dim iCompileErrorColumn As Integer

Dim iCompileErrorOffset As Integer

Dim iCompileErrorLength As Integer

Dim lFormulaAddress As Long

Dim hCompute As Long

Dim hResult As Long

Dim iResultLength As Integer

Dim bNoteMatchesFormula As Boolean

Dim bNoteShouldBeDeleted As Boolean

Dim bNoteModified As Boolean

Dim lResultAddress As Long

Dim iDataType As Integer

Dim sFormulaLMBCS As String



sFormulaLMBCS = translate(OS_TRANSLATE_NATIVE_TO_LMBCS, p_sFormula)



iReturnValue = NSFFormulaCompile(Byval &H0, 0, sFormulaLMBCS, _

Len(sFormulaLMBCS), hFormula, iFormulaLength, iCompileError, _

iCompileErrorLine, iCompileErrorColumn, iCompileErrorOffset, _

iCompileErrorLength)



If (iReturnValue) Then

	Msgbox "Error " & iReturnValue

	RunFormula = vResult

	Exit Function

End If



lFormulaAddress = OSLockObject(hFormula)

iReturnValue = NSFComputeStart(0, lFormulaAddress, hCompute)



iReturnValue = NSFComputeEvaluate(hCompute, p_hNote, hResult, _

iResultLength, bNoteMatchesFormula, bNoteShouldBeDeleted, _

bNoteModified)



If (iReturnValue) Then

	Msgbox "Error " & iReturnValue

	RunFormula = vResult

	Exit Function

End If



lResultAddress = OSLockObject(hResult)

Call MoveMemory(iDataType, Byval lResultAddress, 2)



If (iDataType = TYPE_TEXT_LIST) Then

	Dim iEntryCount As Integer

	iEntryCount = ListGetNumEntries(Byval lResultAddress, True)

	Redim vResult(0 To iEntryCount - 1) As String

	Dim iIndex As Integer

	For iIndex = 0 To (iEntryCount - 1) Step 1

		Dim lTextPointer As Long

		Dim iTextLength As Integer

		iReturnValue = ListGetText(Byval lResultAddress, _

		True, iIndex, lTextPointer, iTextLength)

		If (iTextLength <> 0) Then

			Dim sText As String

			sText = Space$(iTextLength)

			Call MoveMemory(Byval sText, Byval lTextPointer, iTextLength)

			vResult(iIndex) = translate(OS_TRANSLATE_LMBCS_TO_NATIVE, sText)

		Else

			vResult(iIndex) = ""

		End If

	Next iIndex

Else

	'Msgbox "Error"	

End If



iReturnValue = NSFComputeStop(hCompute)



OSUnlockObject (hResult)

OSMemFree (hResult)

OSUnlockObject (hFormula)

OSMemFree (hFormula)



RunFormula = vResult

End Function

Function Translate( p_iTranslateMode As Integer, p_sInBuffer As String ) As String

sOutBuffer = Space$( 256 )

Call OSTranslate(p_iTranslateMode,p_sInBuffer,Len(p_sInBuffer),sOutBuffer,Len(sOutBuffer))

Translate = Left$( sOutBuffer, Instr( sOutBuffer, Chr$( 0 ) ) -1 )

End Function

Subject: Lotusscript call to @Formula API not working

Evaluate() not working anymore? This is a lot of code to do the same thing.

Subject: RE: Lotusscript call to @Formula API not working

Yeah, it should work like the Evaluate statement, with the exception of the restrictions the Evaluate statement (might) have, that’s what I’m trying to find out.

Subject: RE: Lotusscript call to @Formula API not working

As long as you have a good reason. I can’t see what restrictions you could get around (returned data length, etc., should be the same – although Damien Katz might be a better source for authoritative info on that part).

That being said, I don’t see any glaring errors.

Subject: I noticed 2 thing in your NSFFoumulaCompile declarations

My declarations have it:Declare Function NSFFormulaCompile Lib “nnotes.dll” _

(Byval sFormulaName As String, Byval wFormulaNameLength As Integer,_

Byval sFormulaText As Lmbcs String, Byval wFormulaTextLength As Integer,_

hSelFormula As Long, wSelFormulaLen As Integer, wCompError As Integer,_

wCompErrorLine As Integer, wCompErrorColumn As Integer,_

wCompErrorOffset As Integer, wCompErrorLength As Integer) As Integer

Subject: RE: I noticed 2 thing in your NSFFoumulaCompile declarations

That changed something, but didn’t quite fix the problem, with your declaration the NSFFormulaCompile() suddenly compiles a formula code which returns aConst TYPE_NUMBER_RANGE = &H301, instead of

Const TYPE_TEXT_LIST = &H501

which is imo correct for this formula.

It could be also a misdeclaration of the NSFComputeStart(), but everything seems to execute fine with no error returns, only the result remains an blank array.

Subject: Lotusscript call to @Formula API not working

Mika, are you sure that your characterset translations are working correctly?

I would declare OSTranslate as:

Declare Function OSTranslate Lib “nnotes.dll” (Byval TranslateMode As Integer, Byval InData As Unicode String, Byval InLength As Integer, Byval OutData As Unicode String, Byval OutLength As Integer ) As Integer

and use OS_TRANSLATE_UNICODE_TO_LMBCS, etc. else you need different declarations for translation in either direction.

as with your declaration Notes will convert from Unicode to platform native encoding before passing to OSTranslate call and then translate obtained LMBCS back to Unicode interpreting it as platform native string.

or even

Declare Function OSTranslate Lib “nnotes.dll” (Byval TranslateMode As Integer, Byval InData As Unicode String, Byval InLength As Integer, ByVal pOutData As Long, Byval OutLength As Integer ) As Integer

and use buffer to hold returned string (as you will use it as input for NSFFormulaCompile; and use another declaration for the return translation.

Besides you should provide as input to OSTranslate length in Bytes not characters. That refers to all other C API string operations as well. And I would not bee happy to use LS strings and string operations too much as it becomes hard to track down this kind of nuances. Also NSFFormulaCompile should be able to take as input unconverted string by declaring “ByVal FormulaText as LMBCS String”, no? BTW if you declare “ByVal FormulaText as String” it will expect native string not LMBCS that you seem to try to provide it.

Also BOOL is Long (4 bytes) not Boolean (2 bytes) although I do not see how that could matter in this particular case.

In any case I tried some of my suggestions. I managed to translate correctly to LMBCS, but somewhere along the line it still fails. Looks like the line:

Call MoveMemory(Byval sText, Byval lTextPointer, iTextLength)

does not do what you expect.

Then I used different a declaration for OSTranslate that accepted as input pointer (lTextPointer):

Declare Function OSTranslatePtrIn Lib “nnotes.dll” Alias “OSTranslate”(Byval TranslateMode As Integer, _

Byval InData As Long, Byval InLength As Integer, Byval OutData As Unicode String, Byval OutLength As Integer ) As Integer

and it worked then.

vResult(iIndex) = translatePtrIn(OS_TRANSLATE_LMBCS_TO_UNICODE, lTextPointer, iTextLength)

Function TranslatePtrIn( p_iTranslateMode As Integer, pInBuffer As Long, inlen As Integer) As String

sOutBuffer = Space$( 256 )

Dim retlen As Integer



retlen = OSTranslatePtrIn(p_iTranslateMode,pInBuffer,inlen,sOutBuffer,Len(sOutBuffer))

’ use returned length in bytes

TranslatePtrIn = Leftb$( sOutBuffer, retlen )

End Function

You may say that this last change is enough to make your sample work and codeset conversions discussed above is irrelevant. True enough while you use plain ascii characters it works with only this last change. However if you put some Japanese characters in your string they get screwed up unless you do the conversion upfront in a correct way.

Does this help? BTW, do you have my book about calling C API from LotusScript? http://www.ls2capi.com

It has a chapter about codeset conversions…

Subject: BTW one more tip

when doing LS2CAPI stuff it’s frequently much easier if you can look at particular memory area and see what’s in there. There is code around in this forum that allows to build such a viewer.

But i usually use a function contained in rtLib:

Function viewHex (pointer As Long, length as Integer) As String

It returns a string with the hex dump starting with the provided pointer.

There is also another variation of this call:

Function viewHexText (pointer As Long, length as Integer) As String

that returns also the “text” provided the memory contains some kind of string.

You can get rtlib at http://www.rtlib.com and for development stuff like this its free. viewHex/viewHexText are undocumented functions as they are by-products of the main focus of rtLib, it’s rather a part of debugging code left in. But it makes my life much easier as I can see at a glance if LMBCS conversion is correct (of course provided a memory buffer not string variable is used to store the output), what’s contained in returned buffer or item, etc.

Maybe I should draw a list of useful undocumented calls available in rtLib(?)

good luck

Normunds

Subject: RE: BTW one more tip

Thanks very much for the detailed explanation. I will check which strings need to be exclusively LMBCS translated (and which are automatically handled by platform/character set independant Notes features), and take a look at your LS2CAPI book too.

Subject: RE: BTW one more tip

Seems that the problem remains, the line:

Call MoveMemory(Byval sText, Byval lTextPointer, iTextLength)

which should move the string to the sText variable, always moves 3 spaces (instead of “abc”). It doesn’t seem to be a LMBCS issue, but rather a memory addressing issue.

Subject: RE: BTW one more tip

sorry if I was not clear about it:

I wrote that I replaced your moveMemory cody with my code I provided (including a different OSTranslate declaration as I converted straight from the result buffer to the string). That fixed the issue. But ONLY in case the string is plain ASCII (or rather CP850) – that’s why LMBCS.

Besides if you used viewHex routine I mentioned you could clearly see the data in returned list, that means that your Formula/evaluation part works correctly.

Subject: RE: BTW one more tip

Ok, now I found my mistake, I was using:

vResult(iIndex) = translatePtrIn(OS_TRANSLATE_UNICODE_TO_LMBCS,lTextPointer,iTextLength)

instead of the correct:

vResult(iIndex) = translatePtrIn(OS_TRANSLATE_LMBCS_TO_UNICODE, lTextPointer, iTextLength)

when I was playing around with the LMBCS conversions, and apparently forgot to put it back.

Thanks a lot, it works now!