Crash after accessing Map object set as an environment variable

Hi there guys... wondering if you can help figure out why I'm crashing Opus with this sample script. If you'd rather I dumb down the sample way more than it is now, I can... I originally pulled out all the functional stuff to produce this sample - but have since come to figure it's probably NOT a result of any of my passing things around between functions, and probably something either I'm doing wrong in how I'm assigning my values, or possibly a goofy issue with Opus' support for using Map objects as environment variables.

I've attached a zip archive with the following:

  • Clipboard Image.png == screenshot of the crash notification
  • dopus.20151213.141858.dmp == crash dump saved to /temp\DOpus.Minidumps
  • vbTest.dcf == toolbar button to toggle script behavior ON/OFF
  • vbTest.vbs == script

You should be able repro the crash by doing the following:

  • Drop the toolbar button onto a toolbar
  • open a dual display lister and set the source folder to a dir with a sub-dir that also contains at least one other sub-dir (parent dir\child dir 1\child dir 2)
  • Drop the script into the Script AddIns folder
  • From the "parent dir" toggle the script on using the toolbar button, you should get the following bit of output in the script log:

vbTest.vbs: Initializing... vbTest: OnvbTest() vbTest: ---------------------------------------------------------------- vbTest: vbTest: On vbTest: vbTest_On() vbTest: ---------------------------------------------------------------- vbTest: SOURCE TAB: STATS vbTest: ---------------------------------------------------------------- vbTest: SrcTab-> ID: 8325124 vbTest: SrcTab-> CurrentPath: lib://My/Projects/opus/scripting/my.NavSync vbTest: SrcTab-> LinkedTabID: 52299712 vbTest: SrcTab-> InitialPath: lib://My/Projects/opus/scripting/my.NavSync vbTest: SrcTab-> LastPath: lib://My/Projects/opus/scripting/my.NavSync vbTest: SrcTab-> SyncState: True vbTest: ---------------------------------------------------------------- vbTest: getLinkedTab() vbTest: checkTabExists() vbTest: DESTINATION TAB: STATS vbTest: ---------------------------------------------------------------- vbTest: DstTab-> ID: 52299712 vbTest: DstTab-> CurrentPath: C:\Users\steje\AppData\Roaming\GPSoftware\Directory Opus\Script AddIns vbTest: DstTab-> LinkedTabID: 8325124 vbTest: DstTab-> InitialPath: C:\Users\steje\AppData\Roaming\GPSoftware\Directory Opus\Script AddIns vbTest: DstTab-> LastPath: C:\Users\steje\AppData\Roaming\GPSoftware\Directory Opus\Script AddIns vbTest: DstTab-> SyncState: True

  • Now dblclick into the first level "child dir 1", you should get the following additional bit of output in the script log:

vbTest: Beginning OnAfterFolderChange handler... vbTest: vbTest: Typename(objSrcTabVar): Object vbTest: objSrcTabVar-> LinkedTabID: 52299712 vbTest: We made it past the fetched objSrcTabVar var vbTest: getLinkedTab() vbTest: checkTabExists() vbTest: ---------------------------------------------------------------- vbTest: Folder change detected: vbTest: ---------------------------------------------------------------- vbTest: Current SrcPath: lib://My/Projects/opus/scripting/my.NavSync/test vbTest: Current DstPath: C:\Users\steje\AppData\Roaming\GPSoftware\Directory Opus\Script AddIns vbTest: vbTest: Completed OnAfterFolderChange handler...

  • Now dblclick into the second level "child dir 2", and you should crash right after the attempt to access data in the imported environment map in my OnAfterFolderChange() function directly after the last log message below:

vbTest: Beginning OnAfterFolderChange handler... vbTest: vbTest: Typename(objSrcTabVar): Object

If you want me to dumb the script down way further to make it easier to troubleshoot just shout.

Maybe you can tell me I just can't Set the local copies of the environment the way I am. I can certainly rebuild a new DOpusFactory.Map object and assign the values one at a time I guess before I write it out to the environment, which is where things are going wrong after the first folder change event I think. But doing it the same way as I am now with a Vector as opposed to a Map object works fine...?

The relevant code flow from the steps above is basically:

  • Toggling the script on calls a script command accessible function (vbTest_On) that creates a new Map object with several key/value pairs and then sets that object as the value of my environment variable
  • At this point things work... I can access the data as a Map object in a subsequent run of the script by running 'vbtest stats' command which the script adds
  • And the first folder change event triggers code that accesses the Map as well (objSrcTabVar-> LinkedTabID)
  • When I come back the next time around with the second folder change event - accessing the data written out as part of the first folder change, after the way I'm reading in the old environment data, then changing some of it before writing the new data back out to the environment without explicitly creating a new Map object is causing the crash

But it works as is with Vectors as a drop in replacement for the Map object - which I can show in a modified sample script if you like. Is this something basic I'm just doing wrong that first folder change?
my.vbTest.zip (81.1 KB)

This seems to be a bug that you've managed to trigger by effectively assigning an object to itself.

The problem seems to be triggered by these lines at the bottom of the If (getLinkedTab(objSrcTab, objDstTab)) Then test:

objSrcTab.vars.Set("vbTest") = objSrcTabVarNew objDstTab.vars.Set("vbTest") = objDstTabVarNew

If you comment those out the crash goes away, and from what I can tell they don't do anything anyway - the "vbTest" variable is already set to the map that objSrcTabVarNew points to.

We'll fix the bug but in the mean time commenting out those lines should solve it for you.

Well the crash is gone - but doing that goofs things up in interesting and confusing ways...

The previous code that is intended to set one of the elements of the environment object (objSrcTabVarNew("LastPath")) now appears to set all key values of the Map to a string value of LastPath instead of the intended value of objSrcTab.path.

What gives?

Let me chop out all of the surrounding stuff and make a much simpler sample to show what I thought I should be able to do, and what seems wrong to me.

Actually, before I screw around with more samples let me just give you my use case and straight up ask for some guidance...

  • I want my script to use a Map object for a tab scoped environment variable - I'm playing around quite a bit with how much data I'm putting in there, and being able to refer to an element by key name instead of the index of a Vector makes extending my script as I go sooooooo much easier.
  • On subsequent runs of the script, I want to read the environment Map into the script, make some decisions based on some of the key values, modify some of the key values, then write it back out to the environment.

Can I do that without enumerating through each key/value pair in the environment Map and individually writing each of them out to a NEW Map object so that I can make some changes to some of them and then use the new Map object to write my changes back out to the environment?

I had originally wanted to use the Map.assign method to do this - but with either of these attempts:

[code]Set objSrcTabVarMapNew = DOpusFactory.Map

objSrcTabVarMapNew.assign(objSrcTab.vars("vbTestMap"))
objSrcTabVarMapNew("LastPath") = objSrcTab.path

objSrcTab.vars.Set("vbTestMap") = objSrcTabVarMapNew[/code]
...or...

[code]Set objSrcTabVarMap = objSrcTab.vars("vbTestMap")
Set objSrcTabVarMapNew = DOpusFactory.Map

objSrcTabVarMapNew.assign(objSrcTabVarMap)
objSrcTabVarMapNew("LastPath") = objSrcTab.path

objSrcTab.vars.Set("vbTestMap") = objSrcTabVarMapNew[/code]
I get yelled at:

Invalid procedure call or argument: 'objSrcTabVarMapNew.assign' (0x800a0005)

Maps are read / write, you can change them after you've created them. You don't need a new one, just change the existing one.

But my Map exists in an environment variable between script runs...

So my script is pulling that environment variable into a local script object... are you saying that if I then make a change to one of the Maps values via the local script object, that those changes actually get written out to the environment without having to use the Tab.vars.Set(varname) method?

The local object just stores a "reference" to the map; when you modify it, you're modifying the original map.

Hmmm... Ok that makes sense in normal conventions, I thought I was getting a "copy" of the environment variable. I also thought messing with environment variables required us to write any changes out using that Tab.vars.Set method, but I guess that's just to define it if it doesn't exist.

But in any case - can you say why my attempted use of the assign method for my new Map throws the invalid procedure call or argument error though? I've explicitly created a new Map object (objSrcTabVarMapNew), and can access the reference to the Map stored in the environment variable (objSrcTabVarMap) as map (i.e. by key name, etc). So shouldn't that work?

It's because objSrcTab.vars("vbTestMap") isn't a Map, it's a Var. The Var is able to pass method and property calls through to the Map that it contains, but Map.assign doesn't recognise a Var when going the other way.

This is something we can improve in the future though.

Ok, thanks for the dialogue Jon, appreciate it.

Ok, the sample script could still be alot simpler I know :slight_smile:...

Please bear with me, and see if you can school me on what's still going wrong - though I'm sure it's just a variation on what you mentioned regarding the Var object I'm pulling into the script not ACTUALLY being a Map anymore (or in my alternate attempt, not a Vector either).

I've attached a new sample... In addition to the script and a test folder structure, it's also got two toolbar buttons. One calls the script and has it use a Vector object for the environment var, and the other has it use a Map object. The issue is the same regardless of what type of object I use to set the environment variable initially.

my.vbTest.zip (3.15 KB)
With either object type - the problem is the same. When you initially toggle it ON, the script uses a proper object created through either DOpusFactory.Vector or DOpusFactory.Map to initialize the object with the desired data, and then sets it using Tab.vars.Set(object). There's a subroutine that then gets that data back from the environment and prints it to the script log.

And thing are fine at that point:

vbTest.vbs: Initializing... vbTest: MAP STATS vbTest: ---------------------------------------------------------------- vbTest: SrcTab-> ID: 10487230 vbTest: SrcTab-> CurrentPath: lib://My/?2625f55a/Projects/opus/scripting/my.vbTest vbTest: ---------------------------------------------------------------- vbTest: SrcTab-> InitPath: lib://My/?2625f55a/Projects/opus/scripting/my.vbTest vbTest: SrcTab-> LastPath: lib://My/?2625f55a/Projects/opus/scripting/my.vbTest vbTest:
Upon the first folder change, the script just wants to save the new path to the second element in the environment object. But doing it the way I'm attempting goofs up the entire object:

vbTest: MAP STATS vbTest: ---------------------------------------------------------------- vbTest: SrcTab-> ID: 10487230 vbTest: SrcTab-> CurrentPath: lib://My/?2625f55a/Projects/opus/scripting/my.vbTest/test vbTest: ---------------------------------------------------------------- vbTest: SrcTab-> InitPath: LastPath vbTest: SrcTab-> LastPath: LastPath vbTest:
In this case (use of a Map object), it's the attempt to update the object referencing the environment variable that clobbers all the elements in the vector:

objSrcTabVarMap("LastPath") = objSrcTab.path

For some reason - referencing the second element of the vector ("LastPath") to assign it a new value actually sets all element values in it to "LastPath"?

But making an assignment with the exact same syntax works like you'd expect if it's a true locally defined Map object - i.e. - from running the "vbtest test" command the script provides:

vbTest: MAP STATS vbTest: ---------------------------------------------------------------- vbTest: Initial Value 0: dummy data 0 vbTest: Initial Value 1: dummy data 1 vbTest: ---------------------------------------------------------------- vbTest: Updated Value 0: dummy data 0 vbTest: Updated Value 1: But this works just fine like you'd expect... vbTest:

So, if I am correct in my assumption that this is also part of the objects original type being obfuscated once it gets written out to the environment like you pointed out - in my use of using these sorts of objects as environment variables and needing to modify some elements - is this part of what you think could be improved in the future?

If not, is there any other way for me to make the kind of updates I want to make short of recasting an entirely new object and assigning individual element values from the original except for the ones I want to make updates to as part of the script action - and then resetting the entire environment variable to the new object?

Yeah, I wound up just creating a new object with DOpusFactory.Map and passing the individual key/values to initialize the new object. Not as concise as being able to copy directly from the env var, but it works.

Something just bugs me about getting a reference to the actual environment variable within the tab. I don't think you should let us write directly to the environment by making a value assignment. Even though you can, I'm still updating the environment var with an explicit Tab.vars.Set() call with the new environment object.

Both these issues should be fixed now in 11.17.1.