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
Related
I leave it to you because I can't find a solution to my problem : /
Let me explain, when I press a button I display a panel containing other buttons, at the click of one of the buttons on the panel it should launch a method that will convert the selected files to pdf. As soon as the user has clicked on one of the buttons and confirmed the choice of file, I make my panel invisible and I then launch the conversion method.
The problem is that my panel disappears let's say by half (not entirely) because it launches the conversion method as quickly. I told myself that I was going to go through a secondary thread, however I cannot modify graphic elements on the second thread.
There is my code :
Private Sub PBFolder_Click(sender As Object, e As EventArgs) Handles PBFolder.Click
Try
Insert2Db("Debut de la fonction BTransforme_Click " + Environment.UserName.ToString, 1, 0, "ConvertFiles2PDF")
'Log("Debut de la fonction BTransforme_Click")
Dim OFD As New FolderBrowserDialog
If OFD.ShowDialog = Windows.Forms.DialogResult.OK Then
PanFileFolder.Visible = False
ConvertFileFolder(False, OFD.SelectedPath.ToString)
End If
Catch ex As Exception
'Log("Error " + ex.Message)
Insert2Db("Error " + ex.Message + "User : " + Environment.UserName.ToString, 0, 3, "ConvertFiles2PDF")
Finally
Insert2Db("function BTransforme_Click Terminé " + Environment.UserName.ToString, 1, 0, "ConvertFiles2PDF")
'Log("function BTransforme_Click Terminé")
End Try
LAppOne.Visible = True
GifLoad.Visible = False
Button1.Enabled = True
BLog.Enabled = True
End Sub
As you can see I hide my panel thanks to line: PanFileFolder.Visible = False then I launch my conversion method convertFileFolder (False, OFD.SelectedPath.ToString)
I have put 2 images to illustrate my problem.
the 1st image shows you the panel that appears on click:
the second image shows you the problem that this causes me to choose the folder:
When it has finished converting the files, the panel disappears correctly at this time.
Do you have an idea to solve this problem thank you in advance ;)
I told myself that I was going to go through a secondary thread,
however I cannot modify graphic elements on the second thread.
That is exactly what will fix your problem; ConvertFileFolder() needs to be running in a different thread so that GUI can refresh itself and be responsive to user interact. You can update the GUI from that secondary thread using Invoke() calls.
Here I've added Async to the Button click handler, then we Await the ConvertFileFolder() FUNCTION, which now returns a Task:
Private Async Sub PBFolder_Click(sender As Object, e As EventArgs) Handles PBFolder.Click
' ... other code ...
Using OFD As New FolderBrowserDialog
If OFD.ShowDialog = DialogResult.OK Then
PanFileFolder.Visible = False
Await ConvertFileFolder(False, OFD.SelectedPath.ToString)
End If
End Using
' ... other code ...
End Sub
Public Function ConvertFileFolder(ByVal someFlag As Boolean, ByVal someString As String) As Task
Return Task.Run(Sub()
' ... long running code in here ...
For i As Integer = 1 To 10
System.Threading.Thread.Sleep(1000) ' some "work"
' whenever you need to update the GUI, use Invoke()
Dim value As String = i.ToString
Me.Invoke(Sub()
Label1.Text = value
End Sub)
Next
End Sub)
End Function
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'm using background worker to manipulate some of the elements on my WebBrowser using vb.net.
For example the background worker RFIDReader will check if I'm on a specific link.
ElseIf TerminalBrowser.Url.ToString.Contains(baseUrl + "box-office/ticket-verification") And rf_code <> "" Then
' Insert RF Code in "rf_code" hidden text field
Me.Invoke(Sub() Me.TerminalBrowser.Document.GetElementById("rf_code").SetAttribute("value", rf_code.ToLower))
End If
What happens here is, if I tap my RFID card. It will include that corresponding value to my rf_code element in my browser.
Now what I want to happen is, I want to check if the container itself (synchronize-rfid) does exist (Since it's a pop up). See image for reference.
Here's our code for that.
If Me.TerminalBrowser.Document.GetElementById("synchronize-rfid") IsNot Nothing Then
' Code here
end if
Reference: https://stackoverflow.com/a/2022120/1699388
The problem is, BackgroundWorker does not really interacts with UI as per the code above.
Is there any method for me to determine if that element exist using background worker?
I think I've done this and it does not work though.
Dim synchronize_rfid = Me.Invoke(Sub() Me.TerminalBrowser.Document.GetElementById("synchronize-rfid"))
If synchronize_rfid IsNot Nothing Then
' Code here
end if
Try declaring the variable first, then set it during the invocation (which will set it from the main thread).
The Sub() lambda behaves like a normal Sub()-End Sub method, which means you can do the same things in there as if you had a separate method.
This works for me:
Dim synchronize_rfid As HtmlElement
If Me.InvokeRequired = True Then
Me.Invoke(Sub() synchronize_rfid = Me.TerminalBrowser.Document.GetElementById("synchronize-rfid"))
Else
synchronize_rfid = Me.TerminalBrowser.Document.GetElementById("synchronize-rfid")
End If
If synchronize_rfid IsNot Nothing Then
' Code here
End If
I have written in vb.net for progress bar. I am thinking of there is a better way than this.
Here is what my code is :
Private Function ImportDataFiles(ByRef pobjDataLoadDTO As DataLoadDTO) As Boolean
Try
lblStatus.Visible = True
lblStatus.Text = ""
myProgressBar.Visible = True
myProgressBar.Value = 0
For Each drRow As ImportData.TRow In pobjDataLoadDTO.FileInfo.Select("categ_code = 'abc'")
If pobjDataLoadDTO.FileTimes.ContainsKey(drRow.KEY_CODE) AndAlso _
pobjDataLoadDTO.FileTimes(drRow.KEY_CODE) > pobjDataLoadDTO.UploadTimes(drRow.KEY_CODE) Then
pobjDataLoadDTO.DestinationTablename = drRow.KEY_CODE
If mobjDataLoadBO.ImportDataFiles(pobjDataLoadDTO) Then
drRow.DATA_TXT = mobjCommonBO.ONow.ToString
End If
End If
lblStatus.Text = drRow.KEY_CODE.Trim & "is loading...."
lblStatus.Refresh()
myProgressBar.PerformStep()
lblStatus.Refresh()
Next
Return True
Catch ex As Exception
Return False
End Try
End Function
Right now It is working, But I want to use more efficient way, like using Backgroundworkerprocess...etc., Any Ideas on this one?
Since your function runs in main thread, I assume your application is freezing and not very smooth while upload in progress.
1 - Drop Backgroundworker control on the form
2 - set "reportProgress" property of the worker to "True"
3 - Move your loop code into "DoWork" event of the worker control. And call worker.RunWorkerAsync. You can pass needed arguments to it
4- the code that refreshes progress bar move into "ProgressChange" event of the worker. This is important as you can't call control from worker thread. and ProgressChange is running in the main thread. You can also delete "Refresh" method call. That will not be needed anymore. Every time you want to refresh the progress bar call "ReportProgress" method of the worker
5-Use "RunWorkerCompleted" worker event, to do your clean up, and hide your progress bar
Its also might be a good idea to check if worker is already working before initiating, like
If worker.IsBusy Then
If worker.CancellationPending = False Then
worker.CancelAsync()
End If
Exit Sub
End If
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.