CreateWindow throwing access violation from pointer from managed handleref - wpf

I may be way out of my depth here, so I'm sorry if I seem completely lost, but it's just because I am. (But, if I never went out of my depth, I guess I'd never learn anything)
I'm trying to figure out how to host a Win32 window in a WPF control but, being completely unknowledgable about the Windows API, I'm struggling to interact with it in basic ways.
Currently, I'm getting an access exception when I try to pass the parent window's HWND to the method "CreateWindow."
Here is my code:
///------------- VisualDerived.h -----------------
using namespace System::Runtime::InteropServices;
namespace POCPP
{
namespace WPF
{
namespace Controls
{
public ref class VisualDerived : System::Windows::Interop::HwndHost
{
private:
HWND *childWin;
protected:
virtual HandleRef BuildWindowCore(HandleRef trg) override;
virtual void DestroyWindowCore(HandleRef trg) override;
public:
VisualDerived();
};
}
}
}
// ------------------ VisualDerived.cpp -----------------------
#include "Stdafx.h"
#include <Windows.h>
#include <WinUser.h>
#include "VisualDerived.h"
#pragma comment(lib, "user32.lib")
HandleRef POCPP::WPF::Controls::VisualDerived::BuildWindowCore(HandleRef trg)
{
DWORD windowOptions = WS_CHILDWINDOW;
HWND *parentWindow = (HWND*)trg.Handle.ToPointer();
HWND chld = CreateWindow(NULL, NULL, windowOptions, 0, 0, 200, 200, *parentWindow, 0, 0, NULL); // throws access exception as is, returns null reference exception without the pointer to the parentWindow
return HandleRef(NULL, System::IntPtr(&chld));
}
void POCPP::WPF::Controls::VisualDerived::DestroyWindowCore(HandleRef trg)
{
}
POCPP::WPF::Controls::VisualDerived::VisualDerived()
{
}
Anyway, I'm sorry if my problem seems noobish to the seasoned professionals here. I'm pretty far outside my comfort zone not knowing either the Windows API well, nor C++/CLI.
But I'm determined to learn how to do this, so any help would be great.

HWND *parentWindow = (HWND*)trg.Handle.ToPointer();
is wrong because Handle is the HWND. Change it to:
HWND parentWindow = (HWND)trg.Handle.ToPointer();
You are passing NULL as the window class when you call CreateWindow. You have to provide a window class.
Your return statement is also wrong. You are returning the address of a local variable. It should be:
return HandleRef(NULL, System::IntPtr(chld));

Related

Accessing WinForms member properties via delegates

I was somewhat surprised that can't easily access the properties of the WinForms class with functions you add as bublic, years ago with Borland VCL this wasn't an issue. Apparently 'delegates' is said to be the right keyword ... and I've been struggling with this for days now and can't get it to work. Is my approach beyond or is it just that the plug is not plugged in? Via the event handler, the forms property is successfully changed. Now i'm asking for a hint to transfer this capability down to sub1()?
#include "form1.h"
#include <Windows.h>
using namespace System;
using namespace System::Windows::Forms;
void sub1(void);
public delegate void Deleg(String^ st);
[STAThread]
void Main() // array<System::String^> ^args)
{
Application::EnableVisualStyles();
Application::SetCompatibleTextRenderingDefault(false);
App1::Form1 form;
Deleg^ Deleg1;
Application::Run(% form);
// Deleg1 += gcnew Deleg(form,&App1::Form1::newTxt); //?
}
void App1::Form1::button1_Click(System::Object^ s, System::EventArgs^ e)
{
String^ st = "ABC";
App1::Form1::newTxt(st);
Refresh();
sub1();
}
void sub1(void)
{
String^ s = "DEF";
// Deleg1(s);
}
I tried to copy some examples I found on the net, but apparently I don't hit the core.

Bringing wpf App to Foreground which is running in background? [duplicate]

I'm adding some code to an app that will launch another app if it isn't already running, or if it is, bring it to the front. This requires a small amount of interop/WinAPI code, which I've gotten examples for from other sites but can't seem to get to work in Win7.
If the window is in some visible state, then the API's SetForegroundWindow method works like a treat (and this would be the main case, as per company policy if the external app is running it should not be minimized). However, if it is minimized (exceptional but important as my app will appear to do nothing in this case), neither this method nor ShowWindow/ShowWindowAsync will actually bring the window back up from the taskbar; all of the methods simply highlight the taskbar button.
Here's the code; most of it works just fine, but the call to ShowWindow() (I've also tried ShowWindowAsync) just never does what I want it to no matter what the command I send is:
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
...
//The app is named uniquely enough that it can't be anything else,
//and is not normally launched except by this one.
//so this should normally return zero or one instance
var processes = Process.GetProcessesByName("ExternalApp.exe");
if (processes.Any()) //a copy is already running
{
//I can't currently tell the window's state,
//so I both restore and activate it
var handle = processes.First().MainWindowHandle;
ShowWindow(handle, SW_RESTORE); //GRR!!!
SetForegroundWindow(handle);
return true;
}
try
{
//If a copy is not running, start one.
Process.Start(#"C:\Program Files (x86)\ExternalApp\ExternalApp.exe");
return true;
}
catch (Exception)
{
//fallback for 32-bit OSes
Process.Start(#"C:\Program Files\ExternalApp\ExternalApp.exe");
return true;
}
I've tried SHOWNORMAL (1), SHOWMAXIMIZED (3), RESTORE (9), and a couple other sizing commands, but nothing seems to do the trick. Thoughts?
EDIT: I found an issue with some of the other code I had thought was working. The call to GetProcessesByName() was not finding the process because I was looking for the executable name, which was not the process name. That caused the code I thought was running and failing to actually not execute at all. I thought it was working because the external app will apparently also detect that a copy is already running and try to activate that current instance. I dropped the ".exe" from the process name I search for and now the code executes; however that seems to be a step backwards, as now the taskbar button isn't even highlighted when I call ShowWindow[Async]. So, I now know that neither my app, nor the external app I'm invoking, can change the window state of a different instance programmatically in Win7. What's goin' on here?
Working code using FindWindow method:
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string className, string windowTitle);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags);
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref Windowplacement lpwndpl);
private enum ShowWindowEnum
{
Hide = 0,
ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
Maximize = 3, ShowNormalNoActivate = 4, Show = 5,
Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8,
Restore = 9, ShowDefault = 10, ForceMinimized = 11
};
private struct Windowplacement
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
}
private void BringWindowToFront()
{
IntPtr wdwIntPtr = FindWindow(null, "Put_your_window_title_here");
//get the hWnd of the process
Windowplacement placement = new Windowplacement();
GetWindowPlacement(wdwIntPtr, ref placement);
// Check if window is minimized
if (placement.showCmd == 2)
{
//the window is hidden so we restore it
ShowWindow(wdwIntPtr, ShowWindowEnum.Restore);
}
//set user's focus to the window
SetForegroundWindow(wdwIntPtr);
}
You can use it by calling BringWindowToFront().
I always have one instance of the application running so if you can have several open instances simultaneously you might want to slightly change the logic.
... Apparently you cannot trust the information a Process gives you.
Process.MainWindowHandle returns the window handle of the first window created by the application, which is USUALLY that app's main top-level window. However, in my case, a call to FindWindow() shows that the handle of the actual window I want to restore is not what MainWindowHandle is pointing to. It appears that the window handle from the Process, in this case, is that of the splash screen shown as the program loads the main form.
If I call ShowWindow on the handle that FindWindow returned, it works perfectly.
What's even more unusual is that when the window's open, the call to SetForegroundWindow(), when given the process's MainWindowHandle (which should be invalid as that window has closed), works fine. So obviously that handle has SOME validity, just not when the window's minimized.
In summary, if you find yourself in my predicament, call FindWindow, passing it the known name of your external app's main window, to get the handle you need.
I had the same problem. The best solution I have found is to call ShowWindow with the flag SW_MINIMIZE, and then with SW_RESTORE. :D
Another possible solution:
// Code to display a window regardless of its current state
ShowWindow(hWnd, SW_SHOW); // Make the window visible if it was hidden
ShowWindow(hWnd, SW_RESTORE); // Next, restore it if it was minimized
SetForegroundWindow(hWnd); // Finally, activate the window
from comments at: http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx
Tray calling ShowWindow(handle, SW_RESTORE); after SetForegroundWindow(handle);
This might solve your problem.
It sounds like you're trying to perform an action that has the same result as alt-tabbing, which brings the window back if it was minimized while "remembering" if it was maximized.
NativeMethods.cs:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
// Specify your namespace here
namespace <your.namespace>
{
static class NativeMethods
{
// This is the Interop/WinAPI that will be used
[DllImport("user32.dll")]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fUnknown);
}
}
Main code:
// Under normal circumstances, only one process with one window exists
Process[] processes = Process.GetProcessesByName("ExternalApp.exe");
if (processes.Length > 0 && processes[0].MainWindowHandle != IntPtr.Zero)
{
// Since this simulates alt-tab, it restores minimized windows to their previous state
SwitchToThisWindow(process.MainWindowHandle, true);
return true;
}
// Multiple things are happening here
// First, the ProgramFilesX86 variable automatically accounts for 32-bit or 64-bit systems and returns the correct folder
// Secondly, $-strings are the C# shortcut for string.format() (It automatically calls .ToString() on each variable contained in { })
// Thirdly, if the process was able to start, the return value is not null
try { if (Process.Start($"{System.Environment.SpecialFolder.ProgramFilesX86}\\ExternalApp\\ExternalApp.exe") != null) return true; }
catch
{
// Code for handling an exception (probably FileNotFoundException)
// ...
return false;
}
// Code for when the external app was unable to start without producing an exception
// ...
return false;
I hope this provides a much simpler solution.
(General Rule: If a string value is ordinal, i.e. it belongs to something and isn't just a value, then it is better to get it programmatically. You'll save yourself a lot of trouble when changing things. In this case, I'm assuming that the install location can be converted to a global constant, and the .exe name can be found programmatically.)
I know its too late, still my working code is as follows so that someone later can get quick help :)
using System.Runtime.InteropServices;
using System.Diagnostics;
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
private static void ActivateApp(string processName)
{
Process[] p = Process.GetProcessesByName(processName);
if (p.Length > 0)
{
IntPtr handle = FindWindowByCaption(IntPtr.Zero, p[0].ProcessName);
ShowWindow(handle, 9); // SW_RESTORE = 9,
SetForegroundWindow(handle);
}
}
ActivateApp(YOUR_APP_NAME);
Actually, FindWindowByCaption is the key here, this method collects the window handle correctly when app is running silently in the system tray and also when app is minimized.

WM_HELP stops being send when vtk is started or spy++ is running

In our software we occasionally use sending WM_HELP via SendMessage api to a control. Normally the "HelpRequested" event is then fired (or up in the parent hierarchy until an event handler is registered).
We included an external complex 3d visualization library called "VTK" and after that, this Message passing does not work anymore. In trying to track down the problem I used Spy++ to see whether the message is shown there and realized that running spy++ is generating the same problem! (Also without any vtk stuff). It can be shown with this little Program:
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace TestHelp
{
public partial class Form1 : Form
{
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
struct HelpInfo
{
public uint cbSize;
public int iContextType;
public int iCtrlID;
public int hItemHandle;
public int dwContextID;
public int MouseX;
public int MouseY;
}
[DllImport("user32.DLL", EntryPoint = "SendMessage", SetLastError = true)]
private static extern int SendHelpMessage(int hWnd, uint Msg, uint wparam, ref HelpInfo helpinfo);
public static void RaiseHelp(Control ctrl)
{
HelpInfo helpInfo = new HelpInfo
{
cbSize = 48,
iContextType = 1,
iCtrlID = 0,
hItemHandle = ctrl.Handle.ToInt32(),
dwContextID = 0,
MouseX = 10,
MouseY = 10,
};
var res = SendHelpMessage(ctrl.Handle.ToInt32(), 0x053, 0, ref helpInfo);
Debug.WriteLine($"SendMessage returns:{res}");
}
public Form1()
{
InitializeComponent();
button1.HelpRequested += (sender, hlpevent) => { Trace.WriteLine("HelpRequested called"); };
timer = new Timer() {Interval = 1000, Enabled = true};
timer.Tick += (sender, args) => RaiseHelp(button1);
}
private Timer timer;
}
}
The form only contains a single button named "button1".
When you start in debugger you see "HelpRequested called" in Output window every second. When you start Spy++, nothing more, just start, it will stop that! When closing spy++ it continues to work again. Does everyone have an explanation for this behaviour? What is Spy++ doing with my application? My hope is that the same mechanism is responsible for the same problem with vtk (there only in-process, though).
Of course, using win32 api SendMessage may seem inappropriate for a WinForms application, but we don't have time now to refactor all that stuff, and I nevertheless want to understand whats happening here!
Btw.: user window message are not affected (WM_USER to 0x7FFF), checked by overriding WndProc. WM_HELP also does not show in WndProc while spy++ is running, btw.
Problem was wrong size for HelpInfo.cbSize. In 64-bit mode it is 40, in 32-bit it is 28. Yes I should use sizeof(), but this is only allowed in "unsafe" mode.
But how the hell spy++ or VTK interfere with this?

Getting error The calling thread cannot access this object because a different thread owns it wpf, How to use Dispatch.invovef

I am trying to catch bitmap image and display in bitmapsource I used conversion methos as follows
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
private BitmapSource Bitmap2BitmapImage(Bitmap bitmap)
{
BitmapImage retval;
try
{
retval = Imaging.CreateBitmapSourceFromHBitmap(
bitmap.GetHbitMap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
finally
{
DeleteObject(hBitmap)
}
return retval;
}
and code to display image is follows
Bitmap bmp= (Bitmap)e.frame.clone();
imgPic.source= imageconversion(bmp);
I have google it for this error...I come to know about dispatch.invoke method. Please suggest me how to use in my code
The dispatcher can be invoked with a new instance of System.Threading.ThreadStart which gets a lambda expression containing the code you want to be executed on the UI thread. Make sure to use Application.Current.Dispatcher and not Dispatcher.Current as the latter doesn't necessarily return the UI thread.
Application.Current.Dispatcher.Invoke(
DispatcherPriority.Input,
new ThreadStart(() =>
{
// Do something
}));

Activate WPF Window without losing focus on previous application/window

I have a WPF window in application which I Activate on some specific scenarios by calling MainView.Activate(); and MainView.BringIntoView(); method. it also sets focus on this 'MainView' window.
But my requirement is this window should not get Focus. i.e. my cursor should still remain on previous application(notepad,word etc..)
I tried using MainView.ShowActivated="False" but it didn't work.
Do I need to use HwndSource as mentioned here or what?
Code I have used after Kent's help (Its working only if Window is minimized):
IntPtr HWND_TOPMOST = new IntPtr(-1);
const short SWP_NOMOVE = 0X2;
const short SWP_NOSIZE = 1;
const short SWP_NOZORDER = 0X4;
const int SWP_SHOWWINDOW = 0x0040;
Process[] processes = Process.GetProcesses(".");
foreach (var process in processes)
{
IntPtr handle = process.MainWindowHandle;
if (handle != IntPtr.Zero)
{
SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW);
}
}
I Have Win32 helper class for such situation here is listing for your case
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace your.namespace {
public static class Win32 {
public static void ShowWindowNoActive( Window window) {
var hwnd = (HwndSource.FromVisual(window) as HwndSource).Handle;
ShowWindow(hwnd, ShowWindowCommands.SW_SHOWNOACTIVATE);
}
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow);
private enum ShowWindowCommands : int {
SW_SHOWNOACTIVATE = 4
}
}
}
Let's call the window that you want to show without focus: YourWindow. Add the following code:
YourWindow.ShowActivated = false;
YourWindow.Owner = TheOtherWindowThatWillStilHaveFocus;
First line you can also set from XAML.
In my recent blog post, I use SetWindowPos to bring a window to the front of the z-order without giving it focus. I don't believe WPF has an in-built means of achieving the same without p/invoke.
I needed to solve a similar problem and got it to work by setting Window.ShowActivate=false and then using Window.Show rather than Window.Activate. Perhaps this will work for you. I have not yet needed to use BringIntoView because my window is newly created and appears on top of other windows by default.

Resources