Watin can't find element when running outside main thread - c#

The problem is with the button:contains('Deal') selector, the button containing deal does exist on the page and Watin can find it without problems when running in main thread.
// Add bet, deal cards and wait for animation
private void dealCards()
{
// Start the game if chip exists
if (browser.Image(Find.BySrc(chipURL)).Exists)
{
browser.Image(Find.BySrc(chipURL)).Click();
// This is where the thread stops
browser.Button(Find.BySelector("button:contains('Deal')")).Click();
Thread.Sleep(dealCardsAnimationTime);
if (Convert.ToInt32(browser.Span(Find.ByClass("player-points")).Text) == 21)
{
consoleTextBox.AppendText("You won by blackjack.");
gameOver = true;
}
return;
}
}
Update:
Browser is a Internet Explore window that I interact with using WatiN, other than the UI changes, the problem seems to be that WatiN can't find the button.
However it's still somewhat related to multithreading, as the same code works fine without multithreading and the element that it's searching for does exist.
Update 2: Changed title and description of problem as the old one appeared to be unrelated.
Context ( in case it's necessary )
// When bet button is clicked
private void button2_Click(object sender, EventArgs e)
{
blackjackThread = new Thread(blackjackGame);
if (dealInProgress == false)
{
blackjackThread.Start();
// blackjackGame();
}
}
// Blackjack deal handler
private void blackjackGame()
{
consoleTextBox.AppendText("\r\n");
resetVariables();
dealCards();
if (gameOver == true)
{
checkResult();
dealInProgress = false;
blackjackThread.Abort();
return;
}
checkCards();
logPoints();
while (gameOver == false)
{
botAction();
}
checkResult();
dealInProgress = false;
blackjackThread.Abort();
return;
}

Related

how to make sure that function only fires once (RawInputEventArg)

I'm currently working on a method that gives the user the possibility to add a handscanner to a dicitionary in order to scan some barcodes with it. (before i started the scanners were hardcoded in the dictionary). my colleague from which i got this project, implemented the rawinput_dll in order to get all of the necessary data from the barcode scanner. The method to get the data is shown below:
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (!Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
return;
}
else if (Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
if (e.KeyPressEvent.KeyPressState == "MAKE")
{
return;
}
if (e.KeyPressEvent.VKeyName != "\n")
{
scanNumber += e.KeyPressEvent.VKeyName;
return;
}
devID = e.KeyPressEvent.DeviceName;
Debug.Print(devID);
Aufrufen(scanNumber);
scanNumber = "";
}
}
Basically there are three classes in this program (FrmMenu, FrmSettings and a Class for the Scanner itself). If you want to add settings for the program you click on a button that opens up a new instance of FrmSettings
private void BtnSettings_Click(object sender, EventArgs e)
{
FrmSettings settings = new FrmSettings();
settings.ShowDialog();
settings.BtnSave_Click(sender, e);
settings.Dispose();
}
In this form there 2 buttons where you can choose if you want to add a scanner that scans even numbers or one that scans odd ones. If you press one of the buttons you need to scan a barcode in order to get the information (VID of Scanner) which is used as key to add the new scanner to the dictionary.
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (newScanner == true)
{
devIDnew = e.KeyPressEvent.DeviceName;
scannerAnlegen(devIDnew);
}
}
scannerAnlegen is the methode that adds the scanner to the dict.
public void scannerAnlegen(string devIDnew)
{
if(EvenOrOdd == true)
{
Scanner ger = new Scanner("dev3", "even");
FrmMenu.Scanners.Add(devIDnew, ger);
newScanner = false;
}
else
{
Scanner ug = new Scanner("dev4", "odd");
FrmMenu.Scanners.Add(devIDnew, ug);
newScanner = false;
}
}
my problem rn is, that it seems like i cant get out of this OneKeyPressed method of the Settings class. the logic of the OneKeyPressed method of the FrmMenu Class is that it can only proceed if the scanner is in the dictionary. Adding the scanner seems to work because when i debug and try to add one scanner the second time it throws and exception and says something like "element with this key already added". But why does this code doesn't continue then?

ManagementObjectSearcher causes re-entrancy issues for onclick handler

I am having an odd problem with protecting a section of code. My application is a tray app. I create a NotifyIcon inside my class (ApplicationContext). I have assigned a balloon click handler and a double click handler to the NotifyIcon object. there is also a context menu but I am not showing all code. Only important pieces.
public class SysTrayApplicationContext: ApplicationContext
{
private NotifyIcon notifyIcon;
private MainForm afDashBoardForm;
public SysTrayApplicationContext()
{
this.notifyIcon = new NotifyIcon();
this.notifyIcon.BalloonTipClicked += notifyIcon_BalloonTipClicked;
this.notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;
// ... more code
}
Both handlers launch or create/show my form:
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
openDashboard();
}
}
private void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
openDashboard();
}
private void openDashboard()
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
There is a problem with the above code. Maybe more than 1. Issue: it is possible to display 2 dashboard forms which is not what I want. If user double clicks on tray icon while balloon message is displaying causes a race condition in openDashboard. I can reproduce this easily. So I added a lock around the code in openDashboard code and, to my surprise, that did NOT prevent 2 dashboard forms from displaying. I should not be able to create 2 MainForms. Where am I going wrong here?
here is the updated code with lock statement:
private void openDashboard()
{
lock (dashBoardFormlocker)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
}
Note: lock object was added to the class and initialized in constructor.
private object dashBoardFormlocker;
UPDATE: Showing more code. this is how code gets started :
static void Main()
{
if (SingleInstance.Start())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
XmlConfigurator.Configure();
// For a system tray application we don't want to create
// a form, we instead create a new ApplicationContext. The Run method takes
Application.Run(new SysTrayApplicationContext());
SingleInstance.Stop();
SingleInstance.Dispose();
}
}
}
UPDATE 2: Provide more code for clarity
public partial class MainForm : Form
{
public MainForm()
{
log.Trace("MainForm constructor...");
InitializeComponent();
// ... code not shown
this.label_OSVersion.Text = getOSFriendlyName();
// .. more code
}
private string getOSFriendlyName()
{
try
{
string result = string.Empty;
var mgmtObj = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
result = mgmtObj != null ? mgmtObj.ToString() : string.Empty;
OperatingSystem os = Environment.OSVersion;
String sp = os.ServicePack ?? string.Empty;
return !string.IsNullOrWhiteSpace(result) ? result + sp : "Unknown";
}
catch (System.Exception ex)
{
log.Error("Error trying to get the OS version", ex);
return "Unknown";
}
}
}
The main UI thread must always pump a message loop to support communication from COM components.
So when you do a blocking operation from the UI thread like locking or joining a thread, (EDIT: edited based on Peter Duniho's fix) the UI thread will enter an 'alertable' state, allowing COM to dispatch certain type of messages, which in turn can cause re-entrancy issues like in your scenario.
Look at the answer to this question (Why did entering a lock on a UI thread trigger an OnPaint event?) for a much more accurate explanation.
Looking at the source code of ManagementObjectSearcher.Get there is a lock (inside Initialize), and since you call it from the constructor of your form, it may lead to the second event triggering while the form's constructor has not finished. The assignment to the dashBoardFormlocker variable only happens after the constructor finishes, so that would explain why it was null on the second entry.
The moral of the story is never do blocking operations on the UI thread.
Without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to know for sure what the problem is. But the guess by answerer tzachs seems reasonable. If so, you can fix your problem by changing your method to look like this:
private bool _dashboardOpen;
private void openDashboard()
{
if (_dashboardOpen)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
}
else
{
log.Debug("Dashboard form does not exist, create it");
_dashboardOpen = true;
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
In that way, any re-entrant attempt to open the window will be detected. Note that you still need the check for null before actually activating; you can't activate a window that hasn't actually finished being created yet. The subsequent call to Show() will take care of activation anyway, so ignoring the activation in the re-entrant case shouldn't matter.

When does the BeginInvoke method runs?

I have two threads which uses the BeginInvoke method to change some Windows Form object's (Panel and Label) visibility attribute to false.The problem is that I'm not sure when the change happens. I can see that the panel is not there (so the BeginInvoke method works) but my if condition to check the visibility status always returns true the first time the form is activated.
bool notVisible = false;
private void LunchMainScreen_Activated(object sender, EventArgs e) {
String CurrentSite = "";
List<DateTime> availableDates = new List<DateTime>();
// Get available dates
Thread availableDatesThread = new Thread(delegate() {
availableDates = LunchUserPreferences.GetUserAvailableDates();
changeObjVisible(notVisible, selectAvailabilityPanel);
changeObjVisible(notVisible, whenLbl);
}
});
availableDatesThread.Start();
// Get user current site
Thread checkSiteThread = new Thread(delegate() {
CurrentSite = LunchUserPreferences.GetUserSite();
changeObjVisible(notVisible, selectSitePanel);
changeObjVisible(notVisible, whereLbl);
}
updateText(CurrentSite, CurrentSiteSetLbl);
});
checkSiteThread.Start();
while (selectSitePanel.Visible == false && selectAvailabilityPanel.Visible == false) {
// it NEVER gets here, even though the panels are NOT visible when the program loads
WhoLunchTable.Visible = false;
WhoLunchTable.SuspendLayout();
listOfAvailableGroups.Clear();
WhoLunchTable.Controls.Clear();
WhoLunchTable.RowStyles.Clear();
PopulateTable();
WhoLunchTable.Visible = true;
WhoLunchTable.ResumeLayout();
break;
}
}
private delegate void changeObjVisibleDelegate(bool visibility, object obj);
private void changeObjVisible(bool visibility, object obj) {
if (this.InvokeRequired) {
this.BeginInvoke(new changeObjVisibleDelegate(changeObjVisible), new object[] { visibility, obj });
return;
}
// downcast to the correct obj
if (obj is Panel) {
Panel panel = (Panel)obj;
panel.Visible = visibility;
}
if (obj is Label) {
Label lbl = (Label)obj;
lbl.Visible = visibility;
}
}
private delegate void updateTextDelegate(string text, Label lbl);
private void updateText(string text, Label lbl) {
if (this.InvokeRequired) {
this.BeginInvoke(new updateTextDelegate(updateText), new object[] { text, lbl });
return;
}
lbl.Text = text;
}
It does work fine when the Form is activated for the second time, for example:
The form loads for the first time and it doesn't go inside the while loop.
I minimize the form/program.
The LunchMainScreen_Activated runs again and it works as it should because it recognises that the panels are not visible.
UPDATE:
I had an idea after reading AlexF answer which solved the problem but it doesn't look like the ideal solution:
I've created a while condition that will only stop when both threads are not alive and an if condition inside it that will get this point in time and execute what I need:
while (availableDatesThread.IsAlive || checkSiteThread.IsAlive) {
// At least one thread is still alive, keeps checking it...
if (!availableDatesThread.IsAlive && !checkSiteThread.IsAlive) {
// Both threads should be dead now and the panels not visible
WhoLunchTable.Visible = false;
WhoLunchTable.SuspendLayout();
listOfAvailableGroups.Clear();
WhoLunchTable.Controls.Clear();
WhoLunchTable.RowStyles.Clear();
PopulateTable();
WhoLunchTable.Visible = true;
WhoLunchTable.ResumeLayout();
break;
}
}
Reading your code the first time the code doesn't enter in the while loop because selectSitePanel.Visible and selectAvailabilityPanel.Visible are true: this is because the availableDatesThread.Start(); and checkSiteThread.Start(); are started but not finished; those two calls are not blocking so the code continues and skips the while.
Meanwhile the two backgrund threads finishes so the second time the "Activated" event is raised the variables values are "correct" (at least for the last cycle).
Without waiting for the threads to finish you are rushing through the code before having a result for the needed value.
In other words, it's better to not use a background thread to update an interface for the use you need.
If you need you may continue to use the code the way you are using it but moving the "while" section in two separate functions: they may be called when the threads have finished their work and refresh the window in this moment and not in the "activate" event.

Kinect New Window Wpf

So I'm making a Kinect application using buttons, and to navigate the app, I'm making new windows for each button. I'm come across an issue I haven't been able to find any help at all on, and would appreciate any help.
So to open the new window, I'm using this:
private void button1_Click(object sender, RoutedEventArgs e)
{
//SUPPOSED to uninitialize the Kinect
UninitializeKinectSensor(this.Kinect;
//To open the new window
Window1 newwindow = new Window1();
newwindow.Show();
//To close the first window...
Close();
{
SO that one line is supposed to uninitialize the Kinect so it'll be free for the new window to use, but when it goes to the new window, the Kinect freezes. If I use the mouse to go back to the first window, it works on the first window again, which it shouldn't.
I also added in this line in the initialization phase
public Window1()
{
//Other init code is here, but this is the line I added. It doesn't seem to do anything.
InitializeKinectSensor(this.Kinect);
}
Any help is greatly appreciated!! I'm sure it's something simple and I just failed miserably haha XD
Do you really have to create a new window instead of using pages?
In your MainWindow you create a frame that takes all the window and use this frame to navigate between pages. This way, you'll keep the focus of the kinect in your whole application.
Depends alot on what UninitializeKinectSensor is actually doing. Just as a quick fix, though, you can try calling uninitialize on a background worker and see if that helps at all.
Instead of using the "Show()" use "ShowDialog()".It's better if you can create a static class or method to initialize and uninitialized kinect.
public static void start()
{
KinectSensor.KinectSensors.StatusChanged += kinectSensorsStatusChanged;
DiscoverSensor();
}
private static void kinectSensorsStatusChanged(object sender, StatusChangedEventArgs e)
{
KinectSensor oldSensor = Kinect;
if (oldSensor != null)
{
UninitializeKinect();
}
var status = e.Status;
if (Kinect == null)
{
//updateStatus(status);
if (e.Status == KinectStatus.Connected)
{
Kinect = e.Sensor;
DiscoverSensor();
}
}
else
{
if (Kinect == e.Sensor)
{
//updateStatus(status);
if (e.Status == KinectStatus.Disconnected ||
e.Status == KinectStatus.NotPowered)
{
Kinect = null;
sensorConflict = false;
DiscoverSensor();
}
}
}
}
private static DispatcherTimer readyTimer;
private static void UninitializeKinect()
{
if (speechRecognizer != null && Kinect != null)
{
Kinect.AudioSource.Stop();
Kinect.SkeletonFrameReady -= kinect_SkeletonFrameReady;
Kinect.SkeletonStream.Disable();
Kinect.Stop();
//this.FrameSkeletons = null;
speechRecognizer.RecognizeAsyncCancel();
speechRecognizer.RecognizeAsyncStop();
}
if (readyTimer != null)
{
readyTimer.Stop();
readyTimer = null;
}
}

Strange in ShakeGesture Library on Windows Phone Application

i've a problem with windows phone shakegesture library.
I build an application which its shaking the sound will go out and its work nicely but strange bug make me confused. I've two page of it. This is my seperated code :
void Instance_ShakeGesture1(object sender, ShakeGestureEventArgs e)
{
Stream stream = TitleContainer.OpenStream("Sounds/C.wav");
effect = SoundEffect.FromStream(stream);
effectInstance = effect.CreateInstance();
if (effectInstance.State != SoundState.Playing || effectInstance == null)
{
FrameworkDispatcher.Update();
effectInstance.Play();
}
else if (effectInstance.State == SoundState.Playing || effectInstance != null)
{
effectInstance.Stop();
}
}
void Instance_ShakeGesture2(object sender, ShakeGestureEventArgs e)
{
Stream stream = TitleContainer.OpenStream("Sounds/D.wav");
effect = SoundEffect.FromStream(stream);
effectInstance = effect.CreateInstance();
FrameworkDispatcher.Update();
if (effectInstance.State == SoundState.Stopped || effectInstance == null)
{
effectInstance.Play();
}
else if (effectInstance.State == SoundState.Playing || effectInstance != null)
{
effectInstance.Stop();
}
}
Instance_ShakeGesture1 is my procedure to play a music when its shaking in Page 1 and Instance_ShakeGesture2 in Page 2.
Strange bug was come when its shaking, if i shake page 1 Instance_ShakeGesture1 will executed after that I try move to page 2 and i shake it will execute Instance_ShakeGesture1 first and than Instance_ShakeGesture2.
The Problem was come same when i try to shake Page 2 first and than Page 1, Instance_ShakeGesture2 will execute first and Instance_ShakeGesture2 in the second.
I know this bug when i use breakpoint.
Anyone know how to solve this problem? Thanks before :)
Possibly the event Instance_ShakeGesture1 is still active when you navigate to the second page. try
Instance.ShakeEvent -= new EventHandler(Instance_ShakeGesture1);
inside the Instance_ShakeGesture1 method.
try this, it worked for me,
protected override void OnBackKeyPress(CancelEventArgs e)
{
e.Cancel = false;
ShakeGesturesHelper.Instance.ShakeGesture -= new EventHandler<ShakeGestureEventArgs>(Instance_ShakeGesture1);
}
Because you should delete the events when you leaving first page. So you can clean hakeGestureEventArgs when back key button is pressed.
Okay my bad. Didn't know that you needed it to work multiple times.
Try this and let me know if it works good:
Write the same line of code that you've added, inside the OnNavigatedFrom method and delete it from your current method('Instance_ShakeGesture2')

Categories

Resources