I have a DX scrolling banner running at the bottom of the desktop. The banner renders, as its backbround, anything beneath it. The problem is that if the background is renewed, I get a copy of the scrolling banner as its own background, even though I've made it invisible before using "CopyFromScreen". I'm probably being an idiot and it maybe obvious to others. Many thanks in advance.

RECT rct = new RECT();
            GetWindowRect((IntPtr)SBHnd, ref rct);
            
            Bitmap bmp = new Bitmap(PanWidth, PanHeight);

            Graphics g = Graphics.FromImage(bmp);

            ShowWindow(SBHnd, INVISIBLE);            

            g.CopyFromScreen(rct.x, rct.y, 0, 0, new Size(rct.Width, rct.Height)); 
            ShowWindow(SBHnd, VISIBLE);

Recommended Answers

All 14 Replies

Welcome to daniweb TotallyBroke! Please use code tags when posting code

You probably need call Invalidate() and Repaint() or Refresh() on the form. If you the screen but don't invalidate the region and repaint it -- then your screen shot will look like what it did before you set the properties. Please see this example:

private void button4_Click(object sender, EventArgs e)
    {
      button4.BackColor = Color.Red;
      //You have to invalidate and repaint or else the button wont change colors until
      //AFTER the screenshot happens, thus you wont find the pixel!
      this.Invalidate(true);
      this.Update();
      Color searchColor = button4.BackColor;
      Rectangle clip = new Rectangle(this.Location, this.Size); //only search the active window
      Point loc = PixelSearcher.PixelSearch(clip, searchColor.ToArgb(), 5);
      Cursor.Position = loc;
    }

I don't know what your ShowWindow() is calling so you may have to use a different approach.

Something else came to mind. If ShowWindow() is a w32 call you could call Application.DoEvents(); which will process the message queue for the application and hide it.

Note -- DoEvents(); is unsafe to call in most cases. If this works and the first suggestion does not work then you need to find another way to repaint the form. It is possible for a user to click a button, then while the button's code is running click the "X" in the upper right corner, then the button's code makes a call to DoEvents() and it shuts down the application while code is still running -- which will throw an error.

Many thanks Scott for such a quick response. I'll need to provide more detail I think. The app has, what could be called, a parent Form (this is not MDI). On this form there are many panels (user created). The panels in question (Scrolling Banner Panels) have an associate exe that renders scrolling banner output to the surface of the panel (using DX). The exe gets what's under this panel and renders as a background texture to give the impression of a transparent background ( for various reasons I can't use "Transparency" for this). The ShowWindow() function is from the user32.dll ( [DllImport("user32.dll")] static extern bool ShowWindow(IntPtr hwnd, int nCmdShow); ). I've tested that the ShowWindow function is operating by excluding the ShowWindow(SBHnd, VISIBLE);. This results in the panel disappearing. So how can g.CopyFromScreen(rct.x, rct.y, 0, 0, new Size(rct.Width, rct.Height)) display it, if it's not there? BTW: SBHnd is the handle for the associated Panel. This is how the exe renders to the panel. If, as an experiment, I change the Coords during runtime so it renders its background from a different location (1st from the area under it and then 5secs later from another location), I don't get the problem. This proves, I believe, that it is somehow rendering an invisible panel.

>> If, as an experiment, I change the Coords during runtime so it renders its background from a different location (1st from the area under it and then 5secs later from another location), I don't get the problem. This proves, I believe, that it is somehow rendering an invisible panel.

I don't think I agree with that statement. What the proves is that making an unmanaged call to win32 doesn't take effect in the screenshot versus making a managed call by changing the coordinate. When you change a color, location, size, etc on a Control the .NET framework marks that control as "needing to be repainted and updated". You bypass all of the built-in checks by calling Win32 yourself, thus you are responsible for updating the screen. Try this as a test:

RECT rct = new RECT();
GetWindowRect((IntPtr)SBHnd, ref rct);
Bitmap bmp = new Bitmap(PanWidth, PanHeight);
Graphics g = Graphics.FromImage(bmp);
ShowWindow(SBHnd, INVISIBLE); 
Application.DoEvents(); //try this
g.CopyFromScreen(rct.x, rct.y, 0, 0, new Size(rct.Width, rct.Height)); 
ShowWindow(SBHnd, VISIBLE);

I added thread.sleep(3000) to observer the panel disappearing...it does

ShowWindow(SBHnd, INVISIBLE);
thread.sleep(3000)
g.CopyFromScreen(rct.x, rct.y, 0, 0, new Size(rct.Width, rct.Height));

let me make sure I understand this properly. You panel resides on app#1 and this code is being called from app#1, but you also have app#2 whose only purpose is to update a panel on app#1?

Did you try the DoEvents() call?

Again, if I render from a different location....no problem. Proving that it's seeing the invisible panel.
This work...I've move the Y coord to a different loc
g.CopyFromScreen(rct.x, rct.y -300, 0, 0, new Size(rct.Width, rct.Height));

You are correct. It simply uses the Panel as a dumb old rendering surface for the directX exe. It does have some of it's own functions but this is just to change loc, resize, regions, etc. The exe takes, as a bmp, what's under it and uses this bmp as a texture (background) for the Scrolling text to run over. The whole lot is then rendered to the panel using its windows handle. It simply uses CopyFromScreen to get what's under this panel as the bmp for the texture.

I dont understand what you're doing.

g.CopyFromScreen(rct.x, rct.y -300, 0, 0, new Size(rct.Width, rct.Height));

That just takes a screenshot of a different part of the surface area -- but doesn't have anything to do with the controls. I don't see how that proves that it is seeing the invisible panel.

Have you tried the DoEvents() call? And can you upload a sample project demonstrating this behavior?

You are correct. If I take a screen shot from a different location, no matter how many times I make it take a screen shot of this loc it only renders what's visible. If however, and bearing in mind that I've made the panel invisible, I take a screen shot from the location of the area the panel was occupying, it gives me a background which includes a shot of scrolling banner (with text) which is invisible. I see where you are going with the DoEvents() call. If, however, I thread sleep before I call CopyFromScreen would this not allow messages to have completed? I apologies if this is rubbish, still quite new to all this

If I use the bmp from file (the same bitmap that the parent uses) and get the same potion of the bitmap from file, all is well. I think this demonstrates that there's nothing else crazy happening in the code. Some how or other it's rendering the invisible scrolling banner...sorry if this is frustrating. I'm baffled and certain it's some really stupid, that I'm missing. I've even delayed making the panels visible again by 5 secs using a timer

I looked around and I think this will probably turn out to be a difficult task to do properly.

A workaround could be to set the Opacity on your form to 0, send a message to repaint the region (or call doevents), take the screenshot, then restore it. This will cause a flicker though. Other than that I don't see how people are doing it. I know it can be done but i'm not quite sure how

Oh by the way -- From my testing I can get a panel to hide/show during a screen shot just fine, it is the form that is the problem. Are you painting to the form's window handle in the bmp's area so effectively *on top* of it?

sknake, thank you so much. You where on the right path from the begging. There's something strange about the way the ShowWindow is working. Although to the human eye the panel is invisible to the background ( the parent Form) it's not. Simply forcing a Parent repaint solved the problem.

SendMessage((IntPtr)ParentPanel, WM_PAINT, IntPtr.Zero, IntPtr.Zero);
SendMessage((IntPtr)ParentPanel, WM_ERASEBKGND, IntPtr.Zero, IntPtr.Zero);

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.