Jump to content
Sign in to follow this  

Menu command parameter record

Recommended Posts

Can anyone throw any light on the subject? Yes, there is documentation related to the matter, but it is, well, obscure. In fact, the sample code cannot be deciphered.

Share this post

Link to post

Hi Petri,

It looks like you can define parameters for your command plugin, and then access them in the plugin script via the "P<Parameter>" convention.

To get a reference to the parameter record, you would call GetPluginInfo instead of GetCustomObjectInfo. You can get the parameter names using the GetFldName function in the Database/Record functions category, but the GetRField and SetRField functions do not seem to have any effect on the parameter record for a menu command. If I see anything else I'll post it.


Share this post

Link to post

Thanks, Rick! I guess I just have to go on by trial and error, starting with GetPluginInfo.

I wonder how & when the user can access the thing. Well, that is quickly tested.


Back after testing. As I expected, the user cannot access the record. If I understand correctly, it can store values from one use to another (say, dialog input) but changing them otherwise is impossible.

Still, it can be useful and changing default values for new files is a lot easier through parameters than via modifying the code - but most users do not quite seem to appreciate this "easy" thing.

To read parameters, you don't seem to need to call for PluginInfo; for writing you obviously do. Thanks again, you put me on track and now the documentation makes some sense.

Edited by Petri

Share this post

Link to post

No luck. Nothing gets written to the parameters.

PROCEDURE StoreTextSettings;
SETRFIELD(me, myName, 'Class', tagClass);
SETRFIELD(me, myName, 'byClass', isByClass);
SETRFIELD(me, myName, 'font', fontName);
SETRFIELD(me, myName, 'size', NUM2STR(0, size));
SETRFIELD(me, myName, 'style', styleName);
SETRFIELD(me, myName, 'justH', NUM2STR(0, justH));
SETRFIELD(me, myName, 'justV', NUM2STR(0, justV));
SETRFIELD(me, myName, 'colour', NUM2STR(0, colour));

Share this post

Link to post

Hi Petri,

this below was my own experimenting field with the matter. I'm sure it can help others too. I didn't check it since a while, so I hope it doesn't have too many problems.

make 3 plugins as follows:

1 object

1 command

1 tool

All identical, with two parameters of type text:

my name, text field, then run this by include:



Orso B. Schmid - Do not copy it if you don't mean to share it!


If an object is selected, the tool will search for eventual records in FSActLayer and display infos about them.

If nothing is selected, or the selection doesn't contain any record, the tool will display infos about himself.

If capslock key is activated while launching the plug-ins, it will deliver the currently available record definitions (document-instances versus object-instances)

if capslock is on and command key is pressed while launching one of the plug-ins, the whole name list is parsed for eventual hidden records.


This little utility allows analizing Plug-In-generated records (VSM, VST, PIO) as well as 'normal' records, up to hidden records, if any given. I hope it can be of use to others. Some of the infos displayed are quite absurd and pedantic. The Handle numbers, just to mention one... instructive to look at, though, expecially as to where they change and where they don't.

This script must ideally be included in three plug-ins identical in parameters and script content but different in type (a command, a tool, a PIO) in order to fully display the nuances between them. All three plugins must have 2 parameters of type text: my name, text field,

The script is kept as simple as it was in my power to do. I privileged declaring redoundant variables instead of nesting routine calls, in order to have the meaning of each easier to comprehend.


There are special objects who, even if they're not attachd to a type 86, create records that do not display on resource browser. They are only accessable parsing the name list:

VW 11.5, 12 Viewports (type 122): creates a record 'NNAcartoon', some sort of attribute-child of 57, normal record (only one name)

VW 12 Walls (type 68): creates a record '_NNA_Wall_Style_Format', some sort of attribute-child of 57, normal record (only one name).


PROCEDURE RecordExplorer;


cBR = chr(13); { ascii char 13 }




gPIOrunMe, gIsMe : BOOLEAN;

objName, gVSobjName : STRING;

gVSobjH, gVSobjRecH,

gObjH, recH : HANDLE;

gRecCnt, gRecIndx : INTEGER;


Modus : STRING;

temp_b : BOOLEAN;

temp_s : STRING;

temp_h : HANDLE;

temp_i : INTEGER;

temp_Li : LONGINT;

{ returns a string with all object's relatives }



temp_s := '';

WHILE getType(aH) <> 0 DO BEGIN

aH := getParent(aH);

temp_s := concat(temp_s, chr(58), getType(aH));


familyQuest := temp_s;


{ collects info about a record }

PROCEDURE recInfo(recHdl: HANDLE);


PI_record = STRUCTURE { Plug-In record info }

univName : STRING;

locName : STRING;

recDefH : HANDLE; { record definition, type 47 }

recH : HANDLE; { record attribute, type 48 }

fieldCnt : INTEGER;


PI_Field = STRUCTURE { Plug-In field info }

univName : STRING;

locName : STRING;

refName : STRING;

appVal : STRING;

docVal : STRING;

objVal : STRING;

tryVal : STRING;



PI_rec : PI_record;

PI_fld : DYNARRAY[] OF PI_field;


{ replace a string }

FUNCTION searchReplace(sourceStr, searchStr, replStr: DYNARRAY OF CHAR): STRING;


theSourceStr : DYNARRAY OF CHAR;

temp_Pos : INTEGER;


theSourceStr := sourceStr;

temp_Pos := pos(searchStr, theSourceStr);

WHILE temp_Pos > 0 DO BEGIN

Delete(theSourceStr, temp_Pos, len(searchStr));

Insert(replStr, theSourceStr, temp_Pos);

temp_Pos := pos(searchStr, theSourceStr);


searchReplace := theSourceStr;



PI_rec.recH := recHdl;

PI_rec.univName := getName(PI_rec.recH);

PI_rec.recDefH := getObject(PI_rec.univName);

{ my thanks to Patrick McConnel, from www.VectorDepot.com }

{ if you're interested, check his 'Set PIO Defaults.vsm' }

temp_b := GetLocalizedPluginName(PI_rec.univName, PI_rec.locName);

{ the mother of all sources for understanding plug-ins records is NNA exaple found under

'GetLocalizedPluginName', in the official VS Function List Guide, thank you NNA... }

PI_rec.fieldCnt := numFields(PI_rec.recH);

gRecordInfo := concat(gRecordInfo,

'***************', PI_rec.univName, '***************', cBR,

'My Record Def. Handle nr.: ',concat(PI_rec.recH), cBR,

'My Record Handle nr.: ', concat(PI_rec.recDefH), cBR, cBR,

'My Universal Name: ', PI_rec.univName, cBR,

'My Localized Name: ', PI_rec.locName, cBR,

'My Record Def. index: ', name2index(PI_rec.univName), cBR,

'My Record index in object: ', concat(gRecIndx), cBR,

'My Record fields count: ', concat(PI_rec.fieldCnt), cBR, cBR);

{ this wanted to be an ugly way to retrive the application value }

{ it doesn't work }

locus(p.x, p.y);

setRecord(LNewObj, PI_rec.univName);

Allocate PI_fld[1..1]; { resize array }

IF PI_rec.fieldCnt > 0 THEN BEGIN

Allocate PI_fld[1..PI_rec.fieldCnt];

{ field infos }

FOR i := 1 TO PI_rec.fieldCnt DO BEGIN

PI_fld.univName := GetFldName(PI_rec.recH, i);

temp_b := GetLocalizedPluginParameter(PI_rec.univName, PI_fld.univName, PI_fld.locName);

{ this has different results according to who is 'PI_rec.recH':}

{ if 'PI_rec.recH' is a HANDLE to a VS object record: }

{ replace space ' ' with underscore '_'}

PI_fld.refName := concat('P', searchReplace(PI_fld.univName, chr(32), chr(95)));

{ UprString(PI_fld.refName ); } { this seems to break the routine }

PI_fld.appVal := GetRField(PI_rec.recH, PI_rec.univName, PI_fld.univName);

PI_fld.docVal := GetRField(PI_rec.recDefH, PI_rec.univName, PI_fld.univName);

PI_fld.objVal := GetRField(gObjH, PI_rec.univName, PI_fld.univName);

{ it is equal to: := GetRField(H, PI_rec.univName, 'my name'); }

{ := PMY_NAME; a reference to a parameter 'my name' }

{ := PMY_TEST_FIELD;a reference to a parameter 'my test field' }

{ the last 2 has only a meaning if the field belong to some plug-in parameter }

{ if you miss the correct field name orthography, an empty value is returned, no error }

{ *****************************************************************?}

{ ****************** PLACEHOLDER FOR EXPERIMENTS ****************** }

{ temp_h := getParent(getObject('NNAcartoon')); }

{ PI_fld.tryVal := GetRField(temp_h, getName(temp_h), GetFldName(temp_h, 7)); }

{ 1 int: 7

2 int: 1

3 int: 1

4 int: 0

5 int: 0

6 int: 0

7 real: 1 }

{ temp_h := getObject('*DEFAULTS*');

PI_fld.tryVal := GetRField(temp_h, getName(temp_h), GetFldName(temp_h, 1)); }

gRecordInfo := concat(gRecordInfo,

'Field Index: ', i, cBR,

'Field Type: ', GetFldType(PI_rec.recH, i), cBR,

'Universal Name: ', PI_fld.univName, cBR,

'Localized Name: ', PI_fld.locName, cBR,

'Reference Name: ', PI_fld.refName, cBR,

'Application value: ', PI_fld.appVal, cBR,

'Document value: ', PI_fld.docVal, cBR,

'Object value: ', PI_fld.objVal, cBR, cBR);



END; { of recInfo }


{ ***** remember that this script should be runned by INCLUDE from 3 plug-ins: a command, a tool, a PIO ***** }

{ all with identical parameters and two rows of script: $INCLUDE Data\Example Record Explorer.vss

the second row is empty, just to avoid VS-related problems in loading the include }

{ this will return false if the tool or the command are running this script }

gPIOrunMe := GetCustomObjectInfo(gVSobjName, gVSobjH, gVSobjRecH, temp_h); { wall handle unused }

gIsOK := gPIOrunMe;

gIsMe := TRUE; { gIsMe is true if the obj analyzed is either tool, command or PIO }

{ not a PIO, is command or menu }


gIsOK := GetPluginInfo(gVSobjName, gVSobjRecH);

{ since is tool or command, the handle to them is not a 'drawable' obj?}

{?the following routine get holds of an obj type 47: a record definition }

{?that is: the handle to a tool or command corresponds to a handle to a document data-object }


Modus := 'Is me! Tool or Command script object';

{ is script object, info about themselves }


Modus := 'Is me! PIO script object';

gObjH := gVSobjH; { is PIO }


gObjH := gVSobjRecH; { is command or tool, its obj = its record }

{ is an object on drawing with one or more attached records }

IF (FSActLayer <> NIL) AND (numRecords(FSActLayer) > 0) THEN BEGIN

Modus := 'FSActLayer with records';

gIsMe := FALSE;

gObjH := FSActLayer;

objName := getName(gObjH);


{ we are using the point for the locus and for create text }

P.x := 0;

P.y := 0;

{ I seem to need this here for making modifier keys work }

{ Objects care themself for user insertion point }



{ is object document, type 0, NIL }


Modus := 'capsLock: document';

gIsMe := FALSE;

gObjH := NIL;

objName := '<record definitions children of container 57>';


Modus := 'capsLock and command: document + hidden';

gObjH := NIL;

objName := '<all record definitions (also hidden)>';



gRecCnt := numRecords(gObjH);

gRecordInfo := concat( 'Modus: ', Modus, cBR, cBR,


'Script runned from: ', gVSobjName, cBR,

'Object analyzed (name if any): ', objName, cBR,

'Object type: ', getType(gObjH), cBR,

'Object parents ', familyQuest(gObjH), cBR,

'Object Handle nr.: ', concat(gObjH), cBR, cBR,

'Record Count: ', gRecCnt, cBR, cBR);


{ tools and command don't have an obj on drawing, pass a handle to their record }



IF capsLock AND command THEN BEGIN

alrtDialog('caps and command');

FOR temp_Li := 1 TO NameNum DO BEGIN

recH := getObject(NameList(temp_Li));

IF getType(recH) = 47 THEN




FOR gRecIndx := 1 TO gRecCnt DO BEGIN


recH := getRecord(gObjH, gRecIndx);




createText(gRecordInfo); { createText accepts dynarray of chars, thus no 255 chars limit }

{ resetObject(gVSobjH); }

{ actually is not ok: message would be 'false'}





Share this post

Link to post


That looks interesting... Thanks, I'm sure I'll have plenty of use for it as I'm about to start to add improved functionality to my collection of tools and commands.

Share this post

Link to post

You're welcome.

It is a bit naive. But still quite helpful.

Mind also that I posted a new Language module, you'll find a link in the thread you started about TextWrangler.

BTW, why don't you come back to the vlist?

This here is really not quite what you need as a forum, with all respect.

Did you ban yourself there? grin.gif


Share this post

Link to post


For many years I have concentrated on other things than VW development - basically, I had "everything". The old stuff still mostly works (only a few completely obsolete procedure calls), but now it is time to take advantage of some new features and even develop a few new tools.

Maybe I should indeed subscribe to the mailing list again - in fact, I'm now desperately trying to get Eudora to work so I could find a utility I know Robert posted some years ago...

But someone might have it or similar - perhaps you do, Orso: it was used to clean the cache so that changes made to a PIO script INCLUDE-file would be activated while testing.

(I can't get this cursed writing to parameters to work...)

Share this post

Link to post

Thanks, Orso! Well, it is simpler that I thought it was, but eaqually different to remember.

Share this post

Link to post

As to overwriting the parm field, probably you pick the wrong obj handle or you make a mistake in the field name. A mistake in the field name induces a silent failing.

Lets say you have a PI (PlugIn) of type object called 'aPlugIn'

Lets say this PIO (=record attached to a script) has a field whose universal name is "__Bla Bla", whose type is number:

And I mean you typed exactly "__Bla Bla" in the Plug-In editor (under the universal name), mind the space between "bla" smile.gif

If you are INSIDE the PIO script first you get a handle to itself (the script) through:

IF GetCustomObjectInfo(gObjN, gObjH, gObjRecH, gwallH) THEN BEGIN

gObjRecN := GetName(gObjRecH);

{ now get hold of the universal name }

{ it is what is displayed first in the Plug-in editor }

{ supposing it is called '__Bla Bla', because you wish it to be invisible in the OIP:}

SetRField(gObjH, gObjRecN, '__Bla Bla', concat(121468797));

{ please note that I convert to string the number }


If the PI would be a command it doesn't matter if you are inside or outside the script attached to the record, but from inside you can use GetPluginInfo, which also returns the name.

Otherwise you can use getObject('aPlugIn'); { obviously it is only a record definition}

Since commands do not draw a parametrical object you will always have ONLY ONE such record named 'aPlugIn': at the document level (record definition). As every other name it is unique in the name list, then can be securely accessed by the routine above. Call it by name when it is the record definition what you want to access or it is such -like a command- that it can only have a record definition and nothing more.

Please compare this two functions:

GetCustomObjectInfo(VAR_objectName_STRING, VAR_objectH_HANDLE, VAR_recordH_HANDLE, VAR_wallHand_HANDLE);

{ for Plug-in Objects }

GetPluginInfo(VAR_pluginToolName_STRING, VAR_recordH_HANDLE);

{ for Plug-in tools, commands }

You'll notice that while GetLocalizedPluginName had a parameter ''VAR_objectH_HANDLE'', this is missing in GetPluginInfo. That's logical, a command doesn't have an object-instance, only an document instance (record definition): you'll have only one record, one name. In the name list reigns the usual name unicity rule.

If you wanted to reset the DEFAULTS for you PIO (script object) you would then search for the record definition in the name list.

myRecN := 'aPlugIn';

myRecH := getObject(myRecN); { call it by name! }

SetRField(myRecH, myRecN, '__Bla Bla', concat(121468797));

{ overwrites defaults }

NOTE: with the same procedure you access also any other "normal" record definition. The record attached to a script has just a little more names, but it is the same.

There is a lot to say about records, but I hope this helps you ahead.

To my knowledge there is no way to read or rewrite the application level of a record (what you set in the Plug-In editor).



like any record definition, you must launch the command at least once in the open doc in order to add it's record name to the names list, that's logical. If you never runned it and try getObject('name') you just get NIL, if nothing else is called 'name', or you get a whatever object called 'name' should you have one.

Edited by orso b. schmid

Share this post

Link to post

I do get field names as MESSAGEs, with exactly the same (copy/paste) arguments, but perhaps I need to do more auditing.

Share this post

Link to post

One little remark to Orso's comments.

When you setrfield from within your script and try to read the data again using Pparametername you won't see the change. Only a second run of the script would reveal the changed data.

So If you plan to use the changed value again in the same run you have to read in the parameters to VARs and assign aswell the VAR as the Plugin's parameter the new value.


Share this post

Link to post

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  


7150 Riverwood Drive, Columbia, Maryland 21046, USA   |   Contact Us:   410-290-5114


© 2018 Vectorworks, Inc. All Rights Reserved. Vectorworks, Inc. is part of the Nemetschek Group.

  • Create New...