Я получаю эту ошибку после того, как мой код выполняется в течение 5-10 минут
Обнаружен CallbackOnCollectedDelegate
Сообщение: был выполнен обратный вызов делегата со сборщиком мусора типа 'CashRecyclerTestapp! MessageMonitor + NativeMethods + WndProc :: Invoke'. Это может вызвать сбои приложения, повреждение и потерю данных. При передаче делегатов в неуправляемый код управляемое приложение должно поддерживать их работу до тех пор, пока не будет гарантировано, что они никогда не будут вызваны.
Я пишу приложение для торгового автомата, и у меня есть dll, который публикует сообщения, которые мне нужно перехватить в моем приложении
это мой код, я получил исходный код из Интернета для перехвата сообщений, и здесь он продолжает получать ошибку
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using System.Threading;
/// <summary>
/// Event handler for the <see cref="MessageMonitor.MessageReceived"/> event.
/// </summary>
/// <param name="hwnd">Handle to the window procedure that received the message.</param>
/// <param name="message">Specifies the message.</param>
/// <param name="wParam">Specifies additional message information. The content of this parameter depends on the value of the Msg parameter.</param>
/// <param name="lParam">Specifies additional message information. The content of this parameter depends on the value of the Msg parameter.</param>
public delegate void MessageMonitorEventHandler(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam);
/// <summary>
/// MessageMonitor monitors window messages in a background thread and generates
/// an event that is invoked in the context of the thread that called WatchMessage.
/// </summary>
/// <remarks>
/// Code is based on Stephen Toub article in June 2007 MSDN Magazine.
/// http://msdn.microsoft.com/en-us/magazine/cc163417.aspx
///
/// What's different here is that this code does not require the use of System.Windows.Forms.
/// Using a Form and Application.Run for this task is like using a jack hammer to pound a
/// finishing nail.
/// </remarks>
public static class MessageMonitor
{
/// <summary>
/// Handle to the monitoring window. This can be passed for event callbacks, or registration methods.
/// </summary>
/// <exception cref="Win32Exception"/>
public static IntPtr WindowHandle
{
get
{
EnsureMonitorWindowCreated();
return monitorWindowHandle;
}
}
/// <summary>
/// Subscribes to a particular message.
/// </summary>
/// <remarks>
/// Although you can call this from any thread, the MessageReceived event will only fire on the thread
/// that first called WatchMessage, or WindowHandle if that was called first.
/// </remarks>
/// <param name="message">Specifies the Windows message to monitor.</param>
/// <exception cref="Win32Exception"/>
public static void WatchMessage(int message)
{
EnsureMonitorWindowCreated();
messageSetLock.AcquireWriterLock(Timeout.Infinite);
try
{
messageSet[message] = message;
}
finally
{
messageSetLock.ReleaseWriterLock();
}
}
/// <summary>
/// Fired when a monitored message is received by the MessageMonitor
/// </summary>
public static event MessageMonitorEventHandler MessageReceived;
private static object syncObject = new object();
private static SynchronizationContext syncContext;
private static IntPtr monitorWindowHandle = IntPtr.Zero;
private static ReaderWriterLock messageSetLock;
private static Dictionary<int, int> messageSet;
private static void EnsureMonitorWindowCreated()
{
lock (syncObject)
{
if (messageSetLock == null)
messageSetLock = new ReaderWriterLock();
if (messageSet == null)
messageSet = new Dictionary<int, int>();
// Get the SynchronizationContext associated with the calling thread. This will be used to post-back
// the MessageReceived event.
// SynchronizationContext.Current is not used because it can return null. AsyncOperationManager.SynchronizationContext
// creates a default context if one does not yet exist on the calling thread.
if (syncContext == null)
syncContext = AsyncOperationManager.SynchronizationContext;
if (monitorWindowHandle == IntPtr.Zero)
{
int lastWin32Error = 0;
// This wait event is used to wait for the thread to create the monitoring window.
using (ManualResetEvent threadCreateSignal = new ManualResetEvent(false))
{
Thread thread = new Thread((ThreadStart)delegate
{
// Create the window on our background thread so that messages to it are 'seen' by our message pump.
if ((monitorWindowHandle = CreateWindow()) == IntPtr.Zero)
// Access to lastWin32Error, a stack variable outside our scope, is made safe by the reset event.
lastWin32Error = Marshal.GetLastWin32Error();
// Signal our creator that we have (or have not) created the message window.
threadCreateSignal.Set();
// Enter message loop only if we successfully created the message window.
if (monitorWindowHandle != IntPtr.Zero)
{
NativeMethods.MSG msg = new NativeMethods.MSG();
while (UnsafeNativeMethods.GetMessage(ref msg, IntPtr.Zero, 0, 0))
UnsafeNativeMethods.DispatchMessage(ref msg);
}
});
thread.Name = "MessageMonitorThread";
thread.IsBackground = true;
thread.Start();
threadCreateSignal.WaitOne();
}
if (lastWin32Error != 0)
throw new Win32Exception(lastWin32Error);
}
}
}
private static IntPtr CreateWindow()
{
IntPtr hWnd = IntPtr.Zero;
string className = RegisterWndClass();
if (className != null)
// Note that we do not use a "message only window" on purpose. Broadcast messages are never
// sent to them and so we just use a regular window to ensure that they too can be monitored.
return UnsafeNativeMethods.CreateWindowEx(0, className, className, 0, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, null);
return hWnd;
}
private static string RegisterWndClass()
{
NativeMethods.WNDCLASS wc = new NativeMethods.WNDCLASS();
wc.lpfnWndProc = WndProc;
// This will keep trying if it has to, varying the class name until it gets one. This
// assures that we will get our own window class object. Overkill, I know, but I like
// to leave no loose ends.
for (int n = 1; n < int.MaxValue; n++)
{
wc.lpszClassName = String.Format(CultureInfo.InvariantCulture, "MessageMonitorWindow:{0}", n);
short ret = UnsafeNativeMethods.RegisterClass(wc);
if (ret != 0)
return wc.lpszClassName;
else if (Marshal.GetLastWin32Error() != NativeMethods.ERROR_CLASS_ALREADY_EXISTS)
return null;
// ret == 0 && ERROR_CLASS_ALREADY_EXISTS, try again
}
return null;
}
public static IntPtr WndProc(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam)
{
messageSetLock.AcquireReaderLock(Timeout.Infinite);
bool watchingThisMessage = messageSet.ContainsKey(message);
messageSetLock.ReleaseReaderLock();
if (watchingThisMessage)
{
// We have to package these values to a struct for the Post call, NativeMethods.MSG is
// just covenient. The handler doesn't use this structure because I don't want to
// expose it, and since I don't want to create yet another message structure for the
// handler, I package them here, and unpack them when invoking the handler.
NativeMethods.MSG msg = new NativeMethods.MSG();
msg.hwnd = hwnd;
msg.message = message;
msg.wParam = wParam;
msg.lParam = lParam;
// Post the invocation of the event handler to the thread that is interested in the event.
syncContext.Post(delegate(object state)
{
MessageMonitorEventHandler handler = MessageReceived;
if (handler != null)
{
NativeMethods.MSG msgCopy = (NativeMethods.MSG)state;
handler(msgCopy.hwnd, msgCopy.message, msgCopy.wParam, msgCopy.lParam);
}
}, msg);
}
return UnsafeNativeMethods.DefWindowProc(hwnd, message, wParam, lParam);
}
#region PInvoke
private static class NativeMethods
{
public const int ERROR_CLASS_ALREADY_EXISTS = 0x582;
public delegate IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class WNDCLASS
{
public int style;
public WndProc lpfnWndProc;
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance = IntPtr.Zero;
public IntPtr hIcon = IntPtr.Zero;
public IntPtr hCursor = IntPtr.Zero;
public IntPtr hbrBackground = IntPtr.Zero;
public string lpszMenuName;
public string lpszClassName;
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct MSG
{
public IntPtr hwnd;
public int message;
public IntPtr wParam;
public IntPtr lParam;
public int time;
public int pt_x;
public int pt_y;
}
}
[SuppressUnmanagedCodeSecurity]
private static class UnsafeNativeMethods
{
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public 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", CharSet = CharSet.Auto)]
public static extern IntPtr DefWindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr DispatchMessage([In] ref NativeMethods.MSG msg);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetMessage([In, Out] ref NativeMethods.MSG msg, IntPtr hWnd, int uMsgFilterMin, int uMsgFilterMax);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern short RegisterClass(NativeMethods.WNDCLASS wc);
}
#endregion
}
Теперь это код моего приложения, все это является частью одного приложения
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
[DllImport("PaymentManager.dll")]
static extern int openpaymentmanager();
[DllImport("PaymentManager.dll")]
static extern int closepaymentmanager();
[DllImport("PaymentManager.dll")]
static extern int startpaymentmanager(IntPtr handle,int messageaddress,int devices,int protocol,int messageconfig);
[DllImport("PaymentManager.dll")]
static extern int stoppaymentmanager();
[DllImport("PaymentManager.dll")]
static extern int setpaymentmanager(int command, int selection, int info1, int info2);
private void Form1_Load(object sender, EventArgs e)
{
int closepaymentmng = closepaymentmanager();
const int WM_USER = 0x0400;
MessageMonitor.MessageReceived += new MessageMonitorEventHandler(MessageMonitor_MessageReceived);
MessageMonitor.WatchMessage(0x3B9); // MM_MCINOTIFY
int openpaymentmng = openpaymentmanager();
int startpaymentmnh = startpaymentmanager(MessageMonitor.WindowHandle , 0x3B9, 0, 0, 0);
long setpaymentmng = setpaymentmanager(0, 0, 0, 0);
textBoxprice.Text = "0";
textBoxpaid.Text = "0";
}
private void MessageMonitor_MessageReceived(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine(string.Format ("message:{0} , wparam= {1} ,lparam = {2}", message,wParam,lParam));
if (wParam.ToString() == "17")
{
textBoxpaid.Text = (Convert.ToDecimal( textBoxpaid.Text)+ Convert.ToDecimal( lParam.ToString())/100).ToString();
}
if (wParam.ToString() == "18")
{
textBoxpaid.Text = (Convert.ToDecimal(textBoxpaid.Text) + Convert.ToDecimal(lParam.ToString()) / 100).ToString();
}
if (wParam.ToString() == "33")
{
textBoxpaid.Text = (Convert.ToDecimal(textBoxpaid.Text) - Convert.ToDecimal(lParam.ToString()) / 100).ToString();
}
}
ect.....
я пробовал следующее, но не работает
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private const string crashDllName = @"C:\Users\Chantelle\Documents\CashRecyclerTestapp\CashRecyclerTestapp\bin\Debug\PaymentManager.dll";
[DllImport(crashDllName)]
static extern int openpaymentmanager();
[DllImport(crashDllName)]
static extern int closepaymentmanager();
[DllImport(crashDllName)]
static extern int startpaymentmanager(IntPtr handle,int messageaddress,int devices,int protocol,int messageconfig);
[DllImport(crashDllName)]
static extern int stoppaymentmanager();
[DllImport(crashDllName)]
static extern int setpaymentmanager(int command, int selection, int info1, int info2);
private static IntPtr hndle;
private static MessageMonitorEventHandler msgmntr;
private void Form1_Load(object sender, EventArgs e)
{
int closepaymentmng = closepaymentmanager();
const int WM_USER = 0x0400;
msgmntr = new MessageMonitorEventHandler(MessageMonitor_MessageReceived);
MessageMonitor.MessageReceived += msgmntr;
MessageMonitor.WatchMessage(0x3B9); // MM_MCINOTIFY
hndle = MessageMonitor.WindowHandle;
int openpaymentmng = openpaymentmanager();
int startpaymentmnh = startpaymentmanager(hndle, 0x3B9, 0, 0, 0);
long setpaymentmng = setpaymentmanager(0, 0, 0, 0);
textBoxprice.Text = "0";
textBoxpaid.Text = "0";
}
public void MessageMonitor_MessageReceived(IntPtr hwnd, int message, IntPtr wParam, IntPtr lParam)
{
Console.WriteLine(string.Format ("message:{0} , wparam= {1} ,lparam = {2}", message,wParam,lParam));
if (wParam.ToString() == "17")
{
textBoxpaid.Text = (Convert.ToDecimal( textBoxpaid.Text)+ Convert.ToDecimal( lParam.ToString())/100).ToString();
}
if (wParam.ToString() == "18")
{
textBoxpaid.Text = (Convert.ToDecimal(textBoxpaid.Text) + Convert.ToDecimal(lParam.ToString()) / 100).ToString();
}
if (wParam.ToString() == "33")
{
textBoxpaid.Text = (Convert.ToDecimal(textBoxpaid.Text) - Convert.ToDecimal(lParam.ToString()) / 100).ToString();
}
}
пробовал другие пути безуспешно. Так что мне действительно нужна любая помощь, даже если это перехватывать сообщения другим способом, но я вне досягаемости и буду признателен за любую помощь