作者 主題: 如何抓Console視窗變全螢幕後的Handle?  (閱讀 7847 次)

0 會員 與 1 訪客 正在閱讀本文。

make026

  • 可愛的小學生
  • *
  • 文章數: 16
    • 檢視個人資料
大家好! :)

我寫了個 VC++ 模仿 Turbo C 繪圖的程式。
代碼: [選擇]
/*========================================
VC++模仿Turbo C的graphics.h繪圖函式
========================================*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string>
using namespace std;
//for Thread
#define _MT
#include <process.h>
#pragma comment(lib, "libcmt.lib")
#pragma comment(linker, "/NODEFAULTLIB:libcd.lib")

//for GetConsoleWindow()
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow();
//Console相關全域變數
CONSOLE_CURSOR_INFO cInfo;
HANDLE hConOut;
COORD NewSBSize;
SMALL_RECT DisplayArea;
//設定Console視窗最大Size
void SetConsoleMaxSize();
//設定Console全螢幕
void SetConsoleFullScreen();

//繪圖相關全域變數
HWND hWnd;
int GraphFlag = 0;
HDC hdc;
RECT rc;
POINT pt;
HPEN hNewPen, hOldPen;
HBRUSH hNewBrush, hOldBrush;
COLORREF Color;
int PenStyle;
int PenSize;

//繪圖副程式
void initgraph(); //開啟繪圖模式
void closegraph(); //結束繪圖模式
void line(int x1, int y1, int x2, int y2); //畫直線
void clr(); //清除畫面

//Thread相關全域變數
HANDLE hThread;
unsigned ThreadID;
DWORD dwExitCode;
//繪圖Thread
unsigned __stdcall DrawThread(LPVOID lp){

initgraph();
while(!kbhit()){ //重繪窗口, 按任意鍵停止

line(100, 200, 350, 350);
line(100, 200, 400, 200);
line(400, 200, 150, 350);
line(350, 350, 250, 100);
line(150, 350, 250, 100);

clr();

Sleep(100); //降低CPU使用率
} //while() End
closegraph();

return 1; //傳回Thread退出碼
};

int main(){
Color = RGB(255,255,255); //預設圖形顏色
PenStyle = PS_SOLID; //筆刷樣式
PenSize = 1; //筆刷粗細
SetConsoleMaxSize(); //設定Console視窗最大Size
//SetConsoleFullScreen(); //設定Console全螢幕


//建立Thread並暫停Thread
hThread = (HANDLE)_beginthreadex(NULL, 0, DrawThread, NULL, CREATE_SUSPENDED, &ThreadID);
//建立Thread失敗時
if(hThread == 0) printf("Failed to create DrawThread\n");
//取得Thread退出碼
GetExitCodeThread(hThread, &dwExitCode);
//繼續執行Thread
ResumeThread(hThread);
//等Thread的信號
WaitForSingleObject(hThread, INFINITE);
//取得Thread退出碼
GetExitCodeThread(hThread, &dwExitCode );
//關閉Thread的Handle
CloseHandle(hThread);

system("pause");
return 0;
}


//畫直線
void line(int x1, int y1, int x2, int y2){
if(GraphFlag==1){
hNewPen = CreatePen(PenStyle, PenSize, Color);
hOldPen = (HPEN)SelectObject(hdc, hNewPen);
SelectObject(hdc, hNewPen);
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
DeleteObject(SelectObject(hdc, hOldPen));
}else{
printf("Call initgraph() first!\n");
}
}

//清除畫面
void clr(){
if(GraphFlag==1){
COORD coordScreen = {0, 0};
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

GetConsoleScreenBufferInfo(hConsole, &csbi);
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
GetConsoleScreenBufferInfo(hConsole, &csbi);
FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
SetConsoleCursorPosition(hConsole, coordScreen);
}else{
printf("Call initgraph() first!\n");
}
}

//開啟繪圖模式
void initgraph(){
GraphFlag = 1; //設置繪圖Flag為1, 允許使用繪圖函式
hWnd = GetConsoleWindow(); //抓取Console視窗的Handle
//隱藏Console游標
cInfo.dwSize = 1;
cInfo.bVisible = false;
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(hConOut, &cInfo);

hdc = GetDC(hWnd); //取得繪圖區
}

//結束繪圖模式
void closegraph(){
GraphFlag = 0; //設置繪圖Flag為0, 禁止使用繪圖函式
//顯示Console游標
cInfo.dwSize = 25;
cInfo.bVisible = true;
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(hConOut, &cInfo);

ReleaseDC(hWnd, hdc); //釋放繪圖區
}

//設定Console視窗最大Size
void SetConsoleMaxSize(){
DisplayArea.Left = 0;
DisplayArea.Top = 0;
DisplayArea.Right = 0;
DisplayArea.Bottom = 0;

    hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    NewSBSize = GetLargestConsoleWindowSize(hConOut);
    SetConsoleScreenBufferSize(hConOut, NewSBSize);

DisplayArea.Right = NewSBSize.X - 1;
DisplayArea.Bottom = NewSBSize.Y - 1;

    SetConsoleWindowInfo(hConOut, TRUE, &DisplayArea);
hWnd = GetConsoleWindow();
}

//設定Console全螢幕
void SetConsoleFullScreen(){
        keybd_event(VK_MENU,0x38,0,0);
        keybd_event(VK_RETURN,0x1c,0,0);
        keybd_event(VK_MENU,0xb8,KEYEVENTF_KEYUP,0);
        keybd_event(VK_RETURN,0x9c,KEYEVENTF_KEYUP,0);

hWnd = GetConsoleWindow(); //???
}

我的問題點在於,一呼叫設定Console全螢幕的那個副程式 ( SetConsoleFullScreen() ),視窗變全螢幕後,畫的圖就會不見。
Handle改變了嗎? ???

Yamaka

  • 俺是博士!
  • *****
  • 文章數: 4913
    • 檢視個人資料
    • http://www.ecmagic.com
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #1 於: 2011-10-18 10:42 »
大家好! :)

我寫了個 VC++ 模仿 Turbo C 繪圖的程式。
代碼: [選擇]
/*========================================
VC++模仿Turbo C的graphics.h繪圖函式
========================================*/
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string>
using namespace std;
//for Thread
#define _MT
#include <process.h>
#pragma comment(lib, "libcmt.lib")
#pragma comment(linker, "/NODEFAULTLIB:libcd.lib")

//for GetConsoleWindow()
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow();
//Console相關全域變數
CONSOLE_CURSOR_INFO cInfo;
HANDLE hConOut;
COORD NewSBSize;
SMALL_RECT DisplayArea;
//設定Console視窗最大Size
void SetConsoleMaxSize();
//設定Console全螢幕
void SetConsoleFullScreen();

//繪圖相關全域變數
HWND hWnd;
int GraphFlag = 0;
HDC hdc;
RECT rc;
POINT pt;
HPEN hNewPen, hOldPen;
HBRUSH hNewBrush, hOldBrush;
COLORREF Color;
int PenStyle;
int PenSize;

//繪圖副程式
void initgraph(); //開啟繪圖模式
void closegraph(); //結束繪圖模式
void line(int x1, int y1, int x2, int y2); //畫直線
void clr(); //清除畫面

//Thread相關全域變數
HANDLE hThread;
unsigned ThreadID;
DWORD dwExitCode;
//繪圖Thread
unsigned __stdcall DrawThread(LPVOID lp){

initgraph();
while(!kbhit()){ //重繪窗口, 按任意鍵停止

line(100, 200, 350, 350);
line(100, 200, 400, 200);
line(400, 200, 150, 350);
line(350, 350, 250, 100);
line(150, 350, 250, 100);

clr();

Sleep(100); //降低CPU使用率
} //while() End
closegraph();

return 1; //傳回Thread退出碼
};

int main(){
Color = RGB(255,255,255); //預設圖形顏色
PenStyle = PS_SOLID; //筆刷樣式
PenSize = 1; //筆刷粗細
SetConsoleMaxSize(); //設定Console視窗最大Size
//SetConsoleFullScreen(); //設定Console全螢幕


//建立Thread並暫停Thread
hThread = (HANDLE)_beginthreadex(NULL, 0, DrawThread, NULL, CREATE_SUSPENDED, &ThreadID);
//建立Thread失敗時
if(hThread == 0) printf("Failed to create DrawThread\n");
//取得Thread退出碼
GetExitCodeThread(hThread, &dwExitCode);
//繼續執行Thread
ResumeThread(hThread);
//等Thread的信號
WaitForSingleObject(hThread, INFINITE);
//取得Thread退出碼
GetExitCodeThread(hThread, &dwExitCode );
//關閉Thread的Handle
CloseHandle(hThread);

system("pause");
return 0;
}


//畫直線
void line(int x1, int y1, int x2, int y2){
if(GraphFlag==1){
hNewPen = CreatePen(PenStyle, PenSize, Color);
hOldPen = (HPEN)SelectObject(hdc, hNewPen);
SelectObject(hdc, hNewPen);
MoveToEx(hdc, x1, y1, NULL);
LineTo(hdc, x2, y2);
DeleteObject(SelectObject(hdc, hOldPen));
}else{
printf("Call initgraph() first!\n");
}
}

//清除畫面
void clr(){
if(GraphFlag==1){
COORD coordScreen = {0, 0};
DWORD cCharsWritten;
CONSOLE_SCREEN_BUFFER_INFO csbi;
DWORD dwConSize;
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

GetConsoleScreenBufferInfo(hConsole, &csbi);
dwConSize = csbi.dwSize.X * csbi.dwSize.Y;
FillConsoleOutputCharacter(hConsole, TEXT(' '), dwConSize, coordScreen, &cCharsWritten);
GetConsoleScreenBufferInfo(hConsole, &csbi);
FillConsoleOutputAttribute(hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten);
SetConsoleCursorPosition(hConsole, coordScreen);
}else{
printf("Call initgraph() first!\n");
}
}

//開啟繪圖模式
void initgraph(){
GraphFlag = 1; //設置繪圖Flag為1, 允許使用繪圖函式
hWnd = GetConsoleWindow(); //抓取Console視窗的Handle
//隱藏Console游標
cInfo.dwSize = 1;
cInfo.bVisible = false;
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(hConOut, &cInfo);

hdc = GetDC(hWnd); //取得繪圖區
}

//結束繪圖模式
void closegraph(){
GraphFlag = 0; //設置繪圖Flag為0, 禁止使用繪圖函式
//顯示Console游標
cInfo.dwSize = 25;
cInfo.bVisible = true;
hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorInfo(hConOut, &cInfo);

ReleaseDC(hWnd, hdc); //釋放繪圖區
}

//設定Console視窗最大Size
void SetConsoleMaxSize(){
DisplayArea.Left = 0;
DisplayArea.Top = 0;
DisplayArea.Right = 0;
DisplayArea.Bottom = 0;

    hConOut = GetStdHandle(STD_OUTPUT_HANDLE);
    NewSBSize = GetLargestConsoleWindowSize(hConOut);
    SetConsoleScreenBufferSize(hConOut, NewSBSize);

DisplayArea.Right = NewSBSize.X - 1;
DisplayArea.Bottom = NewSBSize.Y - 1;

    SetConsoleWindowInfo(hConOut, TRUE, &DisplayArea);
hWnd = GetConsoleWindow();
}

//設定Console全螢幕
void SetConsoleFullScreen(){
        keybd_event(VK_MENU,0x38,0,0);
        keybd_event(VK_RETURN,0x1c,0,0);
        keybd_event(VK_MENU,0xb8,KEYEVENTF_KEYUP,0);
        keybd_event(VK_RETURN,0x9c,KEYEVENTF_KEYUP,0);

hWnd = GetConsoleWindow(); //???
}

我的問題點在於,一呼叫設定Console全螢幕的那個副程式 ( SetConsoleFullScreen() ),視窗變全螢幕後,畫的圖就會不見。
Handle改變了嗎? ???

以一般視窗程式來說, 當視窗大小有變動時, 都會要求重新繪製視窗內容
系統會發出 update 的事件, 然後在 update 相關函數裡呼叫重新繪製的功能
或許樓主可以在 SetConsoleFullScreen() 最後呼叫相關函數來更新視窗內容

=====
剛剛想了一下, 可能不是上面我說的情形
應該是在進入全螢幕模式之後
這個全螢幕視窗已經不是一般視窗模式
所以不支援一般視窗的繪圖功能( ? )
« 上次編輯: 2011-10-18 11:29 由 Yamaka »

make026

  • 可愛的小學生
  • *
  • 文章數: 16
    • 檢視個人資料
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #2 於: 2011-10-18 14:41 »
以一般視窗程式來說, 當視窗大小有變動時, 都會要求重新繪製視窗內容
系統會發出 update 的事件, 然後在 update 相關函數裡呼叫重新繪製的功能
或許樓主可以在 SetConsoleFullScreen() 最後呼叫相關函數來更新視窗內容

=====
剛剛想了一下, 可能不是上面我說的情形
應該是在進入全螢幕模式之後
這個全螢幕視窗已經不是一般視窗模式
所以不支援一般視窗的繪圖功能( ? )
:)謝謝大大的回覆,我想應該不是視窗大小的問題,因為DrawThread()中有無窮while()迴圈會一直重繪視窗。
將 main() 中的 SetConsoleMaxSize(); 前面加上//後,再呼叫SetConsoleFullScreen(),執行程式後螢幕上都沒有圖,
可是按下Alt+Enter將全螢幕變回視窗後,圖出現了!!!不管如何放大,縮小,拉伸視窗,圖都會重繪(全螢幕化時除外)。

我想大大說的有道理,關鍵在於全螢幕模式怎麼繪圖或是將圖輸出到全螢幕模式!
現在已經知道的是GDI的繪圖可以在全螢幕下顯示,因為我使用GDI做過螢幕保護程式,螢幕保護程式一執行就是全螢幕,
但是程式需要#include <scrnsave.h>以及將輸出的exe副檔名改成scr才有效,不知道螢幕保護程式這其中有什麼樣特殊的機制。

Yamaka

  • 俺是博士!
  • *****
  • 文章數: 4913
    • 檢視個人資料
    • http://www.ecmagic.com
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #3 於: 2011-10-18 15:52 »
以一般視窗程式來說, 當視窗大小有變動時, 都會要求重新繪製視窗內容
系統會發出 update 的事件, 然後在 update 相關函數裡呼叫重新繪製的功能
或許樓主可以在 SetConsoleFullScreen() 最後呼叫相關函數來更新視窗內容

=====
剛剛想了一下, 可能不是上面我說的情形
應該是在進入全螢幕模式之後
這個全螢幕視窗已經不是一般視窗模式
所以不支援一般視窗的繪圖功能( ? )
:)謝謝大大的回覆,我想應該不是視窗大小的問題,因為DrawThread()中有無窮while()迴圈會一直重繪視窗。
將 main() 中的 SetConsoleMaxSize(); 前面加上//後,再呼叫SetConsoleFullScreen(),執行程式後螢幕上都沒有圖,
可是按下Alt+Enter將全螢幕變回視窗後,圖出現了!!!不管如何放大,縮小,拉伸視窗,圖都會重繪(全螢幕化時除外)。

我想大大說的有道理,關鍵在於全螢幕模式怎麼繪圖或是將圖輸出到全螢幕模式!
現在已經知道的是GDI的繪圖可以在全螢幕下顯示,因為我使用GDI做過螢幕保護程式,螢幕保護程式一執行就是全螢幕,
但是程式需要#include <scrnsave.h>以及將輸出的exe副檔名改成scr才有效,不知道螢幕保護程式這其中有什麼樣特殊的機制。

螢幕保護程式的全螢幕模式是用 directX 吧
跟 console 模式的全螢幕模式不一樣
在 directX 模式是有支援 GDI 的

make026

  • 可愛的小學生
  • *
  • 文章數: 16
    • 檢視個人資料
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #4 於: 2011-10-18 16:24 »
螢幕保護程式的全螢幕模式是用 directX 吧
跟 console 模式的全螢幕模式不一樣
在 directX 模式是有支援 GDI 的

謝謝大大的回覆。原來如此,螢幕保護模式是有用到directX,但是我是想寫一個VC下的Graphics的Lib,而且要考慮未有安裝directX的情形;
不知道有沒有其他的辦法呢?GDI本身不支援全螢幕模式嗎?

Yamaka

  • 俺是博士!
  • *****
  • 文章數: 4913
    • 檢視個人資料
    • http://www.ecmagic.com
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #5 於: 2011-10-18 21:17 »
螢幕保護程式的全螢幕模式是用 directX 吧
跟 console 模式的全螢幕模式不一樣
在 directX 模式是有支援 GDI 的

謝謝大大的回覆。原來如此,螢幕保護模式是有用到directX,但是我是想寫一個VC下的Graphics的Lib,而且要考慮未有安裝directX的情形;
不知道有沒有其他的辦法呢?GDI本身不支援全螢幕模式嗎?

不用 directX 的話, 可能就要自己切換到繪圖模式
然後自己寫一些低階基本的繪圖功能吧
或找看看還有沒有現成的繪圖函數庫可用

或是, 可以考慮改用一般視窗程式
將視窗設為最大化, 不顯示視窗標與邊框
並且設定固定在最上層
這樣也可以模擬全螢幕  :D

Yamaka

  • 俺是博士!
  • *****
  • 文章數: 4913
    • 檢視個人資料
    • http://www.ecmagic.com
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #6 於: 2011-10-19 02:52 »
.....而且要考慮未有安裝directX的情形.....

印象中, windows 系統安裝之後, 預設會自動安裝 directX
例如 xp 最初的版本(剛剛翻出古早之前用的, 在vbox安裝 XD)
預設會安裝 DirectX 9.0c版, 直接開 dxdiag 來測試
可以跑 DirectDraw, 全螢幕模式也可以正常顯示  :D

make026

  • 可愛的小學生
  • *
  • 文章數: 16
    • 檢視個人資料
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #7 於: 2011-10-20 21:08 »
印象中, windows 系統安裝之後, 預設會自動安裝 directX
例如 xp 最初的版本(剛剛翻出古早之前用的, 在vbox安裝 XD)
預設會安裝 DirectX 9.0c版, 直接開 dxdiag 來測試
可以跑 DirectDraw, 全螢幕模式也可以正常顯示  :D
謝了大大,你真熱心,還特別去測試。 :)
大大的建議也讓我有些寫程式的方向; 我目前是打算寫兩種版本的LIB,一個是Console專用的,一個是一般視窗用的;
我發現Console視窗按下Alt+Enter的解析度比不按還要低很多,所以目前是將Console視窗取桌面解析度再最大化,並置於最上層,這樣比較實用;
一般視窗方面倒是還沒遇到什麼問題。 :D

=====================================

話說這個網站前幾天突然上不來,不曉得怎麼了? ???

make026

  • 可愛的小學生
  • *
  • 文章數: 16
    • 檢視個人資料
Re: 如何抓Console視窗變全螢幕後的Handle?
« 回覆 #8 於: 2011-10-21 21:44 »
話說這個網站前幾天突然上不來,不曉得怎麼了? ???

原來右上角有停機公告,我的眼睛太大了,都沒看見。 :-X