-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBForm.cpp
1988 lines (1669 loc) · 55.3 KB
/
BForm.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//////////////////////////////////////////////////////////////////////
// BForm.cpp: 一些全局函数(如 DoEvents 函数) 和 WinMain 函数的定义,
// CBForm 类的实现
//
//////////////////////////////////////////////////////////////////////
#include <memory.h>
#include "BForm.h"
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib") // 使工程引入 comctl32.lib 库
//////////////////////////////////////////////////////////////////////////
// 全局函数 和 WinMain 函数
//////////////////////////////////////////////////////////////////////////
// 封装 WinMain 函数,用户仍可将 main() 作为程序的入口
int main();
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, char * lpCmdLine, int nShowCmd )
{
// 初始化通用控件库
InitCommonControls(); // 如果任意公有函数中,调用 SHFileOperation 似乎也能达到同样的效果(执行SHFileOperation(0);即可)
// 定义 CBApp 对象,将有关程序信息保存其中。该对象虽为 WinMain 的局部变量
// 但程序运行全程都是存在的,因为 WinMain 函数结束,程序就结束了
// 使全局指针变量 pApp 指向 app,则全局程序中可使用 (*pApp) 访问此变量
CBApp app(hInstance, hPrevInstance, lpCmdLine, nShowCmd);
pApp=&app;
// 调用 main 函数,main 函数由用户在自己的模块中自己定义。这样程序的
// 入口仍是 main 函数
int retMain=main();
// 进入本线程的消息循环,这是在用户 main() 函数执行结束后的事
// 注意用户 main() 函数结束,整个程序并未结束
MessageLoop(0); // 参数 0 表示主消息循环,GetMessage 收到 0 才结束
// WinMain 返回,返回值为用户 main() 函数的返回值
return retMain;
}
// 消息循环函数
// bExitStyle=0时:为主消息循环,无消息、且无窗体时便 return
// bExitStyle>1时:实际是 +1 后的 ms_iModalLevel 的值
// 用于模态对话框显示后的消息循环(每显示一层模态对话框新进一层
// 新的 MessageLoop。若 ms_iModalLevel<iExitStyle,或无窗体时 return
// bExitStyle=-1时:用于 DoEvents(),
// 当前线程无消息(PeekMessage 返回0)时就 return
// 无论 bExitStyle 为多少,在 GetMessage 收到 0 都会 return
// 并在 return 前再次 PostQuitMessage(0); 以将退出消息传播
// 到前面各层的 MessageLoop 使前面各层的 MessageLoop 都能退出
static void MessageLoop(int iExitStyle/*=0*/)
{
// 进入本线程的消息循环:将获得本线程中的所有消息,然后将消息
// 派发到它所属的各自的窗口过程中
// 显示模态窗体时,再次调用本函数,并设置 iExitStyle 为 ms_iModalLevel(>0)
// 当模态窗体隐藏或卸载时,退出新进入调用的本函数,返回上层调用的本函数
// 最低一层是 WinMain 所调用的本函数
MSG msg;
int iret=-1; // 不能初始化为0,因 while 后有条件判断,iret==0 表示收到 WM_QUIT 消息
while (1)
{
if (iExitStyle>0) // 用于模态窗体:ms_iModalLevel<iExitStyle,或无窗体时 return
{
if ( CBForm::ms_iModalLevel<iExitStyle ||
CBForm::ms_hashWnd.Count()==0 ) break;
}
else if (iExitStyle<0) // 用于 DoEvents:当前线程无消息就 return
{
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)==0) break;
}
else // 主消息循环:无消息且无窗体时便 return
{
if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)==0 &&
CBForm::ms_hashWnd.Count()==0 ) break;
}
iret=GetMessage(&msg, NULL, 0, 0); // 获得本线程所有窗口的消息
// GetMessage出错会返回 -1,故
// while (GetMessage( lpMsg, hWnd, 0, 0)) ... 是不可取的
// 返回 0 表示本线程收到退出信号 WM_QUIT 消息,退出消息循环
if (iret==-1 || iret==0) break;
// 处理加速键
// 用 TranslateAccelerator 转换和处理对应消息(如果有加速键,加速键句柄为 iret)
// 如已转换处理,不要再 IsDialogMessage、TranslateMessage、DispatchMessage
// 不能转换为加速键的消息 TranslateAccelerator 不处理,继续
if (CBForm::ms_hAccelCurrUsed && CBForm::ms_hWndActiForm)
{
if (TranslateAccelerator(CBForm::ms_hWndActiForm, CBForm::ms_hAccelCurrUsed, &msg)) continue;
}
// 处理对话框消息(如按 Tab 跳转控件焦点等)
// 用 IsDialogMessage 转换和处理对应消息
// 如该条消息已被 IsDialogMessage 处理,不要再 TranslateMessage、DispatchMessage
// 非对话框的消息 IsDialogMessage 不处理,再 TranslateMessage、DispatchMessage
if (msg.message == WM_KEYDOWN && (msg.wParam==13 || msg.wParam==27))
{
// 按下回车 或 按下 ESC 不交由 IsDialogMessage 处理
// 否则 按下回车 或 按下 ESC 会被 IsDialogMessage 转换为 WM_COMMAND 消息
// (wParam==1 和 2),使窗体和其他控件不能正常接收 回车、ESC
// 消息转换和派发
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else
{
if (!IsDialogMessage(CBForm::ms_hWndActiForm, &msg))
{
// 消息转换和派发
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
} // end of while (1)
// 如果是由于收到程序退出消息 而退出的 while 循环(iret==0)
// 再次 PostQuitMessage(0); 以将退出消息传播
// 到前面各层的 MessageLoop 使前面各层的 MessageLoop 都能退出
if (iret==0) PostQuitMessage(0);
}
void DoEvents()
{
MessageLoop(-1);
}
extern void End( int nExitCode/*=0*/ )
{
PostQuitMessage(nExitCode);
}
//////////////////////////////////////////////////////////////////////////
// CBForm 类的实现
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// 定义 CBForm 类的 友元函数
// 公用窗口过程:所有本类对象(窗体)都用此函数作为窗口过程
// CBForm 类的友元函数
static BOOL WINAPI CBForm_DlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// =======================================================================
// 本函数中不得 PostQuitMessage
// 因为在一个对话框中 PostQuitMessage 后,整个程序就都关闭了,而不是只有
// 这一个对话框被关闭
// =======================================================================
switch(uMsg)
{
case WM_INITDIALOG:
// ===================================================================
// lparam 应为一个对象的地址,将此地址与 hwnd 关联的关系存入哈希表
if (lParam)
{
// =================================================================
// ============ 有新窗体创建:将其信息记录到 ms_hashWnd ============
// =================================================================
// 容错:若哈希表中已存在键为 hwnd 的项目,则先删除它,再用新的内容覆盖
if (CBForm::ms_hashWnd.IsKeyExist((long)hWnd))
CBForm::ms_hashWnd.Remove((long)hWnd,false);
// 向哈希表中添加新项(Key=hwnd,Data=对象地址,
// ItemLong=加速键句柄,ItemLong2=受模态对话框影响的 Enabled 状态)
CBForm::ms_hashWnd.Add(lParam, (long)hWnd, 0, 0);
// 设置对象中的 m_hWnd 成员为 窗口句柄
((CBForm *)lParam)->m_hWnd = hWnd;
// 设置对象中的 类名成员
memset( ((CBForm *)lParam)->m_ClassName, 0, sizeof( ((CBForm *)lParam)->m_ClassName));
((CBForm *)lParam)->m_atom = GetClassLong(hWnd, GCW_ATOM);
GetClassName(hWnd, ((CBForm *)lParam)->m_ClassName,
sizeof( ((CBForm *)lParam)->m_ClassName ) / sizeof(TCHAR)-1 );
// 触发 Form_Load 事件
((CBForm *)lParam)->EventsGenerator(WM_INITDIALOG, wParam, lParam);
}
return 1; // 返回 True, Windows 会自动将输入焦点放到第一个有 WS_TABSTOP 的控件上
break;
default:
// ===================================================================
// 调用各自对象的 EventsGenerator,后者处理这些消息,必要时生成事件
// 本函数返回 EventsGenerator 的返回值
// ===================================================================
CBForm *pForm;
pForm=0;
if (CBForm::ms_hashWnd.IsKeyExist((long)hWnd))
{
pForm=(CBForm *)CBForm::ms_hashWnd.Item((long)hWnd, false);
if (pForm)
{
return pForm->EventsGenerator(uMsg, wParam, lParam);
}
}
}
return 0;
}
// 公用窗口过程:所有本类对象(窗体)中的子窗口控件,都用此函数作为窗口过程,并所有子窗口控件都被子类处理
// CBForm 类的友元函数
static int CALLBACK CBForm_ControlProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CBForm *pForm = 0;
long r;
// 所有消息送到 对应父窗体的.EventsGeneratorCtrl
if (CBForm::ms_hashCtrls.IsKeyExist((long)hWnd))
{
// ItemLong = 所位于窗体的 hWnd,可以此为 key 到 ms_hashWnd 中获得窗体
// CBForm 对象的地址
long lHWndForm=CBForm::ms_hashCtrls.ItemLong((long)hWnd, false);
pForm = (CBForm *)CBForm::ms_hashWnd.Item(lHWndForm, false);
if (pForm)
{
r = pForm->EventsGeneratorCtrl(hWnd, uMsg, wParam, lParam);
if (r != gc_APICEventsGenDefautRet) return r;
}
}
// 从 CBForm::ms_hashCtrls 中获得本窗口的默认窗口程序的地址,并调用默认窗口程序
return CallWindowProc ((WNDPROC)(CBForm::ms_hashCtrls.Item((long)hWnd,false)),
hWnd, uMsg, wParam, lParam);
}
// 枚举子窗口控件、子类处理所有子窗口控件的回调函数:
// lParam 为控件所属窗体的句柄时(非0),表示加载窗体时的子类处理;
// lParam 为 0 时,表示卸载窗体时的恢复子类处理
// CBForm 类的友元函数
static BOOL CALLBACK EnumChildProcSubClass(HWND hWnd, LPARAM lParam)
{
if (lParam)
{
// ======== 窗体加载时(WM_INITDIALOG)调用的 ========
// 处理本窗口的子窗口
EnumChildWindows(hWnd, EnumChildProcSubClass, lParam);
// 设置所有子窗口具有 WS_CLIPSIBLINGS 风格
// 否则控件彼此覆盖时,后方控件重绘时会擦掉前方控件
SetWindowLong( hWnd, GWL_STYLE,
GetWindowLong(hWnd, GWL_STYLE) | WS_CLIPSIBLINGS ) ;
// 修正 ComboBox 若高度太小不能拉下下拉框的问题
// 获得类名字符串 => strClassName
TCHAR strClassName[128];
GetClassName(hWnd, strClassName, sizeof(strClassName)/sizeof(TCHAR)-1);
if (_tcscmp(strClassName, TEXT("ComboBox"))==0)
{
CBWndBase ctrl(hWnd, (HWND)lParam); // 借 CBWndBase 对象修改 ComboBox 高度;lParam 为父窗体句柄
int heightReq = SendMessage(hWnd, CB_GETITEMHEIGHT, 0, 0) *10;
if ( ctrl.Height() < heightReq ) ctrl.HeightSet(heightReq);
}
// 将所有子窗口控件子类处理(Control_Proc),并将它们的原窗口程序地址
// 存入 CBForm 类的静态成员 哈希表 ms_hashCtrls
// 将 lParam (即父窗体的句柄)存入哈希表元素的 ItemLong
if (! CBForm::ms_hashCtrls.IsKeyExist((long)hWnd))
{
// ItemLong2 设为0,表示没有设置过附加属性;只有设置是附加属性时
// 再动态开辟 STRControlProp 的空间,ItemLong2 才为空间地址(不为0)
CBForm::ms_hashCtrls.Add((long)GetWindowLong(hWnd,GWL_WNDPROC), (long)hWnd,
(long)lParam, 0, 0, 0, 0, false);
SetWindowLong(hWnd, GWL_WNDPROC, (long)CBForm_ControlProc);
}
// 返回非0值,以继续枚举同层其他子窗口控件
return 1;
}
else // (lParam == 0)
{
// ======== 窗体卸载时(WM_DESTROY)调用的 ========
// 清理所有子窗口控件的相关信息
// 即 CBForm 类的静态成员 哈希表 CBForm::ms_hashCtrls 中的相关项目
if (CBForm::ms_hashCtrls.IsKeyExist((long)hWnd))
{
// 删除附加属性的空间(如果设置过附加属性的话)
STRControlProp * pProp =
(STRControlProp *)CBForm::ms_hashCtrls.ItemLong2((long)hWnd, false);
if (pProp)
{
// 删除所指 STRControlProp 的空间
if (pProp->hBrushBack) DeleteObject(pProp->hBrushBack);
// 删除所指 tagString 的字符串空间
if (pProp->tagString) delete [](pProp->tagString);
// 删除字体对象
if (pProp->hFont) DeleteObject(pProp->hFont);
// 不必删除光标对象,系统会自动删除
// pProp->hCursor
// 删除附加属性的空间
delete pProp;
}
// 恢复子类处理(原窗口程序地址位于 ms_hashCtrls.Item 中)
SetWindowLong(hWnd, GWL_WNDPROC,
CBForm::ms_hashCtrls.Item((long)hWnd, false));
// 删除 ms_hashCtrls 中的对应项目
CBForm::ms_hashCtrls.Remove((long)hWnd, false);
}
// 处理本窗口的子窗口
EnumChildWindows(hWnd, EnumChildProcSubClass, lParam);
// 返回非0值,以继续枚举同层其他子窗口控件
return 1;
}
}
//////////////////////////////////////////////////////////////////////////
// 定义类中的 static 成员和 static 函数
CBHashLK CBForm::ms_hashWnd;
CBHashLK CBForm::ms_hashCtrls;
// 模态对话框 的层次
int CBForm::ms_iModalLevel = 0;
// 加速键句柄和加速键要发送到的目标窗口
HACCEL CBForm::ms_hAccelCurrUsed=NULL;
HWND CBForm::ms_hWndActiForm=NULL;
// 全局窗口所用鼠标光标索引值:EStandardCursor 枚举值或资源ID
// 0 表示使用各自窗口的鼠标光标,否则使用全局统一光标
long CBForm::ms_CursorGlobalIdx = 0;
// 全局窗口所用鼠标光标句柄(在 ms_CursorGlobalIdx 非0时才有效)
HCURSOR CBForm::ms_hCursorGlobal = 0;
long CBForm::MousePointerGlobal()
{
return ms_CursorGlobalIdx;
}
void CBForm::MousePointerGlobalSet( EStandardCursor cursor )
{
MousePointerGlobalSet((long)cursor);
}
void CBForm::MousePointerGlobalSet( long idResCursor, LPCTSTR typeRes/*=0*/ )
{
ms_CursorGlobalIdx = idResCursor;
if (ms_CursorGlobalIdx)
{
// 加载光标,句柄存入 ms_hCursorGlobal
// =============================================================
// LoadCursor 函数即使重复被调用,也不会重复加载资源;系统会判断
// 如果对应光标已经加载,LoadCursor 直接返回句柄
// =============================================================
if ( ms_CursorGlobalIdx > gc_IDStandCursorIDBase)
{
// 标准光标
// ms_CursorGlobalIdx-gc_IDStandCursorIDBase 才是标准光标的ID号
ms_hCursorGlobal =
LoadCursor(NULL,
MAKEINTRESOURCE(ms_CursorGlobalIdx-gc_IDStandCursorIDBase));
}
else
{
// 资源光标
// ms_CursorGlobalIdx 就是资源 ID
if (typeRes==0)
{
// 加载 Cursor 类型的资源
ms_hCursorGlobal =
LoadCursor(pApp->hInstance, MAKEINTRESOURCE(ms_CursorGlobalIdx));
}
else
{
// 加载自定义类型的资源(typeRes 类型的资源)
unsigned long size=0;
unsigned char * p= LoadResData(ms_CursorGlobalIdx, typeRes, &size);
ms_hCursorGlobal = (HCURSOR)CreateIconFromResource(p, size, 0, 0x00030000);
}
}
}
else // if (ms_CursorGlobalIdx)
{
// 不特殊设置光标,使用默认:设置 ms_hCursorGlobal 为 0
ms_hCursorGlobal = 0;
} // end if (ms_CursorGlobalIdx)
// 先向本程序当前前台窗口发送 WM_SETCURSOR,前台窗口处理 WM_SETCURSOR 以使光标立即生效
SendMessage(GetActiveWindow(), WM_SETCURSOR, (WPARAM)GetActiveWindow(), 0);
// 在本程序的所有窗口(包括子窗口)的、不断接收到的 WM_SETCURSOR 消息中会改变鼠标光标
}
int CBForm::FormsCount()
{
return ms_hashWnd.Count();
}
CBForm * CBForm::FormsObj( int index )
{
return (CBForm *)ms_hashWnd.ItemFromIndex(index, false);
}
STRControlProp * CBForm::PropPtr( HWND hwndCtrl, bool bCreate/*=false*/ )
{
STRControlProp * pProp =
(STRControlProp *)ms_hashCtrls.ItemLong2((long)hwndCtrl, false);
if (bCreate && pProp==NULL)
{
pProp=new STRControlProp;
HM.ZeroMem(pProp, sizeof(STRControlProp)); // 新空间清0
pProp->backColor=-1; // -1 表示使用默认颜色
pProp->foreColor=-1; // -1 表示使用默认颜色
// 新空间地址记录到 ms_hashCtrls.ItemLong2Set
if (! ms_hashCtrls.ItemLong2Set((long)hwndCtrl, (long)pProp, false))
{ delete pProp; pProp = 0; } // 失败容错
}
return pProp;
}
//////////////////////////////////////////////////////////////////////////
// 构造和析构
// 构造函数
CBForm::CBForm( unsigned short int idResDialog /*=0*/)
{
mResDlgID = idResDialog;
m_hWnd = 0;
m_ModalShown = false; // 是否正以模态对话框显示的标志
m_BackColor = -1; // 窗口背景色,-1 表示使用默认色(不修改窗口类,而是通过响应 WM_ERASEBKGND 实现)
m_CursorIdx = 0; // 窗体当前的鼠标光标索引号,0 表示使用系统默认
m_hFont = 0; // 窗体当前的字体对象句柄(为0时表示使用系统默认字体,否则表示用自定义字体)
// 类公有成员赋初值
KeyPreview = false; // 是否优先由窗体处理所有控件的键盘按键消息
}
// 析构函数
CBForm::~CBForm()
{
UnLoad();
ClearResource();
}
//////////////////////////////////////////////////////////////////////////
// 公有成员函数
HWND CBForm::hWnd()
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
return m_hWnd;
}
HMENU CBForm::hMenu()
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
return GetMenu(m_hWnd);
}
void CBForm::hMenuSet( HMENU hMenuNew )
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
SetMenu(m_hWnd, hMenuNew);
if (hMenuNew)
{
ConstructMenuIDItems(GetMenu(m_hWnd), true);
ConstructMenuIDItems(GetSystemMenu(m_hWnd, 0), false); // 系统菜单
}
else
{
ConstructMenuIDItems(NULL, true);
ConstructMenuIDItems(GetSystemMenu(m_hWnd, 0), false); // 系统菜单
}
}
HACCEL CBForm::hAccel()
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
return (HACCEL)ms_hashWnd.ItemLong((long)m_hWnd, false);
}
void CBForm::hAccelSet( HACCEL hAccelNew )
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载,然后才能设置加速键
ms_hashWnd.ItemLongSet((long)m_hWnd, (long)hAccelNew, false); // 记录到 ms_hashWnd 的 ItemLong
// 如果现在前台窗体正是本对象的窗体,就设置 CBForm::hWndAccel、CBForm::hAccel
if (m_hWnd==GetActiveWindow())
{
CBForm::ms_hWndActiForm = m_hWnd;
CBForm::ms_hAccelCurrUsed = hAccelNew;
}
}
bool CBForm::Show( int modal/*=0*/, HWND hwndOwner/*=NULL*/ )
{
// 由于模态方式显示窗体,不能自写消息循环,导致不能处理加速键
// 在本类中,所有窗体以非模态方式显示
// 对于“模态”的实现,是本函数通过将所有顶层窗体全部
// Enabled=False 实现的
HWND hwnd;
HWND hWndActiLast = GetActiveWindow();
if (0==m_hWnd)
{
// 若尚未加载对话框,现在加载
// 所有窗体以非模态方式 CreateDialogParam 加载
hwnd=Load(hwndOwner);
if (hwnd==0) return false;
}
else
{
// 对话框已经加载,在显示时可能会改变其 Owner
// 通过以下语句改变窗体的 Owner (不是Parent)
// MSDN 并没有介绍改变 Owner 的方法,应该是用
// SetWindowLong 以 -8 设置 Owner
if (hwndOwner !=0 && hwndOwner != GetWindow(m_hWnd, GW_OWNER))
SetWindowLong(m_hWnd, -8, (long)hwndOwner);
}
if (modal)
{
// ======== 显示模态对话框 ========
// 在显示一个模态对话框时:
// 若其他某窗口目前为 Disabled 且 ms_hashWnd.ItemLong2 值为0,则维持该窗口的此值不变;
// 否则,其他某窗口目前为 Enabled,或者该窗口此值 >0,都会将该窗口的此值 +1
// 在隐藏一个模态对话框时:
// 如某窗口此值为0,则不做任何操作;否则将此值-1,如-1后为0,则恢复为 Enabled 状态
int i;
BOOL ena;
long level=0;
HWND hwndEach=0;
for (i=1; i<=ms_hashWnd.Count(); i++)
{
hwndEach=(HWND)ms_hashWnd.IndexToKey(i);
if (hwndEach==m_hWnd)
{
// 本模态窗体,不做;但设置 ItemLong2 为 0
ms_hashWnd.ItemLong2FromIndexSet(i, (long)0, false);
continue;
}
// 根据窗体 i 的当前 enabled 状态和 ItemLong2(level) 做出判断
ena = IsWindowEnabled(hwndEach);
level = ms_hashWnd.ItemLong2FromIndex(i, false);
if (ena || level>0)
{
// 记录将来要把本窗口恢复到Enabled
ms_hashWnd.ItemLong2FromIndexSet(i, level+1, false);
}
// else 若窗口原为 Disabled,且 ItemLong2(level) 为0
// 则仍维持此值为0(不变)
// 将窗体 i 设置为 Diabled
EnableWindow(hwndEach, 0);
}
// 设置本对象的显示模态标志
m_ModalShown = true;
// 模态层次 ++
if (ms_iModalLevel<0) ms_iModalLevel=0; // 容错
ms_iModalLevel++;
// 显示“模态”窗体
ShowWindow(m_hWnd, SW_NORMAL);
// 本函数不要立即返回
// 再次调用 MessageLoop,并设置参数为 1
// 直到“模态”窗体被隐藏或关闭后下面的 MessageLoop 函数才返回
MessageLoop(ms_iModalLevel);
}
else
{
// ======== 显示非模态对话框 ========
if ( ms_iModalLevel>0 )
{
// 如果已有模态对话框正在显示,原则上不允许再显示
// 非模态对话框。但本类允许,只要将新对话框设为 Disabled 即可
// 新对话框的 ItemLong2 值应为 ms_iModalLevel
ms_hashWnd.ItemLong2Set((long)m_hWnd, ms_iModalLevel, false);
EnableWindow(m_hWnd, 0);
// 显示新窗口(但不要将新窗口设为前台)
ShowWindow(m_hWnd, SW_SHOWNOACTIVATE);
SetActiveWindow(hWndActiLast); // 恢复原来前台窗口仍为前台
}
else
{
ShowWindow(m_hWnd, SW_NORMAL);
}
// 以非模态显示,设置标志
m_ModalShown = false;
}
return true;
}
long CBForm::Hide()
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
return ShowWindow(m_hWnd, SW_HIDE);
}
// 加载对话框但并不显示出来(只能以非模态方式加载)
// 在需要对话框被加载后才能调用的一些函数如 SetAccelerator 中,可自动调用本函数
HWND CBForm::Load( HWND hwndOwner/*=NULL*/ )
{
// 一律以“非模态”方式加载窗体:调用 CreateDialogParam
// 将 dwInitParam 参数设为本对象地址即this,
// 此将作为 WM_INITDIALOG 的 lParam 参数传给 CBForm_DlgProc
HWND hwnd;
hwnd=CreateDialogParam(pApp->hInstance, MAKEINTRESOURCE(mResDlgID), hwndOwner,
CBForm_DlgProc, (long)this); // 立即返回,返回窗口句柄;在窗口过程处理 WM_INITDIALOG 时会设置 m_hWnd
// 如果没有 WS_VISIBLE 样式此时窗体不会自动显示
if (hwnd)
{
// 初始化菜单
ConstructMenuIDItems(GetMenu(hwnd),true); // 如无菜单即参数为0,将清除菜单哈希表记录
ConstructMenuIDItems(GetSystemMenu(hwnd, 0), false); // 系统菜单
}
return hwnd;
}
// 结束对话框
// 接收到 WM_CLOSE 消息时可自动调用本函数,也可直接由用户调用
void CBForm::UnLoad() const
{
SendMessage(m_hWnd, WM_CLOSE, 0, 0);
}
// 设置本窗口将处理加速键,资源 ID 为 idResAcce
// 若取消加速键,将参数设为 0 即可
void CBForm::SetAccelerator( unsigned short int idResAcce )
{
HACCEL hAcc=NULL;
if (idResAcce) hAcc=LoadAccelerators(pApp->hInstance, MAKEINTRESOURCE(idResAcce));
hAccelSet(hAcc); // 调用公有方法 hAccelSet 设置
}
// 重新设置本窗口的菜单
void CBForm::SetMenuMain( unsigned short idResMenu )
{
HMENU hMenu=NULL;
if (idResMenu)
{
hMenu=LoadMenu(pApp->hInstance, MAKEINTRESOURCE(idResMenu));
hMenuSet(hMenu); // 调用公有方法 hMenuSet 设置
}
else
hMenuSet(NULL); // 调用公有方法 hMenuSet 设置
}
// 返回一个控件(CBControl 对象),之后可对该控件进行操作(调用 CBControl 类的属性方法)
CBControl CBForm::Control( unsigned short int idResControl )
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
if (m_Control.SetResID(m_hWnd, idResControl, &m_hashObjs))
return m_Control;
else
return 0;
}
// 返回一个菜单项(CBMenuItem 对象),之后可对该菜单项进行操作(调用 CBMenuItem 类的属性方法)
CBMenuItem CBForm::Menu( UINT idResMenuItem )
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
// 根据 idMenuItem,在哈希表中查找其所对应的父菜单的 句柄 -> hMenu
HMENU hMenu;
hMenu = (HMENU)m_hashMenuIDs.Item(idResMenuItem, false);
if (hMenu)
{
m_MenuItem.SetFromResID(hMenu, idResMenuItem, m_hWnd, &m_hashMenuIDs, &m_hashObjs);
return m_MenuItem;
}
else
return 0;
}
CBMenuItem CBForm::Menu( int pos1, int pos2, int pos3/*=0*/, int pos4/*=0*/, int pos5/*=0*/, int pos6/*=0*/, int pos7/*=0*/ )
{
HMENU hMenu;
if (pos1>0)
{
// 获得主菜单
hMenu = GetMenu(m_hWnd);
if (pos2)
{
hMenu = GetSubMenu(hMenu, pos1 - 1); //-1变为从0开始编号,获得顶层菜单的句柄:例如若pos1==1,hMenuSub为整个“文件”菜单的句柄
}
else
{
m_MenuItem.SetFromPos(hMenu, pos1, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 返回顶级菜单的一个菜单项(位置参数从1开始),例如若pos1==1,返回“文件”菜单项
return m_MenuItem;
}
}
else if (pos1<0)
{
// 获得系统菜单
hMenu = GetSystemMenu(m_hWnd, 0);
if (pos2==0)
{
// 返回系统菜单的“父菜单项”
// (它引出的子菜单句柄为 GetSystemMenu,即它的子菜单的第一项是【还原】)
m_MenuItem.SetFromPos(0, 1, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 1-1 == 0
return m_MenuItem;
}
}
else // pos1 == 0
{
// 获得顶级菜单
hMenu = GetMenu(m_hWnd);
if (pos2==0)
{
// 返回顶层菜单的“父菜单项”
// (它引出的子菜单句柄为 GetMenu,即它的子菜单的第一项是【文件】)
m_MenuItem.SetFromPos(0, 2, m_hWnd, &m_hashMenuIDs, &m_hashObjs);
return m_MenuItem;
}
}
if (pos3)
{
hMenu = GetSubMenu(hMenu, pos2 - 1); //-1变为从0开始编号
}
else
{
m_MenuItem.SetFromPos(hMenu, pos2, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 返回一级菜单的一个菜单项(位置参数从1开始),例如若pos1==1、pos2==2,返回“文件”-“打开”的菜单项
return m_MenuItem;
}
if (pos4)
{
hMenu = GetSubMenu(hMenu, pos3 - 1); //-1变为从0开始编号
}
else
{
m_MenuItem.SetFromPos(hMenu, pos3, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 位置参数从1开始
return m_MenuItem;
}
if (pos5)
{
hMenu = GetSubMenu(hMenu, pos4 - 1); //-1变为从0开始编号
}
else
{
m_MenuItem.SetFromPos(hMenu, pos4, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 位置参数从1开始
return m_MenuItem;
}
if (pos6)
{
hMenu = GetSubMenu(hMenu, pos5 - 1); //-1变为从0开始编号
}
else
{
m_MenuItem.SetFromPos(hMenu, pos5, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 位置参数从1开始
return m_MenuItem;
}
if (pos7)
{
hMenu = GetSubMenu(hMenu, pos6 - 1); //-1变为从0开始编号
}
else
{
m_MenuItem.SetFromPos(hMenu, pos6, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 位置参数从1开始
return m_MenuItem;
}
m_MenuItem.SetFromPos(hMenu, pos7, m_hWnd, &m_hashMenuIDs, &m_hashObjs); // 位置参数从1开始
return m_MenuItem;
}
CBMenuItem CBForm::Menu( ESysMenu idSysMenu )
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
return this->Menu((UINT)idSysMenu);
}
void CBForm::MenuSysRestore() const
{
GetSystemMenu(m_hWnd, 1);
}
BOOL CBForm::PopupMenu( UINT idResMenu, int x, int y, bool bAllowRightClick/*=true*/ )
{
HMENU hMenuPop;
BOOL ret;
UINT flags=0;
POINT pt;
hMenuPop = LoadMenu(pApp->hInstance, MAKEINTRESOURCE(idResMenu));
if (bAllowRightClick) flags = flags | TPM_RIGHTBUTTON;
pt.x=x; pt.y=y;
ClientToScreen(m_hWnd, &pt);
ret = TrackPopupMenu(GetSubMenu(hMenuPop, 0), flags, pt.x, pt.y, 0, m_hWnd, NULL);
// TrackPopupMenu 只能弹出 popup 式菜单,LoadMenu 得到的不是 popup 式菜单,
// 应用 GetSubMenu 取其子菜单(这里只取第0项的子菜单)
DestroyMenu(hMenuPop);
return ret;
}
void CBForm::IconSet(EStandardIcon icon)
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
HICON hIco = LoadIcon(0, (LPCTSTR)icon); // 获得的是shared icon,不要 DestroyIcon
SendMessage(m_hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIco);
SendMessage(m_hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIco);
}
void CBForm::IconSet(unsigned short iconRes)
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
HICON hIco = LoadIcon(pApp->hInstance, (LPCTSTR)((DWORD)iconRes)); // 获得的是shared icon,不要 DestroyIcon
SendMessage(m_hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIco);
SendMessage(m_hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIco);
}
COLORREF CBForm::BackColor()
{
if (-1 == m_BackColor)
{
// 获得窗口类的背景色
HBRUSH hBrush;
LOGBRUSH lb;
hBrush=(HBRUSH)GetClassLong(m_hWnd, GCL_HBRBACKGROUND);
if (hBrush)
{
GetObject((HGDIOBJ)hBrush,sizeof(lb), &lb);
return lb.lbColor;
}
else
{
// GetClassLong 失败,或窗口类无画刷:返回系统 COLOR_BTNFACE 颜色
return GetSysColor(COLOR_BTNFACE);
}
}
else
// 直接返回 m_BackColor
return m_BackColor;
}
void CBForm::BackColorSet( EColorType color )
{
m_BackColor = color;
InvalidateRect(m_hWnd, NULL, true);
}
void CBForm::BackColorSet( COLORREF color )
{
m_BackColor = color;
InvalidateRect(m_hWnd, NULL, true);
}
long CBForm::MousePointer()
{
return m_CursorIdx;
}
void CBForm::MousePointerSet( EStandardCursor cursor )
{
MousePointerSet((long)cursor);
}
void CBForm::MousePointerSet( long idResCursor, LPCTSTR typeRes/*=0*/ )
{
if (m_hWnd==NULL) Load(); // 如果窗口还未加载,现在加载
m_CursorIdx = idResCursor;
if (m_CursorIdx)
{
// 加载光标,句柄存入 m_hCursor
// =============================================================
// LoadCursor 函数即使重复被调用,也不会重复加载资源;系统会判断
// 如果对应光标已经加载,LoadCursor 直接返回句柄
// =============================================================
if ( m_CursorIdx > gc_IDStandCursorIDBase)
{
// 标准光标
// m_CursorIdx-gc_IDStandCursorIDBase 才是标准光标的ID号
m_hCursor =
LoadCursor(NULL, MAKEINTRESOURCE(m_CursorIdx-gc_IDStandCursorIDBase));
}
else
{
// 资源光标
// m_CursorIdx 就是资源 ID
if (typeRes==0)
{
// 加载 Cursor 类型的资源
m_hCursor = LoadCursor(pApp->hInstance, MAKEINTRESOURCE(m_CursorIdx));
}
else
{
// 加载自定义类型的资源(typeRes 类型的资源)
unsigned long size=0;
unsigned char * p= LoadResData(m_CursorIdx, typeRes, &size);
m_hCursor = (HCURSOR)CreateIconFromResource(p, size, 0, 0x00030000);
}
}
}
else // if (m_CursorIdx)
{
// 不特殊设置光标,使用默认:设置 m_hCursor 为 0
m_hCursor = 0;
} // end if (m_CursorIdx)
// 向本窗口发送 WM_SETCURSOR,以使光标立即生效
SendMessage(m_hWnd, WM_SETCURSOR, (WPARAM)m_hWnd, 0);
// 在本窗口不断接收到的 WM_SETCURSOR 消息中会改变鼠标光标
}
// 设置一个窗体的或子窗口控件的事件(多个重载版本)
// ptrFunHandler 为一个事件处理函数的地址
// 如果是窗体事件,idResControl 参数应设为0
// 如果是控件事件,idResControl 参数应设为 控件的资源ID
// 如果是菜单(包括系统菜单)、加速键事件,第2个参数为 eMenu_Click,将忽略 idResControl 参数
void CBForm::EventAdd( unsigned short int idResControl,
ECBFormCtrlEventsVoid eventType,
ONEventVoid ptrFunHandler )
{
EventAddPrivate(idResControl, (long)eventType, (long)ptrFunHandler, 0);
}
void CBForm::EventAdd( unsigned short int idResControl,
ECBFormCtrlEventsI eventType,