Modal MessageBox on another Process' Handle may lock-up target Process - windows

If I show a MessageBox as modal of a window on another process, it works just fine as long as my program remains responding. If it is closed or terminated while the MessageBox is showing the windows that received the MessageBox will be locked (but still responding) and it will have to be finalized via Task Manager.
Here is a sample code to demonstrate that:
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
namespace TestMessageBox
{
class Program
{
private WindowWrapper notepad;
Program(IntPtr handle)
{
notepad = new WindowWrapper(handle);
}
static void Main(string[] args)
{
Process[] procs = Process.GetProcessesByName("notepad");
if (procs.Length > 0)
{
Console.WriteLine("Notepad detected...");
Program program = new Program(procs[0].MainWindowHandle);
Thread thread = new Thread(new ThreadStart(program.ShowMessage));
thread.IsBackground = true;
thread.Start();
Console.Write("Press any key to end the program and lock notepad...");
Console.ReadKey();
}
}
void ShowMessage()
{
MessageBox.Show(notepad, "If this is open when the program ends\nit will lock up notepad...");
}
}
/// <summary>
/// Wrapper class so that we can return an IWin32Window given a hwnd
/// </summary>
public class WindowWrapper : System.Windows.Forms.IWin32Window
{
public WindowWrapper(IntPtr handle)
{
_hwnd = handle;
}
public IntPtr Handle
{
get { return _hwnd; }
}
private IntPtr _hwnd;
}
}
How to avoid that?

The act of showing a modal dialog disables the parent window of the dialog (Notepad's window in your example). When the modal dialog is closed, the parent window gets re-enabled.
If your program dies before it re-enables the window, that window will never get re-enabled - it's up to the thread that's showing the dialog to re-enable the parent. (In your example, it happens within MessageBox.Show(), after the user clicks OK or whatever.)
The only way to make this work would be to have a second process whose responsibility it was to put things back as they should be if the process creating the modal dialog dies prematurely, but that's horrible. And it's still not bulletproof - what if the watcher process dies too?

Related

WindowOpenedEvent Working for Normal Windows, But Not Push Notifications

I'm trying to use the WindowOpenedEvent in System.Windows.Automation to detect when a new window is opened. This works fine for almost every window that I'm interested in. For example, when I open a notepad window when running the app I get this written to the console:
New Window opened: Notepad
But when I send a web push notification (using this site to test them), it is not detected and nothing is written to the console when the window appears:
Here's the code I'm using in my app. I've used all the various values for the scope field (TreeScope.Subtree, TreeScope.Children, etc)
using System.Windows.Automation;
using System.Diagnostics;
using System.Windows.Forms;
namespace FormWatcherAutomation
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
System.Windows.Automation.Automation.AddAutomationEventHandler(
eventId: WindowPattern.WindowOpenedEvent,
element: AutomationElement.RootElement,
scope: TreeScope.Subtree,
eventHandler: OnWindowOpened);
}
private static void OnWindowOpened(object sender, AutomationEventArgs automationEventArgs)
{
try
{
var element = sender as AutomationElement;
if (element != null)
Debug.WriteLine("New Window opened: " + element.Current.Name);
}
catch (ElementNotAvailableException)
{
}
}
}

Windows phone navigation bug

I encountered an issue.
I have a loading page with progress bar. It is non-interactive, it only shows progress on loading data. AFter data is loaded - it navigates to MenuPage.
If user minimizes app during loading process sometimes a phantom corrupted entry is added to BackStack.
Problem:
If this corrupted entry is in backstack - NavigationService.RemoveBackEntry() throws NullReferenceException.
Question:
My goal is to exit app from 2nd page when user presses back. Can i somehow do that without removing items from backstack and throwing exceptions ?
In short, no. You can't directly quit an application and it sucks. However you can do it with a well handled exception.
private class QuitException : Exception { }
public static void Quit()
{
throw new QuitException();
}
private void Application_UnhandledException(object sender,
ApplicationUnhandledExceptionEventArgs e)
{
if (e.ExceptionObject is QuitException)
return;
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
}
Source:
http://www.imaginativeuniversal.com/blog/post/2010/08/22/How-to-Quit-a-WP7-Silverlight-Application.aspx

Using WM_SHOWWINDOW to Show a Window instead of ShowWindow()

I’m trying to use the SendMessage function of a hotkey utility (or NirCMD, etc.) to get a hidden window to pop up. I can for example get windows to close by sending 0x0010 (WM_CLOSE), but when I try sending 0x0018 (WM_SHOWWINDOW) with a wParam of 1 and an lParam of 0, nothing happens.
I’ve looked around, and the few places where someone complained that WM_SHOWWINDOW did not work, they happily took the suggestion to use ShowWindow() instead.
However I don’t have ShowWindow() available; I can only send Windows messages. But ShowWindow() is not magic, surely it works by SendMessage-ing a WM_SHOWWINDOW or something under the covers.
How can I get a window to display itself by sending it a message?
Thanks.
Try these two messages:
SendMessage(h,WM_SYSCOMMAND,SC_MINIMIZE,0);
SendMessage(h,WM_SYSCOMMAND,SC_RESTORE,0);
Or if using 3rd party apps is ok, try cmdow
WM_SHOWWINDOW is a notification, not a command. From MSDN:
The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown.
I don't believe there is any message that you can use to make a window show itself. Actually, the very idea seems a little strange to me. The window manager is the system component responsible for showing and hiding windows. To show a window, you must use one of the window manager APIs.
I think there is no way to achieve that with SendMessage (WM_SYSCOMMAND didn't work for me). I tried actually in C#. You click the button, the window will be minimized via ShowWindow() and then you can see what messages are sent:
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public class Form1: Form
{
[DllImport("user32.dll", SetLastError = true)]
public static extern bool ShowWindow(IntPtr window, int showCommand);
private const int SW_MINIMIZE = 6;
private bool print = false;
public Form1()
{
Button button = new Button();
button.Click += onButtonsClick;
Controls.Add(button);
}
private void onButtonsClick(object sender, EventArgs e)
{
print = true;
ShowWindow(Handle, SW_MINIMIZE);
print = false;
}
protected override void WndProc(ref Message m)
{
if (print)
Console.WriteLine(m.Msg.ToString() + "\t0x" + m.Msg.ToString("x4") + "\t" + m.WParam + "\t" + m.LParam);
base.WndProc(ref m);
}
}
}

How to navigate back to the previous screen in Blackberry?

In Blackberry I can navigate from one screen to the next screen, but I can't navigate back to the previous screen. Pressing the escape key in the emulator terminates the entire application. Is there any other key in the emulator to go to the previous screen or any code to navigate back? If you know please help me.
As Andrey said, there is a display stack, so if you push screens without popping them, they will stay in stack, so closing current screen, previous screen will be shown automattically, and if there is no prev. screen, application will close.
However it's not good to hold many screens in display stack, so you can implement kind of stack inside of screens, to handle navigation manually.
Abstract screen class for screen stack implementation:
public abstract class AScreen extends MainScreen {
Screen prevScreen = null;
void openScreen(AScreen nextScreen) {
nextScreen.prevScreen = this;
UiApplication.getUiApplication().popScreen(this);
UiApplication.getUiApplication().pushScreen(nextScreen);
}
void openPrevScreen() {
UiApplication.getUiApplication().popScreen(this);
if (null != prevScreen)
UiApplication.getUiApplication().pushScreen(prevScreen);
}
}
Sample first screen:
public class FirstScreen extends AScreen implements FieldChangeListener {
ButtonField mButton = null;
public FirstScreen() {
super();
mButton = new ButtonField("Go second screen", ButtonField.CONSUME_CLICK);
mButton.setChangeListener(this);
add(mButton);
}
public void fieldChanged(Field field, int context) {
if (mButton == field) {
openScreen(new SecondScreen());
}
}
}
Sample second screen:
public class SecondScreen extends AScreen implements FieldChangeListener {
ButtonField mButton = null;
public SecondScreen() {
super();
mButton = new ButtonField("Go first screen", ButtonField.CONSUME_CLICK);
mButton.setChangeListener(this);
add(mButton);
}
public void fieldChanged(Field field, int context) {
if (mButton == field) {
openPrevScreen();
}
}
public boolean onClose() {
openPrevScreen();
return true;
}
}
The BlackBerry maintains a stack of screens; the display stack.
Screens are popped onto the stack, and popped off the stack through the UiApplication in charge of them. Popping the last screen off the stack closes the application by default.
If you are running a UiApplication, named MyUiApplication, you can add the screen to the stack by calling pushScreen(new SomeScreen());
The screen, if derived from MainScreen, as most BlackBerry screens are, is created with the DEFAULT_CLOSE flag, meaning that the ESCAPE button on the BlackBerry will naturally close the screen, causing popScreen() to be called. You can, of course, call popScreen() following any keypress or trackwheel/trackball click. The screen can also call close() on itself, which has the same result; the screen is popped off the stack, returning the application to the previous screen, or terminating the application if the last screen is popped off the display stack.
If the application is not created as a UiApplication, or if the screen was initially pushed onto the display stack from a non-UI thread (such as a background thread), then one must make sure that the call to close the screen is also done from the UI thread. This can be done by making sure that the eventLock is taken on the Application class prior to performing any UI operation (one would typically call invokeLater as well, in this situation).
If the original screen was popped onto the stack as a global screen (modeless, on top of all other screens), then it must be popped off the stack using something like:
Ui.getUiEngine().dismissStatus(this);
In any case, overriding onClose() and close() of the derived Screen will allow you to trap the occurring exception for debugging and further analysis.

How do I write to the Visual Studio Output Window in My Custom Tool?

I am writing a custom tool and I currently have it doing what I want as far as functionality. I would like to be able to write to Visual Studio if something goes wrong. (Incorrectly formatted code or whatever).
Are there any standards for this? Right now I basically can force the tool to fail and Visual Studio puts in a warning that it has done so. I'd like a category in the Output window with any resulting messages I want to send. I could also live with a more descriptive task/warning in the Error list window.
Output Window
To write to the "General" output window in Visual Studio, you need to do the following:
IVsOutputWindow outWindow = Package.GetGlobalService( typeof( SVsOutputWindow ) ) as IVsOutputWindow;
Guid generalPaneGuid = VSConstants.GUID_OutWindowGeneralPane; // P.S. There's also the GUID_OutWindowDebugPane available.
IVsOutputWindowPane generalPane;
outWindow.GetPane( ref generalPaneGuid , out generalPane );
generalPane.OutputString( "Hello World!" );
generalPane.Activate(); // Brings this pane into view
If, however, you want to write to a custom window, this is what you need to do:
IVsOutputWindow outWindow = Package.GetGlobalService( typeof( SVsOutputWindow ) ) as IVsOutputWindow;
// Use e.g. Tools -> Create GUID to make a stable, but unique GUID for your pane.
// Also, in a real project, this should probably be a static constant, and not a local variable
Guid customGuid = new Guid("0F44E2D1-F5FA-4d2d-AB30-22BE8ECD9789");
string customTitle = "Custom Window Title";
outWindow.CreatePane( ref customGuid, customTitle, 1, 1 );
IVsOutputWindowPane customPane;
outWindow.GetPane( ref customGuid, out customPane);
customPane.OutputString( "Hello, Custom World!" );
customPane.Activate(); // Brings this pane into view
Details on IVsOutputWindow and IVsOutputWindowPane can be found on MSDN.
Error List
For adding items to the error list, the IVsSingleFileGenerator has a method call void Generate(...) which has a parameter of the type IVsGeneratorProgress. This interface has a method void GeneratorError() which lets you report errors and warnings to the Visual Studio error list.
public class MyCodeGenerator : IVsSingleFileGenerator
{
...
public void Generate( string inputFilePath, string inputFileContents, string defaultNamespace, out IntPtr outputFileContents, out int output, IVsGeneratorProgress generateProgress )
{
...
generateProgress.GeneratorError( false, 0, "An error occured", 2, 4);
...
}
...
}
The details of GeneratorError() can be found on MSDN.
There is another way using Marshal.GetActiveObject to grab a running DTE2 instance.
First reference EnvDTE and envdte80. This currently works in VisualStudio 2012, I haven't tried the others yet.
using System;
using System.Runtime.InteropServices;
using EnvDTE;
using EnvDTE80;
internal class VsOutputLogger
{
private static Lazy<Action<string>> _Logger = new Lazy<Action<string>>( () => GetWindow().OutputString );
private static Action<string> Logger
{
get { return _Logger.Value; }
}
public static void SetLogger( Action<string> logger )
{
_Logger = new Lazy<Action<string>>( () => logger );
}
public static void Write( string format, params object[] args)
{
var message = string.Format( format, args );
Write( message );
}
public static void Write( string message )
{
Logger( message + Environment.NewLine );
}
private static OutputWindowPane GetWindow()
{
var dte = (DTE2) Marshal.GetActiveObject( "VisualStudio.DTE" );
return dte.ToolWindows.OutputWindow.ActivePane;
}
}
If you want anything to appear in the Output window, it has to come from stdout. To do this, your app needs to be linked as a "console" app. Set the /SUBSYSTEM:CONSOLE flag in the project's property page, under Linker/System set the SubSystem property to CONSOLE.
Once you have your output in the window, if you include the text "Error:" it will appear as an error, or if you set "Warning:" it will appear as a warning. If your error text begins with a path/filename, followed by a line number in parenthesis, the IDE will recognize it as a "clickable" error, and navigate you automatically to the faulting line.
This is demonstrated in the following helper class from a Microsoft sample project:
https://github.com/microsoft/VSSDK-Extensibility-Samples/blob/df10d37b863feeff6e8fcaa6f4d172f602a882c5/Reference_Services/C%23/Reference.Services/HelperFunctions.cs#L28
The code is as follows:
using System;
using System.Diagnostics;
using Microsoft.VisualStudio.Shell.Interop;
namespace Microsoft.Samples.VisualStudio.Services
{
/// <summary>
/// This class is used to expose some utility functions used in this project.
/// </summary>
internal static class HelperFunctions
{
/// <summary>
/// This function is used to write a string on the Output window of Visual Studio.
/// </summary>
/// <param name="provider">The service provider to query for SVsOutputWindow</param>
/// <param name="text">The text to write</param>
internal static void WriteOnOutputWindow(IServiceProvider provider, string text)
{
// At first write the text on the debug output.
Debug.WriteLine(text);
// Check if we have a provider
if (null == provider)
{
// If there is no provider we can not do anything; exit now.
Debug.WriteLine("No service provider passed to WriteOnOutputWindow.");
return;
}
// Now get the SVsOutputWindow service from the service provider.
IVsOutputWindow outputWindow = provider.GetService(typeof(SVsOutputWindow)) as IVsOutputWindow;
if (null == outputWindow)
{
// If the provider doesn't expose the service there is nothing we can do.
// Write a message on the debug output and exit.
Debug.WriteLine("Can not get the SVsOutputWindow service.");
return;
}
// We can not write on the Output window itself, but only on one of its panes.
// Here we try to use the "General" pane.
Guid guidGeneral = Microsoft.VisualStudio.VSConstants.GUID_OutWindowGeneralPane;
IVsOutputWindowPane windowPane;
if (Microsoft.VisualStudio.ErrorHandler.Failed(outputWindow.GetPane(ref guidGeneral, out windowPane)) ||
(null == windowPane))
{
if (Microsoft.VisualStudio.ErrorHandler.Failed(outputWindow.CreatePane(ref guidGeneral, "General", 1, 0)))
{
// Nothing to do here, just debug output and exit
Debug.WriteLine("Failed to create the Output window pane.");
return;
}
if (Microsoft.VisualStudio.ErrorHandler.Failed(outputWindow.GetPane(ref guidGeneral, out windowPane)) ||
(null == windowPane))
{
// Again, there is nothing we can do to recover from this error, so write on the
// debug output and exit.
Debug.WriteLine("Failed to get the Output window pane.");
return;
}
if (Microsoft.VisualStudio.ErrorHandler.Failed(windowPane.Activate()))
{
Debug.WriteLine("Failed to activate the Output window pane.");
return;
}
}
// Finally we can write on the window pane.
if (Microsoft.VisualStudio.ErrorHandler.Failed(windowPane.OutputString(text)))
{
Debug.WriteLine("Failed to write on the Output window pane.");
}
}
}
}
You can use the Debug and/or Trace classes. There is some information here:
http://msdn.microsoft.com/en-us/library/bs4c1wda(VS.71).aspx
Best of luck.
use System.Diagnostics.Debugger.Message

Resources