Link to home
Start Free TrialLog in
Avatar of List244
List244

asked on

Capture Window

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.
Avatar of nffvrxqgrcfqvvc
nffvrxqgrcfqvvc

vbNull in VB is = 1, you want to pass a zero or "" even vbnullstring
Avatar of List244

ASKER

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.
Avatar of List244

ASKER

Also, just to add to this:

The window is DX and is NEVER minimized, just underneath under windows.
>>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
SOLUTION
Avatar of Mark_FreeSoftware
Mark_FreeSoftware
Flag of Netherlands image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of List244

ASKER

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.

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?
Avatar of List244

ASKER

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.

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?
Avatar of List244

ASKER

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.
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)

>>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.
Avatar of List244

ASKER

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?
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.
Avatar of List244

ASKER

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.

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
Avatar of List244

ASKER

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.
Avatar of List244

ASKER

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.
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial

is it not possible to use copymemory?

i have been experimenting with this, but i couldnt come up with a good working example :(
Avatar of List244

ASKER

None of this seems to work, I am beginning to think this is not going to work at all..
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of List244

ASKER

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?
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.
Avatar of List244

ASKER

I have tried parent and normal window, it is not working for DX.  it does work for normal stuff however.
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).

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?
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.

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)
Avatar of List244

ASKER

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.

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

Avatar of List244

ASKER

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.


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.
Avatar of List244

ASKER

Feel free.

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
Avatar of List244

ASKER

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.

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
Avatar of List244

ASKER

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.

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"



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

and press the button to start