关键段(critival section)是一小段代码,他在执行之前需要独占对一些共享资源的访问权。这种方式可以让多行代码以“原子方式”来对资源进行 *** 控。这里的“原子方式”,指的是代码知道除了当前线程之外没有其他任何线程会同时访问该资源。当然,系统仍然可以暂停当前线程去调度其他线程。但是,在当前线程离开关键段之前,系统是不会去调度任何想要访问同一资源的其他线程的。
例如:如果两个线程同时访问一个链表,一个线程可能会在另一个线程搜寻元素的同时向链表中添加一个元素,将导致搜索结果不正确;还有可能两个线程同时向链表中添加元素,这种情况会变的更加混乱;甚至一个线程搜索的时候,另一个线程删除了链表节点,将直接导致程序崩溃。
解决这个问题,我们可以先在代码中定义一个CRITICAL_SECTION数据结构m_sect,然后把任何需要访问共享资源的代码放在EnterCriticalSection和LeaveCriticalSection之间。
[cpp] view plain copy print?
EnterCriticalSection(&m_sect)
// 共享资源的代码段....
LeaveCriticalSection(&m_sect)
一个 CRITICAL_SECTION结构就像是飞机上的一个卫生间,而马桶则是我们想要保护的资源(用EnterCriticalSection和LeaveCriticalSection组成的围墙包围住“马桶”)。由于卫生间很小,因此在同一时刻只允许一个人在卫生间内使用马桶(在同一时刻只允许一个线程在关键段中使用被保护资源)。
如果有多个总是应该在一起使用的资源,那么我们可以把他们放在同一个“卫生间”中:只需要创建一个CRITICAL_SECTION结构来保护所有这些资源。
关于关键段,需要掌握以下几点:
1、任何要访问共享资源的代码,都必须包含在EnterCriticalSection和LeaveCriticalSection之间。如果忘了哪怕是一个地方,共享资源就有可能被破坏。忘记调用EnterCriticalSection和LeaveCriticalSection,就好像是未经许可就强制进入卫生间一样,线程强行进入并对资源进行 *** 控。只要有一个线程有这种粗暴的行为,资源就会被破坏。
2、关键段CRITICAL_SECTION是个未公开的结构,因为Microsoft认为开发人员不需要理解这个结构的细节。对我们来说,不需要知道这个结构中的成员变量,我们绝对不应该在编写代码的时候用到他的成员。
3、为了对CRITICAL_SECTION结构进行 *** 控,我们必须调用Windows API函数,传入结构的地址。(注意是地址!)也就是说,如果该CRITICAL_SECTION结构生命周期没有结束,那么可以将该结构地址通过自己喜欢的任何方式传给任何线程。
4、在任何线程试图访问被保护的资源之前,必须对CRITICAL_SECTION结构的内部成员进程初始化。我们不知道内部成员,但可以调用Windows函数实现:VOID WINAPI InitializeCriticalSection(__out LPCRITICAL_SECTION lpCriticalSection)
5、当线程不再需要访问共享资源的时候,应调用下面的函数来清理该结构:VOID WINAPI DeleteCriticalSection(__inout LPCRITICAL_SECTION lpCriticalSection)
6、其实CRITICAL_SECTION并不知道什么是共享资源,也不会智能保护共享资源。其根本是,同一个时刻如果有多个线程调用EnterCriticalSection的时候,只有一个线程返回,其余线程则暂停执行,等待前面线程调用LeaveCriticalSection之后再执行。
7、可以看出,进入关键段是没有超时设定的,好像永远不会超时。实际上,对EnterCriticalSection的调用也会超时并引发异常。超时的时间长度由下面这个注册表子项中包含的CriticalSectionTimeout值决定:
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager
这个值以秒为单位,他的默认值为2592000秒,大约30天。
8、同一个线程可以随便进入用一个关键段N次,也就是说同一个线程调用EnterCriticalSection无论几次都会返回。不同线程是否能够进入关键段,要看EnterCriticalSection的参数(CRITICAL_SECTION结构的地址)之前是否有线程进入过。要记住:飞机上的卫生间有多个,你可以随便进入无人的卫生间,不能进入有人的卫生间。
弄明白了CRITICAL_SECTION之后,使用CCriticalSection非常方便,如虎添翼。看代码:
//头文件
[cpp] view plain copy print?
class CCriticalSection : public CSyncObjet
{...
public:
CRITICAL_SECTION m_sect
public:
BOOL Unlock()
BOOL Lock()
BOOL Lock(DWORD dwTimeout)
...
}
// 构造函数
[cpp] view plain copy print?
CCriticalSection::CCriticalSection() : CSyncObject(NULL)
{
HRESULT hr = S_OK
if (!InitializeCriticalSectionAndSpinCount(&m_sect, 0))//可以理解为InitializeCriticalSection,为了效率,加了一个旋转锁。
{
hr = HRESULT_FROM_WIN32(GetLastError())
}
if (FAILED(hr))
{
AtlThrow(hr)
}
}
//进入关键段
[cpp] view plain copy print?
BOOL CCriticalSection::Lock()
{
::EnterCriticalSection(&m_sect)
return TRUE
}
// 离开关键段
[cpp] view plain copy print?
BOOL CCriticalSection::Unlock()
{
::LeaveCriticalSection(&m_sect)
return TRUE
}
// 析构
[cpp] view plain copy print?
CCriticalSection::~CCriticalSection()
{
::DeleteCriticalSection(&m_sect)
}
系统学习Windows客户端开发在某些业务场景下希望截全屏时不显示某些窗口特别是自身应用的窗口,比如在屏幕共享时不希望将自己应用的主界面、工具条等共享给对方。
Windows有个特性Magnification(放大镜)特性,它允许将屏幕(或屏幕某个指定区域)进行放大,如果不设置放大比例等同于截屏,其支持选择窗口过滤,利用该特性就可以实现过滤部分窗口下截屏。该特性从Vista开始支持,如果产品需要支持Win XP系统就不能使用该方案。
使用Magnification进行截屏的流程如下:
笔者编写类CScreenCapture,用来实现过滤部分窗口截图,结合MSDN仔细阅读理解就容易掌握其使用。CScreenCapture类提供三个接口SetFilterWindowList()指定过滤窗口列表,SetFrameRate()指定每秒帧数,SetScreenImageArriveCallback()设置回调接收图片。内部开启一个UI线程定期执行截屏,线程创建运行使用 一个简单实用的线程基类CThreadBase,最后DEMO演示如何在接收图片回调中将其保存成BITMAP格式的图片。
类CScreenCapture头文件
#pragma once
#include <Magnification.h>
#include <vector>
#include <map>
#include "ThreadBase.h"
class IScreenImageArriveCallback
{
public:
// called when screen was captured
// param is same as MagImageScalingCallback
virtual void OnScreenImageArriveCallback(MAGIMAGEHEADER srcheader, void *srcdata) = 0
}
class CScreenCapturer : public CThreadBase
{
public:
CScreenCapturer()
~CScreenCapturer()
public:
// set the callback of screen image arrived
void SetScreenImageArriveCallback(IScreenImageArriveCallback* pCallback)
// set filter window list that will not show on the screen image
void SetFilterWindowList(const std::vector<HWND>&vecFilterWindow)
// frame rate represent times of capturing screen per second
bool SetFrameRate(unsigned int nFrameRate)
protected: // override CThreadBase
virtual bool OnStart(const std::string&strParam) override
virtual void OnRun(const std::string&strParam) override
virtual void OnStop() override
private:
static BOOL MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)
static LRESULT CALLBACK MsgWndWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
void CaptureScreen()
private:
CRITICAL_SECTION m_syncObject
DWORD m_nThreadId = 0
HWND m_hwndMagnification = NULL
RECT m_rectScreen
IScreenImageArriveCallback* m_pScreenImageArriveCallback = nullptr
HWND* m_pFilterWindows = nullptr
unsigned int m_nFilterWindowsCount = 0
unsigned int m_nFrameRate = 15
}
复制
类CScreenCapture源文件
#include "ScreenCapturer.h"
#pragma comment(lib, "Magnification.lib")
#define WINDOW_CLASS_NAME L"magnification_host"
#define TIMER_ID_CAPTURE_SCREEN 100
CScreenCapturer::CScreenCapturer()
{
InitializeCriticalSection(&m_syncObject)
}
CScreenCapturer::~CScreenCapturer()
{
if (m_pFilterWindows)
{
delete[] m_pFilterWindows
m_pFilterWindows = nullptr
}
DeleteCriticalSection(&m_syncObject)
}
void CScreenCapturer::SetScreenImageArriveCallback(IScreenImageArriveCallback* pCallback)
{
m_pScreenImageArriveCallback = pCallback
}
void CScreenCapturer::SetFilterWindowList(const std::vector<HWND>&vecFilterWindow)
{
EnterCriticalSection(&m_syncObject)
m_nFilterWindowsCount = vecFilterWindow.size()
if (m_pFilterWindows)
{
delete[] m_pFilterWindows
m_pFilterWindows = nullptr
}
if (m_nFilterWindowsCount >0)
{
m_pFilterWindows = new HWND[m_nFilterWindowsCount]
for (unsigned int i = 0i <m_nFilterWindowsCounti++)
{
m_pFilterWindows[i] = vecFilterWindow[i]
}
}
LeaveCriticalSection(&m_syncObject)
}
bool CScreenCapturer::SetFrameRate(unsigned int nFrameRate)
{
if (nFrameRate == 0 || nFrameRate >60)
{
return false
}
m_nFrameRate = nFrameRate
return true
}
bool CScreenCapturer::OnStart(const std::string&strParam)
{
return true
}
void CScreenCapturer::OnRun(const std::string&strParam)
{
m_nThreadId = GetCurrentThreadId()
// Init magnification
if (!MagInitialize())
{
return
}
// Register magnification host window class
WNDCLASS wc = { 0 }
wc.lpszClassName = WINDOW_CLASS_NAME
wc.hbrBackground = CreateSolidBrush(RGB(255, 255, 255))
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION)
wc.hCursor = LoadCursor(NULL, IDC_ARROW)
wc.lpfnWndProc = MsgWndWindowProc
wc.hInstance = NULL
if (RegisterClass(&wc) == 0 &&GetLastError() != ERROR_CLASS_ALREADY_EXISTS)
{
return
}
// Get screen resolution
HWND hDesktop = ::GetDesktopWindow()
::GetWindowRect(hDesktop, &m_rectScreen)
int nScreenX = m_rectScreen.right - m_rectScreen.left
int nScreenY = m_rectScreen.bottom - m_rectScreen.top
// Create host window
HWND hHostWindow = CreateWindowEx(WS_EX_LAYERED, WINDOW_CLASS_NAME, L"", WS_POPUP, 0, 0, nScreenX, nScreenY, GetDesktopWindow(), 0, NULL, 0)
if (hHostWindow == NULL)
{
return
}
SetLayeredWindowAttributes(hHostWindow, 0, 255, LWA_ALPHA)
SetWindowLong(hHostWindow, GWL_USERDATA, (LONG_PTR)this)
// Create magnification window
m_hwndMagnification = CreateWindow(WC_MAGNIFIER, L"MagnifierWindow", WS_CHILD | WS_VISIBLE, 0, 0, nScreenX, nScreenY, hHostWindow, NULL, NULL, NULL)
if (m_hwndMagnification == NULL)
{
DestroyWindow(hHostWindow)
return
}
// Set capture callback
if (!MagSetImageScalingCallback(m_hwndMagnification, (MagImageScalingCallback)&CScreenCapturer::MagImageScaling))
{
DestroyWindow(hHostWindow)
return
}
// Set timer to capture screen
SetTimer(hHostWindow, TIMER_ID_CAPTURE_SCREEN, 1000/m_nFrameRate, nullptr)
MSG msg
while (GetMessage(&msg, 0, 0, 0))
{
DispatchMessage(&msg)
}
KillTimer(hHostWindow, TIMER_ID_CAPTURE_SCREEN)
DestroyWindow(hHostWindow)
m_hwndMagnification = NULL
}
void CScreenCapturer::OnStop()
{
if (m_nThreadId >0)
{
PostThreadMessage(m_nThreadId, WM_QUIT, 0, 0)
m_nThreadId = 0
}
}
BOOL CScreenCapturer::MagImageScaling(HWND hwnd, void *srcdata, MAGIMAGEHEADER srcheader, void *destdata, MAGIMAGEHEADER destheader, RECT unclipped, RECT clipped, HRGN dirty)
{
HWND hParent = hwnd
while (true)
{
hParent = GetParent(hParent)
if (hParent == NULL)
{
break
}
wchar_t szClassName[200]
memset(szClassName, 0, sizeof(szClassName))
GetClassName(hParent, szClassName, 200)
if (wcscmp(szClassName, WINDOW_CLASS_NAME) == 0)
{
break
}
}
if (hParent == NULL)
{
return TRUE
}
CScreenCapturer* pScreenCapture = (CScreenCapturer*)GetWindowLong(hParent, GWL_USERDATA)
if (pScreenCapture &&pScreenCapture->m_pScreenImageArriveCallback)
{
pScreenCapture->m_pScreenImageArriveCallback->OnScreenImageArriveCallback(srcheader, srcdata)
}
return TRUE
}
LRESULT CALLBACK CScreenCapturer::MsgWndWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_TIMER &&wParam == TIMER_ID_CAPTURE_SCREEN)
{
CScreenCapturer* pScreenCapture = (CScreenCapturer*)GetWindowLong(hWnd, GWL_USERDATA)
if (pScreenCapture)
{
pScreenCapture->CaptureScreen()
}
return 0L
}
return DefWindowProc(hWnd, message, wParam, lParam)
}
void CScreenCapturer::CaptureScreen()
{
EnterCriticalSection(&m_syncObject)
MagSetWindowFilterList(m_hwndMagnification, MW_FILTERMODE_EXCLUDE, m_nFilterWindowsCount, m_pFilterWindows)
LeaveCriticalSection(&m_syncObject)
MagSetWindowSource(m_hwndMagnification, m_rectScreen)
}
复制
接收图片回调中保存成BITMAP格式的图片
void CMFCApplicationDlg::OnScreenImageArriveCallback(MAGIMAGEHEADER srcheader, void *srcdata)
{
// construct bitmap
BITMAPINFOHEADER bmif
bmif.biSize = sizeof(BITMAPINFOHEADER)
bmif.biHeight = srcheader.height
bmif.biWidth = srcheader.width
bmif.biSizeImage = bmif.biWidth*bmif.biHeight * 4
bmif.biPlanes = 1
bmif.biBitCount = (WORD)(bmif.biSizeImage / bmif.biHeight / bmif.biWidth * 8)
bmif.biCompression = BI_RGB
BITMAPFILEHEADER bmfh
LONG offBits = sizeof(BITMAPFILEHEADER) + bmif.biSize
bmfh.bfType = 0x4d42// "BM"
bmfh.bfOffBits = offBits
bmfh.bfSize = offBits + bmif.biSizeImage
bmfh.bfReserved1 = 0
bmfh.bfReserved2 = 0
// 返回的图片数据存储是从上到下,位图要求是从下到上,所以需要调整
LPBYTE pData = (BYTE*)new BYTE[bmif.biSizeImage]
memcpy(pData, (LPBYTE)srcdata+srcheader.offset, bmif.biSizeImage)
LONG nLineSize = bmif.biWidth * bmif.biBitCount / 8
BYTE* pLineData = new BYTE[nLineSize]
LONG nLineStartIndex = 0
LONG nLineEndIndex = bmif.biHeight - 1
while (nLineStartIndex <nLineEndIndex)
{
BYTE* pStart = pData + (nLineStartIndex * nLineSize)
BYTE* pEnd = pData + (nLineEndIndex * nLineSize)
memcpy(pLineData, pStart, nLineSize)
memcpy(pStart, pEnd, nLineSize)
memcpy(pEnd, pLineData, nLineSize)
nLineStartIndex++
nLineEndIndex--
}
delete[] pLineData
pLineData = nullptr
std::ofstream ofs(L"C:\\1.bmp", fstream::out|fstream::binary)
if (ofs.is_open())
{
ofs.write((const char*)&bmfh, sizeof(BITMAPFILEHEADER))
ofs.write((const char*)&bmif, sizeof(BITMAPINFOHEADER))
ofs.write((const char*)pData, bmif.biSizeImage)
ofs.close()
}
delete[] pData
pData = nullptr
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)