Пользовательские сообщения в C#
Что вы подразумеваете под термином "пользовательское" сообщение?
Но насколько я знаю, программист может определить свой тип сообщений и обрабатывать их нужным ему способом, эти сообщения я и имел ввиду, говоря пользовательские сообщения.
Но насколько я знаю, программист может определить свой тип сообщений и обрабатывать их нужным ему способом, эти сообщения я и имел ввиду, говоря пользовательские сообщения.
А какой смысл использовать столь низкоуровневые операции в столь абстрактной среде как .NET? Операции с сообщениями Windows (System.Windows.Forms.Message) требуются только тогда, когда нужно взаимодействовать с другими (не .NET) оконными приложениями.
Вам это нужно для конкретной задачи или интересует чисто теория?
Дело в том, что отправка сообщений потокам не совсем соответствует модели синхронизации потоков, принятой в настоящее время, но в принципе, реализуется достаточно просто: вспомните задачу о синхронизации поставщиков и потребителей с использовнием двух семафоров.
Есть форма и поток, как из потока изменить свайства, например, label-я. Причем не должно быть больших задержек в работе потока.
А как послать и поймать (обработать) сообщение в .Net?
Я уже второй день капаюсь в MSDN и нашел только это
ms-help://MS.VSCC.v80/MS.MSDN.v80/MS.NETDEVFX.v20.en/CPref17/html/P_System_Windows_Forms_Message_Msg.htm
using System.Drawing;
using System.Windows.Forms;
namespace csTempWindowsApplication1
{
public class Form1 : System.Windows.Forms.Form
{
// Constant value was found in the "windows.h" header file.
private const int WM_ACTIVATEAPP = 0x001C;
private bool appActive = true;
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
public Form1()
{
this.Size = new System.Drawing.Size(300,300);
this.Text = "Form1";
this.Font = new System.Drawing.Font("Microsoft Sans Serif", 18F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((System.Byte)(0)));
}
protected override void OnPaint(PaintEventArgs e)
{
// Paint a string in different styles depending on whether the
// application is active.
if (appActive)
{
e.Graphics.FillRectangle(SystemBrushes.ActiveCaption,20,20,260,50);
e.Graphics.DrawString("Application is active", this.Font, SystemBrushes.ActiveCaptionText, 20,20);
}
else
{
e.Graphics.FillRectangle(SystemBrushes.InactiveCaption,20,20,260,50);
e.Graphics.DrawString("Application is Inactive", this.Font, SystemBrushes.ActiveCaptionText, 20,20);
}
}
[System.Security.Permissions.PermissionSet(System.Security.Permissions.SecurityAction.Demand, Name="FullTrust")]
protected override void WndProc(ref Message m)
{
// Listen for operating system messages.
switch (m.Msg)
{
// The WM_ACTIVATEAPP message occurs when the application
// becomes the active application or becomes inactive.
case WM_ACTIVATEAPP:
// The WParam value identifies what is occurring.
appActive = (((int)m.WParam != 0));
// Invalidate to get new text painted.
this.Invalidate();
break;
}
base.WndProc(ref m);
}
}
}
А как отправить сообщение?
Вот вам делать нечего чем искать "костыли".
This is the Way.
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace PropertySetter {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
message_queue = new Queue<CrossThreadProcedure>(MAX_MESSAGE_QUEUE_LENGTH);
writer_sem = new Semaphore(MAX_MESSAGE_QUEUE_LENGTH, MAX_MESSAGE_QUEUE_LENGTH);
reader_sem = new Semaphore(0, MAX_MESSAGE_QUEUE_LENGTH);
heavy_computing_thread = new Thread(HeavyComputingThread);
heavy_computing_thread.IsBackground = true;
message_dispatcher_thread = new Thread(MessageDispatcherThread);
message_dispatcher_thread.IsBackground = true;
heavy_computing_thread.Start();
message_dispatcher_thread.Start();
}
private const int MAX_MESSAGE_QUEUE_LENGTH = 20;
private Queue<CrossThreadProcedure> message_queue;
private Semaphore writer_sem;
private Semaphore reader_sem;
private Thread heavy_computing_thread;
private Thread message_dispatcher_thread;
private void Enqueue(CrossThreadProcedure proc) {
writer_sem.WaitOne();
lock(message_queue) {
message_queue.Enqueue(proc);
}
reader_sem.Release();
}
private CrossThreadProcedure Dequeue() {
CrossThreadProcedure next_proc;
reader_sem.WaitOne();
lock(message_queue) {
next_proc = message_queue.Dequeue();
}
writer_sem.Release();
return next_proc;
}
private void HeavyComputingThread() {
int i = 0;
while(true) {
++i;
// "отправляем" сообщение на обработку
Enqueue(new CrossThreadProcedure(new ThreadStart(delegate {
// формируем код "собщения" для выполнения в потоке интерфейса
this.Text = i.ToString();
})));
}
}
private void MessageDispatcherThread() {
while(true) {
Thread.Sleep(100);
// получаем следующее сообщение из очереди
CrossThreadProcedure next_proc = Dequeue();
// выполняем его код в потоке интерфейса
this.Invoke(next_proc.Method, next_proc.Args);
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e) {
heavy_computing_thread.Abort();
message_dispatcher_thread.Abort();
heavy_computing_thread.Join();
message_dispatcher_thread.Join();
}
}
public sealed class CrossThreadProcedure {
public CrossThreadProcedure(Delegate proc, params object[] args) {
this._proc = proc;
this._args = args;
}
private readonly Delegate _proc;
public Delegate Method { get { return _proc; } }
private readonly object[] _args;
public object[] Args { get { return _args; } }
}
}
Идя алгоритма. Имеем 2 потока и одну очередь сообщений. Один поток выполняет какието особо важную и нетерпящую отлогательств задачу, другой же занимается тем, что слушает, чего периодически посылает тот первый поток, и выполняет синхронизацию с потком интерфейса для отображения инфы. Очередь сообщений имеет некоторую максимальную длину - MAX_MESSAGE_QUEUE_LENGTH, это позволяет "не отвлекаться" основному потоку от выполняемых действий.
Механизм работы с очередью - классичский алгоритм на двух семафорах.