如果不在主代码流中,ActiveX组件调用的回调函数不会触发

JS Callback called from an ActiveX component not triggering if not in the main code flow

本文关键字:函数 回调 调用 组件 代码 ActiveX 如果不      更新时间:2023-09-26

我有一个ActiveX组件,它扫描照片,将文件保存到客户端硬盘上的临时文件中,并上传照片。

我有一个"TwainMan"实例,它执行扫描部分,在扫描完成后,触发"imagescans"事件。这是我的主要代码流:

scanner = new TwainMan();
scanner.ImageScanned += new ImageScannedEventHandler(Scanner_ImageScanned);

执行此操作的代码放置在EventHandler委托方法" scanner_imagescans "中:

private void Scanner_ImageScanned(object Sender, ImageScannedEventArgs e)
{
    this.tempFileName = scanner.ToTempFile(e.Image);
    MessageBox.Show("Scanned picture stored on the following location:'n" + this.tempFileName);
    Upload(this.tempFileName); // works just fine
    TriggerJSCallback(); // here is where the problem appears!           
}

在triggerjcallback方法中,我只触发JS回调:

private void TriggerJSCallback()
{
    EventHandler DataPreparingFinished = this.DataPreparingFinished;
    if (null != DataPreparingFinished) { DataPreparingFinished(this.tempFileName); }
}

请注意,如果我触发jcallback "DataPreparingFinished"从主流程中,JS回调监听器(定义在我的html页面)工作得很好,但问题出现,如果触发"DataPreparingFinished"从" scanner_imagescan "委托内触发,因此不是从主代码流。

我做错了什么?有人能帮忙吗?

这是html页面内的JS Callback定义,在标签内。

<script for="AXTwain" event="DataPreparingFinished(args)" language="javascript" type="text/javascript">
    function AXTwain::DataPreparingFinished(args) {            
        alert("JS ALERT: SUCCESS!!! JS Callback working properly." + args);
        // alert("The temp file is stored on: " + AXTwain.TempFileName); 
    }
</script>

如果我展示更多的代码可能会更好,这样你就能更好地了解我的问题到底是什么。

让我们从头开始…

下面是我的HTML页面,其中包括我的ActiveXObject实例化和JS回调/侦听器函数。

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>ActiveX TWAIN test page</title>
    <object id="AXTwain" name="AXTwain" classid="clsid:d8ea830e-38b0-4f3b-8be4-39c417c27583"></object>
</head>
<body onload="myload();">
    <h1 style="color:green;">AXTwain test page</h1> 
    <script type ="text/javascript">
        function myload() {
            if (AXTwain != null) {
                AXTwain.AcquireImage();
            }           
        }
    </script>
    <script for="AXTwain" event="DataPreparingFinished(args)" language="javascript" type="text/javascript">
        // The "for" attribute should be set to the name of instance of your COM component.
        // The "event" attribute should be set to the JavaScript function signature for the event.
        // The name of the JavaScript function is the instance name of your COM component, followed
        // by double colons, followed by the JavaScript signature of the event.
        function AXTwain::DataPreparingFinished(args) {            
            alert("JS ALERT: SUCCESS!!! JS Callback working properly." + args);            
        }
    </script>
</body>
</html>

下面是我的ActiveX包装器类…

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace AXImageAcquisition
{
    #region ActiveX attributes
    [ProgId("AXImageAcquisition.AXTwain_01")]
    [Guid("d8ea830e-38b0-4f3b-8be4-39c417c27583")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IComEvents))]
    #endregion
    public class AXTwain
    {
        #region Class Properties and Settings
        private TwainMan scanner;
        private String tempFileName;
        public String TempFileName
        {
            get { return this.tempFileName; }
        }
        [ComVisible(false)]
        public delegate void EventHandler(string args);
        public event EventHandler DataPreparingFinished;
        public bool imageScanned = false;
        #endregion
        #region Class Constructors
        public AXTwain()
        {}
        #endregion
        #region Class Methods
        [ComVisible(true)]
        public void AcquireImage()
        {
            this.DataPreparingFinished += new EventHandler(Delegate_DataPreparingFinished);
            this.scanner = new TwainMan();
            this.scanner.ImageScanned += new ImageScannedEventHandler(Scanner_ImageScanned);            
            TriggerJSCallback(); // HERE IN THE MAIN CODE FLOW THE MESSAGE GETS TO JS!!!
        }
        /// <summary>
        /// Delegate that defines functionality for the ImageScanned event, defined in TwainMan class.
        /// </summary>
        /// <param name="Sender"></param>
        /// <param name="e"></param>
        public void Scanner_ImageScanned(object Sender, ImageScannedEventArgs e)
        {
            this.tempFileName = scanner.ToTempFile(e.Image);
            Upload(this.tempFileName);            
            TriggerJSCallback(); // HERE (NOT IN THE MAIN CODE FLOW) THE MESSAGE NEVER GETS TO JS!!! WHYYYY? :(
        }
        /// <summary>
        /// TODO!!!
        /// </summary>
        /// <param name="tempFileName"></param>
        private void Upload(string tempFileName)
        {
            // TODO
        }
        /// <summary>
        /// Test method for the DataPreparingFinished trigger.
        /// </summary>
        public void TriggerJSCallback()
        {           
            EventHandler DataPreparingFinished = this.DataPreparingFinished;
            if (null != DataPreparingFinished) DataPreparingFinished(this.tempFileName);
        }
        /// <summary>
        /// Delegate that defines functionality for the DataPreparingFinished event, which is defined in IComEvents.
        /// </summary>
        /// <param name="msg">Arguments passing from C# to JS.</param>
        void Delegate_DataPreparingFinished(string msg)
        {
            // MessageBox.Show("Delegate_DataPreparingFinished message! (C# code).'n Input message: " + msg);
        }
        #endregion
    }
}

如果你需要更多的代码,我也可以复制/粘贴剩下的代码,这是TwainMan类和它的依赖。然而,所有这些都是从代码项目教程中获取的。顺便说一句,感谢作者。

这很难确定,因为我没有从c#中编写ActiveX组件,但在c++中,如果从主线程以外的线程调用,这样的回调将具有完全相同的效果。ActiveX通常期望所有调用都发生在主线程上。有一些方法可以让其他线程也与ActiveX一起工作,但是c#可能会自动为你做这件事,所以这在c#中可能并不总是准确的。

我建议你想办法在主线程上触发这个函数;我知道在Silverlight中有很多方法可以使用DispatcherTimer;也许你能找到类似的东西?无论如何,这就是我要尝试的。