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.

Community
  • 1
  • 1
Raelshark
  • 2,698
  • 3
  • 25
  • 36
  • 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. – Raelshark Sep 29 '08 at 21:44
  • I have never seen the :: syntax before, where is that documented? – AnthonyWJones Sep 30 '08 at 07:21
  • It's hard to find actual documentation for it, but here's a link to an MSDN article that mentions it: http://msdn.microsoft.com/en-us/library/ms974564.aspx – Raelshark Sep 30 '08 at 11:57
  • 1
    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: http://www.codeproject.com/KB/dotnet/extend_events.aspx?display=Print – Rory Aug 11 '09 at 13:00
  • The link you referred to is now using HTTPS -- (https://msdn.microsoft.com/en-us/library/ms974564.aspx). Also, I've asked and answered on this issue [here](http://stackoverflow.com/q/41713117/111794); I've also written a [library](https://github.com/zspitz/activex-js-events) to work around this. (cc @Rory). – Zev Spitz Feb 01 '17 at 21:15

7 Answers7

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>
Raelshark
  • 2,698
  • 3
  • 25
  • 36
  • In a lot of examples on the internet - I have seen the following code: 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? – Raj Rao Apr 30 '09 at 20:31
  • It seems if you use the :: syntax within a – Rory Aug 11 '09 at 12:58
  • have any of you managed to make this works for a method that has an EventArgs object as argument – Camilo Sanchez Aug 26 '10 at 20:44
  • 1
    nevermind, I figured it out, every type "involved" in the ActiveX interface needs to be ComVisible – Camilo Sanchez Sep 01 '10 at 12:40
  • 1
    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); }'" – J. Andrew Laughlin May 16 '12 at 23:28
  • @J.AndrewLaughlin This works because with `eval` the function declaration is evaluated after the variable initialization. See [here](http://stackoverflow.com/a/41888383/111794). – Zev Spitz Feb 01 '17 at 21:18
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);
}
Zuhaib
  • 1,420
  • 3
  • 18
  • 31
  • 1
    Here is the MSDN link http://msdn.microsoft.com/en-us/library/ms536343(VS.85).aspx – Zuhaib Jul 08 '10 at 12:21
  • 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()`. – Taudris May 11 '12 at 18:42
  • 1
    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 Name – Eric F. Oct 15 '14 at 06:04
  • @Taudris `attachEvent()` has worked for me when control was created dynamically. I have the `` 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. – ewh Nov 05 '15 at 06:05
  • @ewh I refer you to my [question](http://stackoverflow.com/q/41713117/111794) and [answer](http://stackoverflow.com/a/41888383/111794), and to the [library](https://github.com/zspitz/activex-js-events) I've written for registering handlers with any ActiveX object's events. – Zev Spitz Feb 01 '17 at 21:27
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.

Melquiades
  • 8,316
  • 1
  • 27
  • 40
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.

Taudris
  • 1,243
  • 1
  • 14
  • 21
  • 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. – ewh Nov 05 '15 at 06:00
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);
}
Adam
  • 27,361
  • 15
  • 56
  • 70
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>

Frank Schwieterman
  • 23,338
  • 15
  • 87
  • 123
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 :)

J5.
  • 356
  • 4
  • 10