如果不在主代码流中,则从 ActiveX 组件调用的 JS 回调不会触发
我有一个 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 回调:
private void TriggerJSCallback()
{
EventHandler DataPreparingFinished = this.DataPreparingFinished;
if (null != DataPreparingFinished) { DataPreparingFinished(this.tempFileName); }
}
请注意,如果我从主流程中触发 JSCallback“DataPreparingFinished”, JS 回调侦听器(在我的 html 页面中定义)工作得很好,但是如果从“Scanner_ImageScanned”中触发触发器“DataPreparingFinished”,则会出现问题委托,因此不是来自主代码流。
我做错了什么?有人可以帮忙吗?
这是 html 页面标签内的 JS 回调定义。
<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 教程, 这里。顺便说一句,谢谢作者。
I have an ActiveX component which scans the photo, saves the file into a temp file on the client's hard drive and uploads the photo.
I have a "TwainMan" instance that does the scanning part and which after scanning is done, triggers the "ImageScanned" event. This is my main code flow:
scanner = new TwainMan();
scanner.ImageScanned += new ImageScannedEventHandler(Scanner_ImageScanned);
The code that does that is placed into the EventHandler delegate method "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!
}
In the TriggerJSCallback method I only trigger my JS Callback:
private void TriggerJSCallback()
{
EventHandler DataPreparingFinished = this.DataPreparingFinished;
if (null != DataPreparingFinished) { DataPreparingFinished(this.tempFileName); }
}
Note that if I trigger the JSCallback "DataPreparingFinished" from within the main flow, the JS Callback listener (defined in my html page) works just fine, but the problem appears if the trigger "DataPreparingFinished" is triggered from within the "Scanner_ImageScanned" delegate, thus not from the main code flow.
What am I doing wrong? Can anyone help?
Here's the JS Callback definition within the html page, inside the tag.
<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>
Maybe it would be good if I'd show more code, so you would get a better idea of what my problem actually is.
Let's go from the top...
Below is my HTML page which includes my ActiveXObject instantiatin and the JS callback / listener function.
<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>
Below is my my ActiveX wrapper class...
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
}
}
In case you need more code, I can also copy/paste the rest of the code, that is the TwainMan class and it's dependancies. However all that is taken from a codeproject tutorial, here. Btw, thanks to the author.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
很难确定,因为我不是从 C# 编写 ActiveX 组件,但在 C++ 中,如果从主线程以外的线程调用,这样的回调将具有完全相同的效果。 ActiveX 通常期望所有调用都发生在主线程上。不过,也有一些方法可以让其他线程与 ActiveX 一起工作,并且 C# 可能会自动为您执行此操作,因此这在 C# 中可能并不总是准确的。
我建议您找到一种在主线程上触发该函数的方法;我知道有一些方法可以在 Silverlight 中使用 DispatcherTimer 来做到这一点;也许你能找到类似的东西?无论如何,这就是我会尝试的。
It's hard to be certain since I don't write ActiveX components from C#, but in c++ a callback like that would have exactly that effect if called from a thread other than the main thread. ActiveX generally expects all calls to occur on the main thread. There are ways to make other threads work with ActiveX as well, though, and it's possible that C# does this for you automatically so this may not always be accurate in c#.
I would recommend that you find a way to trigger the function on the main thread; I know there are ways to do this in Silverlight using the DispatcherTimer; perhaps you can find something similar? Anyway, that's what I'd try.