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.
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.
vbNull in VB is = 1, you want to pass a zero or "" even vbnullstring
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.
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.
ASKER
Also, just to add to this:
The window is DX and is NEVER minimized, just underneath under windows.
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
Dont think this can be done by using the windows device context.
Brian
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
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?
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.
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?
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.
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)
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.
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.
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?
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.
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.
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
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.
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.
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.
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
is it not possible to use copymemory?
i have been experimenting with this, but i couldnt come up with a good working example :(
ASKER
None of this seems to work, I am beginning to think this is not going to work at all..
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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?
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.
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).
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.
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)
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.
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?
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.
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.
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"
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_AC
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(TH3
rProcessFound = ProcessFirst(hSnapshot, uProcess)
Do While rProcessFound
i = InStr(1, uProcess.szexeFile, Chr(0))
szExename = LCase$(Left$(uProcess.szex
If Right$(szExename, Len(myName)) = myName Then
GetPID = uProcess.th32ProcessID
Exit Function
End If
rProcessFound = ProcessNext(hSnapshot, uProcess)
Loop
GetPID = 0
End Function
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.
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
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.
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