How to handle an ActiveX event in Javascript
Asked Answered
L

7

20

This is somewhat of a follow-up to an answer here.

I have a custom ActiveX control that is raising an event ("ReceiveMessage" with a "msg" parameter) that needs to be handled by Javascript in the web browser. Historically we've been able to use the following IE-only syntax to accomplish this on different projects:

function MyControl::ReceiveMessage(msg)
{
   alert(msg);
}

However, when inside a layout in which the control is buried, the Javascript cannot find the control. Specifically, if we put this into a plain HTML page it works fine, but if we put it into an ASPX page wrapped by the <Form> tag, we get a "MyControl is undefined" error. We've tried variations on the following:

var GetControl = document.getElementById("MyControl");
function GetControl::ReceiveMessage(msg)
{
   alert(msg);
}

... but it results in the Javascript error "GetControl is undefined."

What is the proper way to handle an event being sent from an ActiveX control? Right now we're only interested in getting this working in IE. This has to be a custom ActiveX control for what we're doing.

Thanks.

Lody answered 29/9, 2008 at 21:35 Comment(5)
To clarify one point - the getElementById works fine. We have a reference to the control, Javascript just doesn't like us using the :: syntax with that reference.Lody
I have never seen the :: syntax before, where is that documented?Lightface
It's hard to find actual documentation for it, but here's a link to an MSDN article that mentions it: msdn.microsoft.com/en-us/library/ms974564.aspxLody
It's worth noting that events only get wired up correctly if the ActiveX object is created using an OBJECT tag. If you're creating them using CreateActiveXObject() then you can do something different like this: codeproject.com/KB/dotnet/extend_events.aspx?display=PrintMyotome
The link you referred to is now using HTTPS -- (msdn.microsoft.com/en-us/library/ms974564.aspx). Also, I've asked and answered on this issue here; I've also written a library to work around this. (cc @Rory).Marrowbone
L
13

I was able to get this working using the following script block format, but I'm still curious if this is the best way:

<script for="MyControl" event="ReceiveMessage(msg)">
    alert(msg);
</script>
Lody answered 30/9, 2008 at 11:58 Comment(6)
In a lot of examples on the internet - I have seen the following code: <script for="MyControl" event="ReceiveMessage"> function ReceiveMessage(msg) { alert(msg); } </script> Even though the above will work, be aware that it fails if your event will be fired multiple times. I found this out when I hooked the ReceiveMessag event to a timer in the ActiveX control which fired after a given interval. Every time the event fired, the handler would be called the number of times the event had been fired. (eg: 1st fire: 1 time, 2nd fire: 2 times, 3rd fire : 3 times). No idea why! Answers?Midwifery
It seems if you use the :: syntax within a <script for="... tag then it does what you're saying - wires up the event again each time it's called. If you just do the script for or just do the function :: syntax I think it works fine (excluding this case of being within a FORM tag).Myotome
have any of you managed to make this works for a method that has an EventArgs object as argumentHarlem
nevermind, I figured it out, every type "involved" in the ActiveX interface needs to be ComVisibleHarlem
As of IE 9, this event wire up method works if an HTML object tag is used. If "var ax = new ActiveXObject('myProgId')" is used, then dynamic event wire up works -> "eval('function ax::EventInterfaceMethod(args){ return myJavaScriptMethod(args); }'"Tlaxcala
@J.AndrewLaughlin This works because with eval the function declaration is evaluated after the variable initialization. See here.Marrowbone
D
8

I have used activex in my applications before. i place the object tags in the ASP.NET form and the following JavaScript works for me.

function onEventHandler(arg1, arg2){
    // do something
}

window.onload = function(){
    var yourActiveXObject = document.getElementById('YourObjectTagID');
    if(typeof(yourActiveXObject) === 'undefined' || yourActiveXObject === null){
        alert('Unable to load ActiveX');
        return;
    }

    // attach events
    var status = yourActiveXObject.attachEvent('EventName', onEventHandler);
}
Demarche answered 8/7, 2010 at 12:19 Comment(5)
Here is the MSDN link msdn.microsoft.com/en-us/library/ms536343(VS.85).aspxDemarche
It has been a while since I tested this, but I believe .attachEvent() only works if the control was created using static HTML, or possibly document.write().Emalia
FYI: attachEvent is IE specific and won't work for Chrome, the related method name for WebKit based is addEventListener. And to clarify camilin87's post a bit more, both methods work only for standard event type i.e. onclick, ontouch, etc. Not working for custom ActiveX's Event NameStacey
@Emalia attachEvent() has worked for me when control was created dynamically. I have the <object> tag created dynamically and then used attachEvent() to hook into ActiveX events of the object. Note, for IE11 mode, attachEvent() is not available, so it appears the dynamic method you provided is the only option for IE11. I created a function for my app to use attachEvent() if available, but if not, use the eval() method.Sato
@Sato I refer you to my question and answer, and to the library I've written for registering handlers with any ActiveX object's events.Marrowbone
F
5

OK, but if you are using C# (.NET 2.0) with inherited UserControl (ActiveX)... The only way to make it work is by "Extending" the event's handler functionality: http://www.codeproject.com/KB/dotnet/extend_events.aspx?display=Print

The above project link from our friend Mr. Werner Willemsens has saved my project. If you don't do that the javascript can't bind to the event handler.

He used the "extension" in a complex way due to the example he chose but if you make it simple, attaching the handle directly to the event itself, it also works. The C# ActiveX should support "ScriptCallbackObject" to bind the event to a javascript function like below:

  var clock = new ActiveXObject("Clocks.clock");
  var extendedClockEvents = clock.ExtendedClockEvents();
  // Here you assign (subscribe to) your callback method!
  extendedClockEvents.ScriptCallbackObject = clock_Callback; 
  ...
  function clock_Callback(time)
  {
    document.getElementById("text_tag").innerHTML = time;
  }

Of course you have to implement IObjectSafety and the other security stuff to make it work better.

Flatware answered 19/12, 2008 at 1:29 Comment(0)
L
1

If you have an ActiveX element on your page that has an id of 'MyControl' then your javascript handler syntax is this:

function MyControl::ReceiveMessage(msg)
{
   alert(msg);
}
Lepidote answered 12/11, 2008 at 4:34 Comment(2)
I don't think that works if the <object> tag is within a form.Whilom
@FrankSchwieterman This handler will only respond if MyControl is declared in the global scope. Perhaps only the objects outside of forms get their id in global scope?Marrowbone
W
1

I found this code works within a form tag. In this example, callback is a function parameter passed in by javascript to the ActiveX control, and callbackparam is a parameter of the callback event generated within the activeX control. This way I use the same event handler for whatever types of events, rather than try to declare a bunch of separate event handlers.

<object id="ActivexObject" name="ActivexObject" classid="clsid:15C5A3F3-F8F7-4d5e-B87E-5084CC98A25A"></object>

<script>
function document.ActivexObject::OnCallback(callback, callbackparam){
callback(callbackparam);
}
</script>

Whilom answered 9/1, 2009 at 3:58 Comment(0)
E
1

In my case, I needed a way to dynamically create ActiveX controls and listen to their events. I was able to get something like this to work:

//create the ActiveX
var ax = $("<object></object>", {
    classid: "clsid:" + clsid,
    codebase: install ? cabfile : undefined,
    width: 0,
    height: 0,
    id: '__ax_'+idIncrement++
})
.appendTo('#someHost');

And then to register a handler for an event:

//this function registers an event listener for an ActiveX object (obviously for IE only)
//the this argument for the handler is the ActiveX object.
function registerAXEvent(control, name, handler) {
    control = jQuery(control);

    //can't use closures through the string due to the parameter renaming done by the JavaScript compressor
    //can't use jQuery.data() on ActiveX objects because it uses expando properties

    var id = control[0].id;

    var axe = registerAXEvent.axevents = registerAXEvent.axevents || {};
    axe[id] = axe[id] || {};
    axe[id][name] = handler;

    var script =
    "(function(){"+
    "var f=registerAXEvent.axevents['" + id + "']['" + name + "'],e=jQuery('#" + id + "');"+
    "function document." + id + "::" + name + "(){"+
        "f.apply(e,arguments);"+
    "}"+
    "})();";
    eval(script);
}

This code allows you to use closures and minimizes the scope of the eval().

The ActiveX control's <object> element must already be added to the document; otherwise, IE will not find the element and you'll just get script errors.

Emalia answered 11/5, 2012 at 19:7 Comment(1)
+1. Although your answer uses jquery-isms, this is useful answer when dealing with a dynamic page. I have had sucess with just using attachEvent() method on the object to attach a listener function to an ActiveX event. Unfortunately, in IE11 mode, attachEvent() is no longer supported, so something like the eval() method you have has to be used. The standard addEventListener() method does not support ActiveX-based event types.Sato
Q
0

I think that the MyControl::ReceiveMessage example does not work because the ActiveX control is being exposed with a different name or in a different scope.

With the example GetControl::ReceiveMessage, I believe that the function definition is being parsed before the GetControl reference is being set, thus it does not refer to a valid object and cannot bind the function to the object.

I would attack this problem by using the MS script debugger and trying to determine if a default reference for the control exists with a different name or in a different scope (possibly as a child of the form). If you can determine the correct reference for the control, you should be able to bind the function properly with the Automagic :: method that the MSDN article specifies.

One more thought, the reference may be based on the name of the object and not the ID, so try setting both :)

Quade answered 7/10, 2008 at 0:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.