對於Windows平台,顯示器的分辯率、顏色數、重新整理率等特性很重要,尤其是對於多媒體應用軟體和遊戲軟體。在很多情況下,使用者當前的螢幕設定並不適合軟體的運行需要,軟體通常的做法是提示使用者將螢幕設定到軟體要求的分辯率及顏色數,再重新啟動軟體。這樣無疑會增加普通使用者操作上的負擔和困難,降低了軟體的友好性和易用性。理想的作法是:在軟體開始時,動態改變螢幕設定來達到軟體啟動並執行要求。在軟體運行結束後,再自動把螢幕設定改回原來的設定值。這一切過程都在不知不覺中完成。本例示範了動態設定系統顯示分辯率,只要系統的硬體的支援,你可以將解析度設定為1024*768或800*600;色彩設定為8位、24位、32位等。程式編譯運行後的介面效果一所示:
一、編程方法 我們知道,顯示器所顯示的內容對應於顯存,在顯示器上最小的單位是象素(Pixel,這裡僅考慮邏輯象素),顯存的最小的單位是位(Bits)。 顯示器工作的特徵主要體現在色彩數和解析度兩方面。顯示器所顯示的色彩的數目取決於單位象素所使用的顯存的位元(Bits)。在顯存中,如果8位顯存(即一個向象素使用了一位元組的顯存)對應於顯示器上的一個象素,那麼顯示器所顯示的色彩數目為28=256色;同樣,如果當前的色彩為16位,那麼顯示器所顯示的色彩數目為216=65536種色彩。顯示器的解析度指的是水平解析度和垂直解析度,經常所說的800X600,就是指在水平方面上所顯示的象素為800個,在垂直方面上所顯示的象素為600個。 在VC中提供了修改顯示裝置(如顯示器、印表機等等,本文只就顯示器而言)屬性的函數ChangeDisplaySettings(),該函數能夠按照你的需要對顯示裝置作出相應的修改。其函數的原型為:LONG ChangeDisplaySettings(LPDEVMODE lpDevMode, DWORD dwflags),其參數的含義如下: lpDevMode:一個指向DEVMODE資料結構的指標,DEVMODE的資料結構描述了欲設定顯示器的各類屬性值。通常情況下使用到的成員變數有: dmSize:所用DEVMODE資料結構的大小(以Bytes為單位); dmBitsPerPel :每象素所使用的顯存位元(Bits); dmPelsWidth :水平解析度(點數); dmPelsHeight Pixel height :垂直解析度(點數); dmDisplayFrequency :顯示重新整理率,以赫茲為單位; dmFields:通常情況下,不同的顯示裝置(如印表機)用到的DEVMODE資料結構的內容不同,比如設定印表機時,你不會用到dmDisplayFrequency屬性。所以,在你使用DEVMODE資料結構時,應向系統說明你具體用到的有效資料成員,dmFields的用處便在於此。如果在程式中只用到dmPelsWidth(水平解析度)和dmPelsHeight(垂直解析度),那麼該值應為DM_PELSWIDTH|DM_PELSHEIGHT。 Dwflags:表明對顯示裝置的修改方式。具體取值有以下幾種:0 :動態改變顯示裝置屬性;CDS_UPDATEREGISTRY:動態改變顯示裝置屬性並修改註冊表相關設定,下次啟動電腦時,本次所做的修改依然有效;CDS_TEST:測試所做的修改是否有效。 上述函數調用後傳回值如下:DISP_CHANGE_SUCCESSFUL:修改成功;DISP_CHANGE_RESTART :修改後需重新啟動(在顯示器設定中選擇了"應用新的顏色前重新啟動電腦"); DISP_CHANGE_FAILED :修改失敗;DISP_CHANGE_BADMODE:修改模式錯誤(比如你的顯示器是單色的,但你卻將之修改為256色的)。如果在函數調用時,參數lpDevMode為NULL且dwflags 為0, 則顯示裝置使用註冊表當前值來設定顯示特性。以上是ChangeDisplaySettings()函數的最常見的用法,更詳細的說明請參見MSDN。 對於系統的顯示特性編程,還有一個關鍵的API函數EnumDisplaySettings()用來獲得當前顯示驅動所支援的所有顯示模式。獲得當前顯示模式可以使用下面的函數:
Bool GetCurrentVideoSettings(DEVMODE *devmode) { HWND hwndDesktop=GetDesktopWindow(); HDC hdc=GetDC(hwndDesktop); devmode -> dmSize =sizeof(DEVMODE); devmode -> dmBitsPerPel=GetDeviceCaps(hdc,BITSPIXEL); devmode -> dmPelsWidth=GetSystemMetrics(SM_CXSCREEN); devmode -> dmPelsHeight=GetSystemMetrics(SM_CYSCREEN); devmode -> dmFields=DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; return TRUE; } |
下面的代碼展示了如何使用EnumDisplaySettings獲得當前支援的所有顯示模式:
int modenum,done; DEVMODE devmode; done=0; modenum=0; do { done=!EnumDisplaySettings(NULL,modenum,&devmode); AddToList(&devmode); modenum++; }while (!done); |
設定顯示模式的方法如下: rc = ChangeDisplaySettings(&devmode,CDS_FULLSCREEN));這裡的devmode就是前面使用EnumDisplaySettings獲得的。如果設定正常,傳回值DISP_CHANGE_SUCCESSFUL。 二、 編程步驟 1、啟動Visual C++6.0,產生一個基於對話方塊的項目ChngDsplyMd,在對話方塊上放置三個編輯控制項,ID分別命名為ID_EDIT_WIDTH、ID_EDITHEIGHT、ID_EDIT_BITS,為了說明編輯框控制項的功能,其中前二個編輯框控制項放置在一個標題為"螢幕地區"的組合框中,第三個編輯框控制項旁邊添加一個STATIC控制項,"Caption"設定為"像素位元"。對話方塊架上的兩個按鈕控制項的"Caption分別設定為"改變分辯率"、"關閉",ID分別定義為IDC_CHANGE、IDOK; 2、使用Class Wizard為上述三個編輯控制項定義相應的成員變數,它們分別為: UINT m_nWidthPixels、 UINT m_nHeightPixels、 UINT m_nBitsPerPixel 3、實現對話方塊架中的按鈕單擊情況下的處理函數,編譯運行程式。 三、實現代碼
///////////////////////////////////////////////////////////////////// ChngDsplyMdDlg.h : header file #if !defined(AFX_CHNGDSPLYMDDLG_H__1D52415E_62DE_11D6_8F32_00E04CE76240__INCLUDED_) #define AFX_CHNGDSPLYMDDLG_H__1D52415E_62DE_11D6_8F32_00E04CE76240__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CChngDsplyMdDlg : public Cdialog // CChngDsplyMdDlg dialog { // Construction public: CChngDsplyMdDlg(CWnd* pParent = NULL); // standard constructor // Dialog Data //{{AFX_DATA(CChngDsplyMdDlg) enum { IDD = IDD_CHNGDSPLYMD_DIALOG }; UINT m_nBitsPerPixel; UINT m_nHeightPixels; UINT m_nWidthPixels; //}}AFX_DATA // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CChngDsplyMdDlg) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // Implementation protected: HICON m_hIcon; // Generated message map functions //{{AFX_MSG(CChngDsplyMdDlg) virtual BOOL OnInitDialog(); afx_msg void OnSysCommand(UINT nID, LPARAM lParam); afx_msg void OnPaint(); afx_msg HCURSOR OnQueryDragIcon(); afx_msg void OnChagne(); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; #endif ////////////////////////////////////////////////////////////////// // CChngDsplyMdDlg dialog #include "stdafx.h" #include "ChngDsplyMd.h" #include "ChngDsplyMdDlg.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif CChngDsplyMdDlg::CChngDsplyMdDlg(CWnd* pParent /*=NULL*/) : CDialog(CChngDsplyMdDlg::IDD, pParent) { //{{AFX_DATA_INIT(CChngDsplyMdDlg) m_nBitsPerPixel = 32; m_nWidthPixels = 1024; m_nHeightPixels = 768; //}}AFX_DATA_INIT // Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CChngDsplyMdDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CChngDsplyMdDlg) DDX_Text(pDX, IDC_EDIT_BITS, m_nBitsPerPixel); DDX_Text(pDX, IDC_EDIT_HEIGHT, m_nHeightPixels); DDX_Text(pDX, IDC_EDIT_WIDTH, m_nWidthPixels); //}}AFX_DATA_MAP }BEGIN_MESSAGE_MAP(CChngDsplyMdDlg, CDialog) //{{AFX_MSG_MAP(CChngDsplyMdDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_CHAGNE, OnChagne) //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CChngDsplyMdDlg message handlers BOOL CChngDsplyMdDlg::OnInitDialog() { CDialog::OnInitDialog(); // Add "About..." menu item to system menu. // IDM_ABOUTBOX must be in the system command range. ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // Set the icon for this dialog. The framework does this automatically // when the application's main window is not a dialog SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // TODO: Add extra initialization here return TRUE; // return TRUE unless you set the focus to a control } void CChngDsplyMdDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } } // If you add a minimize button to your dialog, you will need the code below // to draw the icon. For MFC applications using the document/view model, // this is automatically done for you by the framework. void CChngDsplyMdDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); // Center icon in client rectangle int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // Draw the icon dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } // The system calls this to obtain the cursor to display while the user drags // the minimized window. HCURSOR CChngDsplyMdDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void CChngDsplyMdDlg::OnChagne() { UpdateData(TRUE); DEVMODE lpDevMode; lpDevMode.dmBitsPerPel = m_nBitsPerPixel; lpDevMode.dmPelsWidth = m_nWidthPixels; lpDevMode.dmPelsHeight = m_nHeightPixels; lpDevMode.dmSize = sizeof(lpDevMode); lpDevMode.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL; LONG result = ChangeDisplaySettings(&lpDevMode,0); if(result == DISP_CHANGE_SUCCESSFUL) { AfxMessageBox("修改成功"); ChangeDisplaySettings(&lpDevMode,CDS_UPDATEREGISTRY); //使用CDS_UPDATEREGISTRY表示次修改是持久的, //並在註冊表中寫入了相關的資料 } else { AfxMessageBox("修改失敗,恢複原有設定"); ChangeDisplaySettings(NULL,0); } } |
四、 小結 上述執行個體代碼中對ChangeDisplaySettings()函數的傳回值result沒有作過多的分析,在實際操作中出於程式穩健的角度出發,可以對ChangeDisplaySettings()的傳回值作出更加詳細的判斷,以找出修改不成功的原因。通過設定DEVMODE的dmFields成員變數,還可以對系統的重新整理率特性進行設定,它的實現方法和上面幾乎是一樣的,相信不需要再多介紹了。 |