Related
So I'm trying to make a winapi application by using PInvoke. So far I've been able to open a window, but from there, I can't seem to make the WndProc work. Right now, the application starts, but it won't respond neither to resize, nor to movement or anything, except the hovering above the control buttons.
WindowLL.cs:
public delegate long WndProc(IntPtr hWnd, uint message, byte wParam, long lParam);
public static unsafe class WindowLL
{
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern ushort RegisterClassExA(ref WNDCLASSEX _class);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern IntPtr CreateWindowExA(
uint extraStyle,
ushort className,
//[MarshalAs(UnmanagedType.LPWStr)]
//string className,
[MarshalAs(UnmanagedType.LPStr)]
string title,
uint style,
int x,
int y,
int width,
int height,
IntPtr parent,
IntPtr menu,
IntPtr instance,
IntPtr parameter
);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern bool DestroyWindow(IntPtr hWnd);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
public static extern bool ShowWindow(IntPtr hWnd, int cmdShow);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMessage(
out MSG lpMsg,
IntPtr hWnd,
uint wMsgFilterMin,
uint wMsgFilterMax
);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint DispatchMessage(ref MSG lpMsg);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern uint TranslateMessage(ref MSG lpMsg);
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern long DefWindowProcA(
IntPtr hWnd,
uint Msg,
byte wParam,
long lParam
);
}
Structures (split into different files):
[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public uint message;
public byte wParam;
public long lParam;
public short time;
public POINT pt;
public short lPrivate;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public long X;
public long Y;
}
[StructLayout(LayoutKind.Sequential)]
public struct WNDCLASSEX
{
public uint cbSize;
public uint style;
public WndProc lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
[MarshalAs(UnmanagedType.LPStr)]
public string lpszMenuName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpszClassName;
public IntPtr hIconSm;
}
Program.cs:
class Program
{
static long WndProc(IntPtr hWnd, uint message, byte wParam, long lParam)
{
return WindowLL.DefWindowProcA(hWnd, message, wParam, lParam);
}
static void Main(string[] args)
{
var winCalss = new WNDCLASSEX() {
lpszClassName = "test",
lpfnWndProc = WndProc,
cbSize = (uint)Marshal.SizeOf(typeof(WNDCLASSEX)),
};
var classId = WindowLL.RegisterClassExA(ref winCalss);
var overlapped =
WindowStyle.Overlapped |
WindowStyle.Caption |
WindowStyle.SysMenu |
WindowStyle.ThickFrame |
WindowStyle.MinimizeBox |
WindowStyle.MaximizeBox;
IntPtr hWnd = WindowLL.CreateWindowExA(0,
classId, "test",
(uint)overlapped,
0, 0,
1280, 720,
IntPtr.Zero, IntPtr.Zero,
IntPtr.Zero, IntPtr.Zero
);
WindowLL.ShowWindow(hWnd, (int)NCmdShow.Show);
while (WindowLL.GetMessage(out var msg, hWnd, 0, 0)) {
WindowLL.TranslateMessage(ref msg);
WindowLL.DispatchMessage(ref msg);
}
}
}
Your structures are mostly wrong.
wParam / lParam should both be IntPtr, time should be uint, point x/y should be int.
You don't even need to write them yourself, copy-paste from the framework, MIT license allow that:
https://source.dot.net/#WindowsBase/System/Windows/Interop/MSG.cs,8a0b462145bdb8d8
https://source.dot.net/#PresentationFramework/System/Windows/Standard/NativeMethods.cs,527982c752231945
One more thing. You should not be using A-functions and structures like CreateWindowExA. These are compatibility shims for Windows 95, 98 and Me, all 3 discontinued decades ago. Modern software should only use W-versions. As a nice side effect strings marshalling is way cheaper, C# strings are stored as UTF-16 which matches the encoding expected by W WinApi functions.
I've been doing a program to send a text and enter key on a background running program , the sendmessage (string) works fine but after I call postmessage(VK_ENTER) it works only if the window is focused
here is code in C# (Visual Studio 2016)
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam);
private static string SetText(IntPtr handle)
{
const int VK_RETURN = 0x0D;
const int WM_KEYDOWN = 0x100;
SendMessage(handle, 0x000C, 0, textSend);
PostMessage(handle, WM_KEYDOWN, VK_RETURN, IntPtr.Zero);
}
Also I've tried with Sendmessage and it has the same result (the VK_RETURN isn't sent only if the window is not focused).
I want to create a new class and then show a window with the specefied class.
I wrote following codes for that :
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool UpdateWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx")]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
string lpClassName,
string lpWindowName,
int dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern System.UInt16 RegisterClassW(
[System.Runtime.InteropServices.In] ref WNDCLASSEX lpWndClass
);
struct WNDCLASSEX
{
[MarshalAs(UnmanagedType.U4)]
public int cbSize;
[MarshalAs(UnmanagedType.U4)]
public int style;
public IntPtr lpfnWndProc; // not WndProc
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
public IntPtr hIconSm;
}
[DllImport("user32.dll")]
static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
WNDCLASSEX wind_class = new WNDCLASSEX();
wind_class.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX));
wind_class.style = 0x00020000;
wind_class.hbrBackground = (IntPtr) 5;
wind_class.cbClsExtra = 0;
wind_class.cbWndExtra = 0;
wind_class.hInstance = Marshal.GetHINSTANCE(GetType().Module);
wind_class.hIcon = this.Icon.Handle;
wind_class.hCursor = IntPtr.Zero;
wind_class.lpszMenuName = string.Empty;
wind_class.lpszClassName = "MyClass";
wind_class.lpfnWndProc = DefWindowProc(IntPtr.Zero, 0, IntPtr.Zero, IntPtr.Zero);
RegisterClassW(ref wind_class);
IntPtr lolo = CreateWindowEx(0, "MyClass", "MyClass",0,0,0,30,40,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero,IntPtr.Zero);
ShowWindow(lolo, 1);
UpdateWindow(lolo);
}
}
}
But it isn't working correctly.
It doesn't show the window. when i search with Spy++ by my class name , It cant find any results..
I searched in web and do some edits in my code but they didn't work.
Where is my Problem !?
Regards.
Plain-vanilla Win32 is no fun from Win32 C, and it is much less fun from .NET. But anyway, it may be useful in rare cases. Here comes a C# class which should do what you want.
First, a general remarks on your software. In Win32, you should always check the return codes of the system calls, and in case of an error, call GetLastError() to get a more detailed information about the failure. If you had checked the result of the RegisterClass function and it failed, you would have known that it is useless to continue without having fixed that one.
The following is a class which may be used as template for further studies and which should successfully register and create a resizable window. Additionally some simple actions like doubleclick are processed in a custom Window procedure. For some parts of the code credit goes to this site being one of the very few I found in the web presenting something working.
The CreateWindowEx is tricky, because you must NOT put in a string as class name, but the result of the RegisterClassEx. The used constants are found mainly in the C-header file winuser.h. To be able to do something useful with this method, the main painting has to be done "by hand", being a cumbersome and tedious work by manipulationg Device Contexts properly. Of course, all this does not show up in the following example. The "create" method of the class may be called e.g. from a Windows Form button click. The namespace name is of course arbitrarily selected. The WinForm project is x86 32-bit, .NET 4.0.
Have fun!
namespace Win32FromForms
{
delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
class Win32Window
{
const UInt32 WS_OVERLAPPEDWINDOW = 0xcf0000;
const UInt32 WS_VISIBLE = 0x10000000;
const UInt32 CS_USEDEFAULT = 0x80000000;
const UInt32 CS_DBLCLKS = 8;
const UInt32 CS_VREDRAW = 1;
const UInt32 CS_HREDRAW = 2;
const UInt32 COLOR_WINDOW = 5;
const UInt32 COLOR_BACKGROUND = 1;
const UInt32 IDC_CROSS = 32515;
const UInt32 WM_DESTROY = 2;
const UInt32 WM_PAINT = 0x0f;
const UInt32 WM_LBUTTONUP = 0x0202;
const UInt32 WM_LBUTTONDBLCLK = 0x0203;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct WNDCLASSEX
{
[MarshalAs(UnmanagedType.U4)]
public int cbSize;
[MarshalAs(UnmanagedType.U4)]
public int style;
public IntPtr lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
public string lpszMenuName;
public string lpszClassName;
public IntPtr hIconSm;
}
private WndProc delegWndProc = myWndProc;
[DllImport("user32.dll")]
static extern bool UpdateWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
static extern bool DestroyWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx")]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
UInt16 regResult,
//string lpClassName,
string lpWindowName,
UInt32 dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
[DllImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassEx")]
static extern System.UInt16 RegisterClassEx([In] ref WNDCLASSEX lpWndClass);
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("user32.dll")]
static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
static extern void PostQuitMessage(int nExitCode);
//[DllImport("user32.dll")]
//static extern sbyte GetMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin,
// uint wMsgFilterMax);
[DllImport("user32.dll")]
static extern IntPtr LoadCursor(IntPtr hInstance, int lpCursorName);
[DllImport("user32.dll")]
static extern bool TranslateMessage([In] ref MSG lpMsg);
[DllImport("user32.dll")]
static extern IntPtr DispatchMessage([In] ref MSG lpmsg);
internal bool create()
{
WNDCLASSEX wind_class = new WNDCLASSEX();
wind_class.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX));
wind_class.style = (int)(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS ) ; //Doubleclicks are active
wind_class.hbrBackground = (IntPtr) COLOR_BACKGROUND +1 ; //Black background, +1 is necessary
wind_class.cbClsExtra = 0;
wind_class.cbWndExtra = 0;
wind_class.hInstance = Marshal.GetHINSTANCE(this.GetType().Module); ;// alternative: Process.GetCurrentProcess().Handle;
wind_class.hIcon = IntPtr.Zero;
wind_class.hCursor = LoadCursor(IntPtr.Zero, (int)IDC_CROSS);// Crosshair cursor;
wind_class.lpszMenuName = null;
wind_class.lpszClassName = "myClass";
wind_class.lpfnWndProc = Marshal.GetFunctionPointerForDelegate(delegWndProc);
wind_class.hIconSm = IntPtr.Zero;
ushort regResult = RegisterClassEx(ref wind_class);
if (regResult == 0)
{
uint error = GetLastError();
return false;
}
string wndClass = wind_class.lpszClassName;
//The next line did NOT work with me! When searching the web, the reason seems to be unclear!
//It resulted in a zero hWnd, but GetLastError resulted in zero (i.e. no error) as well !!??)
//IntPtr hWnd = CreateWindowEx(0, wind_class.lpszClassName, "MyWnd", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 30, 40, IntPtr.Zero, IntPtr.Zero, wind_class.hInstance, IntPtr.Zero);
//This version worked and resulted in a non-zero hWnd
IntPtr hWnd = CreateWindowEx(0, regResult, "Hello Win32", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 300, 400, IntPtr.Zero, IntPtr.Zero, wind_class.hInstance, IntPtr.Zero);
if (hWnd == ((IntPtr)0))
{
uint error = GetLastError();
return false;
}
ShowWindow(hWnd, 1);
UpdateWindow(hWnd);
return true;
//The explicit message pump is not necessary, messages are obviously dispatched by the framework.
//However, if the while loop is implemented, the functions are called... Windows mysteries...
//MSG msg;
//while (GetMessage(out msg, IntPtr.Zero, 0, 0) != 0)
//{
// TranslateMessage(ref msg);
// DispatchMessage(ref msg);
//}
}
private static IntPtr myWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
{
switch (msg)
{
// All GUI painting must be done here
case WM_PAINT:
break;
case WM_LBUTTONDBLCLK :
MessageBox.Show("Doubleclick");
break;
case WM_DESTROY:
DestroyWindow(hWnd);
//If you want to shutdown the application, call the next function instead of DestroyWindow
//PostQuitMessage(0);
break;
default:
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
}
}
The problem is the marshaling of strings to LPStr, if you do it like this then the class name works:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct WNDCLASSEX
{
[MarshalAs(UnmanagedType.U4)]
public int cbSize;
[MarshalAs(UnmanagedType.U4)]
public int style;
public IntPtr lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
[MarshalAs(UnmanagedType.LPStr)]
public string lpszMenuName;
[MarshalAs(UnmanagedType.LPStr)]
public string lpszClassName;
public IntPtr hIconSm;
}
[DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx")]
public static extern IntPtr CreateWindowEx(
int dwExStyle,
//UInt16 regResult,
[MarshalAs(UnmanagedType.LPStr)]
string lpClassName,
[MarshalAs(UnmanagedType.LPStr)]
string lpWindowName,
UInt32 dwStyle,
int x,
int y,
int nWidth,
int nHeight,
IntPtr hWndParent,
IntPtr hMenu,
IntPtr hInstance,
IntPtr lpParam);
I'm trying to write a hook into MS Excel using VSTO's, and I'm really close to what I need, but I have a minor issue.
I've used the low-level WINAPI calls to get the Keyboard events and check for the key-strokes (which works well).
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook, KeyCaptureDelegate lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr KeyCaptureCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
int pointerCode = Marshal.ReadInt32(lParam);
bool result = false;
if (wParam == (IntPtr)WM_KEYDOWN)
{
if (KeyDown != null)
{
result = KeyDown(pointerCode);
}
}
//if (result)
//return IntPtr.Zero;
}
return CallNextHookEx(_keyCaptureHook, nCode, wParam, lParam);
}
Everything is hooked up and working well (using SetWindowsHookEx) and my code gets called via KeyDown without any issues. The only problem is that I'm trying to override a default command in Excel, i.e. Ctrl+Shift+1. This causes the default functionality to occur.
In my code, result returns whether or not the behavior should be overwritten (i.e. use my new behavior, rather than default). I had hoped, perhaps, that returning IntPtr.Zero would break and remove the keys from the pump, but that seemed to not do anything.
Is there a way to block the other (default) behavior? I would imagine that by canceling/handling the event (like we can in WinForms/WPF), there should be some way to prevent the key stroke from migrating further into Excel. Any thoughts?
So, I needed to make a few minor changes:
private delegate IntPtr KeyCaptureDelegate(int nCode, IntPtr wParam, IntPtr lParam);
became:
private delegate int KeyCaptureDelegate(int nCode, IntPtr wParam, IntPtr lParam);
and
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
became:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
Which allowed me to rewrite it as:
private static int KeyCaptureCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0)
{
int pointerCode = Marshal.ReadInt32(lParam);
bool result = false;
if (wParam == (IntPtr)WM_KEYDOWN)
{
if (KeyDown != null)
{
result = KeyDown(pointerCode);
}
}
if (result)
{
return 1;
}
}
return CallNextHookEx(_keyCaptureHook, nCode, wParam, lParam);
}
I have been tasked with changing the text of a button in a window. I don't have and cannot access the source code as it's owned by a company we have a paid subscription with.
How can I change the button text with no source code? I'm trying with pInvoke and having problems. The window title changes depending on who you are working with:
"Order Entry Sheet - LASTNAME, FIRSTNAME"
So the window title may not be useable for me inside of the win32 call
FindWindow(string lpClassName, string lpWindowName);
I know both params are optional. I'm using Spy++ and I'm not sure what to use for lpClassName. The class name I see listed is #32770 (Dialog). I tried it and got a return of 0.
IntPtr windowHandle = FindWindow("#32770 (Dialog)", null);
How can I change the button text from another process?
UPDATE
According to MSDN I should be able to achieve this via SetWindowText.
Changes the text of the specified window's title bar (if it has one).
If the specified window is a control, the text of the control is
changed. However, SetWindowText cannot change the text of a control in
another application.
I can't use SetWindowText to do what I want. Is it possible to use something else?
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32", SetLastError = true)]
public static extern int EnumWindows(CallBack x, int y);
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, CallBack lpEnumFunc, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);
public const uint WM_SETTEXT = 0x000C;
public delegate bool CallBack(int hwnd, int lParam);
public static void Main()
{
CallBack windowsCallback = new CallBack(IterateWindows);
EnumWindows(windowsCallback, 0);
}
public static bool IterateChildren(int hwnd, int lParam)
{
string newButtonText = "Some text";
bool continueIteratingChildren = true;
//Console.WriteLine("Child handle: " + hwnd);
int length = GetWindowTextLength((IntPtr)hwnd);
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText((IntPtr)hwnd, sb, sb.Capacity);
//Console.WriteLine(sb);
if (sb.ToString().StartsWith("My Button Text "))
{
HandleRef hrefHWndTarget = new HandleRef(null, (IntPtr)hwnd);
SendMessage(hrefHWndTarget, WM_SETTEXT, IntPtr.Zero, newButtonText);
continueIteratingChildren = false;
}
return continueIteratingChildren;
}
public static bool IterateWindows(int hwnd, int lParam)
{
bool continueIteratingWindows = true;
int windowTextLength = GetWindowTextLength((IntPtr)hwnd);
StringBuilder sb = new StringBuilder(windowTextLength + 1);
GetWindowText((IntPtr)hwnd, sb, sb.Capacity);
if (sb.ToString().StartsWith("My Window Caption"))
{
//Console.Write("Window handle is ");
//Console.WriteLine(hwnd);
//Console.WriteLine(sb);
//Console.WriteLine(Marshal.GetLastWin32Error());
var childrenCallback = new CallBack(IterateChildren);
EnumChildWindows((IntPtr)hwnd, childrenCallback, IntPtr.Zero);
continueIteratingWindows = false;
}
return continueIteratingWindows;
}