Công cụ bảo vệ mã nguồn .NET mạnh nhất, không thể unpack, miễn phí cho các khách hàng đầu tiên đăng ký.
Trang 2 trên tổng số 11 Đầu tiênĐầu tiên 1234... Cuối cùngCuối cùng
Từ 11 tới 20 trên tổng số 103 kết quả

Đề tài: Xác định màn hình ngoài (extended monitor) trong ứng dụng c#

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

    Mặc định Xác định màn hình ngoài (extended monitor) trong ứng dụng c#

    Màn hình ngoài (theo cách bạn nói) là tập hợp tổng của tất cả các "display" của thiết bị, Windows tính toán bằng hình chữ nhật bao quanh tất cả chúng. Dễ thấy là diện tích Logic của Screen sẽ >= tổng diện tích tất cả các display (tính theo pixel), ví dụ như ta có 3 thiết bị hiển thị thì phần Screen không thể sử dụng sẽ xấp xỉ 1/4 nó tùy theo cách xắp xếp trong ControlPanel.
    Với chúng ta thì mỗi Monitor được xác định bằng tên thiết bị, thẻ quản lý Monitor (HMONITOR) và một cấu trúc RECT chỉ định vùng chiếm giữ Logic trên Screen.

    Chương trình bên dưới bao gồm :
    _ ListView hiển thi danh sách các thiết bị các thẻ Monitor và vùng RECT mà nó đếm được.
    _ Textbox là để nhập vào 1 chuỗi bất kỳ cho thử nghiệm.
    _ Khi nhấn nút <Send>, chương trình sẽ kiểm tra thiết bị nào trên ListView được chọn thì xuất dòng chữ trên Textbox ra thiết bị đó.

    Visual C++ Code:
    1. #include<Windows.h>
    2. #include<CommCtrl.h>
    3. #include"resource.h"
    4. #pragma comment(lib, "Comctl32.lib")
    5.  
    6. class CMyDialog
    7. {
    8. private:
    9.     HWND                    m_hDlg;         // Thẻ cửa sổ hộp thoại
    10.     HWND                    m_hList;        // Thẻ cửa sổ Listview sẽ chứa thông tin về các Monitors
    11.     HWND                    m_hEdit;        // Hộp soạn text để gới tới 1 Monitor xác định
    12.  
    13.     static BOOL CALLBACK MonitorEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
    14.     {
    15.         HWND    hList = (HWND)dwData;
    16.  
    17.         MONITORINFOEX   mie;
    18.         mie.cbSize = sizeof(MONITORINFOEX);
    19.         GetMonitorInfo(hMonitor, (LPMONITORINFOEX)&mie);
    20.  
    21.         LVITEM  lvi;
    22.         WCHAR   szText[MAX_PATH];
    23.         lvi.mask = LVIF_TEXT;
    24.         lvi.iSubItem = 0;
    25.         lvi.pszText = szText;
    26.  
    27.         wsprintf(szText, L"%s", mie.szDevice);
    28.         int iIndex = ListView_InsertItem(hList, &lvi);
    29.  
    30.         wsprintf(szText, L"%016I64X", hMonitor);
    31.         ListView_SetItemText(hList, iIndex, 1, szText);
    32.  
    33.         wsprintf(szText, (mie.dwFlags & MONITORINFOF_PRIMARY) ? L"yes" : L"no");
    34.         ListView_SetItemText(hList, iIndex, 2, szText);
    35.  
    36.         wsprintf(szText, L"%d", lprcMonitor->left);
    37.         ListView_SetItemText(hList, iIndex, 3, szText);
    38.         wsprintf(szText, L"%d", lprcMonitor->top);
    39.         ListView_SetItemText(hList, iIndex, 4, szText);
    40.         wsprintf(szText, L"%d", lprcMonitor->right);
    41.         ListView_SetItemText(hList, iIndex, 5, szText);
    42.         wsprintf(szText, L"%d", lprcMonitor->bottom);
    43.         ListView_SetItemText(hList, iIndex, 6, szText);
    44.  
    45.         lvi.mask = LVIF_PARAM;
    46.         lvi.iItem = iIndex;
    47.         lvi.lParam = (LPARAM)hMonitor;
    48.         ListView_SetItem(hList, &lvi);
    49.         return TRUE;
    50.     }
    51.     static BOOL CALLBACK DialogProc(HWND hDlg, UINT message, WPARAM wparam, LPARAM lparam)
    52.     {
    53.         if (message == WM_INITDIALOG)
    54.         {
    55.             CMyDialog * pMyDialog = reinterpret_cast<CMyDialog *>(lparam);
    56.             SetWindowLongPtr(hDlg, GWLP_USERDATA, PtrToUlong(pMyDialog));
    57.             return pMyDialog->OnInitDialog(hDlg);
    58.         }
    59.         CMyDialog * pMyDialog = reinterpret_cast<CMyDialog *>(static_cast<LONG_PTR>(GetWindowLongPtr(hDlg, GWLP_USERDATA)));
    60.         switch (message)
    61.         {
    62.         case WM_DISPLAYCHANGE:  return pMyDialog->OnDisplayChange();
    63.         case WM_COMMAND:        return pMyDialog->OnCommand(wparam);
    64.         case WM_CLOSE:          return pMyDialog->OnClose();
    65.         }
    66.         return FALSE;
    67.     }
    68.  
    69.     BOOL OnInitDialog(HWND hDlg)
    70.     {
    71.         m_hDlg = hDlg;
    72.         m_hList = GetDlgItem(hDlg, IDC_LIST1);
    73.         m_hEdit = GetDlgItem(hDlg, IDC_EDIT1);
    74.  
    75.         ListView_SetExtendedListViewStyle(m_hList, LVS_EX_FULLROWSELECT);
    76.  
    77.         LVCOLUMN    lvc;
    78.         lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
    79.        
    80.         lvc.cx = 80;
    81.         lvc.iSubItem = 0;
    82.         lvc.pszText = L"DeviceName";
    83.         ListView_InsertColumn(m_hList, 0, &lvc);
    84.  
    85.         lvc.cx = 120;
    86.         lvc.iSubItem = 1;
    87.         lvc.pszText = L"HMONITOR";
    88.         ListView_InsertColumn(m_hList, 1, &lvc);
    89.  
    90.         lvc.cx = 60;
    91.         lvc.iSubItem = 2;
    92.         lvc.pszText = L"Primary";
    93.         ListView_InsertColumn(m_hList, 2, &lvc);
    94.  
    95.         lvc.cx = 50;
    96.         lvc.iSubItem = 3;
    97.         lvc.pszText = L"Left";
    98.         ListView_InsertColumn(m_hList, 3, &lvc);
    99.         lvc.iSubItem = 4;
    100.         lvc.pszText = L"Top";
    101.         ListView_InsertColumn(m_hList, 4, &lvc);
    102.         lvc.iSubItem = 5;
    103.         lvc.pszText = L"Right";
    104.         ListView_InsertColumn(m_hList, 5, &lvc);
    105.         lvc.iSubItem = 6;
    106.         lvc.pszText = L"Bottom";
    107.         ListView_InsertColumn(m_hList, 6, &lvc);
    108.         // Nạp thông tin mới
    109.         HDC     hdc = GetDC(NULL);
    110.         EnumDisplayMonitors(hdc, NULL, MonitorEnumProc, (LPARAM)m_hList);
    111.         ReleaseDC(NULL, hdc);
    112.         ListView_SetItemState(m_hList, 0, LVIS_SELECTED, LVIS_SELECTED);
    113.         SetFocus(m_hEdit);
    114.         return FALSE;
    115.     }
    116.     BOOL OnDisplayChange()
    117.     {
    118.         // Xóa thông tin
    119.         ListView_DeleteAllItems(m_hList);
    120.         // Nạp thông tin mới
    121.         HDC     hdc = GetDC(NULL);
    122.         EnumDisplayMonitors(hdc, NULL, MonitorEnumProc, (LPARAM)m_hList);
    123.         ReleaseDC(NULL, hdc);
    124.         ListView_SetItemState(m_hList, 0, LVIS_SELECTED, LVIS_SELECTED);
    125.         SetFocus(m_hEdit);
    126.         return TRUE;
    127.     }
    128.     BOOL OnCommand(WPARAM wparam)
    129.     {
    130.         switch (LOWORD(wparam))
    131.         {
    132.         case IDC_BUTTON1:
    133.             Button1_Click();
    134.             return TRUE;
    135.         case IDOK:
    136.         case IDCANCEL:
    137.             return OnClose();
    138.         }
    139.         return FALSE;
    140.     }
    141.     BOOL OnClose()
    142.     {
    143.         EndDialog(m_hDlg, 0);
    144.         return TRUE;
    145.     }
    146.    
    147.     void Button1_Click()
    148.     {
    149.         WCHAR   szText[MAX_PATH];
    150.         GetWindowText(m_hEdit, szText, MAX_PATH - 1);
    151.         if (lstrlen(szText))
    152.         {
    153.             LVITEM  lvi;
    154.             lvi.mask = LVIF_PARAM;
    155.             for (int i = (ListView_GetItemCount(m_hList)) - 1; i >= 0; i--)
    156.             {
    157.                 if ((ListView_GetItemState(m_hList, i, LVIS_SELECTED)) == LVIS_SELECTED)  // Item này được chọn ?
    158.                 {
    159.                     lvi.iItem = i;
    160.                     ListView_GetItem(m_hList, &lvi);
    161.                     if (lvi.lParam)
    162.                     {
    163.                         MONITORINFO info;
    164.                         info.cbSize = sizeof(MONITORINFO);
    165.                         GetMonitorInfo((HMONITOR)lvi.lParam, (LPMONITORINFO)&info);
    166.                         HDC     hdc = CreateDC(L"DISPLAY", NULL, NULL, NULL);
    167.                         SetTextColor(hdc, RGB(255, 0, 0));
    168.                         SetBkColor(hdc, RGB(0, 255, 255));
    169.                         info.rcWork.top += 20;
    170.                         DrawText(hdc, szText, -1, &info.rcWork, DT_CENTER | DT_SINGLELINE);
    171.                         DeleteDC(hdc);
    172.                     }
    173.                 }
    174.             }
    175.         }
    176.     }
    177.  
    178. public:
    179.     void Show(HINSTANCE hInst)
    180.     {
    181.         DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc, (LPARAM)this);
    182.     }
    183. };
    184.  
    185. int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow)
    186. {
    187.     CMyDialog   myDlg;
    188.     myDlg.Show(hInstance);
    189.     return 0;
    190. }

    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		MM2.jpg
Lần xem:	5
Size:		27.9 KB
ID:		59083
    Attached Files Attached Files
    Công cụ bảo vệ mã nguồn .NET mạnh nhất hiện tại, miễn phí cho các khách hàng đầu tiên đăng ký.

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

    Màn hình ngoài bao gồm thiết bị vật lý và thiết lập phần mềm.

    Với cấu hình sau (hình chụp)

    nó không làm việc như bạn mô tả. Thử trên win10_64

    Có thử trên win81_64, nó bị thiếu vcruntime140, cài Visual C++ Redistributable for Visual Studio 2015 gặp lỗi tôi sẽ thử lại sau

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

    Tôi dùng Windows 8 Pro Build 9200, chương trình xây dựng trên VS2015 từ dự án trống trên VC++ có thiết lập cấu hình sang Unicode.

    Tôi đã vào Control Panel chỉnh lại vị trí để có 1 Monitor mang tọa độ âm, chương trình vẫn hoạt động đúng

    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		MM2_A.jpg
Lần xem:	2
Size:		27.7 KB
ID:		59090

    Cũng vậy, kể cả trường hợp cho 2 Monitor sole nhau, nó vẫn hoàn toàn chính xác

    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		mm4.jpg
Lần xem:	3
Size:		28.0 KB
ID:		59091
    Click vào hình ảnh để lấy hình ảnh lớn

Tên:		mm3.jpg
Lần xem:	3
Size:		80.9 KB
ID:		59092
    Đã được chỉnh sửa lần cuối bởi MHoang : 30-10-2017 lúc 12:45 PM. Lý do: Thêm hình minh họa

  4. #14
    Ngày gia nhập
    08 2017
    Bài viết
    3,344

    Sau khi cài bổ xung Visual C++ Redistributable for Visual Studio 2015, Win81 nó chạy đúng:

    hoặc:


    Trong gói Visual C++ Redistributable for Visual Studio 2015 không chứa thư viện trực tiếp vcruntime140.dll tôi copy nó từ HDH khác sang (cùng một PC, máy với dbl-boot)
    Là HDH dùng để kiểm test nó không cài nhiều công cụ, thư viện(, do có ý thu nhỏ dung lượng HDH) nên thời gian chạy Windows Update, cài, tìm thư viện bị kéo dài`

    Định biên dịch lại với Vs2010, chuyển Character Set từ Use Multi-Byte Character Set sang Use Unicode Character Set (hết lỗi biên dịch) nhưng khi chạy, ListView rỗng.

    Còn Win10 tôi chưa kịp kiểm test lại

    Đây là trường hợp tốn nhiều công sức mà vẫn chưa tìm thấy phương án, giải pháp tốt để giải quyết rốt ráo`

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

    Quote : "... biên dịch lại với Vs2010 ... khi chạy, ListView rỗng."

    Đây là lỗi do sơ suất của tôi, lỗi này khá nặng. Nguyên do là khi sử dụng ListView thì phải khởi tạo CommonControl, trước đây sử dụng các phiên bản VC++ ( như VC++6.0) tôi rất nhớ điều này, sau này khi sử dụng VS2015 quá nhiều nên tôi đã quên chuyện an toàn mã (VS2015 không cần mã khởi tạo - Có lẽ nó thừa thông minh để tự thêm mã máy dù mã nguồn không có). Bạn có thể thêm mã khởi tạo trước khi tạo hộp thoại hay khi chương trình bắt đầu :
    Visual C++ Code:
    1. public:
    2.     void Show(HINSTANCE hInst)
    3.     {
    4.         // Khởi tạo CommonControl
    5.         INITCOMMONCONTROLSEX    icc;
    6.         icc.dwSize = sizeof(INITCOMMONCONTROLSEX);
    7.         icc.dwICC = ICC_LISTVIEW_CLASSES;
    8.         InitCommonControlsEx(&icc);
    9.         DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc, (LPARAM)this);
    10.     }

    MM3 bên dưới chỉnh sửa lại sai sót trên, nó cũng được viết để biên dịch có hay không có Unicode tùy vào cấu hình Character Set

    Tôi cũng muốn biết MM3.exe khi đưa sang máy của bạn trên Win81 hay trên Win10 hay các bản khác có chạy đúng không. Còn vấn đề thiếu hoặc không tương thích dll thì quá lớn, theo tôi thì phải nghĩ tới chương trình cài đặt ( dùng Depend của Windows hay pe4 tôi viết tìm tất cả các dll phụ thuộc mà không phải dll cơ bản của Win rồi copy vào máy cài đặt). Một hướng khác là dùng các TBD cũ hơn ( ví dụ viết MFC VC++6.0 với tùy chọn biên dịch vào mã máy chứ không dùng dll) cũng có thể giải quyết vấn đề.
    Attached Files Attached Files

  6. #16
    Ngày gia nhập
    08 2017
    Bài viết
    3,344

    Mặc định Xác định màn hình ngoài (extended monitor) trong ứng dụng c#

    Trên Win81 nó chạy đúng, đáng tiếc trên Win10 (Win10-64-Pro cùng một PC, cài dbl-boot) nó không đúng.

    Tôi đang kiểm test trên Win10: Monitor 2 được chọn làm main display


    Nó xuất ra màn hình 1 được, còn màn hình 2 không nhận thấy tín hiệu nào

  7. #17
    Ngày gia nhập
    08 2017
    Bài viết
    3,344

    Khi người vận hành không có mặt tại máy tính, họ có thể điều khiển từ xa ?
    TeamView không quan sát được màn hình khác, không điều khiển được các dialog /window ở đó.

    Có thể tích hợp CT CaptureScreens. tích hợp gọi ứng dụng (HDH Windows)
    DisplaySwitch.exe /opt
    {opt : clone, extend, external, internal}
    để chuyển các cửa sổ đến màn hình quan sát được, điều khiển được

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

    Để di chuyển các cửa sổ qua lại giữa các Monitor có lẽ là phương cách dễ nhất, nhưng nó cũng tồn tại yếu điểm. Giả sử một công xưởng với vài phân xưởng sản xuất khác nhau, mỗi phân xưởng một màn hình hướng dẫn các thao tác cho công nhân riêng phân xưởng đó. Nếu các hướng dẫn nằm trên cửa sổ và khi người vận hành di chuyển các cửa sổ đến một Monitor khác (bộ phận điều phối), thì thao tác dây chuyền của công nhân có thể bị ảnh hưởng vì màn hình tại đó không còn nội dung hướng dẫn.

    Phương cách khác là không di chuyển cửa sổ mà tạo cửa sổ nhái tại nơi cần quan sát và chương trình gởi các sự kiện tới cửa sổ đó khi người vận hành điều khiển trên cửa sổ nhái.

    Nhưng theo tôi, nếu các nhu cầu như bạn nói ở các mục trước, thì có lẽ các người dùng (hay doanh nghiệp) sẽ nghĩ tới mạng (wan-lan) hơn là dùng Multi-Monitor.

  9. #19
    Ngày gia nhập
    08 2017
    Bài viết
    3,344

    Trên (#17) là giải pháp dùng cho điều khiển từ xa (TeamView, ...).
    Còn trường hợp khi người vận hành điều khiển ngồi tại máy tính điều khiển, họ rê chuột là được, rê chuột qua lại giữa các monitor.

    Cửa sổ nhái tại nơi cần quan sát, chắc là CaptureScreens đã đề cập ở trên ?

  10. #20
    Ngày gia nhập
    08 2017
    Bài viết
    3,344

    Với phương án connect từ xa (qua TeamView), một số trường hợp không quan sát được CT do nó đang hiện thị trên Monitor khác.

    Làm sao phát sinh các event để gọi các chức năng của nó ở #17 ? Có thể tác động lên CT ở TaskBar, ở SystemTray ?
    Công cụ bảo vệ mã nguồn .NET mạnh nhất hiện tại, miễn phí cho các khách hàng đầu tiên đăng ký.

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