如何在控制台应用程序中创建事件循环/消息管道?

时间:2014-01-13 23:51:16

标签: c# .net event-loop

我想创建一个注册了一些事件的控制台应用程序。问题是这些事件永远不会被解雇。在Windows Forms应用程序的这种特殊情况下,我应该在注册这些事件后调用Application.Run(new Form());,但它是Console Application

所以我想创建一个事件循环或消息管道,如果该控制台应用程序是可能的。

申请表:

using Bonjour;
using System;
using System.Threading;

using Bonjour;
using System;
using System.Threading;

namespace ConsoleApplication4 {
    class Program {
        [STAThread]

        static void Main() {

            DNSSDService service = new DNSSDService();
            DNSSDEventManager eventManager = new DNSSDEventManager();
            eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
            DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);

            Console.ReadLine();
        }


        static void eventManager_ServiceFound(DNSSDService browser, DNSSDFlags flags, uint ifIndex, string serviceName, string regtype, string domain) {
            Console.WriteLine("browser: " + browser + "\nDNSSDFlags " + flags + "\nifIndex " + ifIndex + "\nserviceName: " + serviceName + "\nregtype: " + regtype + "\ndomain: " + domain);

            DNSSDEventManager eventManager1 = new DNSSDEventManager();
            eventManager1.ServiceResolved += new _IDNSSDEvents_ServiceResolvedEventHandler(eventManager_ServiceResolved);
            browser.Resolve(flags, ifIndex, serviceName, regtype, domain, eventManager1);
        }


        private static void eventManager_ServiceResolved(DNSSDService service, DNSSDFlags flags, uint ifIndex, string fullname, string hostname, ushort port, TXTRecord record) {
            Console.WriteLine("----------------------------------------");
            Console.WriteLine("FFFFFFFFFFFFFFFFFFFFFOUUUUUUUUUUUUUUUUND");
            Console.WriteLine("DNSSDService " + service + "\nDNSSDFlags " + flags + "\nifindex " + ifIndex + "\nfullname " + fullname + "hostname " + hostname + "\nport " + port + "\nrecord " + record);
            string s = record.ToString();
            Console.WriteLine("mac " + record.GetValueForKey("macaddress"));

            var str = System.Text.Encoding.Default.GetString(record.GetValueForKey("macaddress"));
            Console.WriteLine("mac " + str);
            Console.WriteLine("----------------------------------------");

            DNSSDEventManager eventManager = new DNSSDEventManager();
            eventManager.AddressFound += new _IDNSSDEvents_AddressFoundEventHandler(eventManager_AddressFound);
            DNSSDAddressFamily family = new DNSSDAddressFamily();
            service.GetAddrInfo(flags, ifIndex, family, hostname, eventManager);
        }


        private static void eventManager_AddressFound(DNSSDService service, DNSSDFlags flags, uint ifIndex, string hostname, DNSSDAddressFamily addressFamily, string address, uint ttl) {
            Console.WriteLine("----------------------------------------");
            Console.WriteLine(hostname);
            Console.WriteLine(address);
            Console.WriteLine("----------------------------------------");
        }
    }
}

enter code here

WPF Main0Window.xaml.cs

namespace HomeSecurity {
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window {

        public MainWindow() {
            InitializeComponent();
        }


        private void Window_Loaded(object sender, RoutedEventArgs e) {
            createGUI();
        }


        private void createGUI() {
            Scanner.ScanService();

            //THIS CODE WON'T RUN BECAUSE   Scanner.ScanService(); have frozen it
            AddVideoStream("192.168.0.2");
            AddVideoStream("192.168.0.2");
            AddVideoStream("192.168.0.2");
        }


        //TEN
        private void AddVideoStream(String sourceIP) {
            int cols = 2;
            int formsHostWidth = (int)(VideoPanel.ActualWidth / cols) - 4;

            WindowsFormsHost formsHost = new WindowsFormsHost();
            VideoStream videoStream = new VideoStream(sourceIP);
            formsHost.Width = formsHostWidth;
            formsHost.Height = videoStream.GetPrefferedHeight(formsHostWidth);
            formsHost.Child = videoStream;
            Border lineBorder = new Border();
            lineBorder.BorderBrush = Brushes.Green;
            lineBorder.BorderThickness = new Thickness(2);
            lineBorder.Child = formsHost;
            VideoPanel.Children.Add(lineBorder);
        }
    }
}

具有ScanService方法的类Scanner:

using Bonjour;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace HomeSecurity {
    class Scanner {
        [STAThread]
        public static void ScanService() {
            try {
                DNSSDService service = new DNSSDService();
                DNSSDEventManager eventManager = new DNSSDEventManager();
                eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
                DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
                System.Windows.Threading.Dispatcher.Run(); // BLOCKS EVERYTHING
            } catch (Exception e) {

                Console.WriteLine("--------------------EXCEPTION-----------------");
                Console.WriteLine(e);
                Console.WriteLine("--------------------EXCEPTION-----------------");
            }
        }

        static void eventManager_ServiceFound(DNSSDService browser, DNSSDFlags flags, uint ifIndex, string serviceName, string regtype, string domain) {
            try {
                Console.WriteLine("---------------- eventManager_ServiceFound------------------------");
                Console.WriteLine("browser: " + browser + "\nDNSSDFlags " + flags + "\nifIndex " + ifIndex + "\nserviceName: " + serviceName + "\nregtype: " + regtype + "\ndomain: " + domain);
                Console.WriteLine("----------------------------------------");

                //   DNSSDService service2 = new DNSSDService();
                DNSSDEventManager eventManager1 = new DNSSDEventManager();
                eventManager1.ServiceResolved += new _IDNSSDEvents_ServiceResolvedEventHandler(eventManager_ServiceResolved);
                browser.Resolve(flags, ifIndex, serviceName, regtype, domain, eventManager1);
            }
            catch (Exception e) {

                Console.WriteLine("--------------------EXCEPTION--------eventManager_ServiceFound---------");
                Console.WriteLine(e);
                Console.WriteLine("--------------------EXCEPTION-----------------");
            }
         }

         private static void eventManager_ServiceResolved(DNSSDService service, DNSSDFlags flags, uint ifIndex, string fullname, string hostname, ushort port, TXTRecord record) {
             try {
                 Console.WriteLine("-------------------eventManager_ServiceResolved---------------------");
                 Console.WriteLine("DNSSDService " + service + "\nDNSSDFlags " + flags + "\nifindex " + ifIndex + "\nfullname " + fullname + "hostname " + hostname + "\nport " + port + "\nrecord " + record);
                 var str = System.Text.Encoding.Default.GetString(record.GetValueForKey("macaddress"));
                 Console.WriteLine("mac " + str);
                 Console.WriteLine("----------------------------------------");

                 //    DNSSDService service2 = new DNSSDService();
                 DNSSDEventManager eventManager = new DNSSDEventManager();
                 eventManager.AddressFound += new _IDNSSDEvents_AddressFoundEventHandler(eventManager_AddressFound);
                 DNSSDAddressFamily family = new DNSSDAddressFamily();
                 service.GetAddrInfo(flags, ifIndex, family, hostname, eventManager);
             }
             catch (Exception e) {

                 Console.WriteLine("--------------------EXCEPTION--------eventManager_ServiceResolved---------");
                 Console.WriteLine(e);
                 Console.WriteLine("--------------------EXCEPTION-----------------");
             }
         }

         private static void eventManager_AddressFound(DNSSDService service, DNSSDFlags flags, uint ifIndex, string hostname, DNSSDAddressFamily addressFamily, string address, uint ttl) {
             try {
                 Console.WriteLine("------------------eventManager_AddressFound----------------------");
                 Console.WriteLine("hosname " + hostname);
                 Console.WriteLine("address " + address);
                 Console.WriteLine("----------------------------------------");
             }
             catch (Exception e) {
                 Console.WriteLine("--------------------EXCEPTION-----eventManager_AddressFound------------");
                 Console.WriteLine(e);
                 Console.WriteLine("--------------------EXCEPTION-----------------");
             }
         }
    }
}

3 个答案:

答案 0 :(得分:3)

我不明白您的代码示例与事件或Windows窗体有什么关系。看起来您只是使用ZeroConf .NET libraries,它似乎与Windows窗体或事件循环没有任何关系?

无论如何,如果你想在后台线程上创建(和泵)一个Windows消息循环,你可以使用WPF调度程序类 - 从后台线程调用System.Windows.Threading.Dispatcher.Run()

如果您不想使用WPF,可以使用Windows Forms - 只需调用Application.Run(someForm);,但创建someForm作为0宽0高度隐藏窗口并设置{ {1}}就可以了,这也会创建(并提取)一个Windows消息循环。


看起来ZeroConf库是COM对象,并且库正在尝试使用STA(单线程单元)线程模型回发到主线程。

这是一个猜测。如果COM对象设置为使用'Both'作为其线程模型,那么他们将选择STA,因为您正在主线程上创建对象,并且默认情况下.NET应用程序的主线程是{{ 1}}。 尝试从ShowInTaskBar = false方法中删除[STAThread]属性,然后添加STAThread属性。如果ZeroConf COM对象使用“Both”而不是STA作为其线程模型,那么因为您从MTA线程创建它们,事件将被发布到任意线程池工作线程,而不是发布回主线程STA线程。 如果是这种情况,这应该解决它,您根本不需要运行消息循环。在处理事件时,您必须编写线程安全的代码。

答案 1 :(得分:1)

如果您正在使用外部组件,特别是如果这些组件中的任何组件是COM,那么您真的应该抽水,即在您的线程上运行标准消息循环。其他事情可能变得非常丑陋,但它的细节很糟糕,隐藏在.NET和COM的幕后工作中 - 我不能说我理解自己。

但是,抛开所有不确定性,您是否尝试创建一个简单的Sleep循环并避免使用挂起操作,例如任何Console.Read调用。类似的东西:

while( some condition )
{
    // Check if user pressed a key
    while (Console.KeyAvailable)
    {
        // TODO: Read key
    }

    // Do some other processing maybe?

    // Sleep for 100 ms as to avoid spinning at 100% cpu
    System.Threading.Thread.Sleep(100);
}

事件是否会通过呢?

答案 2 :(得分:1)

在这种情况下,我在Main中手动运行事件循环(而不是Console.ReadLine();)。

    public class User32Wrapper
    {
        // GetMessage
        [DllImport(@"user32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern bool GetMessage(ref MSG message, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax);
        [DllImport(@"user32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern bool TranslateMessage(ref MSG message);
        [DllImport(@"user32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        public static extern long DispatchMessage(ref MSG message);

        [StructLayout(LayoutKind.Sequential)]
        public struct POINT
        {
            long x;
            long y;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct MSG
        {
            IntPtr hwnd;
            public uint message;
            UIntPtr wParam;
            IntPtr lParam;
            uint time;
            POINT pt;
        }
    }

    private const int WM_CUSTOM_EXIT = 0x0400 + 2000;
    private const int WM_CUSTOM_1 = 0x0400 + 2001;
    private const int WM_CUSTOM_2 = 0x0400 + 2002;
    private const int WM_CUSTOM_3 = 0x0400 + 2003;

    static void Main(string[] args)
    {
        ILog log = LogManager.GetLogger("Main");

        // running working threads
        ...

        // Main message loop
        User32Wrapper.MSG msg = new User32Wrapper.MSG();
        while (User32Wrapper.GetMessage(ref msg, IntPtr.Zero, 0, 0))
        {
            if (WM_CUSTOM_EXIT == msg.message) // add WM_CLOSE if you need
            {
                break;
            }
            if (WM_CUSTOM_1 == msg.message)
            {
                onWmCustom1();
            }
            //if (check_any_message_you_need == msg.message)
            // you can for example check if Enter has been pressed to emulate Console.Readline();

            User32Wrapper.TranslateMessage(ref msg);
            User32Wrapper.DispatchMessage(ref msg);
         }

        log.Info("Finished");
    }

在上面的示例中,控制台应用程序将在主线程接收到直到(WM_USER + 2000)消息。所以在另一个应用程序(或同一应用程序中的其他线程)中,您需要这样做:

    const int WM_CUSTOM_EXIT = WM_USER + 2000;
    PROCESS_INFORMATION* pInfo = ...;
    ::PostThreadMessageA(pInfo->dwThreadId, WM_CUSTOM_EXIT, 0, 0)