我有一个接口程序A,是exe的.其中一个发送消息的对象proxy
一个外部的Active Exe程序(B),引用这个发消息的对象,并订阅了该对象的事件.
B通过这个proxy对象发送消息之后,A程序会对B发送事件.通常情况下,这种发送是一对一的,可是在群发的时候,A对B发送的事件会很多.这就早成A发送事件1的方法还没有结束,A又Raise了一个事件2.
发送事件的方法如下:
public sub DoRaiseEvent
log "DoRaiseEvent begine with id<" & id & ">"
RaiseEvent OnMsg(TaskID, Info.InfoType)
log "DoRaiseEvent end with id<" & id & ">"
end subpublic function mProxy_OnMsg(...)
log "OnMsg begine with id<" & id & ">"
RaiseEvent OnMsg(TaskID, Info.InfoType)
log "OnMsg end with id<" & id & ">"
end function
我就接受到了大量的
(process A) DoRaiseEvent begine with...
(Process B) OnMsg begine with ...
(Process B) OnMsg end with
这样的log,可是DoRaiseEvent end with id这样的log总是要等很久(3秒-2分钟不等),这个时候cpu占用是100,可我的process却只是0(拿不到cpu处理权???为什么老是我的process).
我的问题是:
1.我怀疑是com的事件处理机制的问题.不知道大家有没有碰到.或者有没有其他的解释.
2.是否有什么办法能够帮助我快点接受到DoRaiseEvent end with...
3.另外,曾经出现彻底被锁死的情况,锁死的位置正是再第三个log和第四个log之间,时间超过2个小时.不知道各位有什么经验.希望大家给出问题所在,或者解决的方法,如果你们遇到同样的情况,也行!
如果有人能够帮我解决的话, 我再给400分,决不食言!!!!!
一个外部的Active Exe程序(B),引用这个发消息的对象,并订阅了该对象的事件.
B通过这个proxy对象发送消息之后,A程序会对B发送事件.通常情况下,这种发送是一对一的,可是在群发的时候,A对B发送的事件会很多.这就早成A发送事件1的方法还没有结束,A又Raise了一个事件2.
发送事件的方法如下:
public sub DoRaiseEvent
log "DoRaiseEvent begine with id<" & id & ">"
RaiseEvent OnMsg(TaskID, Info.InfoType)
log "DoRaiseEvent end with id<" & id & ">"
end subpublic function mProxy_OnMsg(...)
log "OnMsg begine with id<" & id & ">"
RaiseEvent OnMsg(TaskID, Info.InfoType)
log "OnMsg end with id<" & id & ">"
end function
我就接受到了大量的
(process A) DoRaiseEvent begine with...
(Process B) OnMsg begine with ...
(Process B) OnMsg end with
这样的log,可是DoRaiseEvent end with id这样的log总是要等很久(3秒-2分钟不等),这个时候cpu占用是100,可我的process却只是0(拿不到cpu处理权???为什么老是我的process).
我的问题是:
1.我怀疑是com的事件处理机制的问题.不知道大家有没有碰到.或者有没有其他的解释.
2.是否有什么办法能够帮助我快点接受到DoRaiseEvent end with...
3.另外,曾经出现彻底被锁死的情况,锁死的位置正是再第三个log和第四个log之间,时间超过2个小时.不知道各位有什么经验.希望大家给出问题所在,或者解决的方法,如果你们遇到同样的情况,也行!
如果有人能够帮我解决的话, 我再给400分,决不食言!!!!!
解决方案 »
- VB textbox中字符判断出现次数并按多至少排列
- 请教字符串中提取数字的问题?
- VB执行RAR里面的文件
- 请问代码实现(IE——Internet选项-->高级 找到"允许活动内容在我的计算机上的文件中运行" ,将其打上勾)?
- 各位大哥:如何将当前网页通过程序的方式自动保存到c盘下?并且文件格式转化成mht
- "打开"对话框怎么打开多个文件?
- 我有一个关于ACCESS的问题:一个用户表中,我想用自动编号来为主键赋值,可它总是从1开始增加但我想插入一条pkid为0的记录(Admin用户)
- 如何绘制一圆形渐变区域?
- 目录结构的拷贝(急!)
- 关于字段的长度
- VB如何控制打印纸张类型
- sendmessage问题
RaiseEvent更像是一种过程调用的特殊形式,在事件处理过程没返回前,不会继续后面内容。
一般这种群发事件,应采用队列形式后台处理机制,这样就不会引起程序锁死的问题。
应用程序间的简单通信
心铃学了这么长时间的VB,发觉VB在制作界面上非常简单、方便,但在软件性能、硬件接口等方面离C语言还是差一些,而且VB是学会容易,精通却很难。心铃认为VB的难点和特点在:鼠标事件编程、数据库编程、对文件的存取操作、应用程序间的通信和ActiveX编程等。由于时间关系,在这个讲座中心铃不能和大家一起来探讨所有这些知识,在本讲中,心铃将讲述这些难点中一个有趣而又有用的知识点――应用程序间的通信。
在VB中可以通过动态数据交换DDE(Dynamic Data Exchange)和动态链接库DLL(Dynamic Link Liberary)方法,实现VB和其他Windows应用程序之间的相互通信。DDL是我们在其他语言中都能接触到的,而且这方面知识对于初学者来讲比较复杂,所以心铃在本讲中将只介绍DDE。所谓“动态数据交换”是指各个应用程序间进行实时动态地数据交换,一旦提供服务的服务方(Sever)改变了交换数据的内容,则接收数据的客户方(Client)将自动更新交换数据的内容。从上面的简介,实现DDE有一个最基本的条件:客户程序和服务程序都正在执行,而且连接的对象正处在服务程序中。由此可见,DDE的内容实际上是服务方和客户方共享的一块内存区。在VB提供的标准控件中,只有文本框、标签框、图像框及Form具有动态交换DDE的功能。DDE的实现有两种方法:代码编程和设计状态下实现,下面分别予以介绍。一.编程时实现DDE连接 确定服务应用程序对象:Word,Excel或其他。
确定连接标题――LinkTopic
对于接收端LinkTopic返回或设置发送端应用程序和主题(使用于该应用程序中的基本数据的分组);对于发送端窗体LinkTopic返回或设置在DDE对话中发送端窗体需响应的主题。语法:对象名.LinkTopic [= value]。其中value是一个指定DDE语法元素的字符串表达式,它包括 application、topic和 item;格式:服务程序名|标题。例如:txtNumber.LinkTopic = "Excel|sheet1"或txtNumber.LinkTopic = "excel|c:\tabel.xls!sheet1"LinkItem――返回或设置传给接收端的数据。
为了指定完整的数据链,LinkTopic应当与LinkItem属性一起使用。语法:对象名.LinkItem [= string]。string是字符串表达式,指定传给目标控件的数据。程序中只能对用做目标的控件设置LinkItem。当Visual Basic窗体是DDE的会话源时,窗体上任何Label名、PictureBox或TextBox控件,都可以是目标使用的application|topic!item字符串的item参数。例如,下面的语法表示从Microsoft Excel到Visual Basic应用程序的有效引用:=VizBasicApplication|MyForm!TextBox1。在Microsoft Excel公式条的目标单元中输入前面的语法即可引用。另外,仅就接收端控件而言,为了指定链接的item元素必须设置相关的LinkItem属性。一个单元引用,比如R1C1,相当于Microsoft Excel工作表中的一个项。LinkMode――连接模式
LinkMode决定VB应用程序和其他应用程序之间的DDE连接方式。前面用LinkTopic设置的数据链接必须依靠LinkMode来激活(一般来说,在设置LinkTopic之后应设置LinkMode。),为了指定想要的链接的类型应将LinkMode属性设置为适当的非零数值。语法:对象名.LinkMode [= number]。number指定连接类型的整数,⑴ 当VB应用程序用做目标(客户)的控件,number设置: 常 数
设置值
描 述
vbLinkNone
0
(缺省值)无DDE交互功能。
vbLinkAutomatic
1
自动-每次链接数据改变,目标控件都要更新
vbLinkManual
2
手动-当激活LinkRequest方法时,才更新目标控件。
vbLinkNotify
3
通知-链接数据改变时,会产生LinkNotify事件,但是只有在LinkRequest方法激活时才会更新目标控件。
对于接收端控件,LinkTopic的改变将中断现存的链接并终止DDE对话。对于发送端窗体,LinkTopic的改变将中断使用那个主题的所有接收端链接。由于这些原因,在改变LinkTopic之前总是先把LinkMode属性设置为0。在接收端控件LinkTopic改变之后,必须再将LinkMode设置为1(自动)、2(手动)或3(通知)以便用新的主题建立一个对话。⑵ 对作为DDE会话源(服务)的窗体,number的设置值为: 常数
设置值
描述
vbLinkNone
0
(缺省值)无-没有DDE交互。如果在设计时为0(无),在运行时不能将其改变为1(源)。
vbLinkSource
1
源-允许窗体上的任何Label、PictureBox或TextBox控件为与该窗体建立DDE会话的目标应用程序提供数据。如果存在这种链接,VisualBasic在控件内容改变时会自动提醒目标应用程序。另外,目标应用程序能够向窗体上的Label、PictureBox、TextBox控件存放数据。如果设计时LinkMode为1(源),运行时可以将它改为0(无)也可以再改回来。
LinkTimeout――连接时间设定
连接时间定义为:服务程序和客户程序实现一次动态数据交换的最长允许时间。语法:对象名.LinkTimeout [= number],number是数值表达式,指定等待时间。缺省情况下,VB为LinkTimeout属性设置为50(相当于5秒)。对其它设置可以用十分之一秒为单位进行设定。DDE发送端应用的响应时间是不同的。用该属性调整接收端控件等待源应用响应的时间。在源应用需要太长的时间响应时,用LinkTimeout可以避免产生Visual Basic错误。注意:控件最大等待时间长度为65535个十分之一秒,或大约为1小时49分钟。如果将LinkTimeout设置为-1,则在DDE会话中控件以最大时间长度等待响应。用户可以按ESC键来强制终止等待过程。 图 15下面这个例子简单介绍了DDE的程序代码(见图15)。它完成的功能主要是和Excel应用程序之间建立DDE,并利用Excel的函数来构造了一个对数计算器(VB5实际就有)。当你输入数据,VB将自动计算出它的自然对数值。Private Sub cmdCalculate_Click()txtLogValue.LinkMode = 2 '进行手动连接输出文本框的DDEtxtLogValue.LinkRequest '在DDE对话中请求Excel更新输出文本框内容txtLogValue.LinkMode = 0 '关闭DDEEnd Sub Private Sub cmdCalculate_LostFocus()'将光标移到“输入数据”框txtNumber.SetFocus'离开“计算”按钮后,重置txtNumber.Text = ""txtLogValue.Text = ""End Sub Private Sub cmdEnd_Click()EndEnd SubPrivate Sub Form_Load()Dim TaskID As Double '使用Shell启动源程序Excel,具体见后;'6-程序窗口以一个图标来显示,而焦点仍在当前活动的的窗口。TaskID = Shell("c:\program files\Microsoft Office\office\excel", 6)txtNumber.LinkTopic = "excel|sheet1" '设置连接主题txtNumber.Text = "=LN(A1)" '设置传输内容txtNumber.LinkItem = "R2c1" '设置传输的地址txtNumber.LinkMode = 2 '进行手动连接'在DDE对话过程中将TextBox控件的内容传送给ExceltxtNumber.LinkPoketxtNumber.LinkMode = 0 '关闭DDEtxtNumber.Text = ""txtLogValue.LinkTopic = "excel|sheet1" '建立输出文本框与Excel的联系txtLogValue.LinkItem = "R2c1" '设置联系地址End Sub注释:Shell函数是执行一个可执行文件,返回一个Variant(Double),如果成功的话,代表这个程序的任务ID,若不成功,则会返回0。语法:Shell(pathname[,windowstyle])pathname:必要参数。Variant(String),要执行的程序名,以及任何必需的参数或命令行变量,可能还包括目录或文件夹,以及驱动器。Windowstyle:可选参数。Variant(Integer),表示在程序运行时窗口的样式。如果 windowstyle 省略,则程序是以具有焦点的最小化窗口来执行的,具体参数请查阅帮助。 Private Sub txtNumber_LostFocus()'DDE传输输入数据给ExceltxtNumber.LinkItem = "R1c1"txtNumber.LinkMode = 2txtNumber.LinkPoketxtNumber.LinkMode = 0End Sub 运行上述程序,可以看到VB自动的调用了Excel程序,并完成了设计的DDE功能。 二.设计时实现DDE连接我们从上面的例子中可以看到DDE的编程具有一定的规格,熟悉后就比较简单了。当然,有更简单的方法在VB与其他应用程序之间建立DDE,这就是在设计时进行建立。步骤如下:启动Excel,
定义要建立DDE的块,如R1C1:R4C2;
转到VB5,激活某个文本框,如“Text1”;
在“编辑”菜单上用“粘贴链接”命令,即建立了一个永久性的与窗体一起保存的数据链接。
查看该控件的属性,发现VB已经自动地设置了LinkMode、LinkTopic和LinkItem属性:LinkMode-1,Automatic;LinkTopic-Excel|[Book1]Sheet1;LinkItem-R1C1:R4C2。这时,你在Excel中选定的文本框中改变内容,再切换到VB中会发现Text1框的内容也改变了,当你把Text1的MultiLine属性改为True后,则Text1框显示的内容格式就与Excel的相同了。
然后对应于每一个消息,proxy会raise一个event回去.请各位高手多多给出意见
各位高手过来看看啊!
事件:如果 B 响应 A 的事件,则 A 触发事件后必须等待 B 处理完成事件响应后才能继续执行后继程序;
消息:如果 B 接收 A 的事件,则 A 发送消息后就可用继续执行后继程序,至于 B 是否接收以及处理了该消息是不用 A 关心的。你要用事件的方式来模拟消息,那么就应该在 B 中实现消息队列处理:
1、在 B 中需要有地方存放消息队列,可用用一个全局的集合
Public g_oMessageList As Collection
2、在响应 A 事件的地方仅添加消息队列,用最快的速度返回
public function mProxy_OnMsg(...)
g_oMessageList.Add TaskID & "," & Info.InfoType
end function
3、用一个定时器处理消息队列
Private Sub Timer1_timer()
dim a() as string
if g_oMessageList.Count > 0 then
a = split(g_oMessageList(0))
g_oMessageList.Remove 1 log "OnMsg begine with id<" & id & ">"
RaiseEvent OnMsg(a(0), a(1))
log "OnMsg end with id<" & id & ">"
end if
End Sub
谢谢你的建议,其实我现在的程序已经是这样处理的了.
现在的问题不是B处理事件的所耗费的时间,其实它非常块就处理完了.关键是这个事件处理结束之后,到A从raise event回复过来,也就是第三和第四条log之间,居然出现被锁死的情况
private bInEvent as boolean
public sub DoRaiseEvent
while bInEvent
Doevents
wend
binevent = truelog "DoRaiseEvent begine with id<" & id & ">"
RaiseEvent OnMsg(TaskID, Info.InfoType)
log "DoRaiseEvent end with id<" & id & ">"binevent = false
end sub
good idea!!!我现在就去测试.
这两天跟踪程序,发现问题所在是我的某一次调用被hung住了,就是上面的log3和log4之间,造成之后程序无法获取时间片.总觉得是Com的这种进程中事件调用机制有问题.
神啊!救救我把,我快给这个东西玩疯了!
多谢,我已经明白了你的意思,其实目前我的程序就是这么设计的. 目前锁死的情况基本已经明确了,是接受事件的程序B在接受事件时已经占用了大量的cpu事件(60%左右, 在其他线程中运行),这时候对B RaiseEvent,B要等待较长事件才能够接受到这个事件,而且在极端的情况下,这个事件会被死锁住. 问题在于无法把事件 raise过去,而不是事件的处理消耗事件太长了. 我打算在B中加入一个timer,过一段事件执行一DoEvents,看看效果如何.
应该是cpu时间
我目前的做法是作一个单独的process,隔100毫秒,调用一次A中的方法,这个方法只作Doevents.这样作能够暂时解决问题,但是肯定还有问题,希望各位老大出点注意.谢谢!!
http://community.csdn.net/Expert/topic/3759/3759008.xml?temp=.9623377
http://community.csdn.net/Expert/topic/3759/3759009.xml?temp=.5340235我的问题已经到一阶段,我觉得应该不可能完美解决了,只好选择绕开这个关卡.希望高手们能够多出一点主意.如果能够帮我绕过这个问题,保证400分送上.多谢!
绕开它对你也许是件好事,我也觉得这种用事件方式,引发群起响应(目的却是群发消息)不太合适。
事件与消息还是有很大区别的,前者是发了必须等回应,后者一般却是发了不用管。我前面说的Post方式,实际就是用事件机制模似消息机制。另外,前面提过,我怀疑过你的事件有重复响应问题,不知你查了没有:
A除主动产生OnMsg事件外,在mProxy_OnMsg事件过程中也被动产生OnMsg事件,这有点象邮件的自动回复,两头都自动回复,就会出现无限回荡,会锁死至出错为止!
我想,是否应在OnMsg的参数中加上一个触发端的标识参数,用于OnMsg时进行判断,使自己不至于响应自己触发的事件。对于接分贴,谢谢了!
B 中必须有一个类 MsgReceiver,只有一个方法 PostMsg(...),该方法仅负责把收到的消息加入 B 的待处理消息队列中;
A 做为一个代理,维护一个 MsgReceiver 的集合,所有 B 的实例必须把自己的 MsgReceiver 对象注册到 A 中;A 群发消息就通过循环 MsgReceiver 集合执行 PostMsg() 方法,这样可以避免出现没有响应的情况;
如果消息最初是由某个 B 的实例产生,想群发到其它的 B 实例中,那么 A 中也应该有一个 MsgReceiver 对象以及待群发消息队列;群发时还要根据消息来源跳过某个 B 的实例。