[RESOLVED] Capture a window
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 19

Thread: [RESOLVED] Capture a window

  1. #1
    Join Date
    Jun 2011
    Posts
    25

    [RESOLVED] Capture a window

    Is there any way of capturing an existing window to a bitmap or at least finding it's size and position on screen. It has to be quick by clicking or hovering the mouse over the window (as in screen capture software.) I think I saw a post some time back but can't find it now...! Think it might have been something in Marshall class or maybe Bitmap but can't see anything obvious there - probably staring me in the face
    Thanks.

  2. #2
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    Like any Windows Forms control, Form features the DrawToBitmap() method that does just what the name suggests and may be useful for you. However, I have never used it on an entire form so I'm not sure whether it also renders the non-client area along with the form contents. Also, it only can be used easily if you want to capture a form owned by the very program that does the capture because of course you need to access the Form derivate instance in order to use it.

    And there's Graphics::CopyFromScreen() which can capture an arbitrary rectangular screen area. This can be useful if you want to capture your own form including the non-client area and Form::DrawToBitmap() doesn't do that. But if you intend to capture arbitrary windows not necessarily owned by your program, including Windows owned by non-.NET apps, things get somewhat more complicated. I don't think Windows Forms can do that on it's own, so you'll probably need at least a bit of Win32 API magic to determine the window under the cursor.
    Last edited by Eri523; February 26th, 2013 at 03:31 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  3. #3
    Join Date
    Jun 2011
    Posts
    25

    Re: Capture a window

    Thanks Eri523.
    Sorry I should have made it clearer that by existing window what I meant to say was one pre-existing i.e. not owned by my program. NB Paintshop Pro screen capture can select a range of options including screen, client area and window so I know it "can" be done but I guessed it wouldn't be easy. I'm pretty new to VC and am clueless about Win32 API - any pointers?
    The objective BTW is to locate a star's position in an astronomical image displayed in "real-time" by commercial software. The star is centered using telescope controls then mapped to calibrate the drives. Unfortunately the software designer's omitted to include a crosshair or graticule overlay so I need to create some sort of transparent overlay which will automatically locate itself in the image window. That will be another thread I imagine.

  4. #4
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    To determine the window under the mouse cursor, WindowFromPoint() may be a good place to start researching. Once you got the handle of the subject window, GetWindowRect() will retrieve its screen bounds. All that may be more tricky than it looks like initially, though, for instance because WindowFromPoint() may not instantly get you the handle of the window you're actually interested in. In general, it's probably the Win32 window functions you're primarily interested in. Admittedly, much of that is terra incognita to me as well, in particular when it comes to using it in conjunction with .NET.

    I somehow was hoping the .NET NativeWindow class could be helpful in creating a .NET wrapper around a Win32 window handle so you'd be walking on a more familiar terrain, but it looks like that only works for windows your app creates itself.

    The rest will be, well, at least one more thread. In particular the image segmentation needed to isolate one particular star on a sky shot bitmap may be another thing that's more tricky than you think.

    Sorry for not having better news for you...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  5. #5
    Join Date
    Jun 2011
    Posts
    25

    Re: Capture a window

    So how do I use the Win32 functions? A quick try revealed I need to include windows.h where HWND and HANDLE are defined but that throws errors in the standard library files presumably because /clr is enabled. Simply switching off clr throws more errors even on a new project since VC2010 IDE still creates reference types which need clr... Thinks I need to start learning non - .net programming! My guess is I need to add a win32 project maybe dll or lib and link it to my project so I will need to find how to do those things.

    BTW the rest of the stuff is not difficult. Once I have a bitmap copy of the image window I can easily place a graticule over it and then it's simply a matter of moving the telescope with the manual controls to centre the star It needs to be quick and automatically updated though. ( that's why I can't use the app's Save function) An alternative would be if I could create a movable graticule by means of a transparent form with a scalable X or + drawn on it but I've not been able to do this either
    Last edited by RogerD; March 4th, 2013 at 05:34 PM.

  6. #6
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    Quote Originally Posted by RogerD View Post
    So how do I use the Win32 functions? A quick try revealed I need to include windows.h where HWND and HANDLE are defined but that throws errors in the standard library files presumably because /clr is enabled. [...]
    Where did you include <windows.h> and what was the error message you got? I did that quite a few times im C++/CLI projects and got it to work each time.

    [...] Simply switching off clr throws more errors even on a new project since VC2010 IDE still creates reference types which need clr... [...]
    That's not really an option I think since what you want to build is a Windows Forms app, isn't it?

    [...] My guess is I need to add a win32 project maybe dll or lib and link it to my project so I will need to find how to do those things.
    I don't think that's really needed, and in fact it may even raise some extra problems as can be seen in another recent thread.

    [...] An alternative would be if I could create a movable graticule by means of a transparent form with a scalable X or + drawn on it but I've not been able to do this either
    Attached is a demo project that creates a draggable (though not yet scalable) crosshair:

    Name:  2013-03-05_031330.png
Views: 132
Size:  6.4 KB

    (Note: As the usual window state controls aren't reachable in the demo, it can only be closed using the context menu of its task bar entry or by hitting Alt+F4 when it's focused, i.e. after dragging it.)

    Neither the form's Opacity property does the trick (it applies a flat transparency to the entire form including anything that's displayed on it), nor does a partially transparent crosshair bitmap displayed on the form (it merely lets the form's background shine through, not what's behind the form). What does work is setting up the form's Region property to constrain the actual area taken up by the form on the screen, where the resulting area can practically assume any shape.

    Here's the demo's main form implementation file:

    Code:
    // Form1.cpp
    
    #include "stdafx.h"
    
    #include "Form1.h"
    
    using namespace CrosshairDemo;
    
    System::Void Form1::Form1_Load(System::Object^  sender, System::EventArgs^  e)
    {
      Bitmap ^bmpBackground = safe_cast<Bitmap ^>(BackgroundImage);
      ClientSize = bmpBackground->Size;
    
      // This way of converting the crosshair bitmap into a clipping region for the form most certainly
      // is terribliy inefficient. At least it's *relatively* easy to comprehend (I think)...
    
      Drawing::Size szClientOffset = Drawing::Size(PointToScreen(Point::Empty) - Drawing::Size(Location));
      Drawing::Region ^rgnNew = gcnew Drawing::Region;
      for (int i = 0; i < bmpBackground->Width; ++i)
        for (int j = 0; j < bmpBackground->Height; ++j)
          if (bmpBackground->GetPixel(i, j).A)
            rgnNew->Exclude(Rectangle(Point(i, j) + szClientOffset, Drawing::Size(1, 1)));
      rgnNew->Complement(Rectangle(Point(szClientOffset), ClientSize));
      Region = rgnNew;
    }
    
    System::Void Form1::Form1_MouseDown(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e)
    {
      if (e->Button == Windows::Forms::MouseButtons::Left) {
        m_bDragging = true;
        m_ptLastMousePos = e->Location;
      }
    }
    
    System::Void Form1::Form1_MouseUp(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e)
    {
      if (e->Button == Windows::Forms::MouseButtons::Left)
        m_bDragging = false;
    }
    
    System::Void Form1::Form1_MouseMove(System::Object^  sender, System::Windows::Forms::MouseEventArgs^  e)
    {
      if (m_bDragging)
        Location += Drawing::Size(e->Location.X - m_ptLastMousePos.X, e->Location.Y - m_ptLastMousePos.Y);
    }
    Implementation of the dragging feature is quite standard.

    Next step will most probably be implementing picking up information about the window under the crosshair using native Win32 API functions.
    Last edited by Eri523; March 7th, 2013 at 06:36 PM. Reason: Removed superceded demo project attachment
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  7. #7
    Join Date
    Jun 2011
    Posts
    25

    Re: Capture a window

    Thanks for that - the demo is almost perfect as it is - even the right colour!
    I have adapted it slightly and using your example discovered the GraphicsPath class which should be more efficient. The problem is it's difficult getting it to create single pixel diagonal lines especially on non-45 degree co-ords which is what I need. WIP.

    I have yet to re-create the Windows.h problem although I have discovered this in another of your examples (BringMeUp.) The syntax seems pretty obscure to me!
    Code:
    #pragma comment(lib, "user32.lib"
    I will come back on this when I have time - thanks again.

  8. #8
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    Quote Originally Posted by RogerD View Post
    Thanks for that - the demo is almost perfect as it is [...]!
    You're welcome!

    [...] - even the right colour!
    My choice of color was based on the assumption that few apps have windows containing large red areas, so the crosshair would almost always remain clearly visible.

    The problem is it's difficult getting it to create single pixel diagonal lines especially on non-45 degree co-ords which is what I need.
    Do you mean when drawing the path or when constructing a region from it? If it's about drawing, turning off anti-aliasing may help.

    Avoiding problems of that kind was my reason to use a pre-painted bitmap in the crosshair demo instead of having the demo itself draw the crosshair. This approach becomes impractical, though, if you want the crosshair to be freely sizable.

    In one of my apps I use a path-based form region which is strictly circular, and even that was slightly problematic until I changed the bitmap background making up the static content of the form into a metafile that I pre-render into a background bitmap upon form size changes. (IIRC it was possible to simply set up the metafile as the background as well; the pre-rendering is a cache thing meant to reduce CPU load created by repainting.)

    [...] The syntax seems pretty obscure to me!

    Code:
    #pragma comment(lib, "user32.lib")
    This is just an equivalent to adding user32.lib to the additional dependencies in the linker input project properties. I prefer it that way in code intended for forum posts, since it's much shorter than explaining that this dependency needs to be added in case someone wants to compile my code separately from the provided demo project.
    Last edited by Eri523; March 7th, 2013 at 06:00 PM.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  9. #9
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    Quote Originally Posted by Eri523 View Post
    Next step will most probably be implementing picking up information about the window under the crosshair using native Win32 API functions.
    Ok, so here we go... (The new demo project supercedes the one from post #6, so I removed that.)

    The new version features a tool tip displaying some information about the window targetted by the crosshair. Here are the relevant changed/added parts of the main form implementation file (but that's not the really thrilling stuff):

    Code:
    // Form1.cpp
    
    // ...
    
    System::Void Form1::Form1_Load(System::Object^  sender, System::EventArgs^  e)
    {
      Bitmap ^bmpBackground = safe_cast<Bitmap ^>(BackgroundImage);
      ClientSize = bmpBackground->Size;
      m_szClientOffset = Drawing::Size(PointToScreen(Point::Empty) - Drawing::Size(Location));
      m_szTargetOffset = Drawing::Size(63, 63) + m_szClientOffset;
      m_ptToolTipLocation = Point(76, 76) + m_szClientOffset;
    
      // ...
    }
    
    // ...
    
    System::Void Form1::Form1_Move(System::Object^  sender, System::EventArgs^  e)
    {
      Point ptTarget = Location + m_szTargetOffset;
      IntPtr ipHwndTarget = Win32::WindowFromPoint(ptTarget);
      toolTip1->Show(String::Format("Screen location: {0}\nWindow handle: 0x{1:X8}\nTitle: {2}",
        ptTarget, ipHwndTarget.ToInt32(), Win32::GetWindowText(ipHwndTarget)),
        this, m_ptToolTipLocation, 5000);
    }
    The Win32 interop is encapsulated in the class Win32, which, unlike WndMessageHolder from the BringUpTest demo project you already referred to yourself, is an entirely stateless static-methods-only class, so there's no point in instantiating it. Another difference is that the Win32 API is encapsulated completely, so the client code doesn't need to #include <Windows.h> (nor does Win32.h contain that #include), preventing potential name conflicts.

    Here's the interop source code:

    Code:
    // Win32.h
    // Wrapper for native Win32 API
    
    namespace CrosshairDemo
    {
    using namespace System;
    using namespace System::Drawing;
    
    public ref class Win32 abstract sealed
    {
    public:
      static IntPtr WindowFromPoint(Point pt);
      static String ^GetWindowText_(IntPtr hwnd);
    };
    
    }
    
    // Define function name alias, so client code can use original Win32-style function name.
    // Alias is only available, though, unless the client code also includes <Windows.h>.
    
    #ifndef GetWindowText
    #define GetWindowText GetWindowText_
    #endif
    Code:
    // Win32.cpp
    // Wrapper for native Win32 API
    
    #include "stdafx.h"
    
    #include <Windows.h>
    
    #include "Win32.h"  // Must be included after <Windows.h> to avoid macro redefinition warning (C4005)
    
    #pragma comment(lib, "user32.lib")
    
    using namespace CrosshairDemo;
    
    IntPtr Win32::WindowFromPoint(Point pt)
    {
      POINT ptNative = {pt.X, pt.Y};
      return (IntPtr)::WindowFromPoint(ptNative);
    }
    
    String ^Win32::GetWindowText_(IntPtr ipHwnd)
    {
      int nStringSize = ::GetWindowTextLength((HWND)(void *)ipHwnd);
      if (!nStringSize)
        return nullptr;  // Error exit
      LPTSTR lpNativeText = new TCHAR[++nStringSize];
      if (!::GetWindowText((HWND)(void *)ipHwnd, lpNativeText, nStringSize)) {
        delete [] lpNativeText;
        return nullptr;  // Error exit
      }
      String ^strText = gcnew String(lpNativeText);
      delete [] lpNativeText;
      return strText;
    }
    Attached Files Attached Files
    Last edited by Eri523; March 7th, 2013 at 06:51 PM. Reason: Updated demo code - previously would have leaked lpNativeText memory in case of ::GetWindowText() failure
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  10. #10
    Join Date
    Jun 2011
    Posts
    25

    Re: Capture a window

    You have practically written the whole thing for me! Just left the bit that gets the window size and repo's to the centre of it (NO I'm not hinting you should have done that also - leave me something to do for myself!) So profuse thanks for your help once more.
    Added this method to Win32:-
    Code:
    System::Drawing::Rectangle^ Win32::GetWindowRect(Point pt)
    {
      POINT ptNative = {pt.X, pt.Y};
      HWND hwnd = ::WindowFromPoint(ptNative);
      LPRECT rect = new RECT;
      ::GetWindowRect(hwnd,rect);
      return System::Drawing::Rectangle(rect->left,rect->top,rect->right-rect->left,rect->bottom-rect->top);
    }
    In mouse button up handler :-
    Code:
      Point ptTarget = Location + m_szTargetOffset;
      Rectangle^ newRect = Win32::GetWindowRect(ptTarget);
      Location = Point(newRect->X + (newRect->Width/2),newRect->Y + (newRect->Height/2)) - m_szTargetOffset;
    This works well but if the target app/window is moved the crosshair stays put until "nudged". Is there any way of getting a callback/notification of the move so it can track the moves?
    NB *Long* time since I did this native stuff in my Windows programming course (early 90's and Windows 3.11!) so very deja-vu. Would appreciate any constructive crits.

    From a quick look (only just downloaded the demo early hours this a.m.) there are some really valuable principles there worthy of study in detail - especially the win32 encapsulation. Some time back when I was converting some old apps (from Borland builder) I had used the user32.lib stuff mixed in with the new code and of course got loads of messages about switching between managed and unmanaged code so quickly forgot about doing anything like that in new projects. This demo looks to have solved that issue so I need to re-visit them but the question remains how do you know when you have exhausted the search for a managed solution and use win32 libs?

  11. #11
    Join Date
    Jan 2010
    Posts
    1,099

    Re: Capture a window

    @Eri523: Nice work!

    @OP:
    About that last question: well, generally, you try to find a way to do it using framework library, hoping that the specific functionality is exposed in some way. If you can't find anything that suits your needs, then you consider other options. For example, there might be open source (even cross-platform) libraries that are of a good quality and can solve your problem nicely, or there could be commercial solutions, if that's an option. If you aren't interested in those, or simply can't find what you're looking for, you have to turn to native libraries. You can then try PInvoke, or write a wrapper in C++/CLI, or choose to write an entirely native app.

    @Eri523:
    About the NativeWindow class - I didn't really try it out, but why do you think it won't work with a preexisting native window? Based on the existence of this method, it appears to me that it could accept any window handle?
    Last edited by TheGreatCthulhu; March 12th, 2013 at 05:49 PM.

  12. #12
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    Quote Originally Posted by RogerD View Post
    You have practically written the whole thing for me! Just left the bit that gets the window size and repo's to the centre of it (NO I'm not hinting you should have done that also - leave me something to do for myself!) So profuse thanks for your help once more.
    Quote Originally Posted by TheGreatCthulhu View Post
    @Eri523: Nice work!
    Yo, thanks!

    Quote Originally Posted by RogerD View Post
    This works well but if the target app/window is moved the crosshair stays put until "nudged". Is there any way of getting a callback/notification of the move so it can track the moves?
    I think it's well possible to automate that, but it would require hooking the subject window's messages, which is rather advanced even in a pure native environment.

    [...] but the question remains how do you know when you have exhausted the search for a managed solution and use win32 libs?
    IMO that mainly - and simply - demands experience in working with the .NET framework, so you readily have an idea where to look for the things you want. And things aren't always where at least I would have expected them. My pet example of this is System::Diagnostics::Process: Although the class has plenty of diagnostic feaures, justifying to put it into that namespace, like me many developers probably spawn child processes mostly for non-diagnostic purposes, so I initially failed to look for it there. In fact it literally took me weeks to find it - by accident.

    There also are a few cases where support for some features is implemented in framework classes where I wouldn't have expected them. That's probably even harder to find.

    In fact there have been at least two or so cases in which I first implemented things in terms of Win32 API and then, after more or less time had passed, discovered that there actually is support for that feaure in the .NET framework.

    Quote Originally Posted by TheGreatCthulhu View Post
    @Eri523:
    About the NativeWindow class - I didn't really try it out, but why do you think it won't work with a preexisting native window? Based on the existence of this method, it appears to me that it could accept any window handle?
    When I initally read that paragraph of yours, I was seriously considering this may be the time for another "d'oh!" for overlooking something obvious. Then I followed your link and recalled that I actually had considered and inspected that method. The reason why I dropped it as a candidate was this Note in the Remarks section (and I was really disappointed when reading it):

    Quote Originally Posted by MSDN
    The handle to assign cannot be in a different application process.
    Phew...
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  13. #13
    Join Date
    Jan 2010
    Posts
    1,099

    Re: Capture a window

    Oh, I didn't mean to say you overlooked something, I just didn't have time to take more than a quick look, so I missed that note; I was just interested in how you came to that conclusion. You'd think they'd put info like that somewhere on the main page for the class as well...

  14. #14
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    Quote Originally Posted by TheGreatCthulhu View Post
    Oh, I didn't mean to say you overlooked something
    Actually, I didn't perceive it that way. Chances are I'm my own sharpest critic, and I always suspect I may have done something silly unless I'm entirely convinced of the opposite.

    [...] You'd think they'd put info like that somewhere on the main page for the class as well...
    Yeah, d'accord. And I even found this one relatively clear since they stated that in the doc of the relevant method which more or less obviously is non-trivial. I've already seen important information like this not before I, mostly by accident, read the description of a method that, by it's name and one-line description in the table of methods, appeared so trivial that I probably would never have looked into the actual description page (don't remember why, actually, I still did).
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  15. #15
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,583

    Re: Capture a window

    Ok, the last post in this thread is already one month ago and the thread is [RESOLVED], but I think it's worth noting that this leaks memory:

    Quote Originally Posted by RogerD View Post
    Code:
    System::Drawing::Rectangle^ Win32::GetWindowRect(Point pt)
    {
      POINT ptNative = {pt.X, pt.Y};
      HWND hwnd = ::WindowFromPoint(ptNative);
      LPRECT rect = new RECT;
      ::GetWindowRect(hwnd,rect);
      return System::Drawing::Rectangle(rect->left,rect->top,rect->right-rect->left,rect->bottom-rect->top);
    }
    The dynamically allocated rect is never released. Actually, there's no need to dynamically allocate it anyway. The reason why I dynamically allocated lpNativeText in Win32::GetWindowText_() was that I couldn'd know its size in advance, which is not the case with the fixed-size RECT structure. Here's a fixed version:

    Code:
    Drawing::Rectangle^ Win32::GetWindowRect(Point pt)
    {
      POINT ptNative = {pt.X, pt.Y};
      HWND hwnd = ::WindowFromPoint(ptNative);
      RECT rect;
      ::GetWindowRect(hwnd, &rect);
      return Drawing::Rectangle(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
    }
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Azure Activities Information Page

Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center