I face a problem with a multiple forms application
I have the mainForm and several mdiForms.
One of these child forms (frmDashboardManager) is open new ownedforms (frmDahboard) outside the mainForm
No I want to check if a frmDahboard is Open and if Yes to close it.
Here is what I have:
Dim fDash As New frmDashboard
fDash = isDashboardOpen(tempDash)
If fDash IsNot Nothing Then
fDash.Close() 'HERE I GET THE ERROR
End If
Private Function isDashboardOpen(ByVal dash As clsDashboard) As frmDashboard
isDashboardOpen = Nothing
Try
'search if Dashboard is already open
For Each fr As frmDashboard In Me.OwnedForms
If fr.My_Dashboard.Id = dash.Id Then
isDashboardOpen = fr
Exit For
End If
Next
Catch ex As Exception
gError.GetAppEx(ex, FORM_NAME & ".isDashboardOpen")
Finally
End Try
End Function
The error that I get is :
Object reference not set to an instance of an object.
The crazy thing is that I checked and isDashboardOpen returns actually a frmDashboard (also that's why fDash.Close() is executed).
Any ideas?
Thanks
I just found my error.
I disposed twice a user control that I have in frmDashboard.
I corrected that and everything works fine.
Thank you for your time.
Related
I have a VB.NET 4.6.1 desktop app that has been using FindWindow and FindWindowEx for over 2 years with no issue to locate a MDI child window and capture the window caption text, it has worked flawlessly until recent.
The behavior now is my app can only successfully obtain the MDI client window handle if I go back to either the parent window or MDI client and click anywhere on either window, then return to my app and the process succeeds.
I have tried adding threading sleep events, running the action continuously in a loop multiple times, calling AppActivate method using process ID (thinking I just needed to execute again), my next workaround thought is to try and send a click event to the parent window prior to my action being executed or maybe to use Enumerate all child windows of the parent, hope someone can suggest something because I am at a roadblock, been doing this for years but this one doesn't make sense to me, I have the suspicion that it is related to recent ownership of the software company and them revising this section, but I have no idea why it would interfere with these root level API methods.
Sample Code:
MDIhWnd = FindWindowEx(ParenthWnd, IntPtr.Zero, "WindowsForms10.MDICLIENT.app.0.34f5582_r7_ad1", Nothing)
'Threading.Thread.Sleep(100)
'AppActivate(proc(0).Id)
If MDIhWnd = 0 Then
Threading.Thread.Sleep(100)
'Dim hw = GetTopWindow(ParenthWnd)
For i = 0 To 500
AppActivate(proc(0).Id)
MDIhWnd = FindWindowEx(ParenthWnd, IntPtr.Zero, "WindowsForms10.MDICLIENT.app.0.34f5582_r7_ad1", Nothing)
If MDIhWnd <> 0 Then
Exit For
End If
Next
End If
The solution for me was, based on the above suggestion, to use UI Automation, I
had never worked with it before, however after looking it over I gave a go and
found that it did indeed simplify my needs to capture window text from a 3rd party application window with MDI Client Interface.
Below is a lessor version in VB.NET of the process for anyone needing to do the
same thing:
Imports System.Windows.Automation
' You will also need references to UIAutomationClient, and UIAutomationTypes
Private Sub test_ui_automation()
Dim ParenthWnd As Integer = 0
Dim _AutomationElementA As System.Windows.Automation.AutomationElement = Nothing
Dim _AutomationElementB As System.Windows.Automation.AutomationElement = Nothing
Dim _AutomationElementC As System.Windows.Automation.AutomationElement = Nothing
Dim propCondition As Condition
Try
'Parent Windows Process Stuff
ParenthWnd = FindWindow(Nothing, "Application to Find")
_AutomationElementA = AutomationElement.FromHandle(ParenthWnd)
If _AutomationElementA Is Nothing Then
NotifyIcon1.BalloonTipIcon = ToolTipIcon.Error
NotifyIcon1.BalloonTipText = "Couldn't Locate Parent Window."
NotifyIcon1.Visible = True
NotifyIcon1.ShowBalloonTip(3000)
Exit Sub
End If
' MDI Client Stuff
' I used ClassNameProperty but other conditions are available
propCondition = New PropertyCondition(AutomationElement.ClassNameProperty, "WindowsForms10.MDICLIENT.app.0.34f5582_r7_ad1", PropertyConditionFlags.IgnoreCase)
_AutomationElementB = _AutomationElementA.FindFirst(TreeScope.Element Or TreeScope.Children, propCondition)
If _AutomationElementB Is Nothing Then
NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning
NotifyIcon1.BalloonTipText = "Application warning MDIClient not Available!"
NotifyIcon1.Visible = True
NotifyIcon1.ShowBalloonTip(3000)
Exit Sub
End If
' Final Stage Stuff Locate Window Containing Class with Caption
propCondition = New PropertyCondition(AutomationElement.ClassNameProperty, "WindowsForms10.Window.8.app.0.34f5582_r7_ad1", PropertyConditionFlags.IgnoreCase)
_AutomationElementC = _AutomationElementB.FindFirst(TreeScope.Element Or TreeScope.Children, propCondition)
If _AutomationElementC Is Nothing Then
NotifyIcon1.BalloonTipIcon = ToolTipIcon.Warning
NotifyIcon1.BalloonTipText = "Automation warning, MDI Details are open."
NotifyIcon1.Visible = True
NotifyIcon1.ShowBalloonTip(3000)
Exit Sub
End If
Caption = _AutomationElementC.Current.Name
' If needed you can now parse/strip any data needed from the Caption text.
' I had other processes here but could not include in the post.
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
I have tried to find an answer to this already, but cannot find one that answers this question.
I have a Master Form which contains two panels. In the master Form I am trying to write a subroutine to handle the loading of a form into one of the panels.
One panel always contains the same form and the code which works for this is:
'Configure Toolbar Import
Dim toolbarHandler As _pnl_header = New _pnl_header()
toolbarHandler.Size = pnlHeader.Size
toolbarHandler.TopLevel = False
pnlHeader.Controls.Add(toolbarHandler)
toolbarHandler.Show()
The panel successfully shows the form _pnl_header as expected.
The second panel will change the displayed form depending on user input, so rather than having to write the above code for every eventuality i would like one Public Sub to handle them all...
I've started writing a sub along the lines of:
Public Sub LoadContentPanel(WhichForm As Form)
Try
Dim contentHandler As WhichForm = New WhichForm()
contentHandler.Size = pnlContent.Size
contentHandler.TopLevel = False
pnlContent.Controls.Add(contentHandler)
contentHandler.Show()
Catch ex As Exception
MsgBox("Unable to Handle Content Panel Change. Error: " & ex.Message, vbOKOnly + vbCritical, "Load Error")
End Try
End Sub
However this fails as 'WhichForm' is not defined - how is best to correct this? or is there a better alternative?
Thanks
Without going into what you are doing I can explain where the error comes from.
Here you declare argument variable WhichForm of type Form
Public Sub LoadContentPanel(WhichForm As Form)
. . . . .
Code is incorrect in the next declaration line. WhichForm is a variable and not a type. Hence
Dim contentHandler As WhichForm = New WhichForm()
is invalid at As WhichForm. Because after As you need a type name. If you did
Dim contentHandler As Form = New Form()
it would work.
It seems that all you need to do is remove Dim contentHandler As WhichForm... and rename argument WhichForm to contentHandler.
The situation is as follows:
We want to publish to a remote machine a locally edited file. This file could be of type Word, Excel, Powerpoint. Apparently, after the publishing to the remote machine, we would like the local document to be marked as final, in order to prevent the user from editing it again (, because the intented workflow is first downloading it from the remote server, editing the downloaded document and the publishing it back to the server).
So, there is a bunch of code like this:
Public Sub setDocFinal()
Select Case addin.HostType
Case ADXOfficeHostApp.ohaWord
Dim doc As Word.Document = Nothing
Try
doc = addin.WordApp.ActiveDocument
doc.Final = True
Catch ex As Exception
Throw New Exception(Me.addin.getLabel("cannotSaveCopy", "Cannot Save the document."))
Finally
Marshal.ReleaseComObject(doc)
End Try
Case ADXOfficeHostApp.ohaExcel
Dim doc As Excel.Workbook = Nothing
Try
doc = addin.ExcelApp.ActiveWorkbook
doc.Final = True
'doc.RefreshAll()
'doc.CalculateUntilAsyncQueriesDone()
'doc.Calculate()
Catch ex As Exception
Throw New Exception(Me.addin.getLabel("cannotSaveCopy", "Cannot Save the document."))
Finally
Marshal.ReleaseComObject(doc)
End Try
' Powerpoint case intentionally skipped as is has same format/code
End Select
End Sub
The above code works pretty well for the Word case, but when it comes to Excel, it stacks on the popup which informs the user for the publish action to the remote server:
EDIT
At that particular point, the execution freezes (or maybe gets into an infinite internal loop, because the only available buttons in debugging mode are pause and stop) at the line of setting the document as final and it never reaches the finally statement (where we release the object). It also seems like the execution tries to return control back to the excel document, but nothing more than this notification occurs :
Any idea of what is wrong in the above code, regarding the handling of Excel?
The lines in comments display some trials I have been going through, while trying to find a solution around the net.
In addition, here is also the popup's related code:
Private Sub doWork(ByVal action As String, ByVal e As System.ComponentModel.DoWorkEventArgs)
Dim myDoc As MyDocument
myDoc = DirectCast(e.Argument, MyDocument)
System.Diagnostics.Debug.WriteLine("Post in Thread")
Dim resultObject(3) As Object
resultObject(0) = True
resultObject(1) = myDoc
Dim saveDocumentResult As SaveDocumentResult = Nothing
Try
Select Case action
Case Constants.SAVE_DRAFT
saveDocumentResult = myDoc.getRequestService().saveDraft(myDoc.getSaveFile(), myDoc.getDocName(), myDoc)
Case Constants.PUBLISH
saveDocumentResult = myDoc.getRequestService().publishDocument(myDoc.getSaveFile(), myDoc.getDocName(), myDoc)
myDoc.setDocFinal() 'this line makes the call to the above code
End Select
myDoc.updateDocumentProperty(Constants.VERSION, saveDocumentResult.version)
myDoc.updateDocumentProperty(Constants.HASH, saveDocumentResult.hash)
myDoc.setSaved(True)
System.Threading.Thread.Sleep(1500)
Catch ex As Exception
resultObject(0) = False
resultObject(2) = ex.Message
Finally
e.Result = resultObject
End Try
End Sub
I am hoping you can help me here, in the past you all have been great. I have tried every variation of the kill script for killing excel from vb.net, to no avail.
First I can't post explicit code on here because it is my company's proprietary software, but I can tell you a few things. Also there are over 28,000 lines of code.
I am not using
Imports Excel = Microsoft.Office.Interop.Excel
due to the fact that we have to accommodate different variations of clients software. I am creating the new excel as an object as such
Dim XLObj As Object = CreateObject("Excel.Application")
I have seen this used on several other sites but the kill function they are using is when you save and then close it, which I'm not doing.
The error message I am getting says that "Com object that has been separated from its underlying RCW cannot be used". I'm not sure where this com object is because I have released the sheets, workbook and then the application.
Oh and I don't want to use the excel.kill() because if a client already has the excel open I don't want to kill it without saving it. I only want to kill the newly generated excel process that doesn't have a window open associated with it.
My questions are as follows
I need to be able to close the Excel application when/if the open fails. So say I am click a link and it opens the dialog box to select an Excel template to load but either the data from the database is corrupt or the sql statement is broken. The program throws and error and then Excel should close in the Task Manager. Unfortunately it doesn't close hence the problem.
is there a way to close only the newly created process id? I have tried to use the directions here but it doesn't work either. When I do that it gives me a different error "Value cannot be null Parameter name: o". The line that is throwing the error is on (from the link)
Marshal.FinalReleaseComObject(tempVar)
I only tried this because we are using the With on the XLObj. The With is in reference to the workbook itself so shouldn't it be released when I close the workbook? And being as I'm causing it to error on purpose at the moment it shouldn't reach the With statement anyway.
Is there a way to tell which com object is not closing?
Things I have tried:
This releaseObject that I found on the internet. (don't ask me where I've been through about 75 pages)
Private Sub releaseObject(ByRef obj As Object)
Try
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
If obj Is Nothing Then
Else
obj = Nothing
End If
Catch ex As Exception
If obj Is Nothing Then
Else
obj = Nothing
End If
Finally
GC.Collect()
GC.WaitForPendingFinalizers()
End Try
End Sub
This is used in conjunction with this function (which was pieced together from the many sites I have been on)
Public Sub CloseExcel(ByRef WorkBook As Object, ByRef Application As Object)
Dim xLSheet As Object = WorkBook.Sheets
For Each xLSheet In WorkBook.Sheets
If xLSheet IsNot Nothing Then
releaseObject(xLSheet)
End If
If xLSheet IsNot Nothing Then
Kill(xLSheet)
End If
Next
If WorkBook IsNot Nothing Then
WorkBook.Close(False)
End If
If WorkBook IsNot Nothing Then
Kill(WorkBook)
End If
releaseObject(WorkBook)
If Application IsNot Nothing Then
Application.Quit()
End If
If Application IsNot Nothing Then
Kill(Application)
End If
releaseObject(Application)
GC.Collect()
GC.WaitForPendingFinalizers()
Application.Quit()
End Sub
and because it is also referenced the Kill function
Public Sub Kill(ByRef obj As Object)
Try
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(obj)
Catch ex As Exception
MessageBox.Show("moduleExcel.Kill " & ex.Message)
Finally
obj = Nothing
End Try
End Sub
any help would be greatly appreciated.
Ok so for those of you having this exact same issue. I do have a solution for you. Yes the above code does work but for a few minor adjustments.
you need to take out all the code in the CloseExcel sub and place it EXACTLY where you want it to close. So if you want it to close if the program errors out, put after the catch statement. You cannot call a Sub and pass in your objects and expect it to kill the process.
you need a few bits above the opening of the new Excel process. and they are as follows.
'declare process for excel
Dim XLProc As Process
'loads the financials excel bookmarks
'this will be where you declare your new excel opbject
Dim XLObj As Object = CreateObject("Excel.Application")
'get window handle
Dim xlHWND As Integer = XLObj.hwnd
Dim ProcIDXL As Integer = 0
'get the process ID
GetWindowThreadProcessId(xlHWND, ProcIDXL)
XLProc = Process.GetProcessById(ProcIDXL)
and of course you will need the GetWindowThreadProcessId which I got from the link I included in the original question. I am posting it here so you don't have to search for it.
<System.Runtime.InteropServices.DllImport("user32.dll", SetLastError:=True)> _
Private Function GetWindowThreadProcessId(ByVal hWnd As IntPtr, ByRef lpdwProcessId As Integer) As Integer
End Function
This code will only close the single process you have it associated with, it will not close other open Excel files. Our clients sometimes will have multiple files open and we don't want to close them without telling them. This KILLS the Excel process that was created at run time when the system Errors out.
My startup form is a modal security form which works fine. But, if the user "logs out", the security form must be displayed again as a modal dialog. This last step is where everything goes wrong. It shows the form, in front of my other forms, but it's not modal...
First, I call a method that's written in a module, because I have to be able to call this method from every form I want.
Public Sub CallWaiterKey()
Dim oForm As frmWaiterKey = New frmWaiterKey()
Try
If mWaiterKey.Length > 0 And mWaiterKeyType.Length > 0 Then
If Convert.ToInt32(mWaiterKey) > 0 And Convert.ToInt32(mWaiterKeyType) = 2 Then
oForm.TypeOfKey = 2
ElseIf Convert.ToInt32(mWaiterKey) > 0 And Convert.ToInt32(mWaiterKeyType) = 1 Then
oForm.TypeOfKey = 1
End If
'here it goes wrong
oForm.ShowDialog()
End If
Catch ex As Exception
MsgBox(ex)
End Try
End Sub
When I call oForm.ShowDialog() (that's the frmWaiterKey), it comes up but isn't modal.
I can still click the buttons that are placed on frmMenu, the form from which I called CallWaiterKey().
Am I doing something wrong here?
Or should I make the call in an other way?
(My VB sucks so ignore syntax errors)
To achieve what you are asking, specify the hosting form.
Public Sub CallWaiterKey(ownerForm as Form)
Dim oForm As frmWaiterKey = New frmWaiterKey()
' ....
'here it goes wrong
oForm.ShowDialog(ownerForm)
' ....
End Sub
I don't use ShowDialog; but I believe that you need to specify the window owner to enforce the modality. If I'm wrong here, others will correct me.
oForm.ShowDialog(me)
** HOLD ON ** I will alter this in a second, I just recalled that you're calling from a module, me doesn't evaluate in a basic module.
Here is a MSDN reference