关于窗口重画的初级问题

摘要:
关于窗口重新绘制的初始问题初始问题:为什么当窗口更改时,我在视图中绘制的图像或文本会消失?这是窗口重绘或重绘的问题。当存在需要重新绘制的无效区域窗口时执行。非客户区域的重画系统已经由我们自己完成,而客户区域的重新绘制需要我们自己完成。主要的问题是思路还没有扭转。总之,对具体问题进行了详细分析。记住一个大原则:绘图代码放在OnDraw中。

关于窗口重画的初级问题

初初级问题:

我在视图画的图象或者文字,当窗口改变后(包括最小化后还原,被别的窗口挡住后重新显示等)为什么不见了?

这 就是窗口重绘或者说重画的问题。当窗口改变后,会产生无效区域,这个无效的区域需要重画。什么是无效区域?自己到网上搜索或者看相关资料。我这里给出一个 特殊的解释:以最小化后还原为例,假设只有一个程序在运行,窗口最小化时显示计算机桌面,并不妨假设桌面是蓝色的背景,当窗口还原时,窗口所占的这一块区 域该显示些什么东西呢?操作系统并不知道,因此,就形成一块无效区域。要是我们告诉操作系统,显示一个红方块,于是它就显示一个红方块,我们的程序过一会 想显示一个绿方块呢?同样也要告诉它。在哪里告诉它呢?在OnDraw或OnPaint函数这里(这里不准备讨论他们的区别,并且只关注OnDraw)。OnDraw函数在什么时候执行呢?在存在无效区域窗口需要重绘时执行。

下面开始做例子(为简化程序,不显示方块显示线条),建立一个SDI工程,并添加一个菜单项DrawLine,添加OnDrawline函数:

显示线条A(20,20,50,50)

void CDrawView::OnDrawline()

{

       // TODO: Add your command handler code here

       CClientDC dc(this);

       dc.MoveTo(20,20);

       dc.LineTo(50,50);

}

编译运行,出现一个线条,最小化还原,没有了。更改OnDraw函数:

显示线条B(100,100,70,70)

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       pDC->MoveTo(100,100);

       pDC->LineTo(70,70);

}

运行,最小化还原,你会发现线条B一直存在,线条A没有了。

到这里,你应该有一个疑问:我们新建一个SDI程序,什么都没做啊,为什么这个时候还是能显示菜单,框架这些东西?我们并没有告诉操作系统要显示什么啊?这是因为一般Windows会发送两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化)。非客户区的重画系统自己搞定了,而客户区的重画需要我们自己来完成。

初级问题:

我要在菜单函数里面画图怎么办?或者说,我希望点击菜单就画图,不想把代码放在OnDraw里面。

这个问题主要是思路没有转过弯来。其实很简单,在菜单函数里面设置一个开关,然后在OnDraw里面根据这个开关来决定是否显示就可以了。例如:

BOOL bShowLineA=FALSE;

void CDrawView::OnDrawline()

{

       // TODO: Add your command handler code here

       bShowLineA=TRUE;

        Invalidate();//这个函数暂时只需要知道是用来“调用”OnDraw函数的

}

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

        if(bShowLineA)

       {

           pDC->MoveTo(20,20);

          pDC->LineTo(50,50);

       }    

}

单击菜单项DrawLine就会画线条A,当然你还可以再搞一个函数令bShowLineA=FALSE达到删除线条的目的。

上述做法存在很大局限:必须事先知道要画什么图。考虑这样一种情况:我们用鼠标画图,鼠标按下时决定开始线条开始点,鼠标弹起时决定结束点并画图。你也许会说,还是可以用上面的做法啊,用变量来代替数字就可以了,例如:

void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)

{

       // TODO: Add your message handler code here and/or call default

       start=point;

       CView::OnLButtonDown(nFlags, point);

}

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)

{

       // TODO: Add your message handler code here and/or call default

       end=point;

       Invalidate();

       CView::OnLButtonUp(nFlags, point);

}

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       pDC->MoveTo(start.x,start.y);

       pDC->LineTo(end.x,end.y);

}

这样的确是可以解决一部分问题,考虑更复杂的情况:画若干条线条,还要画一些圆、正方形,甚至还要显示一行文字。这个时候上面的方法就不太适合了。但原理是一样的:在菜单函数里面计算,保存数据结果,通知系统更新;在OnDraw函数里面根据新的结果数据进行重画。在菜单函数里面如何保存数据结果呢?可以使用一个结构(或者类)保存在内存里(例如可以使用数组,链表等等方式),也可以把数据保存在一个文件里,然后,在OnDraw里面读取这个结构(前面说的数组、链表、文件等)的数据进行重画。总之,具体问题具体分析。

记住一个大原则:画图代码(确切的说是用来显示的代码)放在OnDraw里面。

那么,是不是任何时候画图代码都必须要放在OnDraw里面?也不全是,例如操作的一些中间过程就要放在其他函数。一个经典的例子是用鼠标画直线,并且动态显示中间画图过程,鼠标弹起才最终决定最后的直线。那么,在鼠标移动事件中就要动态的画线了,然后保存最终结果,在OnDraw里面只需要画这个最终结果就可以了。当然,这个中间过程其实还是可以放到OnDraw里面来的。特别是一些复杂的图形处理,例如画不规则图形,就需要开一个线程专门用来计算显示画线的过程,这个不在我们讨论范围之内。

我还发现一个初学者有趣的心理活动,包括我自己也是一样的,就是舍不得在OnDraw里面放较多的代码。如下:

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       pDC->MoveTo(start.x,start.y);

       pDC->LineTo(end.x,end.y);

        ....

        下面还有很多的画图的代码。

}

上面这种情况他们总是担心会不会效率太低了?因为窗口动不动就要刷新,这么多代码会不会太慢了?但是,要是下面这样的代码他们就放心多了:

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       fun();

}

fun

{

很多很多很多的画图的代码

}

呵呵,有时候我跟别人说,把画图代码放在OnDraw里面,他们就会认为,每一行代码都放到OnDraw里面。

还 有一些“顽固分子”说,我把画图的代码放在菜单函数里面,然后禁止窗口重画就行了,然后到处去问怎么禁止窗口自动重画。他们都忽略了一个事实,显示器只有 一个!他们都把窗口当作一个个的实体了。他们的想法是:把这个窗口画好了,就再也不动了,最小化吗?好办,请工人把这个窗口拿走,就可以看到后面的窗口 了,还原,就让工人再搬回来就是了。可事实是:窗口(显示器)永远都只有一个。我们只看显示器里面中间的一个象素点:这个时候,这个点在我们的程序里面是 红色的,然后把我们的窗口最小化,这个点并没有随着我们的窗口跑掉啊,它还是在那里,可是下一个窗口是绿色的,你说,这个时候是不是要重新把这个点涂成绿 色的?这就是重画。还原时,又要重新把它涂成红色,如此反复。

觉得太罗嗦,好像还没说清楚,累啊^_^

------------------------------------------------------------------------------
由UpdateData(FALSE)想到的窗口刷新问题
作者:zuilang
一,前言
    有网友提醒我:“在MSDN裡面能找到的東西,再寫BLOG是要被罵的。”确实,全抄MSDN没有一点意思,但加一点自己的理解,或许对初学者有一点用。因此,首先声明,本文适合MFC初学者。
二,前提知识
2关于Invalidate、InvalidateRect和UpdateWindow
    以下资料来源不祥,似乎是vckbase讨论的(不保证每一句都正确,如有错误,请指出)。
(1)Invalidate
       Invalidate标记一个需要重绘的无效 区域,并不意味着调用该函数后就立刻进行重绘。类似于PostMessage(WM_PAINT),需要处理到WM_PAINT消息时才真正重绘。以为您 Invalidate之后还有其他的语句正在执行,程序没有机会去处理WM_PAINT消息,但当函数执行完毕后,消息处理才得以进行。
       Invalidate只是放一个WM_PAINT消息在队列里,不做别的,所以只有当当前函数返回后,进入消息循环,取出WM_PAINT,才执行PAINT,所以不管Invalidate放函数哪个地方,(作用相当于)都是(放在)最后的(但并不是推荐你一律放在函数最后一行)。
       Invalidate()之后:...OnPaint()->OnPrepareDC()->OnDraw(),所以只是刷新在OnPaint()和OnDraw()函数中的绘图语句。其它地方没有影响。
 
(2)InvalidateRect
       InvalidateRect只是增加重绘区域,在下次WM_PAINT的时候才生效,InvalidateRect函数中的参数TRUE表示系统会在你画之前用背景色将所选区域覆盖一次,默认背景色为白色,可以通过设置BRUSH来改变背景色。
       InvalidateRect(hWnd,&rect,TRUE);向 hWnd窗体发出WM_PAINT的消息,强制客户区域重绘制,rect是你指定要刷新的区域,此区域外的客户区域不被重绘,这样防止客户区域的一个局部 的改动,而导致整个客户区域重绘而导致闪烁,如果最后的参数为TRUE,则还向窗体发送WM_ERASEBKGND消息,使背景重绘,当然在客户区域重绘 之前。
 
(3)UpdateWindow
       UpdateWindow只向窗体发送 WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送 WM_PAINT。如果希望立即刷新无效区域,可以在调用InvalidateRect之后调用UpdateWindow,如果客户区的任一部分无效,则 UpdateWindow将导致Windows用WM_PAINT消息调用窗口过程(如果整个客户区有效,则不调用窗口过程)。这一WM_PAINT消息 不进入消息队列,直接由WINDOWS调用窗口过程。窗口过程完成刷新以后立刻退出,WINDOWS将控制返回给程序中UpdateWindow调用之后 的语句。(windows程序设计第5版 P98)
三,问题
    初学者很容易碰到下面这个问题:(其中m_nEdit是一个编辑框的int型成员变量)
void CTestDlg::OnButton1()
{
       // TODO: Add your control notification handler code here
       for(int i=0;i<10;i++)
       {
              m_nEdit=i;
              UpdateData(FALSE);
       }
}
    程序运行的结果是,编辑框里面直接就显示了9,是程序运行太快了看不清楚吗?改:
void CTestDlg::OnButton1()
{
       // TODO: Add your control notification handler code here
       for(int i=0;i<10;i++)
       {
              m_nEdit=i;
              Sleep(1000);
              UpdateData(FALSE);
       }
}关于窗口重画的初级问题

初初级问题:

我在视图画的图象或者文字,当窗口改变后(包括最小化后还原,被别的窗口挡住后重新显示等)为什么不见了?

这 就是窗口重绘或者说重画的问题。当窗口改变后,会产生无效区域,这个无效的区域需要重画。什么是无效区域?自己到网上搜索或者看相关资料。我这里给出一个 特殊的解释:以最小化后还原为例,假设只有一个程序在运行,窗口最小化时显示计算机桌面,并不妨假设桌面是蓝色的背景,当窗口还原时,窗口所占的这一块区 域该显示些什么东西呢?操作系统并不知道,因此,就形成一块无效区域。要是我们告诉操作系统,显示一个红方块,于是它就显示一个红方块,我们的程序过一会 想显示一个绿方块呢?同样也要告诉它。在哪里告诉它呢?在OnDraw或OnPaint函数这里(这里不准备讨论他们的区别,并且只关注OnDraw)。OnDraw函数在什么时候执行呢?在存在无效区域窗口需要重绘时执行。

下面开始做例子(为简化程序,不显示方块显示线条),建立一个SDI工程,并添加一个菜单项DrawLine,添加OnDrawline函数:

显示线条A(20,20,50,50)

void CDrawView::OnDrawline()

{

       // TODO: Add your command handler code here

       CClientDC dc(this);

       dc.MoveTo(20,20);

       dc.LineTo(50,50);

}

编译运行,出现一个线条,最小化还原,没有了。更改OnDraw函数:

显示线条B(100,100,70,70)

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       pDC->MoveTo(100,100);

       pDC->LineTo(70,70);

}

运行,最小化还原,你会发现线条B一直存在,线条A没有了。

到这里,你应该有一个疑问:我们新建一个SDI程序,什么都没做啊,为什么这个时候还是能显示菜单,框架这些东西?我们并没有告诉操作系统要显示什么啊?这是因为一般Windows会发送两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化)。非客户区的重画系统自己搞定了,而客户区的重画需要我们自己来完成。

初级问题:

我要在菜单函数里面画图怎么办?或者说,我希望点击菜单就画图,不想把代码放在OnDraw里面。

这个问题主要是思路没有转过弯来。其实很简单,在菜单函数里面设置一个开关,然后在OnDraw里面根据这个开关来决定是否显示就可以了。例如:

BOOL bShowLineA=FALSE;

void CDrawView::OnDrawline()

{

       // TODO: Add your command handler code here

       bShowLineA=TRUE;

        Invalidate();//这个函数暂时只需要知道是用来“调用”OnDraw函数的

}

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

        if(bShowLineA)

       {

           pDC->MoveTo(20,20);

          pDC->LineTo(50,50);

       }    

}

单击菜单项DrawLine就会画线条A,当然你还可以再搞一个函数令bShowLineA=FALSE达到删除线条的目的。

上述做法存在很大局限:必须事先知道要画什么图。考虑这样一种情况:我们用鼠标画图,鼠标按下时决定开始线条开始点,鼠标弹起时决定结束点并画图。你也许会说,还是可以用上面的做法啊,用变量来代替数字就可以了,例如:

void CDrawView::OnLButtonDown(UINT nFlags, CPoint point)

{

       // TODO: Add your message handler code here and/or call default

       start=point;

       CView::OnLButtonDown(nFlags, point);

}

void CDrawView::OnLButtonUp(UINT nFlags, CPoint point)

{

       // TODO: Add your message handler code here and/or call default

       end=point;

       Invalidate();

       CView::OnLButtonUp(nFlags, point);

}

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       pDC->MoveTo(start.x,start.y);

       pDC->LineTo(end.x,end.y);

}

这样的确是可以解决一部分问题,考虑更复杂的情况:画若干条线条,还要画一些圆、正方形,甚至还要显示一行文字。这个时候上面的方法就不太适合了。但原理是一样的:在菜单函数里面计算,保存数据结果,通知系统更新;在OnDraw函数里面根据新的结果数据进行重画。在菜单函数里面如何保存数据结果呢?可以使用一个结构(或者类)保存在内存里(例如可以使用数组,链表等等方式),也可以把数据保存在一个文件里,然后,在OnDraw里面读取这个结构(前面说的数组、链表、文件等)的数据进行重画。总之,具体问题具体分析。

记住一个大原则:画图代码(确切的说是用来显示的代码)放在OnDraw里面。

那么,是不是任何时候画图代码都必须要放在OnDraw里面?也不全是,例如操作的一些中间过程就要放在其他函数。一个经典的例子是用鼠标画直线,并且动态显示中间画图过程,鼠标弹起才最终决定最后的直线。那么,在鼠标移动事件中就要动态的画线了,然后保存最终结果,在OnDraw里面只需要画这个最终结果就可以了。当然,这个中间过程其实还是可以放到OnDraw里面来的。特别是一些复杂的图形处理,例如画不规则图形,就需要开一个线程专门用来计算显示画线的过程,这个不在我们讨论范围之内。

我还发现一个初学者有趣的心理活动,包括我自己也是一样的,就是舍不得在OnDraw里面放较多的代码。如下:

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       pDC->MoveTo(start.x,start.y);

       pDC->LineTo(end.x,end.y);

        ....

        下面还有很多的画图的代码。

}

上面这种情况他们总是担心会不会效率太低了?因为窗口动不动就要刷新,这么多代码会不会太慢了?但是,要是下面这样的代码他们就放心多了:

void CDrawView::OnDraw(CDC* pDC)

{

       CDrawDoc* pDoc = GetDocument();

       ASSERT_VALID(pDoc);

       // TODO: add draw code for native data here

       fun();

}

fun

{

很多很多很多的画图的代码

}

呵呵,有时候我跟别人说,把画图代码放在OnDraw里面,他们就会认为,每一行代码都放到OnDraw里面。

还 有一些“顽固分子”说,我把画图的代码放在菜单函数里面,然后禁止窗口重画就行了,然后到处去问怎么禁止窗口自动重画。他们都忽略了一个事实,显示器只有 一个!他们都把窗口当作一个个的实体了。他们的想法是:把这个窗口画好了,就再也不动了,最小化吗?好办,请工人把这个窗口拿走,就可以看到后面的窗口 了,还原,就让工人再搬回来就是了。可事实是:窗口(显示器)永远都只有一个。我们只看显示器里面中间的一个象素点:这个时候,这个点在我们的程序里面是 红色的,然后把我们的窗口最小化,这个点并没有随着我们的窗口跑掉啊,它还是在那里,可是下一个窗口是绿色的,你说,这个时候是不是要重新把这个点涂成绿 色的?这就是重画。还原时,又要重新把它涂成红色,如此反复。

觉得太罗嗦,好像还没说清楚,累啊^_^

------------------------------------------------------------------------------
由UpdateData(FALSE)想到的窗口刷新问题
作者:zuilang
一,前言
    有网友提醒我:“在MSDN裡面能找到的東西,再寫BLOG是要被罵的。”确实,全抄MSDN没有一点意思,但加一点自己的理解,或许对初学者有一点用。因此,首先声明,本文适合MFC初学者。
二,前提知识
2关于Invalidate、InvalidateRect和UpdateWindow
    以下资料来源不祥,似乎是vckbase讨论的(不保证每一句都正确,如有错误,请指出)。
(1)Invalidate
       Invalidate标记一个需要重绘的无效 区域,并不意味着调用该函数后就立刻进行重绘。类似于PostMessage(WM_PAINT),需要处理到WM_PAINT消息时才真正重绘。以为您 Invalidate之后还有其他的语句正在执行,程序没有机会去处理WM_PAINT消息,但当函数执行完毕后,消息处理才得以进行。
       Invalidate只是放一个WM_PAINT消息在队列里,不做别的,所以只有当当前函数返回后,进入消息循环,取出WM_PAINT,才执行PAINT,所以不管Invalidate放函数哪个地方,(作用相当于)都是(放在)最后的(但并不是推荐你一律放在函数最后一行)。
       Invalidate()之后:...OnPaint()->OnPrepareDC()->OnDraw(),所以只是刷新在OnPaint()和OnDraw()函数中的绘图语句。其它地方没有影响。
 
(2)InvalidateRect
       InvalidateRect只是增加重绘区域,在下次WM_PAINT的时候才生效,InvalidateRect函数中的参数TRUE表示系统会在你画之前用背景色将所选区域覆盖一次,默认背景色为白色,可以通过设置BRUSH来改变背景色。
       InvalidateRect(hWnd,&rect,TRUE);向 hWnd窗体发出WM_PAINT的消息,强制客户区域重绘制,rect是你指定要刷新的区域,此区域外的客户区域不被重绘,这样防止客户区域的一个局部 的改动,而导致整个客户区域重绘而导致闪烁,如果最后的参数为TRUE,则还向窗体发送WM_ERASEBKGND消息,使背景重绘,当然在客户区域重绘 之前。
 
(3)UpdateWindow
       UpdateWindow只向窗体发送 WM_PAINT消息,在发送之前判断GetUpdateRect(hWnd,NULL,TRUE)看有无可绘制的客户区域,如果没有,则不发送 WM_PAINT。如果希望立即刷新无效区域,可以在调用InvalidateRect之后调用UpdateWindow,如果客户区的任一部分无效,则 UpdateWindow将导致Windows用WM_PAINT消息调用窗口过程(如果整个客户区有效,则不调用窗口过程)。这一WM_PAINT消息 不进入消息队列,直接由WINDOWS调用窗口过程。窗口过程完成刷新以后立刻退出,WINDOWS将控制返回给程序中UpdateWindow调用之后 的语句。(windows程序设计第5版 P98)
三,问题
    初学者很容易碰到下面这个问题:(其中m_nEdit是一个编辑框的int型成员变量)
void CTestDlg::OnButton1()
{
       // TODO: Add your control notification handler code here
       for(int i=0;i<10;i++)
       {
              m_nEdit=i;
              UpdateData(FALSE);
       }
}
    程序运行的结果是,编辑框里面直接就显示了9,是程序运行太快了看不清楚吗?改:
void CTestDlg::OnButton1()
{
       // TODO: Add your control notification handler code here
       for(int i=0;i<10;i++)
       {
              m_nEdit=i;
              Sleep(1000);
              UpdateData(FALSE);
       }
}
    程序开始没有变化,静静运行了一会,直接显示9!看来不是显示太快的原因。
四,思考
    因为UpdateData(FALSE)是更新窗口(编辑框也是窗口)的内容,当然也会更新窗口的“画面”,那么,是不是也是跟Invalidate、InvalidateRect一样的问题呢?尝试一下:
void CTestDlg::OnButton1()
{
       // TODO: Add your control notification handler code here
       for(int i=0;i<10;i++)
       {
              m_nEdit=i;
              Sleep(100);//去掉这一句在这里确实因为显示太快而看不清。
              UpdateData(FALSE);
              UpdateWindow();
       }
}
    终于得到我们要的结果了^_^。那么UpdateData(FALSE)到底做了什么?想了解,就去看深入浅出MFC。
    程序开始没有变化,静静运行了一会,直接显示9!看来不是显示太快的原因。
四,思考
    因为UpdateData(FALSE)是更新窗口(编辑框也是窗口)的内容,当然也会更新窗口的“画面”,那么,是不是也是跟Invalidate、InvalidateRect一样的问题呢?尝试一下:
void CTestDlg::OnButton1()
{
       // TODO: Add your control notification handler code here
       for(int i=0;i<10;i++)
       {
              m_nEdit=i;
              Sleep(100);//去掉这一句在这里确实因为显示太快而看不清。
              UpdateData(FALSE);
              UpdateWindow();
       }
}
    终于得到我们要的结果了^_^。那么UpdateData(FALSE)到底做了什么?想了解,就去看深入浅出MFC。

免责声明:文章转载自《关于窗口重画的初级问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Vue 基础篇二JAVA获取文件本身所在的磁盘位置下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Linux 系统编程 学习:3-进程间通信1:Unix IPC(2)信号

背景 上一讲我们介绍了Unix IPC中的2种管道。 回顾一下上一讲的介绍,IPC的方式通常有: Unix IPC包括:管道(pipe)、命名管道(FIFO)与信号(Signal) System V IPC:消息队列、信号量、共享内存 Socket(支持不同主机上的两个进程IPC) 我们在这一讲介绍Unix IPC,中有关信号(Signal)的处理。...

csharp(C#) 调用SQL自定义函数返回值

代码 1--SQL自定义函数:23CREATEFUNCTION[GetProjectID](@headStrnvarchar(10),@datedatetime)4)56RETURNSNVARCHAR(200)78AS910BEGIN1112--不能在自定义函数中用INSERTINTO1314--insertintoemos_cust(cust_name...

CVE20211732 LPE漏洞分析

概述   CVE-2021-1732是一个发生在windows内核win32kfull模块的LPE漏洞,并且由于创建窗口时调用win32kfull!xxxCreateWindowEx过程中会进行用户模式回调(KeUserModeCallback),从而给了用户态进程利用的机会。   该漏洞由安恒信息在2020年12月在野外攻击样本中发现,在2021年2月份...

cocos 事件转发

CCDirector在函数setOpenGLView中设置pobOpenGLView,同时会掉用 m_pobOpenGLView->setTouchDelegate(m_pTouchDispatcher);m_pTouchDispatcher被初始化为CCTouchDispatcher事件分发管理器。 CCEGLView继承CCEGLViewProt...

Python高级语法之:一篇文章了解yield与Generator生成器

Python高级语法中,由一个yield关键词生成的generator生成器,是精髓中的精髓。它虽然比装饰器、魔法方法更难懂,但是它强大到我们难以想象的地步:小到简单的for loop循环,大到代替多线程做服务器的高并发处理,都可以基于yield来实现。 理解yield:代替return的yield 简单来说,yield是代替return的另一种方案:...

安卓进阶:元注解Support Annotation Library使用详解

Support Annotation Library是一个函数包,包含一系列有用的元注解。 注解目录:  Nullness注解  资源类型注解 类型定义注解 线程注解 RGB颜色值注解 值范围注解 权限注解 重写函数注解 返回值注解 @VisibleForTesting @Keep 实用主义至上,不记录历史和版本这些,现在直接了解一下这个函数包有什么用...