[Solved] Getting a module object for use in CallByName

Creating a macro - Writing a Script - Using the API (OpenOffice Basic, Python, BeanShell, JavaScript)
Post Reply
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

[Solved] Getting a module object for use in CallByName

Post by JeJe »

Is there a way to create a module object from the library and module names?

For example Library1.Module1 from the strings "Library1" and "Module1" so we can use callbyname without having the module object in advance?

eg This calls the testsub sub in module1 in library1 with 7 as a parameter, but you need the Library1.Module1 object so its only callbyname as far as the sub or function name goes.

Code: Select all

ret= CallByName(Library1.Module1, "testsub", 1,7)

(I know there is the alternative method of calling a script with script invoke and another alternative of using the dispatch helper.
And this cumbersome method of a function with select case for every module:

Code: Select all

Sub TestCallByName
module=GetModule("Library1","Module1")
res= CallByName(module, "testsub", 1,7)
End Sub

function testsub(k) as long
msgbox k
end function

function GetModule(libraryname,modulename)
select case Libraryname
case "Library1"
select case modulename
case "Module1"
GetModule = Library1.Module1
'case
'case
end select
'case
'case
end select
end function
)

Edit:

.... No great need for it... just interested....


Edit2:
A crazy way might be to programatically change the calling subs text from Library1.Module1 to whatever was needed before calling that sub. But that would be too crazy...
Last edited by JeJe on Sat Apr 08, 2023 1:27 pm, edited 3 times in total.
Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
User avatar
Lupp
Volunteer
Posts: 3542
Joined: Sat May 31, 2014 7:05 pm
Location: München, Germany

Re: Getting a mudule object for use in CallByName

Post by Lupp »

I probably misunderstood you from the beginning due to the fact that I have no clear idea of waht you hope to get as a "module object".

The libraries you created yourself the standard way are listed in
.../user/basic/script.xlc
in human readable xml structure. [There may also be libraries created by .oxt installs, but I think you will mostly find their .xlb(see below) files elswhere in the user profile (uno_packages?).]

By default again: The mentioned .xlc describes the locations of the libraries giving for everyone a kind of index (human readable in XML again) by its path(index) ending with script.xlb file. This file per library unveils the names of contained modules. From my experience every module is then given as a .xba file in the same folder as the script.xlb. This file, however needs to "escape" many characters the ampersand way.
You see: Everything is extremely simple and we surely had implemented it the same way if asked.

You knew everything, of course?

Do you also know https://api.libreoffice.org/docs/idl/re ... eNode.html ?
On Windows 10: LibreOffice 24.2 (new numbering) and older versions, PortableOpenOffice 4.1.7 and older, StarOffice 5.2
---
Lupp from München
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: Getting a mudule object for use in CallByName

Post by JeJe »

Thanks Lupp...

by module object I mean:

if you put Library1.Module1.testsub in your code then that will run that sub

if you just put Library1.Module1... which for want of a better expression I'm calling a module object... MRI will give ANY for it. That... whatever you want to call it... is needed for Callbyname.

Callbyname is a lot easier to use than script.invoke - but its only partial as you need to put/know that libraryname.modulename in advance in the code.

So I'm wondering if there's a proper way to create it when having only the library name and module name strings.

I expect someone will either have deep knowledge of this and know it or they won't.

Browsing the nodes is useful for information about the text of a module - I get a crash when trying to get the sub or function node. I could use that for the crazy method I suggested of changing module text on the fly to replace the words libraryname.modulename in the calling sub with the text I wanted - but that's too crazy.
Last edited by JeJe on Tue Apr 04, 2023 1:43 pm, edited 1 time in total.
Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
User avatar
Lupp
Volunteer
Posts: 3542
Joined: Sat May 31, 2014 7:05 pm
Location: München, Germany

Re: Getting a mudule object for use in CallByName

Post by Lupp »

Well, I wont't spend much time on this now. I simply feel stunned by the fact that a service named "...script.browse.BrowseNode" and described in short by "This service provides access to the Macros and Macro containers via the XBrowseNode interface." seemingly doesn't provide any browsing means - and no useful functionality at all.
Alas! It's not the first time that I feel this way.
On Windows 10: LibreOffice 24.2 (new numbering) and older versions, PortableOpenOffice 4.1.7 and older, StarOffice 5.2
---
Lupp from München
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: Getting a mudule object for use in CallByName

Post by JeJe »

Thanks for trying... I added, " No great need for it... just interested...." as I don't want people labouring over it if they don't know.

I had tried this, but it doesn't work as the browse node for the module isn't the same thing as when you write library.module in code.

Code: Select all

oBrowseNodeFactory = GetDefaultContext.getValueByName("/singletons/com.sun.star.script.browse.theBrowseNodeFactory")
iViewType = com.sun.star.script.browse.BrowseNodeFactoryViewTypes.MACROORGANIZER
aMasterProviders() = oBrowseNodeFactory.createView(1).ChildNodes()
a= aMasterProviders(0)
b=a(0).getchildnodes(0)
c = b(0).getchildnodes(0)

for i = 0 to ubound(c)
mri c(i)
if c(i).getname = "JeToolbar2" then
d=c(i).getchildnodes(0)
for j = 0 to ubound(d)
if d(j).getname = "AA" then

 CallByName d(j), "tttttt", 1,7		' doesn't work as d(j) not the same as JeToolbar2.AA
exit sub
end if
next
end if
next

(aside) Browsng nodes is useful for getting the module string contents - but try and get the sub or function node below and there's a freeze for me in OO... meaning you have to search the module text and examine it to discover the parameters of the sub or function - which will fail if the module is longer than maximum string length.
Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: Getting a mudule object for use in CallByName

Post by JeJe »

Doesn't answer the posed question but the nearest is possibly made using script invoke and a paramarray, something like this (in parameters only):

Code: Select all

Option VBASupport 1 'for paramarray support

sub testall
msgbox callbyname2("JeToolbar2.AA.testsub",6,"fish") 'library.module.sub,
callbyname2 "JeToolbar2.AA.testsub2"
end sub

function callbyname2(LibAndModandSubName as string,ParamArray parray) 'Lib.Mod.Sub 'paramarray in parameters only 'basic and application assumed
uri="vnd.sun.star.script:" & LibAndModandSubName & "?language=Basic&location=application" 'construct uri
script =  thiscomponent.getscriptprovider.getScript(uri) 
callbyname2=script.invoke(parray,array(),array()) 'aOutParamIndex, aOutParam)
end function

function testsub(b as long,c as string) as string
testsub= c & " " & b
end function

sub testsub2()
msgbox "testsub2"
end sub

Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: Getting a mudule object for use in CallByName

Post by JeJe »

Same as last post but adding support for Out parameters.

(And I think I find bugs in Script invoke:

If the line

function testsub(byref b,byref c as string) as string

in the called sub is changed to

function testsub(byref b as long,byref c as string) as string 'changed to b as long

then script.invoke does not return the changed parameter. If I put this then it does:

function testsub(byref b as integer,byref c as string) as string 'changed to b as integer)


Code: Select all

Option VBASupport 1


sub testall
dim j, stst,varresults,ret
j = 5
stst = "fish"
ret=callbyname4("JeToolbar2.AA.testsub",varresults,j,stst) 
msgbox varresults(0)
msgbox varresults(1)
end sub


function callbyname4(LibAndModandSubName as string,varresults,ParamArray inParams) 'Lib.Mod.Sub 'paramarray in parameters only 'basic and application assumed
dim uri as string,script,aOutParamIndex, aOutParam,ub1,ub2,i
uri="vnd.sun.star.script:" & LibAndModandSubName & "?language=Basic&location=application"
script =  thiscomponent.getscriptprovider.getScript(uri) 
callbyname4=script.invoke(inParams, aOutParamIndex, aOutParam)

ub1 = ubound(inParams) 'put the original parameters into the results array including outparams where modified
ub2 = ubound(aOutParamIndex) 
redim varresults(ub1)
for i = 0 to ub1
varresults(i)= inparams(i)
next
if ub2 >-1 then
for i = 0 to ub2
varresults (aOutParamIndex(i)) = aOutParam(i)
next
end if
end function


function testsub(byref b,byref c as string) as string
b = b+10
c = c & " plus an added bit"
testsub= c & " " & b
end function

Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
User avatar
Lupp
Volunteer
Posts: 3542
Joined: Sat May 31, 2014 7:05 pm
Location: München, Germany

Re: Getting a mudule object for use in CallByName

Post by Lupp »

JeJe wrote: Tue Apr 04, 2023 1:52 pm... - but try and get the sub or function node below and there's a freeze for me ... meaning you have to search the module text and examine it to discover the parameters of the sub or function - which will fail if the module is longer than maximum string length.
(Did not yet study your most recent post.)
Just entered the thread again and first time actually read what's quoted above. Sorry.

Yes there is only the text, imo. No XML file containing nodes on the single-routine level. You surely know better than I do how nested modules are handled where allowed.

Concerning a hand-made function like createSubAndFunctionInfoForBasicModule(pModuleFilePath As String) I wouldn't see the 2^16 limit as the most relevant problem. We can read lines after all, we can import the .xba into a helper text-model (paragraph in LibO no longer limted by 16-bit for a long time now) and we may hope for routine-headers not exceeding the limit, or read byte-oriented.

The problem is the tokenizing/preparsing based on a syntax surely not being regular, but relying on start-to-end tokenizing. We may need to explicitly program lots of steps the Basic IDE and "compiler" have included in a professional and efficient way already, but don't offer as a service to the user. Did you ever try code of the kind? I tried together with the students (17 of age) of a course in 1995/1996 - and there we were it ourselves who defined the syntax of the declarations (excluding comments and left-right-non-distinguishing parenthesing characters like quotes e.g.).

Assuming at least the headers are "regular", and xba files are single-paragraph (with \n linebreaks only) as they should be, a good finder I can offer is the RegEx

Code: Select all

(^|\n)\s*(public\s+)?(function|sub)\s+[a-z_][a-z_0-9]*\s*\([^\(\)]*\)(\s*as\s+[a-z]+)?
(Just found there are exceptions concerning paragraph breaks in ScriptForge code.)

Do you know if ScriptForge offers something usable here? Is the respective task supported by Python? Since my early superficial studies in programming (1964/65) I missed the respective self-support. All the later IDE interfaces had support for syntax highlighting at least (and much more depending), but often didn't offer related tools as modules/units/services whatever. (freepascal seems to have.)

Concerning the level of the discussion: Please regard that I didn't anything in the field on a professional (except teaching) level since 1968. Only a few years ago I started my part-time support for LibO/AOO and due to age I will not again go through fundamental studies now. Not enough time left.
On Windows 10: LibreOffice 24.2 (new numbering) and older versions, PortableOpenOffice 4.1.7 and older, StarOffice 5.2
---
Lupp from München
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: Getting a mudule object for use in CallByName

Post by JeJe »

I forgot about looking at the .xba file. Yes, that would be a way round the module being greater than the string size limit, though very complicated to implement.

That will have to wait until if I ever really need it but thanks for the suggestions.

At the moment I'm just looking at saving macro calls as strings, possibly with simple parameters.

I've settled on the below callbyname type function, using array in a variant instead of a paramarray, and another for the out parameters - though those will be unreliable because the script.invoke in OO looks buggy.

And I'll mark the thread as solved.

I'm sticking with OO at the moment... I tried transfering some code to LO this week and discovered they broke setting the paragraph backcolor in code:

https://bugs.documentfoundation.org/sho ... i?id=99125

Code: Select all

Option VBASupport 1


sub testall
	dim varsin,varresults,ret
	'no parameters
	ret=callbyname5("JeToolbar2.AA.testsubB")
	msgbox ret

	'single parameter
	dim result
	ret=callbyname5("JeToolbar2.AA.testsubC",6 ,result)
	msgbox result

	'multiple parameters
	redim varsin(1)
	varsin(0)= 5
	varsin(1)= "fish"
	ret=callbyname5("JeToolbar2.AA.testsub",varsin,varresults) 
	msgbox varresults(0)
	msgbox varresults(1)

end sub


function callbyname5(LibAndModandSubName as string,optional varsin,optional varresults) 'Lib.Mod.Sub 'varsin,varsout 'basic and application assumed
	dim uri as string,script,aOutParamIndex, aOutParam,ub1,ub2,i,inparams

	uri="vnd.sun.star.script:" & LibAndModandSubName & "?language=Basic&location=application"
	script =  thiscomponent.getscriptprovider.getScript(uri)

	if ismissing(varsin) = true then 'no parameters
		callbyname5=script.invoke(array(),aOutParamIndex,aOutParam)
	else

		if isarray(varsin) then
			inparams =varsin
		else
			redim inparams(0)
			inparams(0) = varsin
		end if

		callbyname5=script.invoke(inParams, aOutParamIndex, aOutParam)

		if isarray(varsin) then 'prepare results array fro multiple parameters

			ub1 = ubound(inParams) 'put the original parameters into the results array including outparams where modified
			ub2 = ubound(aOutParamIndex)
			redim varresults(ub1)
			for i = 0 to ub1
				varresults(i)= inparams(i)
			next
			if ub2 >-1 then
				for i = 0 to ub2
					varresults (aOutParamIndex(i)) = aOutParam(i)
				next
			end if

		else

			if ub2 <>-1 then varresults = aOutParam(0) else varresults = inParams(0) 'single parameter

		end if
	end if

end function



function testsub(byref b,byref c as string) as string
	b = b+10
	c = c & " plus an added bit"
	testsub= c & " " & b
end function

function testsubB() as string
	testsubB= "testsub6"
end function

function testsubC(byref b) as string
	b= b+10
	testsubc= b
end function

Very slight edit: deleted comment left over from previous version.
Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: [Solved - with alternative] Getting a module object for use in CallByName

Post by JeJe »

Actually looking at one of my xba files, line input can easily find the sub or function headings, with only one substitution needed to restore the ' characters.

(The sax xml parser isn't useful as the file has only one element: script:module )

Code: Select all

fc =  CreateUNOService("com.sun.star.sheet.FunctionAccess")
pth = "C:\tmp\AA.xba"
iNumber = freefile
Open pth For Input As iNumber
While not eof(iNumber)
Line Input #iNumber, sLine
If sLine <>"" then
if instr(1, sline,"Sub ")=1 or instr(1, sline,"Function ")=1 then 
sline =fc.callFunction("SUBSTITUTE",array(sline, "&apos;" , "'"))
st = st &  sline & chr(10)
end if
end if
wend
Close #iNumber
msgbox st

Edit: with the line continuation character _ also to handle
Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
RPG
Volunteer
Posts: 2250
Joined: Tue Apr 14, 2009 7:15 pm
Location: Netherlands

Re: [Solved - with alternative] Getting a module object for use in CallByName

Post by RPG »

I have the idea that when you use findobject and findpropertyobject it can be done more easy. I have to admit I do not understand the given explanation by you and also Lupp there I never use toolbar macro's. I have placed also an example. findobject and findpropertyobject seems to work a little bit else in MyMacro's
Attachments
jeje01.ods
(13.88 KiB) Downloaded 48 times
LibreOffice 7.1.4.2 on openSUSE Leap 15.2
JeJe
Volunteer
Posts: 2764
Joined: Wed Mar 09, 2016 2:40 pm

Re: [Solved - with alternative] Getting a module object for use in CallByName

Post by JeJe »

Excellent thanks RPG! That solves the original question... and its in Pitonyak's Useful Macro Information too (doh!)... though using it may be a bit ropey according to:

https://flylib.com/books/en/4.290.1.89/1/

I hadn't noticed that it was unnecessary to use Library.Module and just putting the library will do. So this will do.

Code: Select all

ret= CallByName(JeToolbar2, "testtest", 1)
And FindObject solves it completely:

Code: Select all

t=FindObject("JeToolbar2")
ret= CallByName(t, "testtest", 1)
Cheers!
Windows 10, Openoffice 4.1.11, LibreOffice 7.4.0.3 (x64)
Post Reply