Вызов JS Callback из компонента ActiveX не запускается, если не в основном потоке кода - PullRequest
0 голосов
/ 27 октября 2011

У меня есть компонент ActiveX, который сканирует фотографию, сохраняет файл во временный файл на жестком диске клиента и загружает фотографию.

У меня есть экземпляр "TwainMan", который выполняет сканирование и который после завершения сканирования вызывает событие "ImageScanned". Это мой основной поток кода:

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

Код, который делает это, помещается в метод делегата EventHandler "Scanner_ImageScanned":

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!           
}

В методе TriggerJSCallback я запускаю только свой JS Callback:

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

Обратите внимание, что если я запускаю JSCallback "DataPreparingFinished" из основного потока, прослушиватель обратного вызова JS (определенный на моей html-странице) работает просто отлично, но проблема возникает, если триггер "DataPreparingFinished" запускается изнутри " Scanner_ImageScanned "делегат, таким образом, не из основного потока кода.

Что я делаю не так? Кто-нибудь может помочь?

Вот определение обратного вызова JS на html-странице внутри тега.

<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 и его зависимости. Однако все, что взято из учебника codeproject, здесь . Кстати, спасибо автору.

1 Ответ

1 голос
/ 27 октября 2011

Трудно быть уверенным, поскольку я не пишу компоненты ActiveX из C #, но в c ++ подобный обратный вызов имел бы именно такой эффект, если бы вызывался из потока, отличного от основного. ActiveX обычно ожидает, что все вызовы происходят в главном потоке. Однако есть способы заставить другие потоки работать и с ActiveX, и, возможно, C # сделает это автоматически, так что это не всегда может быть точно в c #.

Я бы порекомендовал вам найти способ вызвать функцию в главном потоке; Я знаю, что есть способы сделать это в Silverlight, используя DispatcherTimer; Может быть, вы можете найти что-то подобное? Во всяком случае, это то, что я попробую.

...