winapi - a standard way to retrieve text from running text editor - c++

Is there a standard message that can be sent to a text editor window or a certain WinApi call, that will retrieve the contents of the currently edited text?
For example, to retrieve the current contents of a Notepad window. (assumed that the most up to date text wasn't yet written to a file)
I've tried retrieving the text via SendMessage using WM_GETTEXT, WM_GETTEXTLENGTH but I was able to retrieve the title text only.

In general no there is no standard message for this.
But Windows' Notepad has an "Edit" child which responds to WM_GETTEXT and WM_GETTEXTLENGTH - messages normally used to retrieve text from input controls.
Here's a PoC demonstrating the idea:
#include <iostream>
#include <vector>
#include <string.h>
#include <Windows.h>
BOOL CALLBACK enumProc(HWND hwnd, LPARAM) {
std::vector<char> buf(100);
GetClassNameA(hwnd, buf.data(), 100);
if (strcmp(buf.data(), "Notepad")) return TRUE;
hwnd = FindWindowEx(hwnd, NULL, "Edit", NULL);
if (!hwnd) return TRUE;
int textLength = SendMessageA(hwnd, WM_GETTEXTLENGTH, 0, 0) + 1;
if (textLength <= 0) return TRUE;
buf.resize(textLength);
SendMessage(hwnd, WM_GETTEXT, textLength, (LPARAM)buf.data());
std::cout << buf.data() << "\n";
return TRUE;
}
int main() {
EnumWindows(&enumProc, 0);
}
Works on Windows 10:

Related

How do I display a C++/WinRT FileOpenPicker from an exe without a console or window?

From a C++ executable on windows, I want to display a FileOpenPicker.
To do this, I need a window to set as the object's owner:
https://learn.microsoft.com/en-us/windows/apps/develop/ui-input/display-ui-objects#winui-3-with-c
But where do I get the HWND from? I need to call Initialize, but the docs assume you have a hWnd already:
folderPicker.as<::IInitializeWithWindow>()->Initialize(hWnd);
My process does not always have a console window, so ::GetConsoleWindow() will not work.
Here's what I have so far by attempting to use CreateWindow. Nothing happens.
// clang-format off
#include <ShObjIdl.h>
#include <Windows.h>
// clang-format on
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Pickers.h>
#include <iostream>
#include <string>
#include <string_view>
#include <system_error>
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
}
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
int main() {
try {
winrt::init_apartment();
winrt::Windows::Storage::Pickers::FileOpenPicker openPicker;
WNDCLASSEX wc = {sizeof(WNDCLASSEX), CS_CLASSDC, WndProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
L"ImGui Example", NULL};
::RegisterClassEx(&wc);
HWND hwnd = ::CreateWindow(
wc.lpszClassName, L"Dear ImGui DirectX10 Example", WS_OVERLAPPEDWINDOW,
100, 100, 1280, 800, NULL, NULL, wc.hInstance, NULL);
if (!hwnd) {
std::cerr
<< std::error_code(::GetLastError(), std::system_category()).message()
<< "\n";
return 1;
}
openPicker.as<::IInitializeWithWindow>()->Initialize(hwnd);
openPicker.SuggestedStartLocation(
winrt::Windows::Storage::Pickers::PickerLocationId::Desktop);
openPicker.FileTypeFilter().Append(L"*");
openPicker.FileTypeFilter().Append(L".jpeg");
openPicker.FileTypeFilter().Append(L".png");
auto file = openPicker.PickSingleFileAsync().get();
std::string str = winrt::to_string(file.Path());
std::cout << "name: " << str << "\n";
return 0;
} catch (std::exception& e) {
std::cerr << "error: " << e.what() << "\n";
return 1;
}
}
This question appears to be asking a similar thing, but OP solved it with ::GetConsoleWindow(), which is not an option.
Based on empirical evidence, IInitializeWithWindow::Initialize() is happy with just about any top-level window handle. A window handle associated with a process running in the CONSOLE subsystem is commonly available through the GetConsoleWindow API:
openPicker.as<::IInitializeWithWindow>()->Initialize(::GetConsoleWindow());
This produces the desired behavior1 when using the standard command prompt (hosted by ConHost.exe). Launching the same program from a process using a pseudoconsole instead (such as Windows Terminal) things start to get flaky: The FilePicker does show up, but it no longer follows the rules of owned windows. It's hiding behind the host process' window, and doesn't move to the foreground, when the (not actually an) owner is activated.
Whether this is how things should work, or are designed to work, or if any of this is within specification is unclear. None of this is documented, which seems to be the most convenient way to ship interfaces these days.
The fact that FileOpenPicker isn't documented to implement IInitializeWithWindow doesn't exactly help, either. All hail to cloaked interfaces of which there is only anecdotal evidence.
Ranting aside, you'll probably want to use std::wcout in place of std::cout. Otherwise you'll see an address printed rather than a string.
1 Just because you can doesn't necessarily mean you should create a window hierarchy across threads.

EM_SETSEL not working in .docx (word document)

I'm working on a simple program that sends WM_COPY to an open .docx file (word document). I tested my code on other applications and it seems to work all fine. But whenever I try to use my code on an open word document, it returns false at SendMessage(hwndChild, EM_SETSEL, 0, -1).
I have tried a couple things:
Use code on notepad (not richtext) and stickynotes (richttext) and it works perfectly with the code beneath.
Remove the line SendMessage(hwndChild, EM_SETSEL, 0, -1) and then manual highlight the text in the word document and run program. It succeeds and copies the content to clipboard with help of WM_COPY.
Does anyone know why EM_SETSEL is not working in a word document?
int main() {
HWND app = FindWindowEx(0, 0, "OpusApp", 0);
EnumChildWindows(app, EnumChildProc, (LPARAM) NULL);
return 0;
}
BOOL CALLBACK EnumChildProc(HWND hwndChild, LPARAM lParam) {
uint8_t max_path_length = 255;
char *str = new char[max_path_length];
memset(str, 0, max_path_length);
GetClassName(hwndChild, str, max_path_length);
/// Execute when edit handle is obtained
if(strcmpi(str, "_WwG") == 0) {
if(SendMessage(hwndChild, EM_SETSEL, 0, -1)) {
SendMessage(hwndChild, WM_COPY, 0, 0);
} else {
MessageBox(0, "Can't select all!", "Report", MB_OK);
}
}
str = NULL;
delete [] str;
return TRUE;
}
EM_SETSEL only works for an Edit control. I doubt that Word uses the standard control.

Push button and combo box WinAPI

I am trying to do some windows programming. I created a combo box and a push button. My objective is, when the user selects an item in the combo box, the button is activated and needs to be clicked to proceed. I have done something like this so far:
This is how i create the combo box:
Func.h
#ifndef FUNCS_H
#define FUNCS_H
// Winapi headers....
#include <windows.h>
#include <windowsx.h>
#include <shlobj.h>
//created lib
#include "Resource.h"
// Standard C/C++ headers...
#include <iostream>
#include <string>
#include <dirent.h> // directory manipulation....
#include <cstring>
#include <fstream>
using std::cout;
using std::endl;
using std::string;
using std::ofstream;
HWND hselectOK;
HWND hComboBox1;
void createControls(HWND hwnd) // Create our combo box
{
HWND hselectFeature;
HINSTANCE hInstance = GetModuleHandle(NULL);
// Create our List box
hComboBox1 = CreateWindow(WC_COMBOBOX,"", CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_CHILD |WS_OVERLAPPED |WS_VISIBLE , 7, 20, 300, 100, hwnd, NULL, hInstance, NULL);
SendMessage(hComboBox1, CB_ADDSTRING, 0,(LPARAM)"Histogram of Oriented Gradients (HOG)");
SendMessage(hComboBox1, CB_ADDSTRING, 0,(LPARAM)"Scale Invariant Feature Transform (SIFT)");
SendMessage(hComboBox1, CB_ADDSTRING, 0,(LPARAM)"Speeded Up Robust Features (SURF)");
// SendMessage(hComboBox1, CB_SETCURSEL, (WPARAM)2,(LPARAM)0); //CB_SETCURSEL
// Create our push bottons
hselectFeature = CreateWindow("STATIC", "Select Feature", SS_LEFT | WS_CHILD, 320,20, 100, 21,hwnd, (HMENU)1, hInstance, NULL);
ShowWindow(hselectFeature,1);
hselectOK = CreateWindow("BUTTON", "Ok", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON | WS_DISABLED, 320,45, 100, 21,hwnd, NULL, hInstance, NULL);
}
#endif // FUNCS_H
WinProc.h
#ifndef WINPROC_H
#define WINPROC_H
// Winapi Headers
#include <CommDlg.h>
#include <winuser.h>
// OpenCV headers
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
// Standard C/C++ headers
#include <iostream>
#include <string>
// Created headers;
#include "Funcs.h"
#include "Resource.h"
using std::cout;
using std::endl;
using std::string;
using namespace cv;
int classNumber;
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
string dirPath;
int comboIndex;
switch (message) /* handle the messages */
{
case WM_CREATE:
createControls(hwnd);
break;
case WM_COMMAND:
{
switch(HIWORD(wParam))
{
case CBN_SELCHANGE: // When user select item in combo box, enable the button.
{
EnableWindow(hselectOK, TRUE); // enable the button
}
break;
case BN_CLICKED: // When user has chosen a list, the button is used to proceed with further task associated to the selected item.
{
char listName[200];
comboIndex = SendMessage(hComboBox1, (UINT) CB_GETCURSEL, 0, 0);
SendMessage(hComboBox1, (UINT)CB_GETLBTEXT, (WPARAM)comboIndex, (LPARAM)listName);
if(comboIndex == 0)
{
MessageBox(hwnd,listName, "You chose", MB_OK);
// Want to Do some function here.
}
else if(comboIndex == 1)
{
MessageBox(hwnd,listName, "You chose", MB_OK);
// Want to Do some function here.
}
else if(comboIndex == 2)
{
MessageBox(hwnd,listName, "You chose", MB_OK);
// Want to Do some function here.
}
}
break;
}
switch LOWORD(wParam)
{
case IDM_IMG_PATH:
{
dirPath = browseFolder(hwnd);
DialogBox(GetModuleHandle(NULL),MAKEINTRESOURCE(IDD_CLASS),hwnd, ClassDlgProcedure);
createClassLabelFile(hwnd, dirPath, classNumber);
return 0;
}
case IDM_EXIT:
{
PostMessage(hwnd, WM_CLOSE,0 , 0);
}
break;
}
}
break;
case WM_DESTROY:
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
#endif // WINPROC_H
Those part are only some of the relevant parts of the code.
The problem is, when I exit the program using the case IDM_EXIT, the message box under the case BN_CLICKED always appears and then the program closes. I am expecting that when we close the program, no such message box would appear, but this is not the case. What I'm saying is, the message box appears twice, once when you select click the button and when you want to close the program. Why is that happening. Any Ideas or suggestions ?
The problem is that you are assuming that WM_COMMAND is a message that is unique to your ComboBox, Which is not.
Take a look at this old but gold blog post.
Quoting from the author,
The high-order word of the wParam parameter "specifies the
notification code if the message is from a control". What does
"control" mean here? Remember that you have to take things in context.
The WM_COMMAND message is being presented in the context of Win32 in
general, and in the context of the window manager in particular.
Windows such as edit boxes, push buttons, and list boxes are commonly
called "controls", as are all the window classes in the "common
controls library". In the world of the window manager, a "control" is
a window whose purpose is to provide some degree of interactivity
(which, in the case of the static control, might be no interactivity
at all) in the service of its parent window. The fact that the
WM_COMMAND is used primarily in the context of dialog boxes further
emphasizes the point that the term "control" here is just a synonym
for "child window".
To summarize, any button click inside your application window will translate as a WM_COMMAND with a BN_CLICKED wParam. This includes the close button of the window.
To handle specific clicks from your combo box you have two options. The easy way is to filter the hWnd of the control that sent the message, you should already know the window handle of your combo box and it shouldn't be a problem to compare against it.
The other option is to define your own messages and check against those in your WndProc handler. The web is full of examples of how to define your own application/control specific messages.
When you click your 'Exit' menu, windows also send a 'BN_CLICKED' message to the 'WindowProcedure' function and that is why the message box appears. You should use an ID for the button rather than making the 'hmenu' parameter 'NULL' like this:
hselectOK = CreateWindow("BUTTON", "Ok", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON | WS_DISABLED, 320,45, 100, 21,hwnd,
(HMENU)305, // Here is the ID of your button ( You may use your own ID )
hInstance, NULL);
And you have to add some ID checking code in your 'case BN_CLICKED' like that:
case BN_CLICKED: // When user has chosen a list, the button is used to proceed with further task associated to the selected item.
{
// You must do this check otherwise the message box will appear again when you click the 'Exit' menu
if ( LOWORD(wParam) == 305 ) // '305' is the ID which I have used as the button ID in above code
{
// Now add your click event code here
}
}
break;

PostMessage not working for WM_PASTE , mfc

I have an application which has a button on Click of button am trying to paste the text already available to Notepad. My application gathers the text first and puts it on the clipboard(this is working perfectly fine), I am facing problem with the Paste part. Here is the code, Please let me know where am i going wrong.
CWnd *pCwnd = FindWindow(NULL, _T("Untitled - Notepad"));
HWND handle = pCwnd->GetSafeHwnd();
pCwnd->PostMessageA(WM_PASTE,0,0);
I am using Notepad to test it so the name is ("Untitled - Notepad").
Please help me. Thanks in advance.
I don't use MFC, but you can probably translate to what you need. The issue is you need to send the message to the edit control, not the main window.
#include <Windows.h>
#include <string>
#include <cstdlib>
int main()
{
const std::string data("This is some text from the clipboard.");
HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, data.size() + 1);
std::memcpy(GlobalLock(hMem), data.c_str(), data.size() + 1);
GlobalUnlock(hMem);
OpenClipboard(NULL);
EmptyClipboard();
SetClipboardData(CF_TEXT, hMem);
CloseClipboard();
HWND mainWindow = FindWindow(NULL, "Untitled - Notepad");
HWND editWindow = FindWindowEx(mainWindow, NULL, "edit", NULL);
PostMessage(editWindow, WM_PASTE, 0, 0);
return 0;
}

Browse For Folder dialog window handle C++

How to get the handle HWND of the dialog which user open when clicking on a button.
I'm using Spy++ to find the window class and tittle, but it says that no such window is found. And how then to get the handle of that dialog in C++ using Win API ?
I hope that I will be able to do that using simple functions as FindWindow, GetParent, any WIN APi function. I do not like to inject something or load DLL. Thanks
UPDATE:
the folder browser dialog is opened by other program. I want to get it's handle from different program , my program. Thanks.
The closest to want i need is for now the function WindowFromPoint
Accessibility will let you capture window creation events from other processes without DLL injection. You can modify the example to accommodate for the browsing window specifically. Here's an example I made previously to test that is based on the one from the article. Modify it however you wish:
#include <iostream>
#include <windows.h>
void CALLBACK proc(HWINEVENTHOOK hook, DWORD event, HWND hwnd, LONG obj, LONG child, DWORD thr, DWORD time) {
if (hwnd && obj == OBJID_WINDOW && child == CHILDID_SELF) {
switch (event) {
case EVENT_OBJECT_CREATE: {
std::cout << "Window created!\n";
break;
}
case EVENT_OBJECT_DESTROY: {
std::cout << "Window destroyed!\n";
break;
}
}
}
}
int main() {
HWINEVENTHOOK hook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_DESTROY, nullptr, proc, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
if (hook) {
UnhookWinEvent(hook);
}
}