我想创建一个注册了一些事件的控制台应用程序。问题是这些事件永远不会被解雇。在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
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-----------------");
}
}
}
}
答案 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)