Trang 1 trên tổng số 2 12 Cuối cùngCuối cùng
Từ 1 tới 10 trên tổng số 18 kết quả

Đề tài: Thảo luận về xử lý thông điệp trên Windows.

  1. #1
    Ngày gia nhập
    02 2014
    Nơi ở
    TP.HCM
    Bài viết
    902

    Mặc định Thảo luận về xử lý thông điệp trên Windows.

    Bài viết chỉ là cố gắng nêu ra hiểu biết của cá nhân nên phiến diện là điều không tránh khỏi. Vì vậy các sai sót hoặc không đúng các bạn nên chỉ rõ, nếu có chỉ dẫn khắc phục càng tốt.
    Các câu hỏi lấp lửng, tu từ hay cạnh khóe MHoang xin từ chối không thảo luận.

    Để bắt đầu xin lấy một câu hỏi của Monre làm điểm nhập.

    Trích dẫn Nguyên bản được gửi bởi Monre
    Một CT đang chạy tốt thử thêm code này vào formMain xem nó chạy thế nào, biên dịch chắc chắn không lỗi.
    Visual C# Code:
    1.     //...
    2.             protected override void WndProc(ref Message m) {
    3.                 switch (m.Msg) {
    4.                 default:
    5.                     break;
    6.                 }
    7.                 //base.WndProc(ref m);
    8.             }
    9.     //...
    Hầu như bạn nào lập trình trên C# hay lập trình cho Windows đều trả lời được. Nhưng câu hỏi không chỉ đơn thuần nhiêu đó, tôi biết Monre nghĩ xa hơn đó nhiều, chỉ là cách đưa
    vấn đề khiến các thành viên khác khó có ý kiến thảo luận tiếp.

    I. Sự kiện trong lập trình Windows.

    Trong Windows có tới hàng trăm sự kiện được định nghĩa trước, theo thời gian số lượng lên tới cả ngàn. Tới lượt các ngôn ngữ hay các thư viện hay chính người lập trình tự thêm vào hàng dài đó.

    Nhưng các sự kiện đó liên quan như thế nào với chương trình ta viết ?
    Không kể các chương trình đa luồng hay nền chéo thì các chương trình của chúng ta là thụ động cho tới khi chính Windows thấy cần thiết phải đánh động nó.
    Ví dụ :
    Người dùng nhấp chuột trên một vùng trống màn hình thì Windows có đánh động chương trình ta không. Không nếu hiện tại cửa sổ (form) đang không có sự quan tâm (focus). Có nếu cửa sổ đang
    có focus, Windows nghĩ là phải báo cho chương trình để chương trình làm mờ thanh tiêu đề...

    Vậy Windows đánh động chương trình của ta bằng cách nào ?
    Khi tạo một chương trình có cửa sổ/form thì chính Windows sẽ tạo và gán cho nó một hàng đợi thông điệp. Khi Windows muốn đánh động chương trình thì nó gởi sự kiện vào hàng đợi thông điệp, từ đây chúng ta có thể gọi là thông điệp
    vì nó đã xác định nơi đến. Hàng đợi thông điệp này không có sự công bằng, FIFO chỉ là tương đối.

    Nhưng từ hàng đợi thông điệp, từng thông điệp đến với chương trình bằng cách nào ?
    Theo thông lệ mà Windows đưa ra thì mỗi chương trình phải tự đưa ra cách thức nhận thông điệp từ hàng đợi. Windows đưa ra một số hàm API để hỗ trợ cho thao tác này như GetMessage, TransparentMessage, DispatchMessage...
    Chương trình tự phối hợp các API này theo một cách thức (vòng lặp) nào đó để đưa thông điệp tới mã xử lý (thực ra là các hàm API trên sẽ làm việc này).

    Nhưng làm sao các hàm API đó biết được mã xử lý thông điệp của chương trình ta ở đâu mà đưa thông điệp tới ?
    Khi chúng ta tạo cửa sổ/form thì hoặc trực tiếp (C\C++) hoặc gián tiếp (C#) chúng ta đã tạo ra thủ tục xử lý, và nó được lưu trong cấu trúc tạo cửa sổ khi đăng ký kiểu lớp cửa sổ với Windows nên các hàm Message dễ dàng truy xuất nó.

    Như vậy hàm xử lý thông điệp là bắt buộc phải có ?
    Đúng với chương trình "kiểu Windows".

    Hàm này bắt buộc phải có tên là WndProc ?
    Không. WndProc như mã trên của Monre chỉ là sự bao bọc trong các lớp của C#, khi thực thi nó sẽ tham chiếu tới thủ tục thực sự.

    ...

  2. #2
    Ngày gia nhập
    08 2017
    Bài viết
    2,769

    Thông điệp window.

    C# có thể không cần biết, biết cũng tốt, không biết cũng không sao.
    WndProc là tên quy định (từ Control), cũng như Main là tên, entryPoint - pointEntry, khó mà đổi khác, không biết có cách nào đổi được không!

    G/s vì lý do nào đó cần phải giấu thanh tiêu đề :
    this.FormBorderStyle = FormBorderStyle.None;
    thì cách nào có thể di chuyển (Move) form trên màn hình bằng chuột? Rộng hơn, di chuyển form bằng điểm khác (HTCLIENT) không chỉ bằng Title (HTCAPTION)

    Visual C# Code:
    1. using System;
    2. using System.Windows.Forms;
    3.  
    4. namespace WinApp01 {
    5.     public partial class Form2 : Form {
    6.         public Form2() {
    7.             //InitializeComponent();
    8.             //this.FormBorderStyle = FormBorderStyle.None;
    9.         }
    10.         protected override void WndProc(ref Message m) {
    11.             base.WndProc(ref m);
    12.  
    13.             if (m.Msg == 0x84 && (int)m.Result == 0x1)
    14.                 m.Result = (IntPtr)0x2;
    15.         }
    16.  
    17.         [STAThread]
    18.         static void Main() {
    19.             Application.EnableVisualStyles();
    20.             Application.SetCompatibleTextRenderingDefault(false);
    21.             Application.Run(new Form2());
    22.         }
    23.     }
    24. }

  3. #3
    Ngày gia nhập
    02 2014
    Nơi ở
    TP.HCM
    Bài viết
    902

    Đoạn mã trên của Monre gọn gàng và giải quyết trực tiếp nhu cầu. Nếu bạn nào có nhu cầu tương tự trên C\C++ có thể xem thêm
    C Code:
    1. #include<Windows.h>
    2.  
    3. LRESULT CALLBACK MyWndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
    4. {
    5.     switch (message)
    6.     {
    7.     case WM_NCHITTEST:
    8.         LRESULT lr;
    9.         lr = DefWindowProc(hwnd, message, wparam, lparam);
    10.         if (lr == HTCLIENT)
    11.             lr = HTCAPTION;
    12.         return lr;
    13.     case WM_KILLFOCUS:      // Xem việc cửa sổ mất focus tương tự như người dùng thoát chương trình
    14.         SendMessage(hwnd, WM_CLOSE, 0, 0);
    15.         return 0;
    16.     case WM_DESTROY:
    17.         PostQuitMessage(0);
    18.         return 0;
    19.     }
    20.     return DefWindowProc(hwnd, message, wparam, lparam);
    21. }
    22.  
    23. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow)
    24. {
    25.     WNDCLASS        w;
    26.     HWND            hwnd;
    27.     MSG             msg;
    28.  
    29.     w.cbClsExtra = 0;
    30.     w.cbWndExtra = 0;
    31.     w.hbrBackground = 0;
    32.     w.hCursor = LoadCursor(NULL, IDC_ARROW);
    33.     w.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    34.     w.hInstance = hInstance;
    35.     w.lpfnWndProc = MyWndProc;
    36.     w.lpszClassName = "MyClass";
    37.     w.lpszMenuName = 0;
    38.     w.style = CS_HREDRAW | CS_VREDRAW;
    39.     if (!RegisterClass(&w))
    40.         return 0;
    41.     // Tạo cửa sổ không Caption
    42.     hwnd = CreateWindow(w.lpszClassName, NULL, WS_POPUP | WS_BORDER, 300, 200, 220, 140, NULL, NULL, hInstance, NULL);
    43.     if (!hwnd)
    44.         return 0;
    45.     ShowWindow(hwnd, nShow);
    46.     UpdateWindow(hwnd);
    47.     while (GetMessage(&msg, NULL, 0, 0))
    48.     {
    49.         TranslateMessage(&msg);
    50.         DispatchMessage(&msg);
    51.     }
    52.     return msg.wParam;
    53. }

    Trong C chúng ta có thể lấy một tên bất kỳ cho hàm xử lý thông điệp, khi thực thi Windows chỉ cần nắm địa chỉ mà thôi. Trong C# ta có thể xem Control.WndProc là cầu nối dẫn tới hàm xử lý.

  4. #4
    Ngày gia nhập
    08 2017
    Bài viết
    2,769

    Với những CT nhỏ, đơn giản như trên có thể biên dịch trực tiếp - không cần phải mở IDE (, có thể thêm thiết lập đường dẫn cho hợp lý)

    Copy đoạn code paste vào notepad / notepad++ lưu lại, biên dịch trong cmd

    #2: form2.cs ==> form2.exe
    Code:
    csc.exe /nologo form2.cs
    #3: MoveW.cpp ==> MoveW.exe
    Code:
    CL.exe /nologo user32.lib MoveW.cpp

  5. #5
    Ngày gia nhập
    02 2014
    Nơi ở
    TP.HCM
    Bài viết
    902

    II. Thông điệp từ hàng đợi và thông điệp từ bên ngoài hàng đợi.

    Như phần đầu đã đưa ra chặng đường mà một sự kiện trở thành một thông điệp trong hàng đợi và rồi được đưa vào hàm xử lý của chương trình.
    Giờ chúng ta đặt ngược lại câu hỏi : Như vậy hàm xử lý mà ta tạo ra (trực tiếp và gián tiếp C-C#) phải chăng chỉ để xử lý các thông điệp được nhặt ra từ hàng đợi thông điệp ?
    Không phải, hàm xử lý bao gồm cả xử lý các thông điệp không đến từ hàng đợi. Một số hàm của Windows khi thực thi có gọi trực tiếp thủ tục xử lý của chúng ta, bản thân chương trình cũng có thể giả lập thông điệp và gọi hàm xử lý. Tôi có thể viết một bức thư và đem tới nhà đưa trực tiếp cho bạn hoặc tôi có thể nhờ hệ thống bưu cục chuyển tới bạn. Nói gọn lại, hàm xử lý phải được viết để xử lý cả các thông điệp được POST tới hàng đợi lẫn từ các mã riêng SEND tới.

    Vậy khi xử lý các thông điệp thì các mã xử lý có thể tiếp tục SEND thông điệp khác không ?
    Trong phần lớn trường hợp thì không ảnh hưởng gì, tuy nhiên chúng ta phải suy xét cẩn thận để tránh đệ quy bất tận, thứ 2 là phải quan tâm tới dữ liệu có thể bị thay đổi hay không.
    Ví dụ : Trong khi xử lý WM_USER, vì lẽ nào đó bạn muốn SEND một WM_USER khác mà không có điều kiện kiểm tra để thoát, đệ quy bất tận sẽ xảy ra, đương nhiên thực tế có thể tinh vi hơn rất nhiều ví dụ trên.
    Dữ liệu cũng có thể thay đổi theo cách khó kiểm soát bởi trong Windows một hàm có thể phát sinh ra thông điệp, và một thông điệp khi xử lý lại có thể phát sinh ra thông điệp khác, ta cứ tưởng dữ liệu đang dùng là đúng đắn mà quên mất đã thay đổi nó trong thông điệp khác.
    Ví dụ : Lấy ví dụ sát với ví dụ của Monre. Khi chúng ta nhấp đúp vào icon góc trên trái form thì WM_NCHITTEST được Windows POST tới hàng đợi, vì chuột đang ở trên icon và thường thì chúng ta không viết mã sự kiện này nên base.WndProc sẽ xử lý theo cách mặc nhiên của nó nghĩa là bên trong nó mã PInvoice gọi DefWindowProc của Windows, DefWindowProc trả về HTSYSMENU. Windows POST thông điệp WM_NCLBUTTONDBLCLK với tham số wParam = HTSYSMENU vào hàng đợi, chúng ta cũng thường không xử lý thông điệp này nên quá trình trên diễn ra và Windows POST thông điệp WM_SYSCOMMAND với wParam = SC_CLOSE vào hàng đợi. Tương tự là tới WM_CLOSE, rồi tới WM_DESTROY, rồi WM_QUIT. Bất kỳ cài đặt chen ngang vào quá trình trên đều có thể làm form hoạt động bất thường (tức là chương trình không hoạt động đúng như mong muốn người dùng - nhấp đôi icon để đóng chương trình).

    Vậy chúng ta có thể căn cứ các mã hiệu định nghĩa thông điệp để xác định là nó tới từ hàng đợi hay được SEND vào hàm xử lý không ?
    Không. Hàm xử lý không nên mặc định một mã hiệu nào là có hay không có hàng đợi.

    ...

  6. #6
    Ngày gia nhập
    08 2017
    Bài viết
    2,769

    Mặc định Thảo luận về xử lý thông điệp trên Windows.

    Không đáng chi, sau bao nhiêu năm đi tắt đón đầu nay ta đến đâu?

    Tôi sẵn sàng nói chuyện kỹ thuật lập trình với các bạn

  7. #7
    Ngày gia nhập
    02 2014
    Nơi ở
    TP.HCM
    Bài viết
    902

    Trích dẫn Nguyên bản được gửi bởi Monre Xem bài viết
    Không đáng chi, sau bao nhiêu năm đi tắt đón đầu nay ta đến đâu?

    Tôi sẵn sàng nói chuyện kỹ thuật lập trình với các bạn
    Thực sự tôi không hiểu Monre nói chuyện gì, tôi nghĩ rằng đôi ba lon bia bạn đã không giữ được lòng mình !

  8. #8
    Ngày gia nhập
    08 2017
    Bài viết
    2,769

    Tửu lượng thấp vậy à?

    Bận chưa hứng để nói tiếp sao, ví dụ một textbox khi dùng event có thể dẫn đến đệ quy:
    - TextChanged
    - KeyDown / KeyUp, ...

  9. #9
    Ngày gia nhập
    02 2014
    Nơi ở
    TP.HCM
    Bài viết
    902

    III Gởi thông điệp tới cửa sổ tiến trình khác bằng SendMessage và PostMessage

    Khác nhau cơ bản giữa SendMessage và PostMessage là:
    _ SendMessage sẽ SEND thông điệp tới hàm xử lý sự kiện, và đợi hàm xử lý thông điệp xử lý xong mới trở về.
    _ PostMessage sẽ POST thông điệp tới hàng đợi thông điệp và trở về ngay.

    _ Nếu ta SEND thông điệp WM_QUIT tới thủ tục xử lý thì thủ tục xử lý vẫn nhận.
    _ Nếu ta POST thông điệp WM_QUIT tới hàng đợi thông điệp thì thủ tục xử lý sẽ không nhận được thông điệp này. Lý do là hàm GetMessage trả về 0 nên vòng lặp thông điệp sẽ thoát.

    Vài điều chung giữa 2 hàm SendMessage và PostMessage:
    _ Windows có thể chặn lại không gởi đến tiến trình khác nếu thông điệp muốn được gởi có tham số wParam hay lParam là một tham chiếu bộ nhớ của tiến trình gởi.
    Ví dụ : với thông điệp WM_CREATE có tham số lParam là con trỏ cấu trúc LPCREATESTRUCT, vì vậy Windows chặn lại, tôi chưa thử với cách viết bộ nhớ và thực thi tiến trình ở xa.

    Tôi đã viết thử 2 cặp chương trình minh họa, khi chạy chương trình Sender sẽ gởi các thông điệp tới chương trình Target, Target hiển thị thông điệp trên Listbox của nó,
    bạn có thể gởi nhận cả trong trường hợp C->C# hay C#->C. Sender vs Target là các mã nguồn C#, Sender2 vs Target2 là của C.
    Attached Files Attached Files

  10. #10
    Ngày gia nhập
    08 2017
    Bài viết
    2,769

    2 CT nói chuyện với nhau, gửi thông điệp cho nhau thì phải thế nào ?

    ngoài SendMessage và PostMessage còn có hàm nào để trao gửi thông điệp không ?

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn