[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Capture Window

Posted on 2006-05-20
41
Medium Priority
?
1,096 Views
Last Modified: 2008-01-09
Hello, I am doing the following:

Class = String(500, 0)
GetClassName Wnd, Class, 500
Trim$ (Class)

Once, then I have the following running numerous times:

Form1.Cls
Wnd = FindWindow(Class, vbNull)
DC = GetWindowDC(Wnd)
BitBlt Form1.hdc, 0, 0, 640, 140, DC, 0, 340, vbSrcCopy
ReleaseDC Wnd, DC
Form1.Refresh

All variables are defined publicly

This code works, but as soon as I leave the window I am trying to capture, it starts capturing a new window.
I have also tried the same thing, first getting the window by point (windowfrompoint) and getting the dc and
drawing.

The problem:
If a window overlaps the window I am capturing, it grabs that window too.  I want to capture ONLY a certain
section of a window which will NOT have focus and will be overlapped.
0
Comment
Question by:List244
  • 17
  • 15
  • 4
  • +3
41 Comments
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
ID: 16726000
vbNull in VB is = 1, you want to pass a zero or "" even vbnullstring
0
 
LVL 8

Author Comment

by:List244
ID: 16726013
FindWindow API takes NULL in place of window title to include all windows of class name.  My problem is not
with getting the window hwnd nor the dc.  The problem is with every API I have tried, it does not grab window
specific content, it grabs what is in that position.  So if I have a folder for exampled opened and over the window
it captures the folder rather than the program.

I need a way to capture the program which is not foreground on the screen.  There is a certain part of the application
that I want to see, but the program itself is too large and gets in my way.  So I would like to have my own program that
monitors this certain part which is small allowing me to continue with other work.
0
 
LVL 8

Author Comment

by:List244
ID: 16726060
Also, just to add to this:

The window is DX and is NEVER minimized, just underneath under windows.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
LVL 19

Expert Comment

by:BrianGEFF719
ID: 16727557
>>I need a way to capture the program which is not foreground on the screen.  There is a certain part of the >>application that I want to see, but the program itself is too large and gets in my way.  So I would like to have my >>own program that monitors this certain part which is small allowing me to continue with other work.



Dont think this can be done by using the windows device context.


Brian
0
 
LVL 13

Assisted Solution

by:Mark_FreeSoftware
Mark_FreeSoftware earned 400 total points
ID: 16728044
BitBlt relies on a visible part of the window.

The following is possible (however difficult)

if you want to capture just a few variables, you can do that with readprocessmemory.

first you would have to find the address that a value is stored in, and than you can read it and display it in your own form
0
 
LVL 8

Author Comment

by:List244
ID: 16728317
What I am trying to capture is not a variable.  It is in memory, no doubt, but let me explain my problem a little more:

I have a window which runs 640x480 (DX)  It runs windowed mode and never minimizes.  However, it can be
overlapped.  What I want is to create a program which can grab JUST the bottom of the screen (The chat box)
so that I can monitor the chat without having my screen filled with a 640X480 window.  I have tried many methods
bitblt being just one of them.  I have tried setting myself as parent, getting window rect .. and more..  I do not care
which method this is done.  I just need a way to get that chat only.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16728341

you say it is a chat screen,

is it from an app (what sort of app, .net, coded in vb, coded in c or whatever)
or is it from a webpage?
0
 
LVL 8

Author Comment

by:List244
ID: 16728383
As I have explained earlier, it is a DX app.  It was coded with C++ and NOT .NET.  The application
is a game, but it has a chat at the bottom in which I would like to capture.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16728435

ok sorry for not reading very well,



now i'm reading it all over again, and i notice somthing you didnt explain:

Class = String(500, 0)
GetClassName Wnd, Class, 500
Trim$ (Class)

what value is in the variable Wnd, is it 0 or is it the handle to the app you try to capture?
0
 
LVL 8

Author Comment

by:List244
ID: 16728448
It is the handle to the application.  I get the classname fine.  Again, I am not having trouble with ANY
of my code, it all works great.  What I am having trouble with is finding a way to capture part of a
window.
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
ID: 16728460
Trim$(Class) is suppose to be the classname of window handle. But Trim$() isn't going to remove nulls, if you check the length of your string its still going to be 500. You should use the return value of GetClassName which returns the length of the classname. I am surprised that FindWindow didn't crash yet since you are passing a value of 1 for WindowTitle.

Dim ClassBuffer(512)    As Byte
Dim ClassName            As String
ClassLen = GetClassNameW(Wnd, ByVal VarPtr(ClassBuffer(0)), 512)
ClassName = Left$(ClassBuffer, ClassLen)

0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16728463
>>If a window overlaps the window I am capturing, it grabs that window too.

that is what i was trying to find a solution for,

>> I want to capture ONLY a certain section of a window

that is easier, if you can capture it first.

you could for example put a picturebox (box2) in a picturebox (box1), and than by moving box2 up so the upper part is not visible anymore, you have "hidden" that part.
this is not a very neat solution, but it does work.
0
 
LVL 8

Author Comment

by:List244
ID: 16728497
EQL, you are right, and maybe I should be passing VBNullString instead of VBNull, but that is not my problem.
My problem is finding a way to get part of a window.

Mark_FreeSoftware, I don't even know what you are getting at.  That has nothing to do with grabbing
part of another window?
0
 
LVL 29

Expert Comment

by:nffvrxqgrcfqvvc
ID: 16728640
I know I havent answered your actual problem yet but I just wanted to point out some things that I have seen. I know that if a window is over the clientrect area then its going to capture the top most window. The  only way to get it to capture the DX chat dialog would be to set it to call SetForeGround and add a sleep(1) call. Because currently BitBlt is just grabing the screen coordinates of the desktop window. Whatever is there will be captured and not the windows behind it. I would check the findwindow handles because if it returns 0 then GetWindowDC is just going to use the desktop handle.
0
 
LVL 8

Author Comment

by:List244
ID: 16728686
The handles are all correct.  It grabs the correct window everytime.   For example, if I grab 0,0 to 100,100 it grabs the
top left of whichever window is chosen.  If it were grabbing the desktop, it wouldn't be giving me that.  So despite the
troubles with that code, it is working.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16728691

assume you know how to grab a part of a window, and you can put it on a picturebox (box1)

you than have an exact copy of the game (640*480)

if you put that box1 into another picturebox (container), you can resize the container so that ony the part you want to see is visible
see this image for example:

http://img504.imageshack.us/my.php?image=thing7mr.gif
0
 
LVL 8

Author Comment

by:List244
ID: 16728710
Yes, but my problem is I can not grab the window at all.  That is the whole reason I am here.  I have been
coding in Visual Basic for MANY years.  I know how to do such things.  My trouble is that I can not grab the
window when it is overlapped.
0
 
LVL 8

Author Comment

by:List244
ID: 16728730
EQL is there no other way?  I can not set the window to foreground, I do not want it disturbing my applications.
The whole reason I want to do this, is so that I can get rid of the huge screen and just have a very small one.
0
 
LVL 29

Assisted Solution

by:nffvrxqgrcfqvvc
nffvrxqgrcfqvvc earned 400 total points
ID: 16728850
I don't belive you can capture the window because essentially its being overlapped and doesn't exist because its not visible. Example is below but I don't believe you will be able to do this. The only other thing I could think of is trying to send a message to all window telling it to Force a paint message.


Option Explicit

Private Type RECT
    Left As Long
    Top As Long
    Right As Long
    Bottom As Long
End Type
   
Private Declare Function GetClassNameW Lib "user32" ( _
    ByVal hwnd As Long, _
    ByVal lpClassName As Long, _
    ByVal nMaxCount As Long) As Long

Private Declare Function FindWindowW Lib "user32" ( _
    ByVal lpClassName As Long, _
    ByVal lpWindowName As Long) As Long

Private Declare Function GetWindowDC Lib "user32" ( _
    ByVal hwnd As Long) As Long
     
Private Declare Function ReleaseDC Lib "user32" ( _
    ByVal hwnd As Long, _
    ByVal hdc As Long) As Long

Private Declare Function GetClientRect Lib "user32" ( _
    ByVal hwnd As Long, _
    lpRect As RECT) As Long

Private Declare Sub Sleep Lib "kernel32" ( _
    ByVal dwMilliseconds As Long)

Private Declare Sub SwitchToThisWindow Lib "user32" ( _
    ByVal hwnd As Long, _
    ByVal fAltTab As Boolean)

Private Declare Function SetForegroundWindow Lib "user32" ( _
    ByVal hwnd As Long) As Long

Private Declare Function BitBlt Lib "gdi32" ( _
    ByVal hDestDC As Long, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal nWidth As Long, _
    ByVal nHeight As Long, _
    ByVal hSrcDC As Long, _
    ByVal xSrc As Long, _
    ByVal ySrc As Long, _
    ByVal dwRop As Long) As Long

Private Const SRCCOPY = &HCC0020

Dim ClassBuffer(512)    As Byte
Dim MyRECT              As RECT

Private Sub Command1_Click()

    Dim ClassLen    As Long
    Dim ClassName   As String
    Dim Wnd         As Long
    Dim Dc          As Long
   
    Form1.Cls
    Wnd = FindWindowW(0, StrPtr("Calculator"))
    GetClientRect Wnd, MyRECT
    'Get Classname from hwnd (SciCalc)
    ClassLen = GetClassNameW(Wnd, ByVal VarPtr(ClassBuffer(0)), 512)
    'Length of classname
    ClassName = Left$(ClassBuffer, ClassLen)
    Debug.Print ClassName
    'Set to foreground
    SwitchToThisWindow Wnd, False
    Sleep (1)
    'Get Window DC
    Dc = GetWindowDC(Wnd)
    'Blit it
    BitBlt Form1.hdc, 0, 0, _
        MyRECT.Right, _
        MyRECT.Bottom, Dc, 0, 0, SRCCOPY
    'Clean
   ReleaseDC Wnd, Dc
  SetForegroundWindow Form1.hwnd
End Sub
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16728868

is it not possible to use copymemory?

i have been experimenting with this, but i couldnt come up with a good working example :(
0
 
LVL 8

Author Comment

by:List244
ID: 16728909
None of this seems to work, I am beginning to think this is not going to work at all..
0
 
LVL 17

Accepted Solution

by:
zzzzzooc earned 1200 total points
ID: 16730022
There is a PrintWindow() API function that works similar to BitBlt() but it does not require the window being visible. It's only supported on XP and above, however, and I'm not sure of how well it'll function with DirectX rendering.

http://msdn.microsoft.com/library/en-us/gdi/prntspol_6qpj.asp

Form1:
----------------------
Option Explicit

Private Declare Function PrintWindow Lib "user32" (ByVal hWnd As Long, ByVal hdcBlt As Long, ByVal nFlags As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Sub Form_Load()
    Dim wnd As Long
    wnd = FindWindow(vbNullString, "Calculator")
    If (wnd <> 0) Then
        Me.AutoRedraw = True
        Call PrintWindow(wnd, Me.hdc, 0)
    End If
End Sub


0
 
LVL 8

Author Comment

by:List244
ID: 16730637
zzzzzooc, that is exactly what I need, only with DX support.  It grabs black from DX.  Is there perhaps something
I can download which extends the capability of capturing directx windows?
0
 
LVL 17

Expert Comment

by:zzzzzooc
ID: 16745642
Hmm... which window have you tried PrintWindow() with? You may need to either try it's parent (or top-level window) or the child window itself. I don't know whether it's a DX issue or not as I'm not exactly sure how PrintWindow() obtains it's copy of the image when it's not visible. If it sends WM_PRINT to the window, it may just be that the window is ignoring the message or, if it reads the memory directly, DX probably has it's own buffers.
0
 
LVL 8

Author Comment

by:List244
ID: 16745864
I have tried parent and normal window, it is not working for DX.  it does work for normal stuff however.
0
 
LVL 17

Expert Comment

by:zzzzzooc
ID: 16759341
Doh.

I'm not familiar enough to DX to offer any further suggestions. You may have to just force the window on top briefly or see what the SDK for DX offers (I think DX9 has some functions that may help but unsure of cross-process utilization of them).
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16760060

just thinking:

is it not possible to have that window inside a control?

or is it possible to change the hwnd the directx paints on, so stuff get painted on your window instead of the game window?
0
 
LVL 1

Expert Comment

by:Tanlain
ID: 16825121
I know I'm probably a day late and a dollar short, but without performing an entire screen capture, there is no way to capture part of a window that uses DirectX because the window really is just black.  DirectX works directly with hardware and it renders an image directly to the video output, not to the window.  There might be a way to use DirectX to get the image after it has been processed by the DirectX library, but I honestly have no idea how to do that; I'll see if I can come up with anything, though.  Sorry to be the barer of bad news.

I may be wrong about this, so don't hold me to it, but I'm almost positive.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16825284

ok, i know this is c++, but maybey you can try this:

http://www.codeguru.com/cpp/g-m/directx/directx8/article.php/c11453/


it is about making a "proxy" for directx

read it to get a nice explanation of this, but this is an idea you could use:

create your own "proxy"

intercept all calls and send them to the real dll, except the textout calls (or whatev they may be named)

you can forward them too, but copy them to your own program too

(i hope you get the idea)
0
 
LVL 8

Author Comment

by:List244
ID: 16827662
I do not think this is going to happen as a project.  I will go ahead and find another way to do what I was hoping for.
Thank you all for the help, I will split points.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16827703

just for myself, what game is it that you are trying to read the chat?

0
 
LVL 8

Author Comment

by:List244
ID: 16827710
Haha... www.illusorystudios.com
I am pretty much in charge of the game and am pretty much looking for a way to be able to watch over it while
still doing my ordinary work.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16828527


ok, i think i do have it,

i have now created an app that monitors that game, and prints the text said to a listbox


only problem i have:

i do use some memory locations from the game, so i don't know if i can post it here.....
it's up to you to decide.
0
 
LVL 8

Author Comment

by:List244
ID: 16828536
Feel free.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16828580

Add a listbox to your form
List1

add a Command button
Command1

and add aTimer
Timer1
interval = 50 (or 100, depends on the speed of your pc)
enabled = false




paste this code in your form:

Option Explicit

Private Const PROCESS_ALL_ACCESS As Long = &H1F0FFF
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, ByVal lpBaseAddress As Any, lpBuffer As Any, ByVal nSize As Long, lpNumberOfBytesWritten As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long

Private Declare Function CreateToolhelpSnapshot Lib "kernel32" Alias "CreateToolhelp32Snapshot" (ByVal lFlags As Long, lProcessID As Long) As Long
Private Declare Function ProcessFirst Lib "kernel32" Alias "Process32First" (ByVal hSnapshot As Long, uProcess As PROCESSENTRY32) As Long
Private Declare Function ProcessNext Lib "kernel32" Alias "Process32Next" (ByVal hSnapshot As Long, uProcess As PROCESSENTRY32) As Long

Const MAX_PATH& = 260

Private Type PROCESSENTRY32
    dwSize As Long
    cntUsage As Long
    th32ProcessID As Long
    th32DefaultHeapID As Long
    th32ModuleID As Long
    cntThreads As Long
    th32ParentProcessID As Long
    pcPriClassBase As Long
    dwFlags As Long
    szexeFile As String * MAX_PATH
End Type


Const What As String = "client.exe"
Const TextPos As Long = &H13CAC0



Dim WhatID As Long
Dim tmpStr As String, oldStr As String

Private Sub Command1_Click()
   Timer1.Enabled = Not Timer1.Enabled
End Sub

Private Sub Form_Load()
   WhatID = GetPID(What)
   If WhatID = 0 Then
      MsgBox "dinges", vbOKOnly, "Error"
      Exit Sub
   End If
End Sub

Private Sub Timer1_Timer()
   tmpStr = ReadVal
   If tmpStr <> oldStr Then
      List1.AddItem tmpStr
      oldStr = tmpStr
   End If
End Sub

Private Function ReadVal() As String         ''pId As Long, Address As Long
    Dim pHandle As Long
    Dim bytvalue As Long
    Dim i As Long
    Dim Txt As String
    Txt = ""
    pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, WhatID)
    If (pHandle = 0) Then
         Exit Function
    End If
    If TextPos = 0 Then Exit Function
    For i = 1 To 512 Step 2
       ReadProcessMemory pHandle, TextPos + i - 1, bytvalue, 1, 0&
       If bytvalue = 0 Then Exit For
       Txt = Txt & Chr(bytvalue)
    Next
    ReadVal = Txt
    CloseHandle pHandle
End Function

Private Function GetPID(myName As String) As Long
    Dim uProcess As PROCESSENTRY32
    Dim rProcessFound As Long
    Dim hSnapshot As Long
    Dim szExename As String
    Dim i As Long
    myName = LCase$(myName)
    Const TH32CS_SNAPPROCESS As Long = 2&
    uProcess.dwSize = Len(uProcess)
    hSnapshot = CreateToolhelpSnapshot(TH32CS_SNAPPROCESS, 0&)
    rProcessFound = ProcessFirst(hSnapshot, uProcess)
    Do While rProcessFound
        i = InStr(1, uProcess.szexeFile, Chr(0))
        szExename = LCase$(Left$(uProcess.szexeFile, i - 1))
        If Right$(szExename, Len(myName)) = myName Then
            GetPID = uProcess.th32ProcessID
            Exit Function
        End If
        rProcessFound = ProcessNext(hSnapshot, uProcess)
    Loop
    GetPID = 0
End Function
0
 
LVL 8

Author Comment

by:List244
ID: 16828631
That doesn't seem to work for me, I am not sure that the address is constant.  I do not currently have
any memory readers nor care to download one at this time, but I am pretty sure the addresses are not
constant.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16828739

i restarted the game 5 times, and it stayed the same address....


options from the game:

Display:
Alpha, hardware acceleration and windowed mode checked

Audio:
all off

Input:
off

Sockets:
all off

options:
all off

Language:
english
0
 
LVL 8

Author Comment

by:List244
ID: 16828766
For me it is unable to grab any text.  I can get a memory watcher sometime and look into grabbing from memory.
Currently I am a little busy with school to do these things, which is why I was hoping for a quick way.
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16828844

ok


this is the way i did (the fastest i think):

Download Cheat engine
run it, and select the good process (client.exe)
Select as Value Type: "Text"
check Unicode
and case sensitive

type the !first! few characters that were added as last at the screen.
(if it aren't the latest, it won't work)
now click on "First Scan"
it should find atleast one adress

click on it with the rmb, and select "Browse this memory region"

a screen should pop up, with some text in it.

every time somthing is said, or printed to the screen this will change.
if it doesnt change select the next item (if available)
and repeat the "Browse this memory region"


0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16828890

does the code run properly?

to test, set a breakpoint at the beginning of the ReadVal function
if it doesnt execute, try to set the interval of the timer somewhat bigger
0
 
LVL 13

Expert Comment

by:Mark_FreeSoftware
ID: 16828896

and press the button to start
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The debugging module of the VB 6 IDE can be accessed by way of the Debug menu item. That menu item can normally be found in the IDE's main menu line as shown in this picture.   There is also a companion Debug Toolbar that looks like the followin…
Background What I'm presenting in this article is the result of 2 conditions in my work area: We have a SQL Server production environment but no development or test environment; andWe have an MS Access front end using tables in SQL Server but we a…
As developers, we are not limited to the functions provided by the VBA language. In addition, we can call the functions that are part of the Windows operating system. These functions are part of the Windows API (Application Programming Interface). U…
Get people started with the process of using Access VBA to control Outlook using automation, Microsoft Access can control other applications. An example is the ability to programmatically talk to Microsoft Outlook. Using automation, an Access applic…
Suggested Courses

872 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question