I have a WPF app that will be acting like a desktop, with certain apps available, such as Microsoft Edge browser. I am able to open multiple windows of it, add a button to a custom taskbar, but the problem is, you could find the process by name, but then the buttons/program doesnt know which correct window to open. And if I search by ID, VS gives me:
System.ArgumentException:'Process with an Id of #### is not running'
Since Microsoft it self, changes the ID of the initial ID that it starts with.
So the methods right now im using for trying to get the right window is:
public void NewTabClick(object sender, RoutedEventArgs e)
{
//NewTabClick is a click event that gets created with every new button
MessageBox.Show("NewTabClicked");
ShowWindow(GetWindowHandle(), 3);
}
private IntPtr GetWindowHandle()
{
try
{
//Here is where i get the exception error
return Process.GetProcessById(ProcessId).MainWindowHandle;
}
catch (NullReferenceException)
{
return IntPtr.Zero;
}
}
ProcessId is set when we start the app itself in another method like this
private void StartApp()
{
var process = Process.Start(ProgramPath);
SetProcessId(process);
}
private void SetProcessId(Process process)
{
ProcessId = process.Id;
}
Is there a way to keep track/change/update which ID it should look for, or another way to keep track of these windows/browsers?
Thanks in advance!
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
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.
The application takes a lot of database queries. Request is created after the event made by the user or through the use of several timer (10 sec tick).
The problem occurs when the database server suddenly becomes unavailable. This causes a huge amount of on-screen messages containing information about the error in the connection.
I would like to achieve a situation in which a failed open call will freeze the application and open a single window that indicates a problem where the connection attempt will be retried every X seconds (plus a progress bar). If the connection is restored window is closed and the application will unlock.
How to do it? Please assumptions / guidelines or examples of ready-made solutions.
So if I understand you right, it's a usability problem. Your goal is for your users to be happy & confident that all is well, whilst waiting for a db connection. You don't want: panicky users pressing random buttons, phoning for help and complaining. You don't want a load of meaningless technical error messages; nor a frozen app with no messages. But you will accept a temporarily frozen app with a good helpful message.
Good usability doesn't come cheap. If you want to allow the user to cancel, then you have to learn some multi-threading. For that, I'd start here: http://msdn.microsoft.com/en-us/library/ms951089.aspx. You can avoid this if you are satisfied with a static message saying 'please wait, database connection may take up to xxx seconds...'.
I take a wild guess that your WinForms app calls the database from lots of places, but you'd like something that doesn't take days of re-writing.
The simplest single-threaded solution I can think of is to define a PleaseWaitForm and a 'wrapper' method, which I'll call DoWithPleaseWait(), which will go round all your business logic/data access calls, showing and hiding the please wait form:
namespace WinFormsPleaseWaitExample
{
//You don't need these 2 lines if you have .Net 3 or later
public delegate void Action();
public delegate TResult Func<TResult>();
//
public partial class Form1 : Form
{
private readonly Form pleaseWaitForm;
public Form1()
{
InitializeComponent();
pleaseWaitForm = new PleaseWaitForm {Owner = this};
}
private void button1_Click(object sender, EventArgs e)
{
var result= DoWithPleaseWait(delegate { return SomeBusinessLayerClass.ADataRetrieval("boo"); });
MessageBox.Show(result.ToString());
}
private void button2_Click(object sender, EventArgs e)
{
DoWithPleaseWait(delegate { SomeBusinessLayerClass.ADataOperation("boo"); });
}
public void DoWithPleaseWait(Action action)
{
pleaseWaitForm.Show();
action.DynamicInvoke();
pleaseWaitForm.Hide();
}
public TResult DoWithPleaseWait<TResult>(Func<TResult> func)
{
pleaseWaitForm.Show();
TResult result = (TResult)func.DynamicInvoke();
pleaseWaitForm.Hide();
return result;
}
}
public class SomeBusinessLayerClass
{
public static void ADataOperation(string someInput)
{
//Do something that might take several seconds...
Thread.Sleep(3000);
}
public static object ADataRetrieval(string someInput)
{
//Do something that might take several seconds...
Thread.Sleep(3000);
return someInput + " returned";
}
}
}
I have a problem with changing my UI strings after the user has changed the language in the option window. To change the UI strings of the main form, I have to restart the program every time, so that changes take effect, but that's annoying. So I tried it with a delegate to call the function, which loads the strings for the main window in the option window after saving the new settings. The function is called in the option window, but it doesn't change the strings of the main window.
Code in the main window
public delegate void CallLoadUIStrings(SupportedLanguages lang);
public CallLoadUIStrings callLoadUIStrings;
public Renamer()
{
callLoadUIStrings = new CallLoadUIStrings(LoadUIStrings);
}
public void LoadUIStrings(SupportedLanguages lang)
{
try
{
switch (lang)
{
#region "DE/JA/FR/ES/NL"
case SupportedLanguages.De:
case SupportedLanguages.Ja:
case SupportedLanguages.Fr:
case SupportedLanguages.Es:
case SupportedLanguages.Nl:
// reads the language file where the ui strings are stored
langHelper.Read(RenamerLangOpener.RenamerMainWindow);
this.mnuFile.Text = langHelper.Files;
this.mnuClose.Text = langHelper.Close;
this.mnuEdit.Text = langHelper.Edit;
this.mnuUndo.Text = langHelper.Undo;
this.mnuCut.Text = langHelper.Cut;
this.mnuCopy.Text = langHelper.Copy;
this.mnuPaste.Text = langHelper.Paste;
this.mnuDelete.Text = langHelper.Delete;
this.mnuSelectAll.Text = langHelper.SelectAll;
#endregion
}
}
catch (Exception ex) { //exception handling }
}
private void mnuOpt_Click(object sender, EventArgs e)
{
Preferences opt = new Preferences(this);
opt.ShowDialog();
}
Code in the option window
internal Renamer instance = null;
public Preferences(Renamer form)
{
instance = form;
}
public void UpdateUI()
{
langHelper.ReadSettingsValues();
instance.BeginInvoke(instance.callLoadUIStrings,new object[] { langHelper.GetLang});
}
Since I've never worked with delegates I don't have a clue where the mistake is.
I've googled so much to find a solution for a similar problem, but I haven't found something that matched my problem.
I assume this is winforms and not WPF question, and that you have one main form that is open from app's Main function. My solution to changing a language is to open this one form in a loop, and continue the loop as long as the form has a property set to some language identifier. If this property is set then I change the language to that value, and go for another loop iteration. I copy all other properties that need be copied form one form to another, with main being the form's position.
If the form is closed without the language ID being set then we break the loop and exit application as usual.
Im currently facing the problem that when i try to set focus on some control (textBox), nothing happens, maybe i just overlooked something.(somewhere i found that focus is "low-level" method and that select() should be used instead, however, it doesnt work as well)
From form Login, i launch new instance of EncryptPSW form
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
EncryptPSW ePSW = new EncryptPSW();
ePSW.setOsLog(false, this);
ePSW.ShowDialog();
}
On Button(which is located on EncryptPSW form ) click event i call fill method
public void fill()
{
if (textBoxPSW.Text.Length == 8)//psw has to be 8 chars long
{
if (save)//determinating whether save or fetch of data should be done
{ login.launchSave(textBoxPSW.Text,this); }
else { login.launchOpen(textBoxPSW.Text,this); }
}
else { MessageBox.Show("The password must contain 8 characters");}
}
Which launches either save or open method from Login (my problem is just with open, since during save i dont need to do anything with Focus)
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
setFocus();
}
After all the work is done, setFocus() should be called in order to set focus and other properties.
public void setFocus()
{
textBoxDatabase.Focus();
textBoxDatabase.SelectionStart = textBoxDatabase.TextLength - 1;
textBoxDatabase.SelectionLength = 0;
}
I tried so many different ways, like:
Calling setFocus() from within EncryptPSW_FormClosed
Calling whole open process after the EncryptPSW is closed (from within EncryptPSW_FormClosed)
and many more, however i dont remember it all.
In the case of Form_Closed the weird thing is, that when i tried to show a message box from there instead of setting focus (just to see where the problem might be), it's showed before the EncryptPSW form is closed.
My only guess about this is that the instance of EncryptPSW is somehow blocking Login form and it's controls
I hoped i described my problem well enough and that it makes at least a bit of sense ;]
Thanks in advance,
Regards,
Releis
Since the textbox is in the login form, and you are opening the EcryptPWS from it as a dialog (child), your login form will not be able to set focus to anything. You will need to set focus after it is closed. You can do this:
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
using(EncryptPSW ePSW = new EncryptPSW())
{
ePSW.setOsLog(false, this);
if (ePSW.ShowDialog() == DialogResult.OK)
{
textBoxDatabase.Focus();
}
}
}
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.DialogResult = DialogResult.OK;
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
}
OK this maybe the ugliest thing I saw round this but.
using
public void setFocus()
{
textBoxDatabase.Focus();
textBoxDatabase.SelectionStart = textBoxDatabase.TextLength - 1;
textBoxDatabase.SelectionLength = 0;
}
Change your code at
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
setFocus();
}
to
delegate void settingfocus();
public void launchOpen(string psw,EncryptPSW ePSW)
{
ePSW.Close();
Encryptor.DecryptFile("loggin.bin", psw, this); //decrypting data and setting textBoxes Text property into the fetched ones
settingfocus sf = new settingfocus(setFocus);
this.BeginInvoke(sf);
}
This worked for me
(Sorry for apparently thinking insert "this" before procedure, and change line x to this was legable)