I'm experimenting with Watin in Visual Studio 2012 (C#), but I cannot seem to get anything to work. Currently I have two text boxes in an aspx page (txtbox1 and txtbox2). I am trying to have Watin automatically input numbers into these two text boxes and then click on the "Calculate" button which will add the two numbers together. I installed the Watin framework and added the Watin.Core dll as a reference.
Here is my code for so far in my .aspx page:
using WatiN.Core;
[STAThread]
protected void Page_Load(object sender, EventArgs e)
{
IE ie = new IE("http://localhost:5243/Addition.aspx");
ie.TextField(Find.ByName("txtbox1")).TypeText("1");
ie.TextField(Find.ByName("txtbox2")).TypeText("2");
ie.Button(Find.ByValue("Calculate")).Click();
}
I keep getting the error message stating:"The CurrentThread needs to have it's ApartmentState set to ApartmentState.STA to be able to automate Internet Explorer". Do you know what can cause this?
Thanks for any help in advance!
First of all, you're trying to automate Internet Explorer from the server-side ASP.NET code. This is generally a bad idea. This article describes the implications of doing this with Office, the same concerns apply to Internet Explorer.
That said, to succeed with what you're trying to do, you'd need to run an STA thread on the server side, and run your Watin code inside that thread. Placing [STAThread] on your ASP.NET Page_Load handler won't automagically make this happen.
Here is how it can be done, but again, doing so on the server is not recommended:
protected void Page_Load(object sender, EventArgs e)
{
RunOnStaThread<object>(() =>
{
IE ie = new IE("http://localhost:5243/Addition.aspx");
ie.TextField(Find.ByName("txtbox1")).TypeText("1");
ie.TextField(Find.ByName("txtbox2")).TypeText("2");
ie.Button(Find.ByValue("Calculate")).Click();
return null;
});
}
static T RunOnStaThread<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
var thread = new Thread(() =>
{
try
{
tcs.SetResult(func());
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
try
{
return tcs.Task.Result;
}
finally
{
thread.Join();
}
}
Related
I am simply trying to open the Windows RDP application through a button click event in my C# application, but what I get is 4 or 5 instances of RDP on a single click. I was wondering if this is a result of having the code in a button click event. I've researched Mutex, but it doesn't seem to be what I'm looking for in this particular case. Any ideas?
private void btnRemote_Click(object sender, EventArgs e)
{
string rdcSupport = "C:\\Windows\\System32\\mstsc.exe";
try
{
procRDC.StartInfo.FileName = rdcSupport;
procRDC.Start();
procRDC.WaitForInputIdle();
SendKeys.Send("support_server1");
SendKeys.Send("{ENTER}");
}
catch
{
Console.WriteLine("Failed to open...");
}
}
May be SendKeys doing some mess. I would recomend using MSTSC parameters:
try
{
Process procRDC = Process.Start(rdcSupport, "/v:support_server1");
}
catch
{
Console.WriteLine("Failed to open...");
}
You don't show how you create/initialize procRDP.
See MSTSC help: https://technet.microsoft.com/en-us/library/cc753907(v=ws.11).aspx
Hi i have a simple webbrowser running in the ui thread and i have a webbrowser.Document.GeteEmetbyId.Innertext
on another thread running in a while loop, but it gives me a error, so i tried using
this.Invoke((MethodInvoker)delegate {
webbrowser.Document.GeteEmetbyId.Innertext;
});
,but that gave me an error too, my question is, is there a simple way to control a webbrowser from another thread.
I tried looking for the answer from google, but i couldn't find anything that works for me.
My Current Code:
private void test_Click(object sender, EventArgs e)
{
th = new Thread(fncTh) { IsBackground = true };
th.Start();
}
void fncTh()
{
while (true)
{
string tehe = wb.Document.GetElementById("txtTehtav").InnerText;
txtVastus.Text = tehe();
}
}
I created a WPF application and then converted it into a DLL by removing the app.xaml and setting the build to Class Library. I'm using a C# Windows Forms to test the DLL. The WPF DLL is preloaded so it can later on be called to show and display instantly without having to wait for a load. What I am trying to accomplish is to call the Show(ShowWPFApp) and have the code wait until a boolean is flipped by calling WPFAppResponse (this action is passed in via the initial load). The way I have it right now causes the UI to freeze up. Any idea on how i can get it to wait without the UI freezing up?
Windows Form calling WPF DLL
namespace WindowsFormsDLLTest
{
public partial class Form1 : Form
{
WPFDLL.LoadWPFApp wpfApp = null;
public Form1()
{
InitializeComponent();
}
private void btnLoadWPFApp_Click(object sender, EventArgs e)
{
wpfApp = new WPFDLL.LoadWPFApp();
try
{
wpfApp.Load();
}
catch (Exception ex)
{
}
}
private void btnShowWPFApp_Click(object sender, EventArgs e)
{
try
{
string result = null;
result = wpfApp.ShowWPFApp("John Doe");
}
catch (Exception ex)
{
}
}
}
}
WPF DLL Application
namespace WPFDLL
{
public class LoadWPFApp
{
private Application application = null;
private MainWindow mainWindow = null;
private bool waitOnShowWindow {get; set;}
private string returnResults = null;
public void Load()
{
StartLoadingWPFApp();
}
[STAThread]
private void StartLoadingWPFApp()
{
application = new Application();
SplashScreenWindow splashWindow = new SplashScreenWindow();
splashWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
splashWindow.Show();
try
{
mainWindow = new MainWindow(WPFAppResponse);
mainWindow.WindowStartupLocation = WindowStartupLocation.CenterScreen;
splashWindow.Close();
application.Run();
}
catch (Exception ex)
{
splashWindow.Close();
MessageBox.Show("Error starting application:" + Environment.NewLine + ex.ToString(), "WPF App Error", MessageBoxButton.OK, MessageBoxImage.Error);
mainWindow = null;
}
}
public string ShowWPFApp(string person)
{
returnResults = null;
mainWindow.LoadPerson(person);
mainWindow.Show();
while(waitOnShowWindow)
{
//Code waits until bool is set to false
}
return returnResults;
}
public void WPFAppResponse(string person)
{
returnResults = person;
waitOnShowWindow = false;
mainWindow.Hide();
}
}
}
Launching a WPF app from Windows forms is messy. You have stumbled onto a rather complex threading problem. The general recommendation is to instead create that WPF application as a WPF control library. However, I see that this may not resolve the slow loading issue that you have, which is why you made the lightweight WinForms wrapper app.
The problem is your loop:
while(waitOnShowWindow)
{
//Code waits until bool is set to false
}
That loop is running on the UI thread, blocking it from processing windows messages. (If that concept is new to you, go look it up as it is important for Windows UI stuff.) For the UI to respond, it must be running a Windows message loop. I see two solutions:
Create your own message loop.
Return immediately, then get the result later.
Solution 1:
To create your own message loop, you need to use something like Dispatcher.Run() or Dispatcher.PushFrame(). Try this and see if it works:
public string ShowWPFApp(string person)
{
returnResults = null;
mainWindow.LoadPerson(person);
mainWindow.Show();
Dispatcher.CurrentDispatcher.Run();
// do whatever to get the results
return returnResults;
}
If that doesn't work, you might need to use PushFrame() instead. Here are some more in depth articles on that topic in case Dispatcher.Run() doesn't work.
http://www.codeproject.com/Articles/152137/DispatcherFrame-Look-in-Depth
http://reedcopsey.com/2011/11/28/launching-a-wpf-window-in-a-separate-thread-part-1/
Solution 2:
public void ShowWPFApp(string person)
{
returnResults = null;
mainWindow.LoadPerson(person);
mainWindow.Show();
}
Now, the call will not block the UI thread and the WPF window will appear. The windows forms application is running a message loop, so the WPF can now run. But how do you get the result!? Since it is now running asynchronously, you will have to find some other way to get the return value back. I think an event on the MainWindow class would be the easiest way.
Also: That [STAThread] attribute isn't doing anything. [STAThread] only has meaning on the entry point of the app. Fortunately, your Windows forms app already puts [STAThread] on the Main() method, so your thread is an STA thread.
A work around could be using
await Task.Delay(1000);
inside your while loop. This might delay every run of your while loop and the UI will not freeze up. Am not sure if this would work for your case. Try and let me know. Hope this helps.
You will need to give execution back to the UI thread's event loop so that the UI doesn't freeze up. In a forms app, you can do this as follows:
while(waitOnShowWindow)
{
//Code waits until bool is set to false
System.Windows.Forms.Application.DoEvents();
}
Edit:
As Pointed out by Eric, there are potential problems with using DoEvents(), so don't go wild with it. See How to use DoEvents() without being "evil"?.
In a test app like this, it allows the code to work. However, a better solution would be to re-structure the application so that the call is unnecessary, using multi-threading if needed.
I am developing a Windows Forms application that access a WCF service. I ran into a great problem that I can't predict the reason of it. Even the Visual Studio debugger not showing any exception in the Output view. The scenario is like this, I have a custom user control that has a linkLabel on it. Whenever the link label is clicked, a form is opened and a class object is passed to it. The class definition of this object resides on WCF service on a remote server. Now the problem is that when I click the linkLabel, the form opens perfectly loading each of its component according to the class object passed to it. But when I close this form and click that linkLabel again, the form opens but immediately freezes after loading some elements. I tried many code variations. Edited many part of code that I think can affect. But none of them showed the difference. Since, I don't know where actually is the code has error, I am posting the linkLabel click code and functions that are called after it is clicked.
private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
Enabled = false;
string temp = Title.Text;
Title.Text = "Opening...";
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ThreadStart(openTopic));
t.Start();
Title.Text = temp;
Enabled = true;
}
void createTopicWindow()
{
TopicViewer t = new TopicViewer(t);
Invoke(new Action(() => t.Show()));
}
private void openTopic()
{
Invoke(new Action(() => createTopicWindow()));
}
The above is the edited code, since I was getting Cross thread exception before.
Following is the code of constructor of the form that is called when clicked the linkLabel:
try
{
InitializeComponent();
this.t = topic;
if (IsHandleCreated == false)
CreateHandle();
System.Threading.Thread th = new System.Threading.Thread(new System.Threading.ThreadStart(loadTopic));
th.Start();
Common.openedTopics.Add(this);
AddComment addComment1 = new AddComment();
addComment1.Topic = t;
addComment1.Dock = DockStyle.Fill;
panel5.Controls.Add(addComment1);
backgroundWorker1.RunWorkerAsync();
}
catch (Exception)
{ }
void loadTopic()
{
Invoke(new Action(()=>tHead = new TopicHeader()));
Global.SetControlPropertyThreadSafe(tHead,"Topic", t);
Global.SetControlPropertyThreadSafe(tHead,"Dock", DockStyle.Fill);
Invoke(new Action(()=>panel1.Controls.Add(tHead)));
Global.SetControlPropertyThreadSafe(this,"Text", t.Title + " - Topic Viewer");
if (t.Description.Trim().Length > 0)
{
Global.SetControlPropertyThreadSafe(webBrowser1, "DocumentText", t.Description);
}
else
{
Invoke(new Action(() => tabControl1.TabPages[0].Dispose()));
}
Global.SetControlPropertyThreadSafe(tabPage2, "Text", "Comments (" + client.getComCount(t.TopicID) + ") ");
}
TopicHeader is another small user control.
Please anyone tell me the solution to this?
If you are using .Net 4.5, then using async/await would be easiest solution. That way, you don't need any Invokes
async private void Form1_Load(object sender, EventArgs e)
{
string s = await Task<string>.Factory.StartNew(LongRunningTask,
TaskCreationOptions.LongRunning);
this.Text = s;
}
string LongRunningTask()
{
Thread.Sleep(10000);
return "------";
}
I can't give a direct answer to you question, but this may give a hold on.
public void Form_Load()
{
// do some stuff on the gui-thread
// i need to do something that takes a long time:
ThreadPool.QueueUserWorkItem((state) =>
{
// i'll execute it on the ThreadPool
// Long running code....
// update results in mainform on gui thread.
Invoke(new Action( delegate
{
// because the invoke will execute this on the gui-thread, you'll able to update controls.
// update my gui controls.
DataGrid.Source = myReceiveDataThing;
}));
}
}
You might expand the code, to check if the form is still valid.
In my application I have a button to save some information. However, I would like to have a delay in the code before the last line is executed, so that the user could read the message that shows up before he gets redirected to the new page.
I know that doing this isn't at all an optimal way, but by some reasons (time, for example) I want to do it anyway.
So is it possible and if so, how could I do it?
Thanks in advance!
protected void SaveButton_Click(object sender, EventArgs e) {
// Lots of code not relevant for the problem here
Service service = new Service();
service.SaveMovie(movie);
successMessage.Visible = true;
happyMessage.Text = "The movie was successfully added, now add some genres!";
// Here I want a delay of 2 seconds before the next line is executed...
Response.Redirect(String.Format("~/Edit.aspx?id={0}", movie.MovieID), false);
}
You need to do this on the client side. One alternative is this:
Define a Javascript function in the page called redirect as so:
function redirect(url)
{
setTimeout(function(){window.location.href=url;} ,2000);
}
protected void SaveButton_Click(object sender, EventArgs e)
{
// Lots of code not relevant for the problem here
Service service = new Service();
service.SaveMovie(movie);
successMessage.Visible = true;
happyMessage.Text = "The movie was successfully added, now add some genres!";
// Here I want a delay of 2 seconds before the next line is executed...
ClientScript.RegisterStartupScript(this.GetType(),"somekey","redirect('"+String.Format("~/Edit.aspx?id={0}", movie.MovieID)+"');");
}
This will be easy if you are using Javascript. Use javascript will boost performance
Button_Click
{
string js ="<script type='text/javascript'>setTimeout(function()window.location.href="+String.Format("~/Edit.aspx?id={0}", movie.MovieID)+";} ,2000);</script>"
ScriptManager.RegisterStartupScript(Me.Page, GetType(Page), "js", js, False)
}
possible duplicate of
asp.net delay before response redirect