C++ Get GDI raw text from another Win32 application - c++

I'm using the UI Automation C++ library to get text data from another GUI application.
If the GUI application is developed using a framework that support UI Automation (WinForm, WPF, ...), it is very easy to get the text.
This is an example using the Inspect tool of UI Automation SDK:
Here, test with CCleaner Free application, it can get the text "Microsoft Edge"
We can also using Win32 API to send some message (Send Message) to get text, example: WM_GETTEXT, LVM_GETITEMTEXT, ...
But, I can't get the text from following element:
The whole header area is just an element only, and I can't get the text inside it.
First, I think it is just an image, but it is not. This is the result of using Ranorex Spy:
The plugin that Ranorex using to get text is GDI RawText Plug-In:
For some older technologies like VB6.0, Delphi or MFC based
applications, Ranorex has only limited support in addressing UI
elements in a robust way. With the GDI RawText Plug-In Ranorex
increases object recognition for controls which use the Windows GDI
drawing mechanism to display text on screen.
So, is there any tutorial about getting text from Windows GDI drawing in another application? Currently, I can't found any resource about it
PS: First, I think it use some OCR library to recognize text from image, but it get the text very fast, not look like using OCR library.
Ranorex is a commercial tool, so I think they will not let me know what library that they implement the plugin if I ask them
Update 1:
I capture the header bar of CCleaner application to Paint, and use Ranorex Spy to get text from the image (if it use some OCR library):
There is no RawText inside the selected element, so it does not use OCR to get the text
Update 2:
I write some simple MFC application to test:
void CMFCApplication1Dlg::OnPaint()
{
CPaintDC dc(this);
CRect rcText(20, 20, 300, 300);
wchar_t text[36];
wsprintf(text, L"Hello world");
dc.SetBkColor(RGB(255, 255, 255));
dc.DrawText(text, &rcText, DT_LEFT);
CDialogEx::OnPaint();
}
Using Spy++:
Using Inspect:
Using Ranorex Spy:
Update 3
Try set background color to black as well:
void CMFCApplication1Dlg::OnPaint()
{
CPaintDC dc(this);
CRect rcText(20, 20, 300, 300);
wchar_t text[36];
wsprintf(text, L"Hello world");
dc.SetBkColor(RGB(0, 0, 0));
dc.DrawText(text, &rcText, DT_LEFT);
CDialogEx::OnPaint();
}
So, no OCR engine can be used here. But this is the result of using Ranorex Spy:

Here's how you can quickly figure out if Ranorex uses OCR. Take a screenshot of CCleanerFree and paste it to mspaint and then run Ranorex on mspaint window to see if it can read text.
Here's how to detect if Ranorex uses winapi hooks for DrawText to see what text some app is drawing. Create a simple windows app and DrawText manually (preferably draw black text on black background to eliminate OCR possibility). If Ranorex reads it then they use winapi hooks to read your text.
Here's how read text with OCR.
OCR is pretty fast, it doesn't take seconds to process simple screen text. You could take screenshot of that window and process specific area of interest for you. I used ocrad for similar task and it worked out great.
Here's sample code you could try:
#include <stdint.h>
#include <string>
#include "ocradlib.h"
std::string image_to_text(uint8_t *data, int width, int height)
{
OCRAD_Pixmap pixmap = { 0 };
pixmap.data = data;
pixmap.width = width;
pixmap.height = height;
pixmap.mode = OCRAD_greymap; // OCRAD_greymap --> 1 byte per pixel; 256 level greymap
std::string ret;
OCRAD_Descriptor * const ocrdes = OCRAD_open();
if (!ocrdes || OCRAD_get_errno(ocrdes) != OCRAD_ok)
{
OCRAD_close(ocrdes);
fprintf(stderr, "not enough memory.\n");
return ret;
}
if (OCRAD_set_image(ocrdes, &pixmap, false) < 0)
{
const OCRAD_Errno ocr_errno = OCRAD_get_errno(ocrdes);
OCRAD_close(ocrdes);
return ret;
}
if (OCRAD_set_threshold(ocrdes, -1) < 0 || // auto threshold
OCRAD_recognize(ocrdes, false) < 0) // no layout
{
const OCRAD_Errno ocr_errno = OCRAD_get_errno(ocrdes);
OCRAD_close(ocrdes);
return ret;
}
const int blocks = OCRAD_result_blocks(ocrdes);
for (int b = 0; b < blocks; ++b)
{
const int lines = OCRAD_result_lines(ocrdes, b);
for (int l = 0; l < lines; ++l)
{
const char * s = OCRAD_result_line(ocrdes, b, l);
if (s && s[0])
ret += s;
}
if (b + 1 < blocks)
ret += '\n';
}
OCRAD_close(ocrdes);
return ret;
}
note that this code uses grayscale pixmap, e.g. 1 byte per pixel rectangle. You can try to change the code to use other image types:
/* OCRAD_Pixmap.data is a pointer to image data formed by "height" rows
of "width" pixels each.
The format for each pixel depends on mode like this:
OCRAD_bitmap --> 1 byte per pixel; 0 = white, 1 = black
OCRAD_greymap --> 1 byte per pixel; 256 level greymap (0 = black)
OCRAD_colormap --> 3 bytes per pixel; 16777216 colors RGB (0,0,0 = black) */
enum OCRAD_Pixmap_Mode { OCRAD_bitmap, OCRAD_greymap, OCRAD_colormap };
There are also other libraries that may provide better quality for more complex images, but for simple text like that ocrad was perfect for me.
To read text using WinAPI hooks
Read some tutorials to understand how that works. For example Hooks Overview on MSDN.
Here's a tutorial that shows how that can be done using EasyHook: EasyHook - Installing a remote hook using EasyHook with C++. Note that there are multiple functions that draw text in WinAPI, perhaps you'll need to hook one or all of them depending on your needs: DrawText, TextOut, ExtTextOut, ExtTextOutWarp (and others).

Related

Is there a way you can print text in console letter by letter in C++ using graphics?

I'm trying to make a game in C++ using graphics. I'm using code blocks. I'm trying to make a screen where the player tells his/her name or username. I want to make each letter they press appear but I can't. This is what I've done so far
void user_engleza()
{
cleardevice();
while(true)
{
char s[101];
ifstream f("nume.in");
ofstream g("nume.in");
bool ok = false;
char litera[1], nume[101];
int x, y, j = -1;
settextstyle(6, HORIZ_DIR, 7);
outtextxy(300, 100, "Your name will be");
x = 700;
y = 150;
while(ok == false)
{
cin >> litera[0];
g << nume[++j];
outtextxy(x, y, litera);
y = y + 100;
if(GetAsyncKeyState(VK_RETURN)) ok = true;
}
}
}
Thank you for help anticipate.
No you can not use BGI for console !!!
BGI uses VGA graphics video modes and MS-DOS console uses VGA text video mode. Those are not the same they even use different segment of memory. For more info see:
Graphics mode in assembly 8086
In Windows the console is not a text window however its emulation of it. If you get its handle you can access its canvas and render graphical stuff on it but that is not possible with BGI as that is emulation of old BGI window from MS-DOS.
How ever you can render text with BGI on BGI window (no console). To render one character at a time you need to pass a string of length 1 not character. So someting like this:
char c[2]={' ',0}; // null terminanted 1 char string
c[0]='A'; // any character you want
outtextxy(x,y,c);
also y = y + 100; looks like a lot. I would expect y+=20 or similar 100 pixels between characters is too much how big is your window?

How to make window always on top in SFML?

I am trying to make a program with SFML who's window stays always on top. How can I achieve that with SFML? I've searched all around but to no avail.
"Program" is a small red dot in the middle of a screen that would imitate a crosshair and I need it to be on top of everything because a real game would be in the background (game does not have crosshair, only sighting).
Only other idea I have, is to use SFML's method getSystemHandle() which would give me OS-specific handle of a window. I am using Ubuntu 16.04 with Gnome and X and I am not quite sure how to code that functionality after I obtain the handle.
It's not possible with the current version of SFML, but since you only need it for X so far, you can just implement it yourself using a snippet from this old/rejected pull request.
void WindowImplX11::setTopmost(bool topmost)
{
static Atom wmStateAbove = XInternAtom(m_display, "_NET_WM_STATE_ABOVE", 1);
static Atom wmNetWmState = XInternAtom(m_display, "_NET_WM_STATE", 1);
if (wmStateAbove)
{
XClientMessageEvent emsg;
memset(&emsg, 0, sizeof(emsg));
emsg.type = ClientMessage;
emsg.window = m_window;
emsg.message_type = wmNetWmState;
emsg.format = 32;
emsg.data.l[0] = topmost;
emsg.data.l[1] = wmStateAbove;
XSendEvent(m_display, RootWindow(m_display, m_screen), false, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent*)&emsg);
}
}
You'll have to retrieve m_display, m_window etc. on your own and/or reimplement the pull request into your source version.

Rounded corners with C++

I'm looking for some c++ drawing graphics library to create rounded corners with anti-aliasing option for dynamic keyboard key creator. I've already tested OpenCV and Magick++ functions but the result was not so good. Can anyone help me with this?
This is a sample of one code to create a rounded corner with Magick++ library
void create_rounded_image (int size, int border) {
Magick::Image image_bk (Magick::Geometry (size, size), Magick::Color ("black"));
image_bk.strokeColor ("white");
image_bk.fillColor ("white");
image_bk.strokeWidth(1);
image_bk.draw (DrawableCircle(size, size, size*0.3, size*0.3));
image_bk.write ("rounded.png");
}
This is the result I'm getting
This is the result I'm looking for
Googling some online documentation, I found:
strokeAntiAlias - bool - Enable or disable anti-aliasing when drawing object outlines.
I suggest:
image_bk.strokeAntiAlias(true);
Expanding on Lamar's answer. Magick::Image.strokeAntiAlias and Magick::DrawableStrokeAntiAlias is what you want. But I would suggest using std::list<Drawable> to generate a context stack. This would allow your application to manage what-will-be-drawn independently of image i/o.
using namespace Magick;
size_t size = 405;
size_t border = 6;
std::list<Drawable> ctx;
ctx.push_back(DrawableStrokeAntialias(MagickTrue));
ctx.push_back(DrawableStrokeColor("#CAF99B"));
ctx.push_back(DrawableStrokeWidth(border));
ctx.push_back(DrawableFillColor("#68C77B"));
ctx.push_back(DrawableCircle(size*0.75, size*0.25, size*0.33, size*0.66));
Image image_bk( Geometry(size, size), Color("white"));
image_bk.draw(ctx);

Change the unit for setting position in TextOut, C++

I'm currently working on a printing plugin with C++, and starting working with TextOut to print the text I want. It works great, but apparently, the positions that TextOut uses as params are in pixels. Is there a way to set them to be in cm or mm? or any other?.
Well, it's pretty simple. The coordinates are not in pixels, but they are in the coordinates of your mapping mode. It just so happens that the default mapping mode of a DC is MM_TEXT which has each coordinate unit to be one pixel on the device.
Change your mapping mode using SetMapMode() to the coordinate system you prefer to use. You can also play with window extents, viewport extents, and origins to customize it however you want. You might want to look at the documentation for SetMapMode() and the MM_LOMETRIC (or MM_HIMETRIC) mapping mode.
There should be special handling implemented for printing. Basically, you need to perform conversion based on HIMETRIC units. The paper size is in HIMETRIC units.
Here is the code that will help you get started (MFC-based):
if (pDC->IsPrinting())
{
// printable area in millimeters
int nWidth = pDC->GetDeviceCaps(HORZSIZE);
int nHeight = pDC->GetDeviceCaps(VERTSIZE);
CDC ScreenDC;
ScreenDC.CreateIC(_T("DISPLAY"), NULL, NULL, NULL);
int nPixelsPerInchX = ScreenDC.GetDeviceCaps(LOGPIXELSX);
int nPixelsPerInchY = ScreenDC.GetDeviceCaps(LOGPIXELSY);
// paper size is in HIMETRIC units. we need to convert
CSize PaperSize(MulDiv(nWidth,nPixelsPerInchX*100,HIMETRIC_PER_INCH),
MulDiv(nHeight,nPixelsPerInchY*100,HIMETRIC_PER_INCH));
// now we need to calculate zoom ratio so the layer content fits on page
double fZoomX = (double)PaperSize.cx/(double)m_DocSize.cx;
double fZoomY = (double)PaperSize.cy/(double)m_DocSize.cy;
m_PrintZoom = min(fZoomX, fZoomY);
ResetViewSize(TRUE);
if (pDC->IsKindOf(RUNTIME_CLASS(CPreviewDC)))
{
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(nPixelsPerInchX, nPixelsPerInchY);
pDC->SetViewportExt(pDC->GetDeviceCaps(LOGPIXELSX), pDC->GetDeviceCaps(LOGPIXELSY));
pDC->SetViewportOrg(0,0);
pDC->SetWindowOrg(0,0);
}
}

Issue with texture loading using the DevIl library

I am working with the DevIl library and trying to use it to load a texture to OpenGL to apply to a sprite. The code is directly out of the C# Game Programming for Serious Game Design book (if that helps). The problem I'm having is with the Il.ilLoadImage call. Even when I pass it null, it doesn't throw an image not found error, and the sprite just shows up dark grey (instead of white) when the form pops up.
public void LoadTexture(string textureId, string path)
{
int devilId = 0;
Il.ilGenImages(1, out devilId);
Il.ilBindImage(devilId);
if (!Il.ilLoadImage(path))
{
System.Diagnostics.Debug.Assert(false, "Could not open file [" + path + "].");
}
//Flip the files before passing them to OpenGl
Ilu.iluFlipImage();
int width = Il.ilGetInteger(Il.IL_IMAGE_WIDTH);
int height = Il.ilGetInteger(Il.IL_IMAGE_HEIGHT);
int openGLId = Ilut.ilutGLBindTexImage();
System.Diagnostics.Debug.Assert(openGLId != 0);
Il.ilDeleteImages(1, ref devilId);
_textureDatabase.Add(textureId, new Texture(openGLId, width, height));
}
DevIL is, rather nonsensically, based on OpenGL's API and general structure. As such, the typical way that errors are reported is with ilGetError. If you want to make sure a function succeeded or not, you should use that.