vc问答
问:(1) 在Windows
98中可以设置一个窗口的标题栏有颜色过渡的效果,比如由蓝色过渡到黑色,Windows
95就不行。我想知道,应该如何编程才能实现这种美化程序的效果呢?最好能给出一个范例。
(2) 还是在Windows 95中,我觉得以前Windows 3.1中带有CTL3D效果的窗体很漂亮,能不能在Windows
95的程序中也加入这种效果,使得窗体的框架丰满一些呢?
|
答: (1)
我以前发表在本刊上的一篇文章曾经讨论过自行绘制标题栏的问题,但是那篇文章的重点是如何设置自己的字体。如果使自己的标题栏有颜色过渡的效果,还需要处理其他一些问题。解决这个问题的核心仍然是处理客户区和非客户区的重绘问题。在自己的WndProc中,需要对WM_NCPAINT、WM_ACTIVATE、WM_DEACTIVATE和WM_ERASEBKGND四个消息进行处理。判断当前窗口的激活状态就可以绘制自己的标题栏,从而加上想要的效果。在窗口的WndProc过程中这样写:
switch( uMsg ) {
case WM_NCPAINT:
case WM_ACTIVATE:
case WM_ACTIVATEAPP:
case WM_ERASEBKGND:
DefWindowProc( hDlg, uMsg, wParam, lParam );
DrawTitleBar( hDlg );
break;
}
先调用DefWindowProc的原因是我们需要让Windows先把标准的窗体边框画好。DrawTitleBar过程负责绘制自己的标题栏。
void DrawTitleBar( HWND hWnd )
{
RECT r;
WORD Left, Top, TitleHeight, TitleWeight, IconWeight;
HDC MemDC;
COLORREF SysTitleBkg, SysTitleClr;
HFONT HF;
MemDC = GetWindowDC( hWnd );
GetClientRect( hWnd, &r );
IconWeight = GetSystemMetrics( SM_CXSIZE );
Left = 1;
Top = 1;
TitleHeight = GetSystemMetrics( SM_CYCAPTION );
TitleWeight = r.right - r.left;
SysTitleClr = GetSysColor( COLOR_CAPTIONTEXT );
if( GetActiveWindow() == hWnd )
SysTitleBkg = 0xf0a080;
else
SysTitleBkg = 0;
DrawGradientBar( MemDC, 0x800000, SysTitleBkg, Left + 1, Top
+ 1,
TitleWeight + 1, TitleHeight + 1 );
SetTextColor( MemDC, SysTitleClr );
SetBkColor( MemDC, SysTitleBkg );
SetBkMode( MemDC, TRANSPARENT );
HF = CreateFont( 10, 0, 0, 0, FW_BOLD, 0, 0, 0,
ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, "MS Sans Serif" );
SelectObject( MemDC, HF );
TextOut( MemDC, 4, Top + ( TitleHeight - 10 ) / 2, APPNAME,
strlen( APPNAME ) );
DeleteObject( HF );
DeleteObject( MemDC );
}
其中,APPNAME是程序的标题栏内容。在调用TextOut函数时要注意,一定要把背景设置成透明色,否则过渡效果就表现不出来了。最后我们要实现如何绘制带有颜色过渡效果的背景。实现的关键是如何调配RGB三原色的搭配。另外,绘制的速度也很重要。这里给出的代码采用每隔几个像素进行一次颜色渐变的方法来实现。
void DrawGradientBar( HDC hdc, COLORREF co1, COLORREF co2, int
x, int y, int cx, int cy )
{
int r = GetRValue( co1 );
int g = GetGValue( co1 );
int b = GetBValue( co1 );
int n, m;
int r2 = GetRValue( co2 );
int g2 = GetGValue( co2 );
int b2 = GetBValue( co2 );
RECT rect;
HBRUSH hbr;
rect.left = x;
rect.right = x + GRADLEVEL;
rect.top = y;
rect.bottom = y + cy;
m = x + cx;
if( cx <= 0 || cy <= 0 )
return;
for( n = cx; n > 0; n -= GRADLEVEL ) {
hbr = CreateSolidBrush( RGB( r, g, b ) );
FillRect( hdc, &rect, hbr );
DeleteObject( hbr );
rect.left += GRADLEVEL;
rect.right += GRADLEVEL;
if( rect.right > m )
rect.right = m;
r += ( r2 - r + n / 2 ) / n * GRADLEVEL;
g += ( g2 - g + n / 2 ) / n * GRADLEVEL;
b += ( b2 - b + n / 2 ) / n * GRADLEVEL;
}
}
其中,GRADLEVEL是颜色渐变的等级:等级越小,绘制出来的背景就越接近于自然过渡,然而速度越慢;反之则可以清楚地看到过渡的过程,同时速度也越快。你可以根据实际需求来决定取舍。
(2) CTL3D效果一般也是通过上面的方法来进行绘制的。下面的代码也位于WndProc过程中,当遇到上述四个消息时加厚窗体的框架。
BOOL FAR PASCAL MyWndProc(HWND hDlg, WORD iMessage, WORD
wParam,
LONG lParam)
{
HDC hDlgdc;
HPEN hWhitePen, hOld;
static LOGPEN lpDarkGray = { PS_SOLID, 1, 1, RGB( 128, 128,
128 ) };
static LOGPEN lpLightGray = { PS_SOLID, 1, 1, RGB( 192, 192,
192 ) };
RECT rc, rc1;
HBRUSH hGrayBrush;
int i, TitleHeight, cyMenu, cxDlgFrame, cyDlgFrame;
static HBRUSH hbrGray;
switch( iMessage )
{
case WM_ACTIVATE:
case WM_NCPAINT:
case WM_ERASEBKGND:
case WM_PAINT:
hDlgdc = GetWindowDC(hDlg); // 获取窗口DC
GetWindowRect(hDlg, &rc); // 获取窗口矩形
rc.right -= rc.left; // 计算宽度
rc.bottom -= rc.top; // 计算高度
hWhitePen = GetStockObject( WHITE_PEN ); // 白色 PEN
hOld = SelectObject( hDlgdc, hWhitePen ); // 设置 PEN
MoveTo( hDlgdc, 1, 1 ); //
LineTo( hDlgdc, rc.right - 2, 1 ); // 画上边框
MoveTo( hDlgdc, 1, 1 ); //
LineTo(hDlgdc, 1, rc.bottom - 2); // 画左边框
TitleHeight = GetSystemMetrics( SM_CYCAPTION );
cyMenu = GetSystemMetrics( SM_CYMENU );
cxDlgFrame = GetSystemMetrics( SM_CXDLGFRAME );
cyDlgFrame = GetSystemMetrics( SM_CYDLGFRAME );
MoveTo( hDlgdc, cxDlgFrame + 1, TitleHeight + cyDlgFrame - 1
);
LineTo( hDlgdc, rc.right - cxDlgFrame - 2,
TitleHeight + cyDlgFrame - 1 ); // 画 TITLE 下边框
LineTo( hDlgdc, rc.right - cxDlgFrame - 2,
cyDlgFrame ); // 画 TITLE 右边框
hWhitePen = CreatePenIndirect( &lpDarkGray ); //
深灰色 PEN
SelectObject( hDlgdc, hWhitePen );
myRectangle( hDlgdc, 0, 0, rc.right -2, rc.bottom - 2 );
MoveTo( hDlgdc, cxDlgFrame + 1, TitleHeight + cyDlgFrame - 1
);
LineTo( hDlgdc, cxDlgFrame + 1, cyDlgFrame); //画TITLE左边框
LineTo( hDlgdc, rc.right - cxDlgFrame - 2, cyDlgFrame); //画TITLE上边框
SelectObject( hDlgdc, hOld );
DeleteObject( hWhitePen );
hWhitePen = CreatePenIndirect( &lpLightGray );
SelectObject( hDlgdc, hWhitePen );
myRectangle( hDlgdc, 2, 2, rc.right - 3, rc.bottom - 3 );
for( i = 0; i < cxDlgFrame - 2; i ++ ) { // 填充边框
myRectangle(hDlgdc, i + 3, i + 2, rc.right - i - 4, rc.bottom
- i - 4);
}
SelectObject( hDlgdc, hOld );
DeleteObject( hWhitePen );
ReleaseDC( hDlg, hDlgdc );
return FALSE;
case WM_CTLCOLOR:
switch( HIWORD( lParam ) ) {
case CTLCOLOR_DLG:
return (LRESULT)hbrGray;
case CTLCOLOR_STATIC:
SetTextColor((HDC)wParam, RGB( 0, 0, 255 ) );
SetBkColor((HDC)wParam, RGB( 192, 192, 192) );
return (LRESULT)hbrGray;
}
return NULL;
case WM_INITDIALOG:
hbrGray = CreateSolidBrush( RGB( 192, 192, 192 ) );
GetWindowRect( GetDesktopWindow(), &rc );
GetWindowRect( hDlg, &rc1 );
rc1.left = ( rc.right / 2 ) - ( rc1.right - rc1.left ) / 2;
rc1.top = ( rc.bottom / 2 ) - ( rc1.bottom - rc1.top ) / 2;
SetWindowPos( hDlg, NULL, rc1.left, rc1.top, 0, 0,
SWP_NOSIZE | SWP_NOZORDER );
if( wParam == GetDlgItem( hDlg, ID_INPUT_PASS )) {
return TRUE; //设置热点
}
return FALSE;
case WM_COMMAND:
switch( wParam ) {
case IDCANCEL:
EndDialog( hDlg, TRUE );
DeleteObject( hbrGray );
return FALSE;
}
break;
}
return FALSE;
}
《电子与电脑》99年2期 问与答
|
| 问:我由于工作需要,有几个程序必须使用汇编语言来编制,如果是DOS程序倒也容易,但是项目要求这些程序工作在Windows
95或者Windows NT下,也就是说,我必须用汇编语言来编制Windows应用程序。虽然我有好几年的Windows下C程序开发经验,但是用汇编写Win32程序我从来没试过,也不知道从何下手,我想我缺少的只是一个通用的Win32汇编程序的框架,能否满足我的要求? |
答:确实,有很多程序是不得不用汇编语言编写的,诸如各种底层开发工具等等。我首先建议您自己评估自己的项目要求,如果不是特别要求速度(算法上已经优化过,唯一的可能是从汇编级再次优化),我仍然认为目前C已经足够使用,而且就Visual
C++来说,它的编译代码质量相当高。另外,汇编语言写出的Win32程序一般只能够使用SoftICE等专业调试工具来调试,即使这样,代码也可能存在许多安全问题上的漏洞。如果您确实需要使用汇编语言来写程序,我下面就通过讲述一个最简单的Win32汇编程序来让您了解一下一个Win32汇编程序的框架。
我们这个例子程序的功能是判断当前Windows版本是否是测试版,基本方法是使用GetSystemMetrics函数,配合SM_DEBUG参数调用。下面是汇编程序。注意几点:
第一,所有需要使用的Win32 API函数必须在程序一开头时声明为外部函数;第二,所有需要引用的常量应该用equ关键字来定义;第三,编译模式应该为flat,
stdcall模式;第四,参数调用顺序一般按照Pascal习惯。
另外,这个例子程序是使用TASM 5.0编制的,还需要一个编译BAT文件,也附在后面。您需要把TASM32.EXE和TLINK32.EXE的路径换成您自己的。
[MAKE.BAT]
@echo off
echo.
echo IFDB Compiler - Written by Jiang Hong.
echo.
echo Compiling ......
echo.
d:\tasm5\bin\tasm32 /ml /m3 /z /q IFDB
d:\tasm5\bin\tlink32 -x /Tpe /ap /c IFDB,IFDB,, import32.lib
for %%a in (*.obj) do del %%a
for %%a in (*.map) do del %%a
echo.
echo Finished.
echo.
[IFDB.ASM]
.386p
locals
jumps
.model flat, stdcall
SM_DEBUG equ 22
;
; Function of Win32 API to be imported
;
extrn ExitProcess : proc
extrn GetSystemMetrics : proc
extrn MessageBeep : proc
extrn GetStdHandle : proc
extrn WriteConsoleA : proc
.data
ConHandle dd 0
cchWritten dd 0
Msg1 db 'IFDB ?Judge if Windows 95/98/NT is Debug
Version/Mode', 0dh, 0ah
db 'Written by Jiang Hong, Egis Computing.', 0dh, 0ah, 0dh,
0ah
db 'This is a '
Msg1Len equ $ - Msg1
Msg2 db ' of Win32.', 0dh, 0ah
Msg2Len equ $ - Msg2
DbgInfo db 'Debug version'
DbgInfoLen equ $ - DbgInfo
RelInfo db 'Release version'
RelInfoLen equ $ - RelInfo
;
----------------------------------------------------------------------------
.code
InitConsole proc near
pusha
push -11 ; nStdHandle [ (dword) -11 ]
call GetStdHandle
cmp eax, -1
je Console_Error
mov [ConHandle], eax
popa
ret
Console_Error:
popa
call MessageBeep
push LARGE -1
call ExitProcess
InitConsole endp
WriteConsole macro oText, oNum_of_Chars
pusha
pushf
push 0
push offset cchWritten
push oNum_of_Chars
push oText
push [ConHandle]
call WriteConsoleA
popf
popa
endm
Main:
; Initialize Win32 console interface
call InitConsole
; Print copyright information
WriteConsole
; Get debugging flag via SM_DEBUG
; Zero=Release mode, Non-Zero=Debug mode
push SM_DEBUG
call GetSystemMetrics
test eax, eax
jz Rel
; This is a debugging version
WriteConsole
jmp Exit
Rel: ; This is a release version
WriteConsole
Exit:
WriteConsole
; Exit
push LARGE -1
call ExitProcess
end Main |
|