I'm writing a new tool for my organisation that has to talk via SendMessage to a legacy tool.
I just made a test app using code from here:
http://www.c-sharpcorner.com/Blogs/6444/
I have edited the send code to match my purpose. But I'm having some problems receiving messages in my "GetMessage" form. The message does go through but the program breaks when trying to convert the data into a string.
Here's my code:
Send:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, ref COPYDATASTRUCT lParam);
[DllImport("USER32.DLL", CharSet = CharSet.Unicode)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
private void button1_Click(object sender, EventArgs e)
{
IntPtr hwnd = FindWindow(null, "GetMessage");
if (hwnd != null)
{
string message = textBox1.Text + "-" + System.DateTime.Now.ToString();
COPYDATASTRUCT cds;
cds.dwData = 0;
cds.lpData = (int)Marshal.StringToHGlobalAnsi(message);
cds.cbData = message.Length;
SendMessage(hwnd, (int)WM_COPYDATA, 0, ref cds);
}
}
Recieve:
private const int WM_COPYDATA = 0x4A;
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public int dwData;
public int cbData;
public int lpData;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_COPYDATA:
COPYDATASTRUCT CD = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
byte[] B = new byte[CD.cbData];
IntPtr lpData = new IntPtr(CD.lpData);
//string test = Marshal.PtrToStringAuto(lpData, CD.lpData); // this doesn't work either
Marshal.Copy(lpData, B, 0, CD.cbData); // access violation here
string strData = Encoding.Default.GetString(B);
listBox1.Items.Add(strData);
break;
default:
base.WndProc(ref m);
break;
}
}
The error I'm getting:
Attempted to read or write protected memory. This is often an
indication that other memory is corrupt.
I've tried setting the message string in send to a field (so it doesn't go out of scope), setting the "int wParam" argument of Send Message to an "IntPtr" and using IntPtr.Zero instead of 0 in my SendMessage call, and null terminating the string as below:
cds.lpData = (int)Marshal.StringToHGlobalAnsi(message + '\0');
cds.cbData = (message+'\0').Length;
Still getting the same problem.
I fixed the issue, I believe that the problem was that I was using int instead of IntPtr for my SendMessage and COPYDATASTRUCT.
I got this information from http://pinvoke.net/default.aspx/user32.SendMessage and http://pinvoke.net/default.aspx/Structures/COPYDATASTRUCT.html
Here's the working code:
private const int WM_COPYDATA = 0x004A;
[StructLayout(LayoutKind.Sequential)]
struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
Send:
[DllImport("user32.dll", CharSet = CharSet.Auto)]
//static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); // original
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, ref COPYDATASTRUCT cds); // override
private void SendMsg()
{
if (_hwnd != null)
{
COPYDATASTRUCT cds;
cds.dwData = IntPtr.Zero;
cds.lpData = Marshal.StringToHGlobalAnsi(stockCode);
cds.cbData = stockCode.Length;
SendMessage(_hwnd, (int) WM_COPYDATA, IntPtr.Zero, ref cds);
}
}
Recieve:
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_COPYDATA:
COPYDATASTRUCT CD = (COPYDATASTRUCT)m.GetLParam(typeof(COPYDATASTRUCT));
byte[] B = new byte[CD.cbData];
IntPtr lpData = CD.lpData;
Marshal.Copy(lpData, B, 0, CD.cbData);
string strData = Encoding.Default.GetString(B);
listBox1.Items.Add(strData);
break;
default:
base.WndProc(ref m);
break;
}
}
I believe this is working on 64bit windows.
Related
I'm making winform application which needs to get content of a listview from other windows application which has several tabs, each tab contains a listview, like Windows Task Manager.
I was following this Q&A Get ListView items from other windows and this guide Crossing the process boundry with .NET.
Unluckily, I'm really a newbie in win32api. I always get empty string returned from local buffer.
Below is my code so far.
public partial class Form1 : Form
{
private const int LVM_FIRST = 0x1000;
private const int LVM_GETITEMCOUNT = LVM_FIRST + 4;
private const int LVM_GETITEM = LVM_FIRST + 75;
private const int LVIF_TEXT = 0x0001;
private const int LVM_SETITEM = 0x1006;
private const uint PROCESS_ALL_ACCESS = (uint)(0x000F0000L | 0x00100000L | 0xFFF);
private const uint MEM_COMMIT = 0x1000;
private const uint MEM_RELEASE = 0x8000;
private const uint PAGE_READWRITE = 0x04;
public Form1()
{
InitializeComponent();
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle);
[DllImport("user32.dll")]
static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, IntPtr lParam);
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32")]
static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out int lpwdProcessID);
[DllImport("kernel32")]
static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32")]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32")]
static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, int dwSize, uint dwFreeType);
[DllImport("kernel32")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref LV_ITEM buffer, int dwSize, IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32")]
static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, IntPtr lpBuffer, int dwSize, IntPtr lpNumberOfBytesRead);
[DllImport("kernel32")]
static extern bool CloseHandle(IntPtr hObject);
[StructLayout(LayoutKind.Sequential)]
public struct LV_ITEM
{
public uint mask;
public int iItem;
public int iSubItem;
public uint state;
public uint stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
}
public static string ReadListViewItem(IntPtr hWnd, int item)
{
const int dwBufferSize = 1024;
int dwProcessID;
LV_ITEM lvItem;
string retval;
bool bSuccess;
IntPtr hProcess = IntPtr.Zero;
IntPtr lpRemoteBuffer = IntPtr.Zero;
IntPtr lpLocalBuffer = IntPtr.Zero;
IntPtr threadId = IntPtr.Zero;
try
{
lvItem = new LV_ITEM();
lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
// Get the process id owning the window
threadId = GetWindowThreadProcessId(hWnd, out dwProcessID);
if ((threadId == IntPtr.Zero) || (dwProcessID == 0))
throw new ArgumentException("Invalid hWnd");
// Open the process with all access
hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
if (hProcess == IntPtr.Zero)
throw new ApplicationException("Failed to access process");
// Allocate a buffer in the remote process
lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, MEM_COMMIT, PAGE_READWRITE);
if (lpRemoteBuffer == IntPtr.Zero)
throw new SystemException("Failed to allocate memory in remote process");
// Fill in the LVITEM struct, this is in your own process
// Set the pszText member to somewhere in the remote buffer,
// For the example I used the address imediately following the LVITEM stuct
lvItem.mask = LVIF_TEXT;
lvItem.iItem = item;
lvItem.pszText = (IntPtr)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
lvItem.cchTextMax = 50;
// Copy the local LVITEM to the remote buffer
bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem, Marshal.SizeOf(typeof(LV_ITEM)), IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to write to process memory");
// Send the message to the remote window with the address of the remote buffer
SendMessage(hWnd, LVM_GETITEM, 0, lpRemoteBuffer);
// Read the struct back from the remote process into local buffer
bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize, IntPtr.Zero);
if (!bSuccess)
throw new SystemException("Failed to read from process memory");
// At this point the lpLocalBuffer contains the returned LV_ITEM structure
// the next line extracts the text from the buffer into a managed string
retval = Marshal.PtrToStringAuto((IntPtr (lpLocalBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM))));
}
finally
{
if (lpLocalBuffer != IntPtr.Zero)
Marshal.FreeHGlobal(lpLocalBuffer);
if (lpRemoteBuffer != IntPtr.Zero)
VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE);
if (hProcess != IntPtr.Zero)
CloseHandle(hProcess);
}
return retval;
}
private void button1_Click(object sender, EventArgs e)
{
var mainWnd = FindWindow(null, "Windows Task Manager");
var childWnd = FindWindowEx(mainWnd, IntPtr.Zero, null, "Processes");
IntPtr count = SendMessage(mainWnd, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
Console.WriteLine(count);
Console.WriteLine(ReadListViewItem(mainWnd, 1));
}
}
Please, somebody gives me suggestion!!
Thanks a lot!
Here's the code I'm using:
private void Button_Click(object sender, RoutedEventArgs e) {
SendMessage(GetDesktopWindow(), LVM_ARRANGE, LVA_SNAPTOGRID, 0);
}
public const uint LVM_ARRANGE = 0x1000 + 22;
public const int LVA_SNAPTOGRID = 0x0005;
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
Nothing happens when I run it. Code is borrowed from https://www.codeproject.com/Messages/1168961/Re-Auto-Arrange-desktop-icons.aspx
Tried different Windows versions too.
This code worked for me. Note that it doesn't appear to change the options, so although it arranges to the grid for me the View->Align Icons To Grid is still unchecked. The issue is outlined here: How do I get the window handle of the desktop?
Your problem results from a fairly widespread confusion over what the
desktop window actually is. The GetDesktopWindow function does
precisely what it's documented to do: it returns a handle to the
desktop window. This, however, is not the same window that contains
the desktop icons.
So I used the answer here: Get handle to desktop / shell window but with replacing or adding methods and p/invoke calls until I had everything necessary because that link only gives method calls and not the dll imports (I may have left some superfluous stuff in here).
static void Main()
{
SendMessage(GetDesktopWindow(DesktopWindow.SysListView32), LVM_ARRANGE, LVA_SNAPTOGRID, 0);
Console.ReadLine();
}
public const int LVM_ARRANGE = 4118;
public const int LVA_SNAPTOGRID = 5;
[DllImport("user32.dll")]
static extern IntPtr GetShellWindow();
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
[DllImport("user32.dll")]
private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int GetWindowTextLength(IntPtr hWnd);
public enum DesktopWindow
{
ProgMan,
SHELLDLL_DefViewParent,
SHELLDLL_DefView,
SysListView32
}
public static IntPtr GetDesktopWindow(DesktopWindow desktopWindow)
{
IntPtr _ProgMan = GetShellWindow();
IntPtr _SHELLDLL_DefViewParent = _ProgMan;
IntPtr _SHELLDLL_DefView = FindWindowEx(_ProgMan, IntPtr.Zero, "SHELLDLL_DefView", null);
IntPtr _SysListView32 = FindWindowEx(_SHELLDLL_DefView, IntPtr.Zero, "SysListView32", "FolderView");
if (_SHELLDLL_DefView == IntPtr.Zero)
{
EnumWindows((hwnd, lParam) =>
{
if (GetWindowText(hwnd) == "WorkerW")
{
IntPtr child = FindWindowEx(hwnd, IntPtr.Zero, "SHELLDLL_DefView", null);
if (child != IntPtr.Zero)
{
_SHELLDLL_DefViewParent = hwnd;
_SHELLDLL_DefView = child;
_SysListView32 = FindWindowEx(child, IntPtr.Zero, "SysListView32", "FolderView"); ;
return false;
}
}
return true;
}, IntPtr.Zero);
}
switch (desktopWindow)
{
case DesktopWindow.ProgMan:
return _ProgMan;
case DesktopWindow.SHELLDLL_DefViewParent:
return _SHELLDLL_DefViewParent;
case DesktopWindow.SHELLDLL_DefView:
return _SHELLDLL_DefView;
case DesktopWindow.SysListView32:
return _SysListView32;
default:
return IntPtr.Zero;
}
}
public static string GetWindowText(IntPtr hWnd)
{
int size = GetWindowTextLength(hWnd);
if (size > 0)
{
var builder = new StringBuilder(size + 1);
GetWindowText(hWnd, builder, builder.Capacity);
return builder.ToString();
}
return String.Empty;
}
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 think I've searched all of related topics on this planet by using Chinese and English but cannot find the solution.
I've created one message only window to receive and process the data from WM_COPYDATA, but I cannot find the window in send side, below is demo (WPF of C#):
Receive:
public partial class MainWindow : Window
{
private readonly IntPtr sourceHandle;
private const int WM_COPYDATA = 0x004A;
[StructLayout(LayoutKind.Sequential)]
public struct CopyDataStruct
{
public IntPtr dwData;
public int cbData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
public MainWindow()
{
InitializeComponent();
sourceHandle = this.CreateMessageOnlyWindow();
this.btnReceive.Content = sourceHandle;
}
private IntPtr CreateMessageOnlyWindow()
{
IntPtr HWND_MESSAGE = new IntPtr(-3);
HwndSourceParameters sourceParam = new HwndSourceParameters() { ParentWindow = HWND_MESSAGE };
var source = new HwndSource(sourceParam);
source.AddHook(WndProc);
return source.Handle;
}
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled)
{
if (msg == WM_COPYDATA)
{
MessageBox.Show(lparam.ToInt32().ToString());
handled = true;
return new IntPtr(20);
}
return IntPtr.Zero;
}
}
Send:
public partial class MainWindow : Window
{
IntPtr WM_COPYDATA = new IntPtr(0x004A);
IntPtr HWND_MESSAGE = new IntPtr(-3);
[DllImport("User32.dll")]
public static extern IntPtr SendMessage(IntPtr hwnd, IntPtr msg, IntPtr wParam, ref COPYDATASTRUCT IParam);
[DllImport("User32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cData;
[MarshalAs(UnmanagedType.LPStr)]
public string lpData;
}
public MainWindow()
{
InitializeComponent();
}
private void BtnSend_Click(object sender, RoutedEventArgs e)
{
//Here cannot find the target message only window in receiving
IntPtr WINDOW_HANDLE = FindWindowEx(HWND_MESSAGE, IntPtr.Zero, null, null);
if (WINDOW_HANDLE != IntPtr.Zero)
{
byte[] arr = System.Text.Encoding.Default.GetBytes(txtMessage.Text);
int len = arr.Length;
COPYDATASTRUCT cdata;
cdata.dwData = (IntPtr)100;
cdata.lpData = txtMessage.Text;
cdata.cData = len + 1;
SendMessage(WINDOW_HANDLE, WM_COPYDATA, IntPtr.Zero, ref cdata);
}
}
}
BtnSend_Click method in Send, cannot find the right window handle here, could someone in this pallet can help?
PS: I should describe my requirement first: I want to create a windows service in C#, which is receiver and deal with the data from WM_COPYDATA, so I think message only window is necessary because there is NO window in windows service.
So in Sender, I need to find this message only window to pass the window handle into SendMessage at first, here is the point.
Thanks guys
I need to close a download popup in web browser control (disallow user to downloading file).
How i can achieve this?
I found this:
How to block downloads in .NET WebBrowser control?
And i used second answer. It's working but i have problem with it. It's seems that calling GetText of the created object is blocking a whole thread. I don't have any solution for it.
private static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (idObject == 0 && idChild == 0)
{
if(eventType == EVENT_OBJECT_CREATE)
{
string text = GetText(hwnd);
if (text.Contains("File Download"))
SendMessage(hwnd, 0x0010, IntPtr.Zero, IntPtr.Zero); //close window
}
}
}
public static string GetText(IntPtr hWnd)
{
int length = GetWindowTextLength(hWnd); //my app is freezing here - i think it's because i'm calling it from message loop.
StringBuilder sb = new StringBuilder(length + 1);
GetWindowText(hWnd, sb, sb.Capacity);
return sb.ToString();
}
//edit
Ok, thanks #sgorozco for suggestion. Now i'm using SetWindowsHookEx and WH_CBT. Then in message loop i'm catching HCBT_CREATEWND events. But i have problem with getting CBT_CREATEWND from lparm. I'm getting "Managed Debugging Assistant 'FatalExecutionEngineError'" exception.
Here is my current code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CREATESTRUCT
{
public IntPtr lpCreateParams;
public IntPtr hInstance;
public IntPtr hMenu;
public IntPtr hwndParent;
public int cy;
public int cx;
public int y;
public int x;
public int style;
public string lpszName;
public string lpszClass;
public int dwExStyle;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct CBT_CREATEWND
{
public IntPtr lpcs;
public IntPtr hwndInsertAfter;
}
private static IntPtr MessageLoopFuctnion(int code, IntPtr wParam, IntPtr lParam)
{
if (code < 0)
{
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
if(code == 3)
{
CBT_CREATEWND info;
info = (CBT_CREATEWND)Marshal.PtrToStructure(lParam, typeof(CBT_CREATEWND));
CREATESTRUCT info1;
info1 = (CREATESTRUCT)Marshal.PtrToStructure(info.lpcs, typeof(CREATESTRUCT)); //here exception is throwing
if (info1.lpszName != null && info1.lpszName.Contains("File Download")))
SendMessage(wParam, 0x0010, IntPtr.Zero, IntPtr.Zero); //close popup
//Marshal.FreeHGlobal(info.lpcs); //error, why?
//Marshal.FreeHGlobal((IntPtr)lParam.ToUInt64());
}
return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
}
//set hook
IntPtr hWinEventHook = SetWindowsHookEx(5, myCallbackDelegate, user32DLL, 0);
//edit 2
Here are my definitions:
private delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
private static HookProc myCallbackDelegate = new HookProc(MessageLoopFuctnion);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
My myCallbackDelegate is a static field so it's not GC collected for sure.
Temporary i'm enumerating all windows every 500ms and look for dialog which contains text "File Download". But it is an ugly solution.
The reason for the FatalExecutionEngineError are the strings in CREATESTRUCT. Replace these with a IntPtr and you will get no exception.
[StructLayout(LayoutKind.Sequential)]
public struct CREATESTRUCT {
public IntPtr lpCreateParams;
public IntPtr hInstance;
public IntPtr hMenu;
public IntPtr hwndParent;
public int cy;
public int cx;
public int y;
public int x;
public int style;
public IntPtr lpszName;
public IntPtr lpszClass;
public int dwExStyle;
}
(Maybe you can use GetClassName and GetWindowText instead as workaround. not tested)