PDA

View Full Version : Clients and events.. (W98)



shadowfyr55
05-10-2006, 03:01 PM
This is probably going to generate the online equivalent of blank stares and comments like, "Why don't you just impliment blah?", but I figured I would try anyway.. lol

Situation: I "use" a client application that impliments script engine support, of multiple engines, and some callbacks for events specific to the client itself. Do to the nature of the client it is occationally useful to connect ActiveX controls into the script engine. For the most part these have all been ones with one-way interfaces, like ODBC, etc. However, it would be nice to be able to have events actually trigger one of the script functions.

Problems:
a. This seems stupidly complicated, at least to find any useful code examples for bloody doing it.
b. The one solution I have tracked down involves using ATL to bridge the controls through the client. The developer uses MFC though and is reluctant to impliment such a thing. Part of that is because he knows even lass about ATL than I do.

The actual questions:

1. When you instance an ActiveX object in a script engine "what" is the parent window for it? I personally assume that, since it needs one to work at all, that the parent will be the client that has instanced the engine, but I can't be 100% sure about that.

2. If I am right, then shouldn't the clients main event handler functions be recieving the events from such objects already? Or, in other words, if I do something like:

set a = CreateObject("VBDialog")

Won't the client itself get the "OK" or "Cancel" events from that dialogs window anyway, without having to use IConnectionPointContainer::Advise to link it directly to a function?

Frankly I think the developer is being overly paranoid about ATL possibly breaking or trying to impliment a more correct call to the ActiveX control's IUnknown to get the actual event list. This is something that would help anyway imho, since querying the interface from the script would let the user find out what events, properties, etc. are supported for an unknown (or poorly documented) object. However, what the developer seems most likely to support is more like this:

1. Script calls something like WantEvents(wHnd, "FunctionName").
2. Client queries the script engine for the address of "FunctionName" and puts that into a table.
3. When an event arrives, check to see if its not something like "The mouse just moved", then check its hWnd against the request list and call the script function that it has been connected to.

This is of course reinventing the wheel... But what can you do when the developer wants to be stubborn?

The whole point here is, in theory, to use the built in XML parser of the client to read a window layout you pre-made. So, if for example you want a map or picture list or even a button bar with lots of game specific functions/animations/text, you can create the interface, then when the client loads the script, rebuild your window from that description and have the "script" deal with what to do when all the buttons are pressed. Hopefully without the overhead and minimal amounts of non-portability to other platforms. Burying something like the HTML control or Webbrowser control from MS in there does a number of things I don't want any more than the developer does:

1. Adds extra memory requirements, above and beyond the client, script engine(s) being used (it supports plugins where every one can be running a different language, but comes with LUA as its default engine), the object someone loads with it and the script themselves.

2. Using those means you get all the extras that a browser or HTML control do, even if you don't want some or all of them. This includes any problems IE itself picks up along the way, especially on older versions of Windows.

3. It "may" limit what systems the client could run on, by creating minimum version issues.

4. The scripts for such systems are "not" compatible with the existing implimentation of scripts in the client (usually they are imbedded in the HTML page, not in a seperate engine).

5. Its about as portable as a 30 foot tall bronze elephant, since it would rely on a yet another Windows specific product, with its own interfaces and implimentations

Anyway, hopefully the answer is, "Yes, the client itself will receive all the events for any object the script it hosts has instanced." It will save me a lot of frustration. ;)

Brf
05-10-2006, 03:48 PM
My guess would be you would have to impliment handlers for the events. The parent would receive the events, but not know what to do with them without a handler.

shadowfyr55
05-11-2006, 11:00 AM
That is what I figured. But doesn't exactly answer the main question. Is the client hosting the script engine the "parent"?

I know that a window and its controls, where I explicitly name the client's window as the parent, will be correctly dealt with, but what about something like:

set winamp=createobject("WinampCOM.application")

This includes an event called SongChange, which it would be nice to trap. The problem of course being that without either using a connection point or having the client watch for it... I *think*, but am not *sure* events from such things get recieved by the client. I think the structure is like this:

WinampCOM's Parent = VBscript
VBscript's parent = Hosting client

So, and event from WinampCOM will hit the clients handler, be ignored, get tossed to the script engine and be ignored again. So, trapping it at the client should work, but I am not sure that this is actually what is happening, *especially* with script engines that are not designed by MS. If all else failed I could maybe create a hidden window, attach the object to that and thus circumvent the whole issue, but its a damn roudabout solution, if the client is already getting it.

Brf
05-11-2006, 11:23 AM
Here you go... from MSDN

Wscript.CreateObject
The CreateObject method creates an object specified by the strProgID parameter. If the parameter strPrefix is specified, Windows Scripting Host connects the object's outgoing interface to the script file after creating the object. When the object fires an event, Windows Scripting Host calls a subroutine named strPrefix and the event name.

For example, if strPrefix is "MYOBJ_" and the object fires an event named "OnBegin", Windows Scripting Host calls the "MYOBJ_OnBegin" subroutine located in the script.

Syntax
Wscript.CreateObject(strProgID, [strPrefix]) = objObject

---------------------------------------------
In other words, you use the strprefix....
For instance:
set winamp=createobject("WinampCOM.application","myamp_")

Then you create a subroutine called "myamp_SongChange" which handles that event

shadowfyr55
05-11-2006, 02:46 PM
Umm. That only works in WSH. vbscript and others can exist in two environments a) as a hosted engine inside **your own aplpication** or b) within the wscript hosting environment. The problem is that wscript adds "multiple" addition commands to the languages that are **not** available to vbscript, jscript or anything else when running as a hosted script system in your own client application. Put simply it won't work, since even if I used the version of createobject available to me to create an instance of wscript, that new instance would have *no* access to the functions exposed to the engine in the client, nor would the client be aware of or able to call the function within what is running in wscript.

It simply won't work. It assumes things unique to the *environment* being described, not to vbscript itself.

Lets see if I can explain this better..

Environment 1, wscript:

wscript.createobject is an function exposed "by" WSH to create objects. This is "distinct" from the vbscript createobject. This environment lacks the abaility to allow other clients to expose their own functions to it. Its a stand alone environment, similar to typing Python.exe at the command line, instead of linking python.dll into your own client. Functions specific to the wscript "object" are not available to vbscript "unless" in the wscript environment.

Environment 2: Internet Explorer. All objects in IE are explicitly declared in the HTML "before" scripting is ever loaded. It cannot connect event from createobject to script any more than any other environment that used the vbscript version of that command. It **does** impliment a reference table that allows it to link events like:

Window.OnLoad = GetRef ("MyOnload")

But this *requires* that the event manager exist in the client.

Environment 3: Your own client. To use GetRef in vbscript or the similar functions in jscript, or in anything else, *you must* impliment a method of creating and tracking objects and their events OR include a COM bridge of some sort to connect them. Createobject cannot do this, since it is the engine creating the object, not the client, and the script engines themselves cannot handle events.

Unfortunately, all the documentation on evens for scripting make one of three invalid assumptions. 1. You are running it in WSH. 2. You are running them in IE. or 3. You already know how to implement a creation and event management system for your own application. Well, 1 and 2 are incorrect and 3 is both a case of, "Neither I nor the develper know how to do it", and, "I can't find any useful information on how the hell this is done."

But in the simplest terms possible, thanks, but your talking about an implimentation of that function that is not available in the environment I am running it in.

jwwg
06-09-2006, 08:48 PM
It's complicated to implement and it has to be done by the host application. I've done this, but it's been a few months since I looked at the code and my memory is a bit fuzzy on some of the details.

The object you're creating will not send any events to the host or the scripting engine because they don't support the outgoing connection point interface the object defines.

As a bit of background, all script commands and events are dispatch based, meaning that they use dispatch interfaces, both for incoming and outgoing interfaces. You essentially have to create a dispatch based proxy event object that glues together the created object and the script engine and then uses some kind of scheme to resolve event names raised by the object to object + event name method calls in the script by manually invoking the methods in the script.

You create your proxy event object, register its dispatch interface with the created object so you can sink the object's events. When you receive an event from the object, you map the event call into a script function and invoke the function using the script engine's dispatch interface.

The dispatch interface allows you to translate function IDs to names and vice versa which is how the mapping is done.

Eric Lippert's blog from Microsoft has a ton of good scripting engine information, which is partly where I found out the basis on how to do this.

Depending on how you tie together your script engine and host object implementation, there can be a lot of tricky details with reference counting management.

I did it all in raw C++. Since I didn't have to implement a mass of ActiveX interfaces, it was easier than using ATL and it let me keep track of what was happening with references a lot easier.

shadowfyr55
06-10-2006, 11:25 AM
Hmm. Well, I was operating on the assumption that anything that generates events "must" fire those events to some place. Guess I got that wrong... I just wish I could find some example code, instead of having to hack together something myself. My skill at C++ is only slightly better than my skill at nuclear physics. lol I have been trying to track down a solution for a while now, initially to "fix" the problem with VB that made late bound objects impossible to get events from, then more recently to convince the developer of a client application I use that it "is" possible to get scripts to respond to events. Right now, there are only two options, a) include activeX calls to the client or b) use a udp system to talk to it and the script. The problem with both is that they are impractical for existing activex and if you are trying to do something like creating a custom window and populating it with controls from inside the script, its not even possible to really encapsulate the result in some sort of mini-ap that uses early binding to escape the problem. I know the mechanics of how it "should" work and why it "isn't", but coding it... lol

Doesn't help that my only copy of VC++ is on a dead machine and version 4.0. :p

Anyway, this is a bit more promising, since it can be done without ATL, which the developer was griping about. Something about not knowing if ATL and MFC mix or that something in ATL might change and invalidate the code, etc.. I think he is being paranoid, but then with MS... lol

This problem is right up there with the, "how the hell do you actually impliment switching used modes on containers, instead of just reading them?", problem in terms of the pain in the *** it is to find a) examples or b) useful explainations. Though... ambient_usermode is "probably" worse in that respect. I haven't found "any" useful information on getting that to work right, or at least nothing I wouldn't have to play mad scientist with, at risk of seriously screwing up.

Thanks for the info on where to look for the object event stuff though.

jwwg
06-10-2006, 06:58 PM
VB can't get events for the same reason the script can't. It doesn't know how to register with the object's (source) outbound interface. When you create a variable reference for an object by its specific type (dim foo as <type>) or drop an ActiveX control on the form, VB queries the type library information for the object to figure out if it supports a default source outbound event interface and figures out how to talk to that interface. When you create it dynamically against an untyped variable (as Object), it can't do that (or at least doesn't know how).

All an ActiveX/COM event is, is a function call on an interface provided by the client object. The client creates the object and provides an interface reference to the object for the object to make call backs on when events occur. If you start talking multithreading, then you can run into some complicated apartment issues, but at it's simplest, that's all it really is.

If the client object doesn't register itself on that interface, no function calls (events) are made because the source object doesn't have a sink object reference to make the call against.

I found this link which may help you. I didn't look at it in too much detail, so if it turns out to be a wild goose chase, sorry.

http://msdn.microsoft.com/msdnmag/issues/01/03/connpoints/

Keep in mind that ATL is just C++ templates. If you don't have to use that many of the base ATL templates, they can be reimplemented using non-template C++. I'm totally unfamiliar with MFC, so I have no ideas of the issues you can run into using both (ATL + MFC). An event proxy interface should be all non-GUI code however. Depending on how you're using threading and what interface marshalling is being performed, events will be received as long as the thread that registered its site interface with the script engine services its message queue. The CoInitialize(Ex) documentation has more detail regarding how windows message queues interact with COM interface marshalling.

One tricky part that I forgot to mention about implementing an event proxy is finding the right type library to use at run-time, and finding the necessary information in the TLB.

Unfortunately, I can't provide you any sample code since the implementation I did is included in a product at the company I work at.<P ID="edit"><FONT class="small">Edited by jwwg on 06/10/06 18:05.</FONT></P>

shadowfyr55
06-11-2006, 09:25 AM
Yeah. I get why VB can't either. Same is "technically" true of C++, C#, etc, save that in those you can code around the limitation. What bugs the hell out of me is how they provided the functionality to fix it in OLE, but didn't figure anyone needed a way to actually impliment it in languages that don't let you code around the problem. Same with bloody ambients and containers...

&gt;Unfortunately, I can't provide you any sample code since the implementation I did is included in a product at
&gt;the company I work at.

Story of my life... lol Everything seems to be something they either want to @#$#$ sell you for more than its worth or is protected by confidentiality. Makes you wonder some times if one of these days the only people writing software will be the companies producing the original products, because everyone else will to too afraid of someone sueing them to do something as silly as exchanging ideas or information, even if its something stupid, like a handful of code with which its nearly impossible (if not in fact impossible) to do any other way. Paranoia doesn't promote innovation. It doesn't even promote mediocre replication of existing junk. But, you know this as well as I do and have been handcuffed.