我已经好好打了几天。我正在vb.net中开发一个com对象来显示启动画面。这将从vb脚本和Powershell调用,以便在脚本中发生其他指令时向用户提供状态。我间歇性地收到这个错误,我们将不胜感激。
这是我用来帮助开发我的应用程序的链接。 http://msdn.microsoft.com/en-us/library/ms233639.aspx
Public Class Progress
Private WithEvents appContext As ApplicationContext
Private frmSplash As frmProgress
Private Delegate Sub FormDelegate()
Private ShowDelegate As FormDelegate
Private UnLdDelegate As FormDelegate
Private t As Thread
Public Sub New()
MyBase.New()
If IsNothing(appContext) Then
frmSplash = New frmProgress
appContext = New ApplicationContext(frmSplash)
t = New Thread(AddressOf StartMessageLoop)
t.IsBackground = True
t.SetApartmentState(ApartmentState.STA)
t.Start()
ShowDelegate = New FormDelegate(AddressOf frmSplash.Show)
HideDelegate = New FormDelegate(AddressOf frmSplash.Hide)
UnLdDelegate = New FormDelegate(AddressOf frmSplash.Close)
End If
End Sub
消息循环
Private Sub StartMessageLoop()
Application.Run(appContext)
End Sub
ShowForm
Public Sub Show()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated = True Then
appContext.MainForm.Invoke(ShowDelegate)
End If
End If
End Sub
卸载
Public Sub Unload()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated Then
appContext.MainForm.Invoke(UnLdDelegate)
End If
End If
End Sub
线程退出事件
Private Sub ac_ThreadExit(ByVal sender As Object, ByVal e As System.EventArgs) Handles appContext.ThreadExit
appContext.MainForm.Dispose()
appContext.MainForm = Nothing
appContext.Dispose()
appContext = Nothing
frmSplash.Dispose()
frmSplash = Nothing
End Sub
当我从powershell脚本调用Show Method时,通常会抛出错误。任何帮助将不胜感激。感谢
完整代码
Private ShowLock As New ManualResetEvent(False)
Private WithEvents appContext As ApplicationContext
Private frmSplash As frmProgress
Private Delegate Sub FormDelegate()
Private Delegate Sub DisplayDelegate(ByVal msg As String)
Private Delegate Sub FormSettings(ByVal val As Boolean)
Private ShowDelegate As FormDelegate
Private HideDelegate As FormDelegate
Private UnLdDelegate As FormDelegate
Public Sub New()
MyBase.New()
frmSplash = New frmProgress
End Sub
Public Sub SetDisplayText1(ByVal msg As String)
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetDisplayText1)
frmSplash.Invoke(d, New Object() {[msg]})
Else
frmSplash.Label1.Text = msg
frmSplash.Label1.Refresh()
End If
End Sub
Public Sub SetDisplayText2(ByVal msg As String)
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetDisplayText2)
frmSplash.Invoke(d, New Object() {[msg]})
Else
frmSplash.Label2.Text = msg
frmSplash.Label2.Refresh()
End If
End Sub
Public Sub SetTitle(ByVal msg As String)
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetTitle)
frmSplash.Invoke(d, New Object() {[msg]})
Else
frmSplash.Text = msg
End If
End Sub
Public WriteOnly Property AlwaysOnTop() As Boolean
Set(ByVal value As Boolean)
SetTop(value)
End Set
End Property
Public Sub Show()
AddHandler frmSplash.HandleCreated, AddressOf frm_HandleCreated
If IsNothing(appContext) Then
Dim t As Thread
appContext = New ApplicationContext(frmSplash)
t = New Thread(AddressOf StartMessageLoop)
t.IsBackground = True
t.SetApartmentState(ApartmentState.STA)
t.Start()
Else
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated = True Then
ShowDelegate = New FormDelegate(AddressOf Show)
appContext.MainForm.Invoke(ShowDelegate)
End If
Else
frmSplash.Show()
End If
End If
End Sub
Public Sub SetTop(ByVal val As Boolean)
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetTop)
frmSplash.Invoke(d, New Object() {[val]})
Else
frmSplash.TopMost = val
frmSplash.TopLevel = val
End If
End Sub
Public Sub Hide()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated Then
HideDelegate = New FormDelegate(AddressOf Hide)
appContext.MainForm.Invoke(HideDelegate)
End If
Else
frmSplash.Hide()
End If
End Sub
Public Sub Unload()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated Then
UnLdDelegate = New FormDelegate(AddressOf Unload)
appContext.MainForm.Invoke(UnLdDelegate)
End If
Else
frmSplash.Close()
End If
End Sub
Private Sub StartMessageLoop()
Application.Run(appContext)
End Sub
Private Sub ac_ThreadExit(ByVal sender As Object, ByVal e As System.EventArgs) Handles appContext.ThreadExit
appContext.MainForm.Dispose()
appContext.MainForm = Nothing
appContext.Dispose()
appContext = Nothing
frmSplash.Dispose()
frmSplash = Nothing
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub
Public Sub frm_HandleCreated()
ShowLock.Set()
End Sub
My Debug Lines会让我相信崩溃发生在这里
Public Sub Show()
AddHandler frmSplash.HandleCreated, AddressOf frm_HandleCreated
If IsNothing(appContext) Then
Dim t As Thread
appContext = New ApplicationContext(frmSplash)
t = New Thread(AddressOf StartMessageLoop)
t.IsBackground = True
t.SetApartmentState(ApartmentState.STA)
t.Start()
感谢目前为止提供的所有帮助。
答案 0 :(得分:3)
创建MainForm
后,在最终成为“UI线程”的同一线程上访问其Handle
属性。将它分配给一些一次性变量,重点只是访问它。 WinForms懒惰地初始化Handle
,因此在访问该属性或首次显示该属性之前它无效。 InvokeRequired
通过查看创建Handle
的线程来工作,所以如果尝试在后台线程上懒惰地初始化它,可能会发生不好的事情。
有关详细信息,请参阅this question and answer。
这可能不是它,但它确实听起来像我之前遇到过的东西。希望它能让你走上正轨。
答案 1 :(得分:2)
好的,我明白了问题所在。我的猜测是你正在调用PS脚本中的SetDisplayText
方法之一。您需要保护那些InvokeRequired
和IsHandleCreated
上的支票,并且需要围绕表单的初始显示提供同步。我的VB.NET有点生疏,但我会尽我所能:
首先,将其添加为局部变量:
Private ShowLock As New ManualResetEvent(False);
然后,在HandleCreated
上为frmSplash
事件添加处理程序,并在该处理程序中添加:
Public Sub Form_HandleCreated(...)
ShowLock.Set
End Sub
然后在你的ShowForm方法中,等待那个人设置:
Public Sub Show()
ShowLock.WaitOne
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated = True Then
appContext.MainForm.Invoke(ShowDelegate)
End If
End If
End Sub
这样你就可以确定在第一个线程尝试显示它之前已经创建了表单的句柄(或者做了其他的事情)。
最后,保护那些SetXXX
方法(对于触及表单并可能从脚本调用的任何内容执行此操作):
Public Sub SetDisplayText1(ByVal msg As String)
If frmSplash.InvokeRequired Then
Dim d As New SetDisplayText1Delegate(AddressOf SetDisplayText1)
frmSplash.Invoke(d, New Object() {[msg]})
Else
frmSplash.Label1.Text = msg
frmSplash.Label1.Refresh()
End If
End Sub
编辑:试试这个 - 我从上面拿了你的代码并重新做了一点。
Private ShowLock As New ManualResetEvent(False)
Private WithEvents appContext As ApplicationContext
Private frmSplash As frmProgress
Private Delegate Sub FormDelegate()
Private Delegate Sub DisplayDelegate(ByVal msg As String)
Private Delegate Sub FormSettings(ByVal val As Boolean)
Private ShowDelegate As FormDelegate
Private HideDelegate As FormDelegate
Private UnLdDelegate As FormDelegate
Public Sub New()
MyBase.New()
frmSplash = New frmProgress
AddHandler frmSplash.HandleCreated, AddressOf frm_HandleCreated
Dim t As Thread
appContext = New ApplicationContext(frmSplash)
t = New Thread(AddressOf StartMessageLoop)
t.IsBackground = True
t.SetApartmentState(ApartmentState.STA)
t.Start()
End Sub
Public Sub SetDisplayText1(ByVal msg As String)
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetDisplayText1)
frmSplash.Invoke(d, New Object() {[msg]})
Else
frmSplash.Label1.Text = msg
frmSplash.Label1.Refresh()
End If
End Sub
Public Sub SetDisplayText2(ByVal msg As String)
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetDisplayText2)
frmSplash.Invoke(d, New Object() {[msg]})
Else
frmSplash.Label2.Text = msg
frmSplash.Label2.Refresh()
End If
End Sub
Public Sub SetTitle(ByVal msg As String)
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetTitle)
frmSplash.Invoke(d, New Object() {[msg]})
Else
frmSplash.Text = msg
End If
End Sub
Public WriteOnly Property AlwaysOnTop() As Boolean
Set(ByVal value As Boolean)
SetTop(value)
End Set
End Property
Public Sub Show()
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated = True Then
ShowDelegate = New FormDelegate(AddressOf Show)
appContext.MainForm.Invoke(ShowDelegate)
End If
Else
frmSplash.Show()
End If
End Sub
Public Sub SetTop(ByVal val As Boolean)
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
Dim d As New DisplayDelegate(AddressOf SetTop)
frmSplash.Invoke(d, New Object() {[val]})
Else
frmSplash.TopMost = val
frmSplash.TopLevel = val
End If
End Sub
Public Sub Hide()
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated Then
HideDelegate = New FormDelegate(AddressOf Hide)
appContext.MainForm.Invoke(HideDelegate)
End If
Else
frmSplash.Hide()
End If
End Sub
Public Sub Unload()
ShowLock.WaitOne()
If frmSplash.InvokeRequired Then
If frmSplash.IsHandleCreated Then
UnLdDelegate = New FormDelegate(AddressOf Unload)
appContext.MainForm.Invoke(UnLdDelegate)
End If
Else
frmSplash.Close()
End If
End Sub
Private Sub StartMessageLoop()
Application.Run(appContext)
End Sub
Private Sub ac_ThreadExit(ByVal sender As Object, ByVal e As System.EventArgs) Handles appContext.ThreadExit
appContext.MainForm.Dispose()
appContext.MainForm = Nothing
appContext.Dispose()
appContext = Nothing
frmSplash.Dispose()
frmSplash = Nothing
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
End Sub
Public Sub frm_HandleCreated()
ShowLock.Set()
End Sub