我的代码已经运行5-10分钟之后,我不断收到这个错误
检测到CallbackOnCollectedDelegate消息:在types为“CashRecyclerTestapp!MessageMonitor + NativeMethods + WndProc :: Invoke”的垃圾收集代理上进行callback。 这可能会导致应用程序崩溃,损坏和数据丢失。 将代理传递给非托pipe代码时,它们必须由托pipe应用程序保持活动状态,直到确保它们永远不会被调用。
我正在写一个自动售货机应用程序,我有一个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(); } }
尝试了其他方式没有成功。 所以我真的需要任何帮助,即使它以另一种方式捕捉消息,但即时超越fustrated,并会感谢任何帮助
在RegisterWndClass
,你正在创建一个本地的WNDCLASS
实例 – wc
, lpfnWndProc
字段包含一个用于回调代码的委托。 由于这是一个局部变量,只要该方法完成, wc
就有资格进行垃圾回收,就像用于回调代码的委托一样。
您必须确保您作为回调传递的委托不被垃圾收集。 这可以通过在MessageMonitor
类中添加一个静态字段来实现:
private static readonly NativeMethods.WndProc StaticWndProcDelegate = WndProc;
在RegisterWndClass
中你现在有:
wc.lpfnWndProc = WndProc;
将其替换为:
wc.lpfnWndProc = StaticWndProcDelegate;