communicate with another process (same source code) via wndProc in WPF - c#

I'm working on implementing communicate with another process via HwndSource in WPF.
I want My Program (Let's call it A) to communicate with My Program (also A) that is on another process on windows.
following images will help me to explain what I've been doing and what I want to implement.
two windows are exactly same program (just different background-color), and they know exactly their Handle each other. If I notice two process have same title, then I call sendMessage which is defined in user32.dll with Message -Raw input message constant that can pump message globally- 0x0100WM_KEYDOWN.
// if same program - but different process
IntPtr lpData = Marshal.StringToHGlobalAnsi("Hi, Im Sender");
MessageBox.Show(Marshal.PtrToStringAnsi(lpData));
DisplayAnotherProcessHandle.Text = p.MainWindowHandle.ToString();
SendMessage(p.MainWindowHandle, WM_RawInput_key, IntPtr.Zero, lpData);
I successfully pass WM_RawInput_Key, IntPtr.Zero to opponent program. but I have problem with lParam. I can't get lParam data. I tried to Marshal c# string with Marshal.StringToBSTR or Marshal.StringToAuto ... but in following method I just get Empty.String.
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg != WM_RawInput_key) return IntPtr.Zero;
if (wParam == IntPtr.Zero) // <-- opponent process hit this conditional syntax nicely.
{
var str = Marshal.PtrToStringAnsi(lParam); // but I'm failed pass lParam, or Marshalling lParam I don't know ...
MessageBox.Show(str);
DisplaySucceedOrNot.Text = str;
}
return IntPtr.Zero;
}
I don't know which memory parameter IntPtr lParam indicates. I don't know even this technique is valid. I need your help. thank you for reading
following source code is my entire source code of example program.
<Window x:Class="wndProc_IPC_WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:wndProc_IPC_WPF"
mc:Ignorable="d"
Title="WndProc-IPC-WPF" Height="450" Width="800">
<StackPanel>
<Button Name="AddHandler" Width="Auto" Height="100" Content="Add Handler to This Handle" Click="AddHandler_Click"></Button>
<Button Name="CreateNewProcess" Width="Auto" Height="100" Content="Create New Process Window" Click="CreateNewProcess_Click"></Button>
<Button Width="Auto" Height="100" Content="SendMessage To Another Process" Name="SendMessageBtn" Click="SendMessage_Click"></Button>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MyHandle : "></TextBlock>
<TextBlock x:Name="DisplayMyProcessHandle" Margin="50 0 0 0"></TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Another Handle : "></TextBlock>
<TextBlock x:Name="DisplayAnotherProcessHandle" Margin="50 0 0 0"></TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Succed?"></TextBlock>
<TextBlock x:Name="DisplaySucceedOrNot" Margin="50 0 0 0"></TextBlock>
</StackPanel>
</StackPanel>
</Window>
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
private static extern bool SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
string AppName;
public static UInt32 WM_RawInput_key = 0x0100;
public MainWindow()
{
InitializeComponent();
AppName = this.Title;
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg != WM_RawInput_key) return IntPtr.Zero;
if (wParam == IntPtr.Zero)
{
var str = Marshal.PtrToStringAnsi(lParam);
MessageBox.Show(str);
DisplaySucceedOrNot.Text = str;
}
return IntPtr.Zero;
}
private void CreateNewProcess_Click(object sender, RoutedEventArgs e)
{
if (Process.GetProcessesByName(AppName).Length > 1)
{
MessageBox.Show("Already 2 Process Exist.");
return;
}
var path = (System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
Process p = new Process();
p.StartInfo.FileName = path;
p.Start();
}
private void SendMessage_Click(object sender, RoutedEventArgs e)
{
foreach (var p in Process.GetProcessesByName(AppName))
{
if (p.MainWindowHandle == Process.GetCurrentProcess().MainWindowHandle) continue;
{
IntPtr lpData = Marshal.StringToHGlobalAnsi("Hi, Im Sender");
MessageBox.Show(Marshal.PtrToStringAnsi(lpData));
DisplayAnotherProcessHandle.Text = p.MainWindowHandle.ToString();
SendMessage(p.MainWindowHandle, WM_RawInput_key, IntPtr.Zero, lpData);
}
}
}
private void AddHandler_Click(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(Process.GetCurrentProcess().MainWindowHandle);
source.AddHook(new HwndSourceHook(WndProc));
}
}

The wparam and lparam are just a pointers to your local process memory, which the other process cannot access. The wparam works well in your example as you just process the pointer value and not the data the it points to.
For a similar purpose I used WM_COPYDATA message where you provide a pointer to a COPYDATASTRUCT. There you can encapsulate also complex data structs, which can be handled read-only in your receiving process.
Microsoft provides a useful example here:
https://learn.microsoft.com/en-us/windows/win32/dataxchg/using-data-copy

Thank you Mihaeru, actually I was on finding example source of sendMessage/wndProc with WM_COPYDATA in Wpf after posting my question. Like Raw-Input message WM_KEYDOWN, WM_COPYDATA also provide global message pump(right?, I don't know exact theory on old windows... -_-;;)
I failed many times. and I found that I should allocate cbData with Length of string. if I add Attribute [MarshalAs(UnmanagedType.LpWStr)] which 2 byte per character, then I need allocate string.length*2 to cbData.
I succeed to pass message to opponent process.
following source is answer source
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
[DllImport("user32.dll")]
private static extern bool SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, ref COPYDATASTRUCT lParam);
string AppName;
public static UInt32 WM_COPYDATA = 0x004A;
public MainWindow()
{
InitializeComponent();
AppName = this.Title;
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg != WM_COPYDATA) return IntPtr.Zero;
if (wParam == IntPtr.Zero)
{
COPYDATASTRUCT cd = new COPYDATASTRUCT();
cd = (COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(COPYDATASTRUCT));
MessageBox.Show(cd.lpData);
DisplaySucceedOrNot.Text = cd.lpData;
}
return IntPtr.Zero;
}
private void CreateNewProcess_Click(object sender, RoutedEventArgs e)
{
if (Process.GetProcessesByName(AppName).Length > 1)
{
MessageBox.Show("Already 2 Process Exist.");
return;
}
var path = (System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase);
Process p = new Process();
p.StartInfo.FileName = path;
p.Start();
}
private void SendMessage_Click(object sender, RoutedEventArgs e)
{
foreach (var p in Process.GetProcessesByName(AppName))
{
if (p.MainWindowHandle == Process.GetCurrentProcess().MainWindowHandle) continue;
{
IntPtr lpData = Marshal.StringToHGlobalAnsi("Hi, Im Sender");
COPYDATASTRUCT cd = new COPYDATASTRUCT();
cd.lpData = "Hi, Im Sender";
cd.dwData = p.MainWindowHandle;
cd.cbData = cd.lpData.Length+1;
MessageBox.Show(cd.lpData);
DisplayAnotherProcessHandle.Text = p.MainWindowHandle.ToString();
SendMessage(p.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, ref cd);
}
}
}
private void AddHandler_Click(object sender, RoutedEventArgs e)
{
HwndSource source = HwndSource.FromHwnd(Process.GetCurrentProcess().MainWindowHandle);
source.AddHook(new HwndSourceHook(WndProc));
DisplayMyProcessHandle.Text = Process.GetCurrentProcess().MainWindowHandle.ToString();
}

Related

Control the application using C# application

i want to clicked the save button of another application from my c# application.if there is data in that application then save dialogbox will appear while in absence of data message box will appear.After i want to give the filename as date and time similarly i want to click the ok button for messagebox.
i did it using 3 button save,enter filename button and ok button but the problem is when i clicked the save button window is changed to either save dialogbox or message box after that my c# application freezed. if i restart c# application it will work.how to solve this?
if possible i want to do it using single button.
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]static public extern bool GetWindowRect(IntPtr hWnd, out Rectangle lpRect);
[DllImport("user32.dll")]
private static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern int SendNotifyMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
private static extern int SetForegroundWindow(IntPtr points);
[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
//save button
private void saveBtn_Click(object sender, EventArgs e)
{
IntPtr maindHwnd = FindWindow(null, "app1");
IntPtr maindHwnd1 = FindWindow(null, "Error");
if (maindHwnd != IntPtr.Zero)
{
IntPtr panel = FindWindowEx(maindHwnd, IntPtr.Zero, "MDIClient", null);
IntPtr panel1 = FindWindowEx(panel, IntPtr.Zero, "TAveForm", null);
IntPtr panel2 = FindWindowEx(panel1, IntPtr.Zero, "TPanel", "Panel5");
IntPtr panel3 = FindWindowEx(panel2, IntPtr.Zero, "TPanel", null);
IntPtr childHwnd = FindWindowEx(panel3, IntPtr.Zero, "TBitBtn", "Save");
if (childHwnd != IntPtr.Zero)
{
SendMessage(childHwnd, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
}
}
//click messagebox
private void button4_Click(object sender, EventArgs e)
{
IntPtr hWnd = FindWindow(null, "Error");
if (hWnd != IntPtr.Zero)
{
IntPtr childHwnd = FindWindowEx(hWnd, IntPtr.Zero, "Button", "Ok");
if (childHwnd != IntPtr.Zero)
{
SendMessage(childHwnd, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
else
{
textBox3.BackColor = Color.Yellow;
textBox3.Text = "error;
}
}
else
{
textBox3.BackColor = Color.Yellow;
textBox3.Text = "hmd is zero";
}
}
//save dialogbox
String textBox = DateTime.Now.ToString("yyyyMMdd_HH-mm-ss");
IntPtr maindHwnd = FindWindow(null, "save");
IntPtr hWnd = FindWindow(null, "Error");
if (maindHwnd != IntPtr.Zero)
{
{
IntPtr panel = FindWindowEx(maindHwnd, IntPtr.Zero, "ComboBoxEx32", null);
IntPtr panel1 = FindWindowEx(panel, IntPtr.Zero, "ComboBox", null);
IntPtr panel2 = FindWindowEx(panel1, IntPtr.Zero, "Edit", null);
if (panel2 != IntPtr.Zero)
{
SendKeys.Send(textBox);
}
} ```
IMHO the easiest way is to first, find out the target application Window Handle and target application's process ID.
Then you can interrupt with it's child objects.
Example:
public static void chooseMode(IntPtr hnd, int PID)
{
if (hnd != IntPtr.Zero)
{
var proc = Process.GetProcessById(PID);
proc.WaitForInputIdle();
SetForegroundWindow(hnd);
SendKeys.SendWait(#"%V {UP 3} ~");
}
}

I need to simulate input, why HtmlElement.Focus doesn't work?

I'm trying to write a web bot which should be undetected so I don't use InnerText property and instead trying to simulate keypresses. To do so I need to focus an <input> element but the focus is switched to another textfield by itself instead even right after element.Focus() call.
I tried also using element.InvokeMember("click"); but it doesn't work either.
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
public const Int32 WM_CHAR = 0x0102;
public const Int32 WM_KEYDOWN = 0x0100;
public const Int32 WM_KEYUP = 0x0101;
public const Int32 VK_RETURN = 0x0D;
IntPtr BrowserHandle
{
get
{
var hwnd = _browser.Handle;
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell Embedding", null);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Shell DocObject View", null);
hwnd = FindWindowEx(hwnd, IntPtr.Zero, "Internet Explorer_Server", null);
return hwnd;
}
}
readonly Random _rnd = new Random();
private void Form1_Load(object sender, EventArgs e)
{
_browser.Navigate("https://...");
_browser.DocumentCompleted += _browser_DocumentCompleted;
}
private async void _browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (e.Url.AbsoluteUri != "https://...") return;
var inputs = _browser.Document.GetElementsByTagName("input");
{
var el = inputs.OfType<HtmlElement>().FirstOrDefault(x => x.GetAttribute("className").Contains("login"));
await SendTextAsync(el, "sdfoj30jfoigjdlsgfdgd");
}
}
async Task SendTextAsync(HtmlElement element, string text)
{
for (var i = 0; i < text.Length; i++, await Task.Delay(_rnd.Next(0, 500)))
{
if (_browser.Document.ActiveElement != element)
{
element.Focus(); // doesn't work
if (_browser.Document.ActiveElement != element)
{
element.InvokeMember("click"); // either
if (_browser.Document.ActiveElement != element)
{
element.Focus();
await Task.Delay(_rnd.Next(50, 250)); // anyway
}
}
}
var c = text[i];
SendCharacter(c, BrowserHandle);
}
}
static void SendCharacter(char character, IntPtr hWnd)
{
var key = new IntPtr(character);
SendMessage(hWnd, WM_KEYDOWN, key, IntPtr.Zero);
SendMessage(hWnd, WM_CHAR, key, IntPtr.Zero);
SendMessage(hWnd, WM_KEYUP, key, IntPtr.Zero);
}
}
I expected this code to fill the login textfield but instead it writes a few characters there and others go to the password textfield.
The solution is to lookup for element with GetElementsByTagName each time instead of reusing the reference.

DPI scaling problem with hosted native HWND control inside WPF window

I have a high dpi setting on my monitor as the monitor is a fairly small 3840 x 2160 monitor.
This is causing issues with one of the applications I am writing as I am hosting a control in my main application. I developed it based off a nice example in the WPF-Samples.
On my screen, when the example is run in it's default state, the output looks like
I was able to account for the list control being the incorrect size by using
//MainWindow
PresentationSource source = PresentationSource.FromVisual(this); //Account for any scaling on the screen7
_listControl = new ControlHost(ControlHostElement.ActualWidth, ControlHostElement.ActualHeight, source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22);
...
//ControlHost
public ControlHost(double height, double width, double dpXScale, double dpYScale)
{
_hostHeight = (int) (height * dpXScale);
_hostWidth = (int) (width * dpYScale);
}
However, even when it is the correct size, the hosted UI is not scaled as the rest of the program is.
How could the program scale the hosted UI based on the DPI of the user's screen?
There are three main files that make up the this example.
ControlHost.cs
// // Copyright (c) Microsoft. All rights reserved.
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
#region Using directives
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
#endregion
namespace WPFHostingWin32Control
{
public class ControlHost : HwndHost
{
internal const int
WsChild = 0x40000000,
WsVisible = 0x10000000,
LbsNotify = 0x00000001,
HostId = 0x00000002,
ListboxId = 0x00000001,
WsVscroll = 0x00200000,
WsBorder = 0x00800000;
private readonly int _hostHeight;
private readonly int _hostWidth;
private IntPtr _hwndHost;
public ControlHost(double height, double width, double dpXScale, double dpYScale)
{
_hostHeight = (int) (height * dpXScale);
_hostWidth = (int) (width * dpYScale);
}
public IntPtr HwndListBox { get; private set; }
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
HwndListBox = IntPtr.Zero;
_hwndHost = IntPtr.Zero;
_hwndHost = CreateWindowEx(0, "static", "",
WsChild | WsVisible,
0, 0,
_hostHeight, _hostWidth,
hwndParent.Handle,
(IntPtr) HostId,
IntPtr.Zero,
0);
HwndListBox = CreateWindowEx(0, "listbox", "",
WsChild | WsVisible | LbsNotify
| WsVscroll | WsBorder,
0, 0,
_hostHeight, _hostWidth,
_hwndHost,
(IntPtr) ListboxId,
IntPtr.Zero,
0);
return new HandleRef(this, _hwndHost);
}
protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
handled = false;
return IntPtr.Zero;
}
protected override void DestroyWindowCore(HandleRef hwnd)
{
DestroyWindow(hwnd.Handle);
}
//PInvoke declarations
[DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
internal static extern IntPtr CreateWindowEx(int dwExStyle,
string lpszClassName,
string lpszWindowName,
int style,
int x, int y,
int width, int height,
IntPtr hwndParent,
IntPtr hMenu,
IntPtr hInst,
[MarshalAs(UnmanagedType.AsAny)] object pvParam);
[DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
internal static extern bool DestroyWindow(IntPtr hwnd);
}
}
MainWindow.xaml
<Window x:Class="WPFHostingWin32Control.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPFHostingWin32Control"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" Loaded="On_UIReady">
<DockPanel Background="LightGreen">
<Border Name="ControlHostElement"
Width="200"
Height="200"
HorizontalAlignment="Right"
VerticalAlignment="Top"
BorderBrush="LightGray"
BorderThickness="3"
DockPanel.Dock="Right"/>
<StackPanel>
<Label HorizontalAlignment="Center"
Margin="0,10,0,0"
FontSize="14"
FontWeight="Bold">Control the Control</Label>
<TextBlock Margin="10,10,10,10" >Selected Text: <TextBlock Name="selectedText"/></TextBlock>
<TextBlock Margin="10,10,10,10" >Number of Items: <TextBlock Name="numItems"/></TextBlock>
<Line X1="0" X2="200"
Stroke="LightYellow"
StrokeThickness="2"
HorizontalAlignment="Center"
Margin="0,20,0,0"/>
<Label HorizontalAlignment="Center"
Margin="10,10,10,10">Append an Item to the List</Label>
<StackPanel Orientation="Horizontal">
<Label HorizontalAlignment="Left"
Margin="10,10,10,10">Item Text</Label>
<TextBox HorizontalAlignment="Left"
Name="txtAppend"
Width="200"
Margin="10,10,10,10" />
</StackPanel>
<Button HorizontalAlignment="Left"
Click="AppendText"
Width="75"
Margin="10,10,10,10">Append</Button>
<Line X1="0" X2="200"
Stroke="LightYellow"
StrokeThickness="2"
HorizontalAlignment="Center"
Margin="0,20,0,0"/>
<Label HorizontalAlignment="Center"
Margin="10,10,10,10">Delete the Selected Item</Label>
<Button Click="DeleteText"
Width="125"
Margin="10,10,10,10"
HorizontalAlignment="Left">Delete</Button>
</StackPanel>
</DockPanel>
</Window>
MainWindow.cs
// // Copyright (c) Microsoft. All rights reserved.
// // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace WPFHostingWin32Control
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
internal const int
LbnSelchange = 0x00000001,
WmCommand = 0x00000111,
LbGetcursel = 0x00000188,
LbGettextlen = 0x0000018A,
LbAddstring = 0x00000180,
LbGettext = 0x00000189,
LbDeletestring = 0x00000182,
LbGetcount = 0x0000018B;
private Application _app;
private IntPtr _hwndListBox;
private int _itemCount;
private ControlHost _listControl;
private Window _myWindow;
private int _selectedItem;
public MainWindow()
{
InitializeComponent();
}
private void On_UIReady(object sender, EventArgs e)
{
_app = Application.Current;
_myWindow = _app.MainWindow;
_myWindow.SizeToContent = SizeToContent.WidthAndHeight;
PresentationSource source = PresentationSource.FromVisual(this); //Account for any scaling on the screen7
_listControl = new ControlHost(ControlHostElement.ActualWidth, ControlHostElement.ActualHeight, source.CompositionTarget.TransformToDevice.M11, source.CompositionTarget.TransformToDevice.M22);
ControlHostElement.Child = _listControl;
_listControl.MessageHook += ControlMsgFilter;
_hwndListBox = _listControl.HwndListBox;
for (var i = 1; i <= 100; i++) //populate listbox
{
var itemText = "Item" + i;
SendMessage(_hwndListBox, LbAddstring, IntPtr.Zero, itemText);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private void AppendText(object sender, EventArgs args)
{
if (txtAppend.Text != string.Empty)
{
SendMessage(_hwndListBox, LbAddstring, IntPtr.Zero, txtAppend.Text);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private void DeleteText(object sender, EventArgs args)
{
_selectedItem = SendMessage(_listControl.HwndListBox, LbGetcursel, IntPtr.Zero, IntPtr.Zero);
if (_selectedItem != -1) //check for selected item
{
SendMessage(_hwndListBox, LbDeletestring, (IntPtr) _selectedItem, IntPtr.Zero);
}
_itemCount = SendMessage(_hwndListBox, LbGetcount, IntPtr.Zero, IntPtr.Zero);
numItems.Text = "" + _itemCount;
}
private IntPtr ControlMsgFilter(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
int textLength;
handled = false;
if (msg == WmCommand)
{
switch ((uint) wParam.ToInt32() >> 16 & 0xFFFF) //extract the HIWORD
{
case LbnSelchange: //Get the item text and display it
_selectedItem = SendMessage(_listControl.HwndListBox, LbGetcursel, IntPtr.Zero, IntPtr.Zero);
textLength = SendMessage(_listControl.HwndListBox, LbGettextlen, IntPtr.Zero, IntPtr.Zero);
var itemText = new StringBuilder();
SendMessage(_hwndListBox, LbGettext, _selectedItem, itemText);
selectedText.Text = itemText.ToString();
handled = true;
break;
}
}
return IntPtr.Zero;
}
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
int msg,
IntPtr wParam,
IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern int SendMessage(IntPtr hwnd,
int msg,
int wParam,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder lParam);
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
internal static extern IntPtr SendMessage(IntPtr hwnd,
int msg,
IntPtr wParam,
string lParam);
}
}
Winforms does not handle high-DPI settings very well by default. As stated in the comments, you can try some of the available manifest settings to get it to resize. If that won't work for your situation then you will need to scale the control yourself. Most Winforms controls have a Scale method you could call when you adjust the height and width:
_listControl.Scale(dpXScale, dpYScale);
of course, since your actual code uses a custom control your mileage may vary.

Getting Wndproc events to work with WPF?

i'm currently trying to get wndproc handling in wpf but without any success..
i need to get the event of window created, window activated and window destroid events.
here's what i tried so far
int uMsgNotify;
public MainWindow()
{
InitializeComponent();
WinApi.SetTaskmanWindow(new WindowInteropHelper(this).Handle);
WinApi.RegisterShellHookWindow(new WindowInteropHelper(this).Handle);
uMsgNotify = WinApi.RegisterWindowMessage("SHELLHOOK");
MainForm.ShowInTaskbar = false;
MainForm.ShowActivated = true;
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
HwndSource source = PresentationSource.FromVisual(this) as HwndSource;
source.AddHook(WndProc);
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
IntPtr handle;
if (msg == uMsgNotify)
{
switch (wParam.ToInt32())
{
case WinApi.HSHELL_WINDOWCREATED:
handle = lParam;
string windowName = GetWindowName(handle);
IntPtr hWnd = WinApi.FindWindow(null, windowName);
add_icon(windowName, handle);// add new task's icon in taskbar
break;
case WinApi.HSHELL_WINDOWACTIVATED:
handle = lParam;
break;
case WinApi.HSHELL_WINDOWDESTROYED:
handle = lParam;
del_icon(handle); //remove icon from taskbar
break;
}
}
return IntPtr.Zero;
}
private static string GetWindowName(IntPtr hWnd)
{
// Allocate correct string length first
int length = WinApi.GetWindowTextLength(hWnd);
StringBuilder sb = new StringBuilder(length + 1);
WinApi.GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
code doesn't give any kind of runtime error whatsoever.... but it wont work..
to make more sense i'm developing an alternate shell for windows for my gaming cafe... where it needs to have a kind of taskbar..
any help?
I don't know if you still need it but : If you use ShowInTaskbar = false, it won't be able to catch any message with WndProc.

Hooking into Windows message loop in WPF window adds white border on the inside

I am trying to create a WPF window with WindowStyle="None" (for custom buttons and no title) that cannot be resized. Setting ResizeMode to NoResize removes the aero border, which I want to keep.
I could set the min/max size properties and be done with it, except that:
The resize cursors are still visible, and
The window is displayed in response to a user action and fits to its contents. It displays an image, so the size changes.
So, I have a simple scheme that gets me 99% of the way there:
public class BorderedWindowNoResize : Window
{
[DllImport( "DwmApi.dll" )]
public static extern int DwmExtendFrameIntoClientArea(
IntPtr hwnd,
ref MARGINS pMarInset );
[DllImport( "user32.dll", CharSet = CharSet.Auto )]
public static extern IntPtr DefWindowProc(
IntPtr hWnd,
int msg,
IntPtr wParam,
IntPtr lParam );
public BorderedWindowNoResize()
{
Loaded += BorderedWindowNoResize_Loaded;
}
private void BorderedWindowNoResize_Loaded( object sender, RoutedEventArgs e )
{
IntPtr mainWindowPtr = new WindowInteropHelper( this ).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd( mainWindowPtr );
mainWindowSrc.AddHook( WndProc );
}
private IntPtr WndProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
{
var htLocation = DefWindowProc( hwnd, msg, wParam, lParam ).ToInt32();
if( msg == (uint)WM.NCHITTEST )
{
handled = true;
switch( htLocation )
{
case (int)HitTestResult.HTBOTTOM:
case (int)HitTestResult.HTBOTTOMLEFT:
case (int)HitTestResult.HTBOTTOMRIGHT:
case (int)HitTestResult.HTLEFT:
case (int)HitTestResult.HTRIGHT:
case (int)HitTestResult.HTTOP:
case (int)HitTestResult.HTTOPLEFT:
case (int)HitTestResult.HTTOPRIGHT:
htLocation = (int)HitTestResult.HTBORDER;
break;
}
}
return new IntPtr( htLocation );
}
}
Basically;
Override the window procedure.
Call the default window procedure.
If the message it is WM_NCHITTEST, check for the border results.
If it is a border, return the regular HTBORDER.
This works as far as allowing me to keep the aero window border and hiding the resize cursor(s), but it adds a ~5 pixel white border to the inside of my window.
In fact, even if I return the default windows procedure result at the top of WndPrc and do nothing else the border is still there. I need a different background color on my window, so this won't work for me.
Any ideas? Thanks in advance as always.
When you add your hook, you should only handle the messages you need to, and ignore the others. I believe you are handling certain messages twice, since you call DefWindowProc, but never set the handled parameter to true.
So in your case, you'd use:
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
if (msg == (uint)WM.NCHITTEST) {
handled = true;
var htLocation = DefWindowProc(hwnd, msg, wParam, lParam).ToInt32();
switch (htLocation) {
case (int)HitTestResult.HTBOTTOM:
case (int)HitTestResult.HTBOTTOMLEFT:
case (int)HitTestResult.HTBOTTOMRIGHT:
case (int)HitTestResult.HTLEFT:
case (int)HitTestResult.HTRIGHT:
case (int)HitTestResult.HTTOP:
case (int)HitTestResult.HTTOPLEFT:
case (int)HitTestResult.HTTOPRIGHT:
htLocation = (int)HitTestResult.HTBORDER;
break;
}
return new IntPtr(htLocation);
}
return IntPtr.Zero;
}
Also, I'd probably add the hook in an OnSourceInitialized override, like so:
protected override void OnSourceInitialized(EventArgs e) {
base.OnSourceInitialized(e);
IntPtr mainWindowPtr = new WindowInteropHelper(this).Handle;
HwndSource mainWindowSrc = HwndSource.FromHwnd(mainWindowPtr);
mainWindowSrc.AddHook(WndProc);
}
You can try from anywhere in a WPF App
ComponentDispatcher.ThreadFilterMessage += new ThreadMessageEventHandler(ComponentDispatcherThreadFilterMessage);
and:
// ******************************************************************
private static void ComponentDispatcherThreadFilterMessage(ref MSG msg, ref bool handled)
{
if (!handled)
{
if (msg.message == WmHotKey)
{
HotKey hotKey;
if (_dictHotKeyToCalBackProc.TryGetValue((int)msg.wParam, out hotKey))
{
if (hotKey.Action != null)
{
hotKey.Action.Invoke(hotKey);
}
handled = true;
}
}
}
}

Categories

Resources