找回密码
 立即注册

QQ登录

只需一步,快速开始

graper

高级会员

45

主题

63

帖子

1348

积分

高级会员

积分
1348

活字格认证

graper
高级会员   /  发表于:2009-12-11 15:42  /   查看:6507  /  回复:0
Post by "ted", 12-14-2007, 15:49
-----------------------------------------------------


Remote Desktop Connection and Painting

An increasingly important developer tax is supporting Remote Desktop Connection properly. When the user is connected via a Remote Desktop Connection, video operations are transferred over the network connection to the client for display. Because networks have high latency and nowhere near the bandwidth of a local video card, you need to adapt to the changing cost of drawing to the screen.

If you draw a line on the screen, the "draw line" command is sent over the network to the client. If you draw text, a "draw text" command is sent (along with the text to draw). So far so good. But if you copy a bitmap to the screen, the entire bitmap needs to be transferred over the network.

Let's write a sample program that illustrates this point.

  1. // new function
  2. void Draw(HWND hwnd, HDC hdc, PAINTSTRUCT *pps)
  3. {
  4. FillRect(hdc, &pps->rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
  5. RECT re;
  6. GetClientRect(hwnd, &rc) ;
  7. for (int i = -10; i < 10; i + +) {
  8. TextOut(hdc, 0, i * 15 + re.bottom / 2, TEXT("Blah blah"), 9);
  9. }
  10. }

  11. void PaintContent(HWND hwnd, PAINTSTRUCT *pps)
  12. {
  13. Draw(hwnd, pps->hdc, pps);
  14. }
复制代码
There is an odd division of labor here; the PaintContent function doesn't actually do anything aside from handing the work off to the Draw function to do the actual drawing. (You'll see why soon.) Make sure the Show window contents while dragging option is enabled and run this program and resize it vertically. Ugh, what ugly flicker!

We fix this by the traditional technique of double-buffering:

  1. void PaintContent(HWND hwnd, PAINTSTRUCT *pps)
  2. {
  3. if ( !IsRectEmpty(&amp;pps->rcPaint) ) {
  4. HDC hdc = CreateCompatibleDC (pps->hdc) ,-
  5. if (hdc) {
  6. int x = pps->rcPamt. lett ;
  7. int y = pps->rcPaint.top;
  8. int cx = pps->rcPaint.right - pps->rcPaint.left;
  9. int cy = pps->rcPaint.bottom - pps->rcPaint.top;

  10. HBITMAP hbm = CreateCompatibleBitmap(pps->hdc, cx, cy);
  11. if (hbm) {
  12. HBITMAP hbmPrev = SelectBitmap(hdc, hbm);
  13. SetWindowOrgEx(hdc, x, y, NULL);
  14. }

  15. Draw(hwnd, hdc, pps);
  16. BitBlt (pps->hdc, x, y, ex, cy, hdc, x, y, SRCCOPY) ;
  17. SelectObject(hdc, hbmPrev);
  18. DeleteObject(hbm);
  19. }
  20. DeleteDC(hdc);
  21. }
复制代码
Our new PaintContent function creates an offscreen bitmap and asks the Draw function to draw into it. After that's done, the results are copied to the screen at one go, thereby avoiding flicker. If you run this program, you'll see that its resizing behavior is nice and smooth.

Now connect to the computer via a Remote Desktop Connection and run it again. Because Remote Desktop Connection disables the Show window contents while dragging option, you can't use resizing to trigger redraws. Instead, maximize the program and restore it a few times. Notice the long delay before the window is resized when you maximize it. That's because we are pumping a huge bitmap across the Remote Desktop Connection as part of that BitBlt call.

Go back to the old version of the PaintContent method, the one that just calls Draw, and run it over Remote Desktop Connection. Ah, this one is fast. That's because the simpler version doesn't transfer a huge bitmap over the Remote Desktop Connection; it just sends 20 Text Out calls on a pretty short string of text. These take up much less bandwidth than a 1024X768 bitmap.

We have one method that is faster over a Remote Desktop Connection, and another method that is faster when run locally. Which should we use? We use both, choosing our drawing method based on whether the program is running over a Remote Desktop Connection:

  1. void PaintContent(HWND hwnd, PAINTSTRUCT *pps)
  2. {
  3. if (GetSystemMetrics(SM_REMOTESESSION)) {
  4. Draw(hwnd, pps->hdc, pps) ;
  5. } else if (!IsRectEmpty(&amp;pps->rcPaint)) {

  6. ... as before . . .
  7. }
  8. }
复制代码
Now we get the best of both worlds. When run locally, we use the double-buffered drawing, which draws without flickering; but when run over a Remote Desktop Connection, we use the simple Draw method, which draws directly to the screen rather than to an off-screen bitmap.

This is a rather simple example of adapting to Remote Desktop Connection. In a more complex world, you might have more complicated data structures associated with the two styles of drawing, or you might have background activities related to drawing that you may want to turn on and off based on whether the program is running over a Remote Desktop Connection. Because the user can dynamically connect and disconnect, you can't just assume that the state of the Remote Desktop Connection when your program starts will be the state for the lifetime of the program. You'll see next how we can adapt to a changing world.

简要翻译一下:

远程桌面给应用程序带来了那些新的挑战,我们应该如何使自己的应用更好地支持远程桌面哪,上面的文章简单地描述了远程桌面同Window绘制的关系.

在Window的绘制过程中,如果你需要绘制一条线,那么”DrawLine”这个命令将被发送到客户端,同样地,如果你需要绘制一些字符串,”DrawText”这个命令将被发送至客户端,但是如果需要绘制一幅图片的话,整个图片将需要通过网络传到客户端进行绘制.

在Window的绘制中为了避免闪烁,我们通常会使用Double-Buffer技术, 常见的做法是先将Window画在图上,然后再绘制该图片到指定的DC上,这样图片将需要传送到远程连接的桌面上。

这个时候我们就需要需要检查在远程桌面连接时程序的性能了.

该文提供的解决方案是:如果当前的Session是远程连接,则使用正常的绘制,如果不是,你可以使用Double-Buffer来绘制以减少闪烁.

不知道你还有没有更好的想法?

你也可以来检查我们的产品在远程连接时的性能-:)

0 个回复

您需要登录后才可以回帖 登录 | 立即注册
返回顶部