Accerlerators
一般是在OnCreate
的时候::LoadAccelerators
, MFC 会在LoadFrame
顺带做了::LoadAccelerators
的操作
Keyboard Message
而WM_SYSKEYDOWN是接受快捷键或系统命令按键的,像Alt键就是。所以捕获Alt键时,在WM_KEYDOWN下是无效的,要在WM_SYSKEYDOWN中。Ctrl和shift不属于WM_SYSKEYDOWN。
Tab cycle
想要一个内嵌式的dialog加入到tab cycle, 需要设置样式DS_CONTROL
WM_CTLCOLORLISTBOX
当listbox内容为空的时候会通过父窗口这个消息控制默认背景色。
关于Auto Cleanup Classes
https://docs.microsoft.com/en-us/cpp/mfc/tn017-destroying-window-objects?view=vs-2019
MFC中的基本规则是:
MFC控件都不是Auto Cleanup Classes, 所以基本都是在栈上创建的。
Auto Cleanup Classes必须在堆上创建,因为他们的PostNcDestroy中都会delete this,如果不是堆上创建的会出错。
总结
1.可以有两种方法销毁窗口对象对应的窗口和释放窗口对象指针。一种是通过DestroyWindow
。这是比较好的方法,因为最后MFC会自动相应WM_CLOSE
导致CframWnd::DestroyWindow
被调用,然后会一次释放所有子窗口的句柄。用户需要做的是在PostNcDestroy
中释放堆窗口对象指针。但因为某些对象是在栈中申请的,所以delete this可能出错。这就要保证写程序时自己创建的窗口尽量使用堆申请。
2.另一种是delete
。Delete一个窗口对象指针有的窗口类(如CWnd
,Cdialog
)会间接调用DestroyWindow
,有的窗口类(如CView
,CframeWnd
)不会调用DestroyWindow
。所以要小心应对。
If you want to break these rules, you must override the
PostNcDestroy
method in your derived class.To add auto-cleanup to your class, call your base class and then do a delete this. To remove auto-cleanup from your class, callCWnd::PostNcDestroy
directly instead of thePostNcDestroy
method of your direct base class.
通常来说,mfc的控件、对话框的删除,如果是在栈上创建的,那么不需要显示调用DestoryWindow,因为它们的基类析构函数会做这件事。如果它们是在堆上创建,那么简单地delete就好了。对于Auto Cleanup Classes直接调用DestroyWindow
来删除,不需要再手动delete一遍。
给CWnd发送WM_CLOSE相当于调用DestroyWindow
。
https://www.cnblogs.com/endenvor/p/9796687.html
WAV 格式
https://www.cnblogs.com/ranson7zop/p/7657874.html
通过IAudioClient GetMixFormat 的 format tag如果是WAVE_FORMAT_EXTENSIBLE (0x00FE),那么格式将由subformat(GUID)来指定,cbsize指定了
WAVEFORMATEX之后至少多少个字节长度.
typedef struct {
WAVEFORMATEX Format;
union {
WORD wValidBitsPerSample;
WORD wSamplesPerBlock;
WORD wReserved;
} Samples;
DWORD dwChannelMask;
GUID SubFormat;
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
RF-64 WAV
EVENT
初始状态在
bInitialState
参数中进行设置。使用SetEvent
函数将事件对象的状态置为有信号状态。使用ResetEvent
函数将事件对象的状态置为无信号状态。
当一个手动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至明确调用
ResetEvent
函数将其置为无符号状态。当事件的对象被置为有信号状态时,任意数量的等待中线程,以及随后开始等待的线程均会被释放。
当一个自动复原的事件对象的状态被置为有信号状态时,该对象状态将一直保持有信号状态,直至一个等待线程被释放;系统将自动将此函数置为无符号状态。如果没有等待线程正在等待,事件对象的状态将保持有信号状态
这段话的意思是,比如一堆线程在等待一个event,当event被SetEvent
设置为有信号状态,如果event是自动复原的,那么只会有一个等待线程被释放,并且event对象会复原到无信号状态;如果事件是手动复原的,那么所有等待线程都会被释放,event会一直保持有信号状态,直到显式调用ResetEvent
。
ResetEvent
function
Sets the specified event object to the nonsignaled state.
Windows C++程序入口点
实际入口是由C运行时库的这几个函数
1. mainCRTStartup(或 wmainCRTStartup) //使用 /SUBSYSTEM:CONSOLE 的应用程序
2. WinMainCRTStartup(或 wWinMainCRTStartup) //使用 /SUBSYSTEM:WINDOWS 的应用程序
3. _DllMainCRTStartup //调用 DllMain(如果存在),DllMain 必须用 __stdcall 来定义
Linker 负责链接其中一种C Runtime startup code到exe中(根据子系统和是否定义了main
或 WinMain
)
SysWow64 与 system32
所以SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方, system32在64位系统下是放64位Windows系统文件的地方(为了兼容性).
https://docs.microsoft.com/zh-cn/archive/blogs/tianlin/syswow64
MFC CClientDC和CPaintDC区别
1.CClientDC(客户区设备上下文)用于客户区的输出它在构造函数中封装了GetDC(),在析构函数中封装了ReleaseDC()函数。一般在响应非窗口重画消息(如键盘输入时绘制文本、鼠标绘图)绘图时要用到它。
2.CPaintDC用于响应窗口重绘消息(WM_PAINT)是的绘图输出。CPaintDC在构造函数中调用BeginPaint()取得设备上下文,在析构函数中调用EndPaint()释放设备上下文。EndPaint()除了释放设备上下文外,还负责从消息队列中清除WM_PAINT消息。因此,在处理窗口重画时,必须使用CPaintDC,否则WM_PAINT消息无法从消息队列中清除,将引起不断的窗口重画。CPaintDC也只能用在WM_PAINT消息处理之中
Windows Layered window
UpdateLayeredWindow 和 SetLayeredWindowAttributes互斥
SetLayeredWindowAttributes: 适用于整个窗口做半透明效果时使用,会持续收到WM_PAINT
UpdateLayeredWindow : 适用于部分透明部分不透明的情况下使用,不会收到WM_PAINT,
MFC定时器等消息无法响应, OnPaint无限触发
https://www.cnblogs.com/jgliuhui1988/p/6229861.html
一定是没有处理BeginPaint和EndPaint(CPaintDC里封装了,一般默认控件的OnPaint里会写)是因为没有从消息队列中移除WM_PAINT,导致了无限响应
MFC 模态对话框的原理
https://my.oschina.net/myspaceNUAA/blog/81187
DoModal returns -1 if WS_VISIBLE is not set
Windows 获取窗口除去透明阴影边框的大小
RECT learnRECT;
::DwmGetWindowAttribute(hWnd, DWMWA_EXTENDED_FRAME_BOUNDS, &learnRECT, sizeof(learnRECT));
https://stackoverflow.com/questions/42473554/windows-10-screen-coordinates-are-offset-by-7/42491227#42491227
https://stackoverflow.com/questions/32159675/retrieve-window-size-without-windows-shadows/32645155#32645155
Desktop Duplication
注意坐标原点的位置,不一定是和虚拟桌面坐标系一致的,需要处理DesktopCoordinates
Windows kit版本与系统的兼容性
https://stackoverflow.com/questions/44262083/which-sdk-do-i-need-to-ensure-windows-7-compatibility-in-visual-studio-c-2017
通过指定header版本来控制,windows 10 sdk也可以支持win7
构筑面向xp的程序
https://docs.microsoft.com/zh-cn/cpp/build/configuring-programs-for-windows-xp?view=msvc-160
文件系统
最大路径 260个字符(不区分ascii和unicode,包括结尾的null字节)
通过配置可以让你的应用程序支持超过260个字符的路径
https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd
目录最长248个字符(包括结尾的null字节)
Visual Studio
资源文件也是有include path的,如果发现资源文件中的文件找不到,可以查看下include path是否正确
GDI+
保存DC到位图
int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
Gdiplus::GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
Gdiplus::GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
BOOL SaveHDCToFile(HDC hDC, LPRECT lpRect, CString name)
{
BOOL bRet = FALSE;
int nWidth = lpRect->right - lpRect->left;
int nHeight = lpRect->bottom - lpRect->top;
HDC memDC = CreateCompatibleDC(hDC);
HBITMAP hBmp = CreateCompatibleBitmap(hDC, nWidth, nHeight);
SelectObject(memDC, hBmp);
BitBlt(memDC, lpRect->left, lpRect->top, nWidth, nHeight, hDC, 0, 0, SRCCOPY);
{
// L"image/bmp" L"image/jpeg" L"image/gif" L"image/tiff" L"image/png"
CLSID pngClsid;
GetEncoderClsid(L"image/bmp", &pngClsid);
Gdiplus::Bitmap* pbmSrc = Gdiplus::Bitmap::FromHBITMAP(hBmp, NULL);
if (pbmSrc->Save(name, &pngClsid) == Ok)
{
bRet = TRUE;
}
delete pbmSrc;
}
SelectObject(memDC, (HBITMAP)NULL);
DeleteDC(memDC);
DeleteObject(hBmp);
return bRet;
}