C# function is excuting twice but only once - c#

I'm using AutoUpdater.Net and I start the check on the application launch (this one work well). A second time from the MainWindow.
This is the function that I call :
public void CheckUpdate()
{
if (IsConnectedToInternet())
{
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
Update.InstalledVersion = new Version(fvi.FileVersion);
Update.Synchronous = true;
//comment the line below to show the AutoUpdater.NET forms
Update.CheckForUpdateEvent += AutoUpdaterOnCheckForUpdateEvent;
Update.Start("http:/myurl/");
}
else
IminaDialog.ShowMessage("Update error", "It seems you're not connected to internet.", ModalWindowButtons.Ok);
}
And the function AutoUpdateOnCheckForUpdateEvent is executing twice. It come to the last line of the method and directly restart the method. The call stack from the loading or from the MainWindow still the same. From the MainWindow I call it from the Instance of the parent class.
I hope someone can help and tell me if you want more informations.
EDIT
This is the call that execute twice AutoUpdateOnCheckForUpdateEvent (CheckUpdate execute once every time):
public static WpfCommand CommandCheckUpdate
{
get
{
if (_commandCheckUpdate == null)
{
_commandCheckUpdate = new WpfCommand();
_commandCheckUpdate.Executed += (sender, e) =>
{
FrameworkController.Instance.CheckUpdate();
};
}
return _commandCheckUpdate;
}
}
If I only call FrameworkController.Instance the CheckUpdate don't execute.
And here it's were the method is called on load (inside FrameworkController) :
public void Start(string[] mainArgs)
{
InitializeLog();
if (!License.RegisterLicense(null))
return;
DefaultMenuItems.PopulateMenuService(MenuService.Instance);
IminaUtils.Initialize(); // Initialize helper methods
CheckUpdate();
_firstConfigurationLoading = Configuration.Load();
MainWindowViewModel.ClearDiagnostic();
if (RightRepository.Instance.CurrentUser == null)
MainWindowViewModel.OpenLogin();
if (Configuration.ShutdownApp)
return;
ConnectConfiguration(mainArgs);
}

Related

Calling public method of form instance from different form causes error

I'm slightly baffled as to what the reason for this issue is. I'm receiving the error "Form does not contain a definition for 'GetProgressBar' and no accessible extension method 'GetProgressBar' accepting a first argument of Type 'Form' could be found." I'm simply trying to update the progress bar on a different form for a task. Here is method in Form1,
private async void button_patches_Click(object sender, EventArgs e)
{
if (dbConnect.Connection != null)
{
if (dbConnect.Connection.State == ConnectionState.Open)
{
if (textBox1.Text != "")
{
Form form12 = new Form12();
form12.ShowDialog();
ProgressBar progressBar1 = form12.GetProgressBar();
WSUS wsus = new WSUS(dbConnect, textBox1.Text);
Array kbList = wsus.ReadPatchList();
int patchesLength = kbList.Length;
progressBar1.Maximum = 100;
progressBar1.Step = 1;
var progress = new Progress<int>(v => { progressBar1.Value = v; }) ;
await Task.Run(() => AddPatches(wsus, kbList, progress));
}
else { MessageBox.Show("Please select your path for patches file first"); }
}
else { MessageBox.Show("Please open a connection to the database first."); }
}
else { MessageBox.Show("Please open a connection to the database first."); }
}
The error is occurring on the line ProgressBar progressBar1 = form12.GetProgressBar();
Here is my code in Form12 class.
namespace myDBTemplate1.Forms
{
public partial class Form12 : Form
{
public Form12()
{
InitializeComponent();
}
public ProgressBar GetProgressBar()
{
return progressBar1;
}
}
}
progressBar1 is an instance of ProgressBar that I added to the form via visual studio designer. Also, I set my modifier for my progress bar to public although that shouldn't cause this issue. Evidently, creating an instance of Form12 and then calling one of its public methods isn't working here. I have plenty of other classes in this project where I've calling public instance methods on already instantiated objects and they've worked fine. What's the difference here?
It's because GetProgressBar() is defined in Form12 but you trying to access it from variable of Form type which is parent class and does not have the method.
if (textBox1.Text != "")
{
Form form12 = new Form12();
form12.ShowDialog();
Replace Form with Form12 in your form12 variable declaration or simply use var keyword.

Capture Button Click event inside a MessageBox in another application

I want to capture the OK Button's Click event on a MessageBox shown by another WinForms application.
I want to achieve this using UI Automation. After some research, I have found that IUIAutomation::AddAutomationEventHandler will do the work for me.
Though, I can capture the Click event of any other button, I'm unable to capture a Click event of the MessageBox.
My code is as follows:
var FindDialogButton = appElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (FindDialogButton != null)
{
if (FindDialogButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, FindDialogButton, TreeScope.Element, new AutomationEventHandler(DialogHandler));
}
}
private void DialogHandler(object sender, AutomationEventArgs e)
{
MessageBox.Show("Dialog Button clicked at : " + DateTime.Now);
}
EDIT:
My Complete code is as follows:
private void DialogButtonHandle()
{
AutomationElement rootElement = AutomationElement.RootElement;
if (rootElement != null)
{
System.Windows.Automation.Condition condition = new PropertyCondition
(AutomationElement.NameProperty, "Windows Application"); //This part gets the handle of the Windows application that has the MessageBox
AutomationElement appElement = rootElement.FindFirst(TreeScope.Children, condition);
var FindDialogButton = appElement.FindFirst(TreeScope.Descendants, new PropertyCondition(AutomationElement.NameProperty, "OK")); // This part gets the handle of the button inside the messagebox
if (FindDialogButton != null)
{
if (FindDialogButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, FindDialogButton, TreeScope.Element, new AutomationEventHandler(DialogHandler)); //Here I am trying to catch the click of "OK" button inside the MessageBox
}
}
}
}
private void DialogHandler(object sender, AutomationEventArgs e)
{
//On Button click I am trying to display a message that the button has been clicked
MessageBox.Show("MessageBox Button Clicked");
}
I tried to keep this procedure as generic as possible, so that it will work whether the application you're watching is already running when your app is started or not.
You just need to provide the watched Application's Process Name or its Main Window Title to let the procedure identify this application.
Use one of these Fields and the corresponding Enumerator:
private string appProcessName = "theAppProcessName"; and
FindWindowMethod.ProcessName
// Or
private string appWindowTitle = "theAppMainWindowTitle"; and
FindWindowMethod.Caption
passing these values to the procedure that starts the watcher, e.g., :
StartAppWatcher(appProcessName, FindWindowMethod.ProcessName);
As you can see - since you tagged your question as winforms - this is a complete Form (named frmWindowWatcher) that contains all the logic required to perform this task.
How does it work:
When you start frmWindowWatcher, the procedure verifies whether the watched application (here, identified using its Process name, but you can change the method, as already described), is already running.
If it is, it initializes a support class, ElementWindow, which will contain some informations about the watched application.
I added this support class in case you need to perform some actions if the watched application is already running (in this case, the ElementWindow windowElement Field won't be null when the StartAppWatcher() method is called). These informations may also be useful in other cases.
When a new Windows is opened in the System, the procedure verifies whether this Window belongs to the watched application. If it does, the Process ID will be the same. If the Windows is a MessageBox (identified using its standard ClassName: #32770) and it belongs to the watched Application, an AutomationEventHandler is attached to the child OK Button.
Here, I'm using a Delegate: AutomationEventHandler DialogButtonHandler for the handler and an instance Field (AutomationElement msgBoxButton) for the Button Element, because these references are needed to remove the Button Click Handler when the MessageBox is closed.
When the MessageBox's OK Button is clicked, the MessageBoxButtonHandler method is called. Here, you can determine which action to take at this point.
When the frmWindowWatcher Form is closed, all Automation Handlers are removed, calling the Automation.RemoveAllEventHandlers() method, to provide a final clean up and prevent your app from leaking resources.
using System.Diagnostics;
using System.Linq;
using System.Windows.Automation;
using System.Windows.Forms;
public partial class frmWindowWatcher : Form
{
AutomationEventHandler DialogButtonHandler = null;
AutomationElement msgBoxButton = null;
ElementWindow windowElement = null;
int currentProcessId = 0;
private string appProcessName = "theAppProcessName";
//private string appWindowTitle = "theAppMainWindowTitle";
public enum FindWindowMethod
{
ProcessName,
Caption
}
public frmWindowWatcher()
{
InitializeComponent();
using (var proc = Process.GetCurrentProcess()) {
currentProcessId = proc.Id;
}
// Identify the application by its Process name...
StartAppWatcher(appProcessName, FindWindowMethod.ProcessName);
// ... or by its main Window Title
//StartAppWatcher(appWindowTitle, FindWindowMethod.Caption);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
Automation.RemoveAllEventHandlers();
base.OnFormClosed(e);
}
private void StartAppWatcher(string elementName, FindWindowMethod method)
{
windowElement = GetAppElement(elementName, method);
// (...)
// You may want to perform some actions if the watched application is already running when you start your app
Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
try
{
if (element == null || element.Current.ProcessId == currentProcessId) return;
if (windowElement == null) windowElement = GetAppElement(elementName, method);
if (windowElement == null || windowElement.ProcessId != element.Current.ProcessId) return;
// If the Window is a MessageBox generated by the watched app, attach the handler
if (element.Current.ClassName == "#32770")
{
msgBoxButton = element.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "OK"));
if (msgBoxButton != null && msgBoxButton.GetSupportedPatterns().Any(p => p.Equals(InvokePattern.Pattern)))
{
Automation.AddAutomationEventHandler(
InvokePattern.InvokedEvent, msgBoxButton, TreeScope.Element,
DialogButtonHandler = new AutomationEventHandler(MessageBoxButtonHandler));
}
}
}
catch (ElementNotAvailableException) {
// Ignore: this exception may be raised if you show a modal dialog,
// in your own app, that blocks the execution. When the dialog is closed,
// AutomationElement element is no longer available
}
});
Automation.AddAutomationEventHandler(WindowPattern.WindowClosedEvent, AutomationElement.RootElement,
TreeScope.Subtree, (elm, e) => {
AutomationElement element = elm as AutomationElement;
if (element == null || element.Current.ProcessId == currentProcessId || windowElement == null) return;
if (windowElement.ProcessId == element.Current.ProcessId) {
if (windowElement.MainWindowTitle == element.Current.Name) {
windowElement = null;
}
}
});
}
private void MessageBoxButtonHandler(object sender, AutomationEventArgs e)
{
Console.WriteLine("Dialog Button clicked at : " + DateTime.Now.ToString());
// (...)
// Remove the handler after, since the next MessageBox needs a new handler.
Automation.RemoveAutomationEventHandler(e.EventId, msgBoxButton, DialogButtonHandler);
}
private ElementWindow GetAppElement(string elementName, FindWindowMethod method)
{
Process proc = null;
try {
switch (method) {
case FindWindowMethod.ProcessName:
proc = Process.GetProcessesByName(elementName).FirstOrDefault();
break;
case FindWindowMethod.Caption:
proc = Process.GetProcesses().FirstOrDefault(p => p.MainWindowTitle == elementName);
break;
}
return CreateElementWindow(proc);
}
finally {
proc?.Dispose();
}
}
private ElementWindow CreateElementWindow(Process process) =>
process == null ? null : new ElementWindow(process.ProcessName) {
MainWindowTitle = process.MainWindowTitle,
MainWindowHandle = process.MainWindowHandle,
ProcessId = process.Id
};
}
Support class, used to store informations on the watched application:
It's initialized using the App's Process Name:
public ElementWindow(string processName)
but of course you can change it as required, using the Window Title as described before, or even remove the initialization's argument if you prefer (the class just need to not be null when the watched Application has been detected and identified).
using System.Collections.Generic;
public class ElementWindow
{
public ElementWindow(string processName) => this.ProcessName = processName;
public string ProcessName { get; set; }
public string MainWindowTitle { get; set; }
public int ProcessId { get; set; }
public IntPtr MainWindowHandle { get; set; }
}

Cannot call Close when Closing event is override

In my MainWindow constructor I ovverided the Closing event because I need to call another method that perform some task, like:
public MainWindow()
{
InitializeComponent();
Closing += (x, y) =>
{
y.Cancel = true;
_discard = true;
CheckSettings();
};
}
public void CheckSettings(bool x)
{
if(x)
Close();
}
on the Close line I get:
cannot set visibility or call show or showdialog after window has closed
why??
(as requested in you comment...)
You cannot call Close from a Closing event handler.
If the logic determining if the form can be closed is implemented in CheckSettings:
public MainWindow()
{
InitializeComponent();
Closing += (sender, args) =>
{
args.Cancel = !CheckSettings();
...
};
}
public bool CheckSettings()
{
// Check and return true if the form can be closed
}
Until you return from your event handler (that's made the call to CheckSettings), the UI framework you're using may not evaluate the content of the EventArgs that you've named as y and set Cancel = true on.
If you're using WPF, for example, the Close method eventually calls down into another method called VerifyNotClosing (via InternalClose) which at the time of writing looks like this:
private void VerifyNotClosing()
{
if (_isClosing == true)
{
throw new InvalidOperationException(SR.Get(SRID.InvalidOperationDuringClosing));
}
if (IsSourceWindowNull == false && IsCompositionTargetInvalid == true)
{
throw new InvalidOperationException(SR.Get(SRID.InvalidCompositionTarget));
}
}
The relevant bit there is the first if that checks a member variable called _isClosing and throws an exception if the form is in the process of closing.
The InternalClose method reacts to the state of the Cancel property of the EventArgs after the event handlers have been called:
CancelEventArgs e = new CancelEventArgs(false);
try
{
// The event handler is called here
OnClosing(e);
}
catch
{
CloseWindowBeforeShow();
throw;
}
// The status of the .Cancel on the EventArgs is not checked until here
if (ShouldCloseWindow(e.Cancel))
{
CloseWindowBeforeShow();
}
else
{
_isClosing = false;
// 03/14/2006 -- hamidm
// WOSB 1560557 Dialog does not close with ESC key after it has been cancelled
//
// No need to reset DialogResult to null here since source window is null. That means
// that ShowDialog has not been called and thus no need to worry about DialogResult.
}
The code above (from the InternalClose method) is after the call to VerifyNotClosing which is why the subsequent call to Close, before the first one has finished, results in the exception being thrown.

Form not loading correctly

I'm trying to build a TAPI based phone call system using JulMar's Atapi x86. One of the functions is to pop a specific form on an inbound call. However, whenever the form pops, it comes up incorrect, as shown below (I have tried several forms as a test and they all do the same thing). There is no error, nothing in the output window to suggest what the issue is.
Code:
private void incomingcall(object sender, NewCallEventArgs e)
{
string phonenumber = e.Call.CallerId; //get the phone number of the call
SqlCommand getincoming = new SqlCommand(Querystrings.getincomingquery(), DB);
getincoming.Parameters.AddWithValue("##TELEPHONE", phonenumber);
DataTable results = new DataTable();
try
{
DB.Open();
using (var results = getincoming.ExecuteReader())
{
results.Load(results);
}
}
catch (Exception ex)
{
Inbound ib = new Inbound(phonenumber, null);
ib.Show();
}
finally
{
DB.Close();
}
if (results.Rows.Count == 1)
{
loadcontactrequest(Convert.ToInt32(results.Rows[0].ItemArray[0]), phonenumber);
}
else
{
loadinbound(phonenumber, results);
}
}
I have loaded these forms outside of this function at other points, meaning it is something to do with this function. Does anybody know where I'm going wrong?
EDIT:
private void loadcontactrequest(int ContactID, string phonenumber)
{
ContactRequest cr = new ContactRequest(ContactID, Global.loginbound("Single customer found", phonenumber));
cr.Show();
}
These functions have been tested elsewhere and work correctly individually, I believe it might be TAPI related.
EDIT 2 - Delegate:
public static void inittapi()
{
if (TestOptions.notapi)
return;
tapi = new TapiManager("Omitted");
tapi.Initialize();
foreach (TapiLine ad in tapi.Lines) //Get all lines available to this PC
{
if (ad.Name.ToUpper().Contains("Omitted"))
{
phoneline = ad;
phoneline.Open(MediaModes.All); //Open the phone line for making and receiving calls
phoneline.NewCall += new EventHandler<NewCallEventArgs>(new TAPI().incomingcall); //Add the incoming call event handler
}
}
}
It's possible that this event is triggered on a different thread than the UI thread of your application.
Modify the method like this to test whether this is the problem:
private void incomingcall(object sender, NewCallEventArgs e)
{
Form form;
if(Application.OpenForms.Count > 0)
{
form = Application.OpenForms[0];
}
if (form != null && form.InvokeRequired)
{
form.BeginInvoke(new Action(() => { incomingcall(sender, e); }));
return;
}
// Your current code goes here
}
This will identify that we are in a different thread than your main form (form) was created on and then execute the function again on the main form's thread.

Why this winform does not show up after Hide is called on its second iteration?

My current situation is as follow:
Application A injects my library into Application B
Application A then uses GetProcessAddress and GetModule to call the OnStart method in my library
Within my OnStart/OnStop method I initiate/hide a winform, in context its something like this:
private static PluginManager _pluginManager;
public static void OnStart()
{
// some log file code here
if (_pluginManager == null)
{
_pluginManager = new PluginManager();
}
// some log file code here
_pluginManager.Show();
// some log file code here
}
public static void OnStop()
{
if (_pluginManager != null)
{
// some log file code here
_pluginManager.Hide();
// some log file code here
}
}
The PluginManager winform close event is override to call Hide() so technically(I assume) my winform will never be disposed/closed?
Once application A is done its tasks and will close, it uses GetProcessAddress and GetModule to call the OnStop method of my library
Everything works as expected so far the winform shows up at OnStart and hides on OnStop as expected.
Now when I start Application A again and it calls the OnStart method, the winform never shows up, I don't get any error messages nor anything it simple doesn't open.
I know the function is called because it outputs to the log file the line before and the line after it calls Show().
As the title says, why the winform doesn't show up in the second iteration?
What else can I do to find the issue?
On further tests I found out that if I dispose of the winform and reinitialize it, the winform will work for every call:
public static void OnStart()
{
// some log file code here
if (_pluginManager == null)
{
_pluginManager = new PluginManager();
}
else
{
_pluginManager.Dispose();
_pluginManager = new PluginManager();
}
// some log file code here
_pluginManager.Show();
// some log file code here
}
However I am still unaware of why this is required given the winform is never disposed of with the initial code.
My case is probably off-topic? As I can't provide you with a reproducible code of the issue and unhappily the above is all I could collect, but hopefully some one will be able to point me in the right direction or where to look at.
This is probably a threading problem. You should only call .Show() from an [STAThread] and you should call .Hide() from the same thread that called .Show(). So do this:
private static ISynchronizeInvoke _invoker = null;
public static void OnStart()
{
_invoker.Invoke((Action)(() => {
// some log file code here
if (_pluginManager == null)
{
_pluginManager = new PluginManager();
}
// some log file code here
_pluginManager.Show();
// some log file code here
}), null);
}
public static void OnStop()
{
if (_pluginManager != null)
{
// some log file code here
_pluginManager.Invoke((Action)(() => _pluginManager.Hide()));
// some log file code here
}
}
I don't know how the application's main is structured, but you can fill in _invoker from any open form.
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// Application.Run(new Form1()); <-- This is the usual startup
// Instead, do this to grab an object to invoke on
Form1 form1 = new Form1();
_invoker = form1;
Application.Run(form1);
}
You could also create a wholly separate STAThread and call Application.Run(_pluginManager) instead of pluginManager.Show().
You have mentioned that your PluginManager winform close event is override to call Hide(), did you set e.Cancel = true?
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
this.Hide();
base.OnClosing(e);
}

Categories

Resources