InvalidCastException when trying to automate WebBrowser C# - c#

Basically I'm automating a site that has an iframe and doesn't fire DocumentComplete. I'm running a Form in an independant thread, which contains a WebBrowser. I'm trying to click search within the WebBrowser, it works normally but I'm trying to separate the Browser from the form thread-wise so I can make my code overall more procedural and implement an AutoResetEvent.
Here's the method for Clicking search:
public void ClickSearch()
{
if (search == null)
{
HtmlElementCollection links = Document.Window.Frames[0].Frames[1].Document.Links;
search = links.Cast<HtmlElement>()
.Where(x => x != null)
.FirstOrDefault(x => x.InnerText == "Search");
}
Doc.InvokeScript("htmlbSL", new object[]{search, 2, search.Id+":SEARCH", '0'});
}
Here's the code that calls it:
var evt = new AutoResetEvent(false);
HtmlElementEventHandler handler = null;
handler = new HtmlElementEventHandler(
delegate(object sender, HtmlElementEventArgs ev)
{
ev.BubbleEvent = false;
smsBrowser.Doc.Focusing -= handler;
worklist = new Worklist();
worklist.Load(smsBrowser.GetWorklistCsv());
SQLiteDatabase.InsertWorklist(worklist);
SQLiteDatabase.Commit();
SQLiteDatabase.FillWorklistGrid();
evt.Set();
});
smsBrowser.Doc.Focusing += handler;
Task.Factory.StartNew(() => smsBrowser.ClickSearch());
evt.WaitOne();
The line:
HtmlElementCollection links = Document.Window.Frames[0].Frames[1].Document.Links;
is giving me an InvalidCastException. Also, any advice as to how to best do this would be much appreciated, I want to avoid Application.DoEvents(), I also want it to be more procedure (I have many events attaching and detaching). The calling method is also running within an event handler, I'd like to put them within the same method with implemented waits to clean up my code.
I followed this guide earlier: http://www.albahari.com/threading/part2.aspx.
Here are the exception details, though they don't provide much help.
An exception of type 'System.InvalidCastException' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Specified cast is not valid.

Related

OpenSilver and Dispatcher.CheckAccess

I am working on porting an old Silverlight application over to OpenSilver. Throughout the Silverlight code there are if( <control>.CheckAccess())... to make sure to be on the correct thread. Is my impression this check is no longer needed in OpenSilver? In other words, the following Silverlight code can be transformed into the following:
Yes, I know that callback-based async methods have been replaced with awaitable tasks. I am going to ask some questions about that conversion in my next question, here. This question is exclusively about the fate of the Dispatcher.CheckAccess
Silverlight:
private void GetNextImage()
{
var cmc = ServiceFactories.CreateCartManager();
cmc.getSlideImageCompleted += (s, e) =>
{
if (imageGrid.CheckAccess())
{
cmc_getSlideImageCompleted(s, e);
}
else
{
var args = new object[] { s, e };
imageGrid.Dispatcher.BeginInvoke(new getSlideImageCompletedDelegate(cmc_getSlideImageCompleted),
args);
}
};
var lastTime = SystemSettings.GetInstance().SlideShowData.LastImageTime;
cmc.getSlideImageAsync(string.IsNullOrEmpty(lastTime) ? null : lastTime);
}
to OpenSilver:
private void GetNextImage()
{
var cmc = ServiceFactories.CreateCartManager();
cmc.getSlideImageCompleted += (s, e) =>
{
cmc_getSlideImageCompleted(s, e);
};
var lastTime = SystemSettings.GetInstance().SlideShowData.LastImageTime;
cmc.getSlideImageAsync(string.IsNullOrEmpty(lastTime) ? null : lastTime);
}
There is no need to use Dispatcher.CheckAccess since OpenSilver is currently single threaded (it uses mono.wasm runtime which doesn't support threads yet).
However, OpenSilver keeps compatibility with Silverlight, so if you have an old Silverlight code which does the check you can just keep it (it will always return true when running in Browser).

How to attach an event handler to an EnvDTE.Window

I can open a new browser window in Visual Studio and open an url in it with the following snippet of code. The last line sets the title of this window to "newTitle". However, once the window is finished loading, the caption is updated to reflect the content of the URL. I like to attach an event handler to the window or to the embedded object, but I do not know how to do that.
var dte2 = Package.GetGlobalService(typeof(SDTE)) as DTE2;
var window = dte2.ItemOperations.Navigate(url, EnvDTE.vsNavigateOptions.vsNavigateOptionsNewWindow);
window.Caption = "newTitle";
What have I tried:
Check if the object inside the window is of type vsWindowKindWebBrowser and cast that object to WebBrowser. But window.Object cannot be cast to WebBrowser; wb1 is null.
var wb1 = window.Object as System.Windows.Forms.WebBrowser;
wb1.DocumentCompleted += (o, e) => { window.Caption = "newTitle"; };
Used the approach listed here: How to Get List of Open Web Pages in Visual Studio Using EnvDTE. wb2 is not null but the object inside is disposed, thus also of no use.
var wb2 = new VisualStudioWebBrowser(window.Object);
wb2.DocumentCompleted += (o, e) => { window.Caption = "newTitle"; };
Asked this question on MSDN, see here, but no useful answer.
Question: How to attach an event handler to an EnvDTE.Window such that I can listen for the event that the page is finished loading?

How to Capture 'Send' button event for Outlook using UI Automation in C#?

I want to capture 'Send' button event of outlook using UI Automation.
Right now i am able to get 'Focus Change Event' like whenever iam minimizing or maximizing the WINWORD window the the event is raised instead of that i want to get the event on Send button click.
private void SendButtonInvoke()
{
Process[] processes = Process.GetProcessesByName("WINWORD");
AutomationElement aeOutLook = null;
foreach (var item in processes)
{
aeOutLook = AutomationElement.FromHandle(item.MainWindowHandle);
}
//AutomationElement outlookelm = AutomationElement.FromHandle(processName.MainWindowHandle);
AutomationElement buttonAddInstance = aeOutLook.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Send"));
if (buttonAddInstance == null)
{
MessageBox.Show("Add button instance not found");
}
else
{
AutomationPropertyChangedEventHandler ButtonEvent =
new AutomationPropertyChangedEventHandler(ButtonChecked_EventHandler);
//Attaching the EventHandler
Automation.AddAutomationPropertyChangedEventHandler(buttonAddInstance, TreeScope.Children,
ButtonEvent, AutomationElement.NameProperty);
}
}
private void ButtonChecked_EventHandler(object sender, AutomationEventArgs e)
{
AutomationElement ar = sender as AutomationElement;
MessageBox.Show("Button Clicked Sucessfully.");
}
You have to specifiy the EventHandler for the involved UIA Pattern. (For your case it's likely to be the InvokePattern):
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, AutomationElement buttonAddInstance ,TreeScope.Element, new AutomationEventHandler(OnStartInvoke));
private static void OnStartInvoke(object src, AutomationEventArgs e)
{
//logic
}
I wrote and tested the code below and it seems to work for me.
private void AddEmailSendEvent()
{
// Find the new email window
PropertyCondition newEmailWindowCondition = new PropertyCondition(AutomationElement.NameProperty, "Untitled - Message (HTML) ");
AutomationElement NewEmailWindow = AutomationElement.RootElement.FindFirst(TreeScope.Children, newEmailWindowCondition);
// Find the Send Button
PropertyCondition sendEmailButtonCondition = new PropertyCondition(AutomationElement.NameProperty, "Send");
AutomationElement sendButton = NewEmailWindow.FindFirst(TreeScope.Descendants, sendEmailButtonCondition);
// If supported, add the invoke event
if (sendButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, sendButton, TreeScope.Element, handler);
}
private void handler(object sender, AutomationEventArgs e)
{
// Do whatever is needed, for testing this just adds a message to my forms Main UI
AddMessage("Invoke event occured");
}
I should note that I'm using the .Net 4.0 automation libs. I've found the older ones don't always work the way I want them. I also tested this with Outlook 2013, and both outlook and the new email message were already open when I tested this. It doesn't handle waiting for them to appear.
Just so your aware, these events don't always work for all controls. Some custom controls are made in such a way the invoke events are not reported to the UI in a way the event can register. With that said, from my testing you should be able to use this method on the send button.
Invoking vs mouse clicks: Just to add a little more detail, the standard control causes the invoke event to fire when a user clicks it. "Invoke" is just the standard event fired on clickable controls. The only time a click wouldn't fire the same invoke is if the developer decided to intercept the click somehow and redirect it elsewhere. I've seen this a lot when people build there own custom controls.
If your not sure about whether a control using/firing the invoke event or not you can get use the Accessible Event Watcher to watch a control as you click it. You can get more information on the tool here: https://msdn.microsoft.com/en-us/library/windows/desktop/dd317979(v=vs.85).aspx

Serial IO Async Issues

I've got serial data coming in to my application and by definition, it's async, so I'm running into troubles when trying to update a label to show what the incoming data is. Every now and then, I get an error on the lblRx.AsyncUpdate line, telling me the object is in use elsewhere.
At present, I use the following code;
private void IODataReceived(object sender, IODataEventArgs e)
{
lblRx.AsyncUpdate(() => lblRx.Text = string.Format("{0}:\t{1}", e.Timestamp, e.Data));
SetBackColors(false, eIODirection.In);
}
public static void AsyncUpdate(this Control ctrl, ActionCallback action)
{
if (ctrl != null)
{
if (!ctrl.IsHandleCreated && ctrl.IsDisposed)
ctrl.CreateControl(); // MSDN says CreateControl() is preferred over CreateHandle().
if (!ctrl.IsDisposed)
AsyncInvoke(ctrl, action);
}
}
The AsyncUpdate method isn't an issue (AFAIK...works well in other situations).
I think I need to put a lock on the control before calling AsyncUpdate. Or is there a better way to handle this situation?

problem with RX and web service collection loading wp7

I'm beginner with C# and wp7 platform and I have some problem with good idea to get request from web service.
I made webservice in PHP (nusoap - WSDL) and everything is working fine in "normal" using.
Now I have ObservableCollection saved in IsolatedStorage with I load when Page is open (List of watched stacks exchange). Then I want to refresh data for every item from web service.
I don't know whether this is a good idea.
Code:
private GPWWebservicePortTypeClient client = new GPWWebservicePortTypeClient();
private ObservableCollection<WebServiceClass.ItemGetValues> StoredStock =
new ObservableCollection<WebServiceClass.ItemGetValues>();
public const string _fileName = "listaObserwowanych.xml";
public Page()
{
InitializeComponent();
DataContext = App.ViewModel;
this.Loaded += new RoutedEventHandler(Page_Loaded);
client.GetLastValueCompleted +=
new EventHandler<GetLastValueCompletedEventArgs>(client_GetLastValueCompleted);
foreach (var itemGetValuese in App.ViewModel.Items)
{
client.GetLastValueAsync(itemGetValuese.name);
}
var o =
Observable.FromEvent<GetLastValueCompletedEventArgs(client,"GetLastValueCompleted")
.Subscribe(setList);
}
void client_GetLastValueCompleted(object sender, GetLastValueCompletedEventArgs e)
{
if (e.Error != null)
{
MessageBox.Show(Convert.ToString(e.Error));
}
else
{
ObservableCollection<WebServiceClass.ItemGetValues> ListValues =
(ObservableCollection<WebServiceClass.ItemGetValues>)
JsonConvert.DeserializeObject(e.Result,
typeof(ObservableCollection<WebServiceClass.ItemGetValues>));
StoredStock.Add(ListValues[0]);
}
}
private void setList(IEvent<GetLastValueCompletedEventArgs> ex)
{
List.ItemsSource = StoredStock;
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
App.ViewModel.LoadData();
List.ItemsSource = App.ViewModel.Items;
}
Like u see I use RX to call method client_GetLastValueCompleted add store result to auxiliary variable (StoredStock). Then refresh List in setList method, but that method is client_GetLastValueCompleted what is not soo good idea, becouse I need to run that method only when all of runned GetLastValueAsync in foreach is completed.
Second problem: becouse of async web service method StoredStock sometime have different order than App.ViewModel.Items .
Any good idea how to do that in right way?
Best regards,
Lukas
You're really mixing up a number of ways to call web services and Rx. You really need to decide on a single way and stick to it.
If you're going to use Rx, then you'll have something like this:
public Page()
{
InitializeComponent();
DataContext = App.ViewModel;
this.Loaded += new RoutedEventHandler(Page_Loaded);
}
void Page_Loaded(object sender, RoutedEventArgs e)
{
App.ViewModel.LoadData();
var storedStock =
new ObservableCollection<WebServiceClass.ItemGetValues>();
List.ItemsSource = storedStock;
var values =
Observable.Using<WebServiceClass.ItemGetValues, GPWWebservicePortTypeClient>
(() => new GPWWebservicePortTypeClient(), ws =>
{
var clientGetLastValue = Observable
.FromAsyncPattern<string, GetLastValueResponse>
(ws.BeginGetLastValue, ws.EndGetLastValue);
Func<string, WebServiceClass.ItemGetValues> deserializeFirst = r =>
((List<WebServiceClass.ItemGetValues>)JsonConvert
.DeserializeObject(r,
typeof(List<WebServiceClass.ItemGetValues>)))
.First();
return
from item in App.ViewModel.Items
from e in clientGetLastValue(item)
select deserializeFirst(e.Result);
});
values.Subscribe(storedStock.Add);
}
You'll have to get the right method call names for your web service client, but the code should roughly be right. Let me know how you go.
I corrected the code above. Should have returned the query inside the Using call rather than assign it to values.
I corrected the call to FromAsyncPattern to use the correct method names and return type from the actual web service reference class sent via email.
It should look like this:
Observable.FromAsyncPattern<string, GetLastValueResponse>
(ws.BeginGetLastValue, ws.EndGetLastValue);
If you're a beginner with C#, try to avoid RX for the time being. It is a cool technology, but if you use it without clear understanding of what is going on, it will bring more problems than solve.
Use a simple event, and when each async item arrives, locate and update the correspondent one in the stored list.

Categories

Resources