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



我想创建一个注册了一些事件的控制台应用程序。问题是,这些事件永远不会被触发。在Windows窗体应用程序中的这种特殊情况下,我应该在注册这些事件后调用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.xam.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-----------------");
}
}
}
}

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

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

如果你不想使用WPF,你可以使用Windows窗体-只需调用Application.Run(someForm);,但将someForm创建为0宽0高的隐藏窗口,并在其上设置ShowInTaskBar = false,这也将创建(并泵送)一个Windows消息循环。


看起来ZeroConf库是COM对象,并且该库正试图发布回您的主线程,因为它正在使用STA(单线程单元)线程模型。

这是一个猜测。如果COM对象设置为使用"Both"作为其线程模型,则它们将选择STA,因为您在主线程上创建对象,并且默认情况下.NET应用程序的主线程为STAThread。请尝试从Main方法中删除[STAThread]属性,然后放入[MTAThread]属性。如果ZeroConf COM对象使用"Both"而不是STA作为其线程模型,则由于您是从MTA线程创建它们的,因此事件将被发布到任意线程池工作线程,而不是发布回您的主STA线程。如果是这种情况,这应该可以解决它,并且您根本不需要运行消息循环。不过,在处理事件时,您必须编写线程安全代码。

我手动运行事件循环(而不是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)

如果您正在使用外部组件,特别是如果这些组件中的任何一个是COM,那么您确实应该进行泵浦,即在线程上运行一个标准消息循环。否则,事情可能会变得非常丑陋,但它的细节很糟糕,隐藏在.NET和COM在幕后的工作方式中——我自己也不能说我理解这一点。

但是,抛开所有这些不确定性不谈,你是否尝试过创建一个简单的睡眠循环,并避免使用任何挂起操作,如任何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);
}

那么事件发生了吗?

最新更新