调用COM组件的计时器问题
我有一个windows服务。作用是使用一个Timer组件周期性检查数据库,然后调用一个COM组件执行一个功能。COM组件要求单线程执行,代码如下:
Public Class dxt_Service
Public WithEvents SendTimer As System.Timers.Timer '定时发送计时器
Public Sub New()
'初始化计时器
SendTimer = New System.Timers.Timer(3000)
With SendTimer
.AutoReset = True
End With
End Sub
Private Sub SendTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles SendTimer.Elapsed
smutex.WaitOne()
SendTimer.Stop()
Try
SendMessage()
Catch comex As COMException
Throw comex
Catch ex As Exception
Throw ex
End Try
SendTimer.Start()
smutex.ReleaseMutex()
End Sub
SendMessage就是读取数据库,然后调用COM的方法。可是运行的时候,第一次执行SendMessage没有任何问题,但是第二次执行就会出现一个COMException异常信息为“服务器出现意外情况。 (异常来自 HRESULT:0x80010105 (RPC_E_SERVERFAULT))”,ErrorCode为“-2147417851”不知道是什么原因?
我把Public WithEvents SendTimer As System.Timers.Timer改成Dim t As System.Windows.Forms.Timer程序可以正常使用,但是windows服务不能用Forms.Timer。请问这个要怎么处理呢?
--------------------编程问答-------------------- 没人回答,我修改下问题吧。怎么让System.Timers.Timer象System.Windows.Forms.Timer那样单线程运行? --------------------编程问答-------------------- 你的SendMessage代码没贴出来,可能是里面有些需要关闭的组件或者端口没有关闭,第二次执行的时候就冒错,另外在你设置的周期时间内是不是能确认可以完成调用,而不会导致第一次调用没有结束,第二次调用又过来了造成冲突。 --------------------编程问答-------------------- SendMessage过程如下:因为还要监视COM组件引发的事件,所以不能关闭COM。
Public Sub SendMessage()
Dim db As New dxtdbEntities
Dim waiting = dxt.MessageState.Waiting.ToString
Dim msgs = From a In db.tabMessage Where a.State = waiting Order By a.BornTime Descending
If msgs.Any Then
For Each dxt_msg In msgs
If (Now - dxt_msg.BornTime).TotalHours > 3 Then
dxt_msg.State = dxt.MessageState.TimeOver.ToString
Else
dxt_msg.State = MessageState.Posting.ToString
Dim empplog As New EmppLog
empplog.Msg = <SendMessage>
<msgid><%= dxt_msg.id %></msgid>
<Telephones><%= dxt_msg.Telephones.ToString %></Telephones>
<Message><%= dxt_msg.Message %></Message>
</SendMessage>.ToString
empplog.Time = Now
db.AddToEmppLog(empplog)
Try
db.SaveChanges()
Catch ex As Exception
End Try
Open()
'发送短信开始
Dim msg As New EMPPLib.ShortMessage
Dim mobiles As New EMPPLib.Mobiles
mobiles.Add(dxt_msg.Telephones)
msg.DestMobiles = mobiles
msg.content = dxt_msg.Message
msg.needStatus = dxt_msg.NeedStatus '需要状态报告
dxt_msg.dxtServerDataReference.Load()
msg.ServiceID = dxt_msg.dxtServerData.Account 'ServiceID值
msg.srcID = dxt_msg.dxtServerData.Sender
msg.SequenceID = dxt_msg.id
msg.SendNow = dxt_msg.SendNow
If Not dxt_msg.SendNow Then msg.atTime = dxt_msg.AtTime
SyncLock emppctl
emppctl.needStatus = dxt_msg.NeedStatus '需要状态报告
emppctl.submit(msg)
End SyncLock
mobiles = Nothing
msg = Nothing
'发送短信结束
End If
Next
End If
End Sub
--------------------编程问答-------------------- System.Timers.Timer改成System.Windows.Forms.Timer就不会出错了。真是奇怪。 --------------------编程问答-------------------- http://msdn.microsoft.com/zh-cn/library/system.timers.timer(v=vs.80).aspx
System.Timers.Timer()
Elapsed 事件在 ThreadPool 线程上引发。如果 Elapsed 事件的处理时间比 Interval 长,在另一个 ThreadPool 线程上将会再次引发此事件。因此,事件处理程序应当是可重入的。
在一个线程调用 Stop 方法或将 Enabled 属性设置为 false 的同时,可在另一个线程上运行事件处理方法。这可能导致在计时器停止之后引发 Elapsed 事件。Stop 方法的示例代码演示了一种避免此争用条件的方法。
SendTimer_Elapsed方法执行的线程是从线程池中取出来的,并不是主线程,并且smutex.WaitOne()
是没用的,到时间后会在另外一个线程中继续执行的
--------------------编程问答-------------------- 你好。谢谢你的回答,你给的MSDN的链接打不开。
我的问题就是我的事件处理程序不可重入,又不能使用System.Windows.Forms.Timer要怎么办呢?
--------------------编程问答--------------------
Imports System.Threading
Public Class Form1
Dim tm As Threading.Timer
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'3秒之后启动定时器,并间隔3秒触发。采用的是线程池中的线程
tm = New Threading.Timer(New TimerCallback(AddressOf work), Nothing, 3000, 3000)
Console.WriteLine("id :" + Thread.CurrentThread.ManagedThreadId.ToString + "----" + DateTime.Now.ToString("hh:mm:ss"))
End Sub
Sub work(ByVal obj As Object)
Console.WriteLine("Begin -->" + "id :" + Thread.CurrentThread.ManagedThreadId.ToString + "----" + DateTime.Now.ToString("hh:mm:ss"))
'停止定时器
tm.Change(Timeout.Infinite, Timeout.Infinite)
System.Threading.Thread.Sleep(5000)
Console.WriteLine(My.Computer.Clock.LocalTime.ToString("id :" + Thread.CurrentThread.ManagedThreadId.ToString + "----" + DateTime.Now.ToString("hh:mm:ss")))
'重新启动
tm.Change(3000, 3000)
Console.WriteLine("Stop -->" + "id :" + Thread.CurrentThread.ManagedThreadId.ToString + "----" + DateTime.Now.ToString("hh:mm:ss"))
End Sub
End Class
http://msdn.microsoft.com/zh-cn/library/system.threading.timer(VS.80).aspx#Y1034
http://msdn.microsoft.com/zh-cn/library/system.threading.timer.aspx#Y1037
使用 TimerCallback 委托指定希望 Timer 执行的方法。 计时器委托在构造计时器时指定,并且不能更改。 此方法不在创建计时器的线程上执行,而是在系统提供的 ThreadPool 线程上执行。
创建计时器时,可以指定在第一次执行方法之前等待的时间量(截止时间)以及此后的执行期间等待的时间量(时间周期)。 可以使用 Change 方法更改这些值或禁用计时器。
说明
只要在使用 Timer,就必须保留对它的引用。 对于任何托管对象,如果没有对 Timer 的引用,计时器会被垃圾回收。 即使 Timer 仍处在活动状态,也会被回收。
当不再需要计时器时,请使用 Dispose 方法释放计时器持有的资源。 如果希望在计时器被释放时接收到信号,请使用接受 WaitHandle 的 Dispose(WaitHandle) 方法重载。 计时器已被释放后,WaitHandle 便终止。
由计时器执行的回调方法应该是可重入的,因为它是在 ThreadPool 线程上调用的。 在以下两种情况中,此回调可以同时在两个线程池线程上执行:一是计时器间隔小于执行此回调所需的时间;二是所有线程池线程都在使用,此回调被多次排队。
System.Threading.Timer 是一个使用回调方法的计时器,而且由线程池线程服务,简单且对资源要求不高。也可以考虑与 Windows 窗体一起使用的 System.Windows.Forms.Timer 和基于服务器计时器功能的 System.Timers.Timer。这些计时器使用事件并具有附加功能。
--------------------编程问答-------------------- --------------------编程问答-------------------- 7楼的方法可行,不过又出现新的错误。
事件类型: 错误
事件来源: .NET Runtime 2.0 Error Reporting
事件种类: 无
事件 ID: 5000
日期: 2011-3-3
事件: 10:45:47
用户: N/A
计算机:
描述:
EventType clr20r3, P1 dxt_servce2.exe, P2 1.0.0.0, P3 4d6effef, P4 dxt_servce2, P5 1.0.0.0, P6 4d6effef, P7 14, P8 1d, P9 pszqoadhx1u5zahbhohghldgiy4qixhx, P10 NIL.
有关更多信息,请参阅在 http://go.microsoft.com/fwlink/events.asp 的帮助和支持中心。
数据:
0000: 63 00 6c 00 72 00 32 00 c.l.r.2.
0008: 30 00 72 00 33 00 2c 00 0.r.3.,.
0010: 20 00 64 00 78 00 74 00 .d.x.t.
0018: 5f 00 73 00 65 00 72 00 _.s.e.r.
0020: 76 00 63 00 65 00 32 00 v.c.e.2.
0028: 2e 00 65 00 78 00 65 00 ..e.x.e.
0030: 2c 00 20 00 31 00 2e 00 ,. .1...
0038: 30 00 2e 00 30 00 2e 00 0...0...
0040: 30 00 2c 00 20 00 34 00 0.,. .4.
0048: 64 00 36 00 65 00 66 00 d.6.e.f.
0050: 66 00 65 00 66 00 2c 00 f.e.f.,.
0058: 20 00 64 00 78 00 74 00 .d.x.t.
0060: 5f 00 73 00 65 00 72 00 _.s.e.r.
0068: 76 00 63 00 65 00 32 00 v.c.e.2.
0070: 2c 00 20 00 31 00 2e 00 ,. .1...
0078: 30 00 2e 00 30 00 2e 00 0...0...
0080: 30 00 2c 00 20 00 34 00 0.,. .4.
0088: 64 00 36 00 65 00 66 00 d.6.e.f.
0090: 66 00 65 00 66 00 2c 00 f.e.f.,.
0098: 20 00 31 00 34 00 2c 00 .1.4.,.
00a0: 20 00 31 00 64 00 2c 00 .1.d.,.
00a8: 20 00 70 00 73 00 7a 00 .p.s.z.
00b0: 71 00 6f 00 61 00 64 00 q.o.a.d.
00b8: 68 00 78 00 31 00 75 00 h.x.1.u.
00c0: 35 00 7a 00 61 00 68 00 5.z.a.h.
00c8: 62 00 68 00 6f 00 68 00 b.h.o.h.
00d0: 67 00 68 00 6c 00 64 00 g.h.l.d.
00d8: 67 00 69 00 79 00 34 00 g.i.y.4.
00e0: 71 00 69 00 78 00 68 00 q.i.x.h.
00e8: 78 00 20 00 4e 00 49 00 x. .N.I.
00f0: 4c 00 0d 00 0a 00 L.....
--------------------编程问答-------------------- http://social.microsoft.com/Forums/it-IT/267/thread/0c507a88-c893-40c6-95dd-355b38ebfd77
http://www.eventid.net/display.asp?eventid=5000&eventno=7334&source=.NET%20Runtime%202.0%20Error%20Reporting&phase=1
http://tech.www.com.cn/read.php?tid-5513-page-e.html
http://bytes.com/topic/c-sharp/answers/599247-net-2-0-runtime-error-event-id-5000-a
http://blog.sina.com.cn/s/blog_53864cba01000d6d.html
进程产生未捕获异常。造成进程异常终止
Save it in the same directory as your app and call it something like
myApp.exe.config.
<?xml version="1.0" encoding="utf-8" ?>--------------------编程问答-------------------- 最终发现还是老问题没有解决。只是VS无法扑捉线程内的异常了。 --------------------编程问答--------------------
<configuration>
<runtime>
<legacyUnhandledExceptionPolicy enabled="true" />
</runtime>
</configuration>
Public Class Form1
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
Dim td As New Threading.Thread(AddressOf work)
td.Start()
Catch ex As Exception
MsgBox("form----" + ex.ToString)
End Try
End Sub
Sub work()
Try
Throw New Exception("hello")
Catch ex As Exception
MsgBox("thread ----" + ex.ToString)
End Try
End Sub
End Class
线程里的异常要在该线程的执行函数上捕捉
http://blog.sina.com.cn/s/blog_65e729050100m7ms.html
0x80010105:RPC_E_SERVERFAULT
The server threw an exception
Very straight forward, the server throws an exception.
Repro:DCOM方法中直接抛出一个C++ exception就可以重现这个问题。聪明的你可能会问,为啥米C++ exception不会导致DCOM Server crash呐,那是因为:
"Normally, an exception that happens in a DCOM server during execution of a method call is caught by an exception handler in OLE32.DLL, and the method call returns RPC_E_SERVERFAULT."
如果要改变这个行为,可以参考IgnoreServerExceptions这个注册表键:
http://support.microsoft.com/kb/198623/en-us
JIT debug a COM local server
Set the following registry key:
HKEY_LOCAL_MACHINE\Software\Microsoft\Ole
IgnoreServerExceptions="Y"
When the above key is set, the COM/RPC run time will pass the following exceptions on to the caller (for debug purposes):
STATUS_ACCESS_VIOLATION
STATUS_POSSIBLE_DEADLOCK
STATUS_DATATYPE_MISALIGNMENT
STATUS_INSTRUCTION_MISALIGNMENT
STATUS_ILLEGAL_INSTRUCTION
STATUS_PRIVILEGED_INSTRUCTION --------------------编程问答-------------------- http://blog.csdn.net/hz932/archive/2009/02/08/3868816.aspx
WinForm程序主线程默认是STAThread方式运行的,Windows服务下开的线程默认是MTAThread的方式。
自定义一个线程
Imports System.Threading--------------------编程问答-------------------- 试过了。还是不行。
Public Class Form1
Dim tm As Threading.Timer
Shared manualWork As New AutoResetEvent(False)
Shared manualElapsed As New AutoResetEvent(False)
Dim bolExit As Boolean = False
Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
bolExit = True
manualWork.Set()
manualElapsed.Set()
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim td As New Threading.Thread(AddressOf work)
td.SetApartmentState(ApartmentState.STA)
td.Start()
'3秒之后启动定时器,并间隔3秒触发。采用的是线程池中的线程
tm = New Threading.Timer(New TimerCallback(AddressOf Elapsed), Nothing, 3000, 3000)
Console.WriteLine("main id :" + Thread.CurrentThread.ManagedThreadId.ToString + "----" + DateTime.Now.ToString("hh:mm:ss"))
End Sub
Sub work()
Dim bolResult As Boolean = False
While bolExit = False
bolResult = manualWork.WaitOne()
''do something
Thread.Sleep(5000)
Console.WriteLine("work id :" + Thread.CurrentThread.ManagedThreadId.ToString + "----" + DateTime.Now.ToString("hh:mm:ss"))
manualElapsed.Set()
End While
End Sub
Sub Elapsed()
tm.Change(Timeout.Infinite, Timeout.Infinite)
manualWork.Set()
manualElapsed.WaitOne()
If bolExit = False Then
tm.Change(3000, 3000)
Else
manualWork.Set()
End If
End Sub
End Class
程序卡manualElapsed.WaitOne()了。
补充:.NET技术 , VB.NET