COM objects

Posted by Anaristos on Fri 04 Feb 2011 01:23 AM — 22 posts, 110,649 views.

USA #0
I have written .NET applications which are exposed COM. I am able to use them in JScript scripts. What I would like to know is how can I make these COM references global to the session rather than having to re-establish the connection each time the script is invoked. From what I read the global variables are all strings which for obvious reasons don't help in this situation.
Amended on Fri 04 Feb 2011 01:24 AM by Anaristos
Australia Forum Administrator #1
I think you are confusing two sorts of variables. MUSHclient world variables are strings, but you get at these with the GetVariable and SetVariable function calls.

Normal Jscript variables could be any type, including at the global level.
USA #2
Oh, I understand. What I am trying to do is to have the reference to the COM object as a global variable so that other scripts may reference the same object.
Or, are you saying that what is saved with SetVariable() is the name of the variable and GetVaraiable(variable_name) retrieves the actual value?
Amended on Fri 04 Feb 2011 04:05 AM by Anaristos
Australia Forum Administrator #3
Er, no.

When you say "other scripts" what do you mean exactly? Other functions in your main script file? Or are you using plugins?

Plugins all have independent script spaces (they may even be different languages, such as Lua, VBscript, Python etc.). Thus there is no way to share a global variable like a COM object between them.

Personally I wouldn't be trying to mix .NET with MUSHclient scripts, but that's just me. :)

One approach could be to have a single plugin "talk" to the .NET COM object, and the other plugins get this single plugin to do stuff (by using CallPlugin etc.).
USA #4
Oh, I get the picture :) I was thinking that I could have a global variable space which could be shared by all the scripts that were called by aliases, etc. From what I can see by my few scripting experiments once the script is finished everything goes out of scope and it doesn't remember anything it did on previous calls. This is essentially what I was trying to avoid. I was really looking to persist variables, COM references in particular. I do understand the mechanism now, though. I will look at how plug-ins work. Thanks.
Amended on Fri 04 Feb 2011 07:03 AM by Anaristos
Australia Forum Administrator #5
Ah, no, that doesn't happen. I'm not sure what you mean by "once the script is finished". What script? Small scripts can be run from "send to script" for individual aliases/triggers/timers. Also they can call functions defined in the script file (if you are using one). However these share the same scripting space/engine.

This is all pretty vague, perhaps some concrete examples would help. But in principle, the main script space is not re-initialized, nor do variables go out of scope, for the entire execution of the program. Excepting, however, if you do "reload script file" from the menu.

Anaristos said:

I was thinking that I could have a global variable space which could be shared by all the scripts that were called by aliases, etc.


Yes you should be able to do that. If you are experiencing problems, please post a demonstration script.
USA #6
OK, let me see if I can explain:

Let's say I have an alias which is sent to script. The script resides in an external file and it looks something like this:

function cominit(name, line, wildcards)
{
    var args = VBArray(wildcards).toArray();

    var appObject = "dmsManager.Agent";

    var COMObject = new ActiveXObject(appObject);

/*
    properties are set here to guide COM the application.
*/
}


My question is: Can I refer to COMObject, or use it rather, from another script invoked by another alias?
Amended on Fri 04 Feb 2011 07:23 AM by Anaristos
Australia Forum Administrator #7
Can you post the alias?

Template:copying
For advice on how to copy aliases, timers or triggers from within MUSHclient, and paste them into a forum message, please see Copying XML.


In any case, you have declared COMObject locally to that function. It isn't a global variable.
USA #8
This is all that the alias does, call the COM initialization routine:


<aliases>
  <alias
   name="dbInit"
   script="cominit"
   match="^dbinit$"
   enabled="y"
   omit_from_log="y"
   regexp="y"
   send_to="12"
   ignore_case="y"
   sequence="100"
  >
  </alias>
</aliases>


and yes, that was my question all along. How can I make COMObject global?
Australia Forum Administrator #9
You don't need to send to script if you are putting a script name in the script box. Sending to script is for a literal script in the send box.

Now that we see what you are attempting it is easier to answer. Instead of:


function cominit(name, line, wildcards)
{
    var args = VBArray(wildcards).toArray();

    var appObject = "dmsManager.Agent";

    var COMObject = new ActiveXObject(appObject);

/*
    properties are set here to guide COM the application.
*/
}


Just move the variable out of the function. Variables inside a function only persist during the function execution. eg.



var COMObject;  // this is a global variable


function cominit(name, line, wildcards)
{
    var args = VBArray(wildcards).toArray();

    var appObject = "dmsManager.Agent";

    //Note: no "var" here - this is just assigning a value
    COMObject = new ActiveXObject(appObject);

/*
    properties are set here to guide COM the application.
*/
}

USA #10
Oh, I see. Just as if I were scripting a web page. Gotcha! Thanks. :)
USA #11
OK, things are not happening yet, and here is the core problem. Please forgive my ignorance. I understand what the change you made to the script works as it is a standard way to make global variables. However, persistence still seems to be the issue. As you see, the script only creates the COM connection and nothing else. Now, some other aliases are going to attempt to use this object for their purposes. However when I try I get the "'COMObject' is null or not an object" message. This tells me that the global variable that I created is not remembered by the scripting engine, most likely because it exits after it finishes running each script. Is there a way around this?

The script that gets the error is as follows:

function dbopen(name, line, wildcards)
{
    dbName = VBArray(wildcards).toArray()[0];
	
    COMObject.open(dbName); //I must have COMObject previously initialized.
	
    if (COMObject.isOpen) world.note("\nDatabase " + COMObject.path + " is ready for operations.\n");
	
	else world.note("\nAn error occurred: " + COMObject.lasterror + "\n");
}


That is what I meant by persistence. Each time a global variable is initialized, the value persists until the session is finished so each time I run a script that requires a variable initialized by the invocation of a previous script, the value is available.
Amended on Sat 05 Feb 2011 01:09 AM by Anaristos
Australia Forum Administrator #12
You are just showing snippets here. First you show me cominit function, then this time dbopen function.

There is no indication of which functions are called, and in which order.

As far as I can see, cominit creates the COM object. Does it get called?

It would help to see all the things that are happening here. What are your aliases? What order do you call them in?
USA #13
OK, let me explain this another way:

A skeleton of what I want to do is a follows:

2 aliases... dbinit and dbopen (there are more but the pattern is the same):


<aliases>
  <alias
   name="dbInit"
   script="cominit"
   match="^dbinit$"
   enabled="y"
   omit_from_log="y"
   regexp="y"
   ignore_case="y"
   sequence="100"
  >
  </alias>
 <alias
   name="dbOpen"
   script="comopen"
   match="^dbopen\s+([^$]+)$"
   enabled="y"
   omit_from_log="y"
   regexp="y"
   ignore_case="y"
   sequence="100"
  >
  </alias>
</aliases>

</aliases>

The first alias just invokes the script that initializes the COM object. It is separate because the same object can spawn different instances of the application. One for each database.

var COMObject;

function cominit(label, line, wildcards)
{
    args = VBArray(wildcards).toArray();
	
    var appObject = "dmsManager.Agent";

    COMObject = new ActiveXObject(appObject);
}

This script is invoked by dbinit.

Then there is this script:


var dbObject;

function comopen(name, line, wildcards)
{
     var dbName = VBArray(wildcards).toArray()[0];

     dbObject = COMObject.start(); //spin out an instance.

     dbObject.open(dbName);
	
     if (dbObject.isOpen) world.note("\nDatabase " + dbObject.path + " is ready for operations.\n");
	
     else world.note("\nAn error occurred: " + dbObject.lasterror + "\n");
}

This script is invoked by dbopen.
However, when dbopen is called, it doesn't know anything about COMObject.
Each script is in its own file.

So what I want to happen is that both COMObject and dbObject are available anytime an alias requiring their services is executed.
Amended on Sat 05 Feb 2011 06:27 AM by Anaristos
Australia Forum Administrator #14
Anaristos said:

Each script is in its own file.


I don't understand that part. MUSHclient supports a single script file for the "main" world scripting, plus plugins. Are you saying that each alias is in a different plugin?
USA #15
No, I am not saying that. After I made the post above I finally figured out that all scripts were supposed to go into a single file, though that's not really apparent right away. So I did move all the scripts into a single file. However, that doesn't change what is happening.
When I execute the first alias, it invokes the cominit function which establishes the COM connection and places the reference in the variable COMObject, which is declared as global. However when I then execute the second alias, dbopen, which invokes the comopen function the following error appears:

COMObject is null or not an object

which means to me that the function sees the variable, but its value has been reset, and therefore the value does not persist after the first alias exits.
Amended on Sat 05 Feb 2011 09:14 PM by Anaristos
Australia Forum Administrator #16
Template:summary

Please provide a summary of your world configuration:

  • Either use the scripting Immediate window (Ctrl+I) to execute: Debug ("summary")

    or

  • Install the Summary plugin (see "Summary" feature) and type "summary"

Then copy the resulting information from the output window, and paste into a Forum message.

You need version 4.55 onwards of MUSHclient to do this.

USA #17

-------------- MUSHclient summary --------------

MUSHclient version: 4.72
Compiled: Feb  5 2011.
Time now: Saturday, February 05, 2011, 7:29 PM
Operating system: Windows 7
Libraries: Lua 5.1.4, PCRE 8.10, PNG 1.4.3, SQLite3 3.7.3, Zlib 1.2.5
World name: 'AardolfTest', ID: a843716cc2d8b4e108d17a74
-- Scripting --
Script language: JScript, enabled: yes
Scripting active: yes
Script file: C:\...\MUSHClient\worlds\scripts.js
Lua sandbox is 127 characters, DLL loading allowed: NO
Scripting prefix: '!'. External editor in use: yes.
Editor path: C:\Program Files (x86)\Notepad++\notepad++.exe
Scripting for: 1.115930 seconds.
-- Triggers, aliases, timers, variables --
** Triggers: 0 in world file, triggers enabled: yes.
   0 enabled, 0 regexp, 0 attempts, 0 matched, 0.000000 seconds.
** Aliases: 3 in world file, aliases enabled: yes. [Aliases]
   3 enabled, 3 regexp, 20 attempts, 3 matched, 0.000129 seconds.
** Timers: 0 in world file, timers enabled: yes.
   0 enabled, 0 fired.
   Timers checked every 0.1 seconds.
** Variables: 1. [Variables]
-- MCCP --
MCCP active, took 0.000229 seconds to decompress
MCCP received 1367 compressed bytes, decompressed to 2814 bytes.
MCCP compression ratio was:   48.6% (lower is better)
-- Plugins (Processing order) --
ID: 71a90acddb14f784437b8b80, 'Summary', (Lua, 0.076 s) Enabled [Al]
** Plugins: 1 loaded, 1 enabled.
-- Comms --
Connect phase: 8 (Open). NAWS wanted: NO
Received: 4961 bytes (4 Kb)
Sent: 408 bytes (0 Kb)
Received 28 packets, sent 14 packets.
Total lines received: 240
This connection: Sent 4 lines, received 192 lines.
Telnet (IAC) received: DO: 6, DONT: 0, WILL: 11, WONT: 1, SB: 15 [Telnet]
-- MXP --
MXP active: yes, Pueblo mode: NO, Activated: Yes - always
MXP tags received: 0
MXP entities received: 0
MXP errors: 3
-- Commands --
Commands in command history: 6
Speed walking enabled: NO. Speed walking prefix: #
Command stacking enabled: yes. Command stack character: ';'
Accelerators defined: 0
-- Miniwindows --
** Miniwindows: 0 loaded, 0 shown.
-- Output window --
Output pixels: width 1659, height: 703, font width: 8, font height: 15
               can show 207 characters, wrapping at column 236, height 46 lines.
Output buffer: 258 of 5000 lines.
-- Miscellaneous --
Logging: NO, tracing: NO
** SQLite3 databases: 0
Sound buffers in use: 0

---------------------- End summary ----------------------

------ Variable List (alphabetic order) ------

username                       = Anaristos
Amended on Sun 06 Feb 2011 03:50 AM by Anaristos
Australia Forum Administrator #18
Anaristos said:

COMObject is null or not an object

which means to me that the function sees the variable, but its value has been reset, and therefore the value does not persist after the first alias exits.


Well maybe it never got created. Can you amend the function to confirm it worked?


var COMObject;

function cominit(label, line, wildcards)
{
    COMObject = new ActiveXObject("dmsManager.Agent");

    Note ("COMObject is type: " + typeof (COMObject) );
}


You should see:


COMObject is type: object


If you get that far (and it does indeed create the object) but still doesn't work, please post your whole scripts.js file. I would like to see what else you are getting up to.
USA #19
The output is:
Quote:

COMObject is type: object

So here is my scripts.js file:

var COMObject; // dmsManager COM reference.

var COMmds; // handler instance reference.


function cominit(name, line, wildcards)
{
	var dmsProgID = "dmsManager.Agent";
	
	COMObject = new ActiveXObject(dmsProgID);
	
	Note ("COMObject is type: " + typeof (COMObject) );
}

function dbopen(name, line, wildcards)
{
	var dbPath = VBArray(wildcards).toArray()[0];
	
	COMmds = COMObject.start(dbPath);
	
	if (COMmds.isOpen)
	{
	    COMmds.usemap     = true;
	    COMmds.jsonformat = true;
		
	    world.note("\nDatabase " + COMmds.path + " ready for operations\n");
	}
	
	else world.note("An error occured: " + COMmds.lasterror);
}
USA #20
Nitpicking a bit, but... You used 'dbopen' just now, but 'comopen' in a previous post. Did you rename it?
USA #21
Yes, when I moved the scripts to one file and had a chance to look at both functions at once, I decided to make the naming somewhat more consistent throughout functions that were related. I used comopen to be consistent with cominit, but dbopen is more consistent with what will follow once I get this thing going :)
I did update the aliases to reflect the change.