Properly handle WM_PASTE in subclass procedure - c++

RELEVANT INFORMATION:
I have a subclass procedure that needs to validate the content of the clipboard before it gets pasted.
I have managed to get the content of the clipboard successfully, at least I think so.
QUESTION:
I do not know how to construct the following if statement ( the following is pseudo code ):
if( clipboard content is OK )
defaul handler;
else
discard message;
MY EFFORTS TO SOLVE THIS:
So far this is what I have in mind:
LRESULT CALLBACK Decimalni( HWND hwnd,
UINT message, WPARAM wParam, LPARAM lParam,
UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch (message)
{
case WM_PASTE:
{
bool IsTextValid = true; // indicates validity of text
if( OpenClipboard(hwnd) ) // open clipboard
{
HANDLE hClipboardData;
// get clipboard data
if( hClipboardData = GetClipboardData(CF_UNICODETEXT) )
{
// Call GlobalLock so that to retrieve a pointer
// to the data associated with the handle returned
// from GetClipboardData.
wchar_t *pchData = (wchar_t*)GlobalLock(hClipboardData);
// copy clipboard data so we can free clipboard
wchar_t result[10]; // I just need first 9 characters
memset( result, L'0', sizeof(result) );
// copy clipboard data WITH TRUNCATION!!!
wcsncpy_s( result, 10, pchData, _TRUNCATE );
// Unlock the global memory.
GlobalUnlock(hClipboardData);
/*** parse the text for validity ****/
// code for parsing text
// update IsTextValid to indicate success or fail
/*** end of parsing *******/
}
// Finally, when finished I simply close the Clipboard
// which has the effect of unlocking it so that other
// applications can examine or modify its contents.
CloseClipboard();
}
// here should be the problematic if statement
if( IsTextValid )
return ::DefSubclassProc( hwnd, message, wParam, lParam);
else
return FALSE;
}
break;
case WM_CHAR:
{
// filter out some invalid keys
}
break;
case WM_NCDESTROY:
::RemoveWindowSubclass( hwnd, Decimalni, 0 ); // remove subclassing
break;
}
return ::DefSubclassProc( hwnd, message, wParam, lParam);
}
Is my idea correct or is there another way to form my if statement?
Thank you.
Best regards.

The code seems plausible, down to the action taken. Bit clunky, but that's Windows API. There may be better ways, but this should work.
One mistake: if the text is OK, you should call DefSubclassProc, not the default window procedure.
If the text is not OK you could consider emptying the clipboard. There is not enough here about your other requirements to talk about that.

Related

C++ Hook Windows Explorer paste event

I would like to hook Windows Explorer paste event to copy files from a remote connection.
Description: The goal is remote copy/paste files. Like Team Viewer or Remote Desktop. Ctrl+C file on one computer, and Ctrl+V on another...
Well let's break this problem into 3 parts:
1. Detect for clipboard changes:
This is pretty easy, by registering a hook using SetClipboardViewer, Windows will nicely send us an WM_DRAWCLIPBOARD message:
HWND nextClipboardViewer = nullptr;
void HandleClipboardChanges()
{
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
nextClipboardViewer = SetClipboardViewer(hwnd);
break;
case WM_CHANGECBCHAIN:
if (reinterpret_cast<HWND>(wParam) == nextClipboardViewer)
{
nextClipboardViewer = reinterpret_cast<HWND>(lParam);
}
else if (nextClipboardViewer != nullptr)
{
SendMessage(nextClipboardViewer, msg, wParam, lParam);
}
break;
case WM_DRAWCLIPBOARD:
HandleClipboardChanges();
SendMessage(nextClipboardViewer, msg, wParam, lParam);
break;
}
}
2. Get the active Windows Explorer directory
In the HandleClipboardChanges function above, we should iterate through all the opened Windows Explorer, check if any of them is focused, and get their current directory, thanks to zett42's answer, we could do this fairly easily:
HWND hWndExplorer = nullptr;
HWND hWndFocused = GetActiveWindow();
std::wstring explorerDir;
for (const auto& info : GetCurrentExplorerFolders())
{
if (hWndFocused == info.hwnd)
{
CComHeapPtr<wchar_t> pPath;
if (SUCCEEDED(::SHGetNameFromIDList(info.pidl.get(), SIGDN_FILESYSPATH, &pPath)))
{
hWndExplorer = info.hwnd;
explorerDir = pPath;
}
break;
}
}
3. Handle the copy operation and show a progress dialog
For the progress dialog, we will use IProgressDialog, although IOperationsProgressDialog has more features, but it is also more difficult to use, you can consider switching to it.
The hWndParent passed into IProgressDialog::StartProgressDialog could be nullptr, but we will use the explorer's hWnd for consistency.
The below code doesn't check for errors for readability.
// don't forget the include and CoInitialize
#include <atlbase.h>
#include <shlobj_core.h>
CoInitializeEx(nullptr, COINIT_MULTITHREADED);
CComPtr<IProgressDialog> pDialog;
pDialog.CoCreateInstance(CLSID_ProgressDialog);
pDialog->StartProgressDialog(hWndExplorer, nullptr, PROGDLG_AUTOTIME, nullptr);
pDialog->SetTitle(L"Copying from network");
pDialog->SetLine(1, L"Copying 69 files", false, nullptr);
// Do your copy operation here
for (DWORD i = 0; i < 1'000'000; i++)
{
pDialog->SetProgress(i, 1'000'000);
pDialog->SetLine(2, L"Copying file_a.txt", false, nullptr);
// Check if the user had cancelled the operation
// See also: pDialog->SetCancelMsg()
// BOOL isUserCancelled = pDialog->HasUserCancelled();
}
pDialog->StopProgressDialog();
Related:
Monitoring clipboard
How to get the path of an active file explorer window in c++ winapi

Display formatted text on selecting item in the Combobox

I have a combobox in that I want to display different string on selecting an item in Combo.
My combo box is a dropdown combobox.
For eg: I have following in my combobox.
Alex - Manager
Rain - Project Lead
Shiney - Engineer
Meera - Senior Engineer
OnSelecting an item in combobox I want to diaply only name i.e. Alex.
I tried below code
struct details{
CString name;
CString des;
};
BOOL CComboTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
details d1;
d1.name = _T("alex");
d1.des =_T("manager");
m_vec.push_back(d1);
details d2;
d2.name = _T("Rain");
d2.des =_T("Engineer");
m_vec.push_back(d2);
// TODO: Add extra initialization here
for(int i=0;i<m_vec.size();i++)
{
m_ctrlCombo.AddString(m_vec[i].name+m_vec[i].des);
m_ctrlCombo.SetItemData(i,(DWORD_PTR)&m_vec[i]);
}
m_ctrlCombo.SelectString(-1,m_vec[0].name);
m_ctrlCombo.SetWindowText(m_vec[0].name);
return TRUE; // return TRUE unless you set the focus to a control
}
void CComboTestDlg::OnCbnSelchangeCombo1()
{
int nItem = m_ctrlCombo.GetCurSel();
details* det = (details*)m_ctrlCombo.GetItemData(nItem);
PostMessage(SETCOMBOTEXT,IDC_COMBO1,(LPARAM)(LPCTSTR)det->name);
}
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg)
{
MSG msg1=*pMsg;//I am loosing the value after checking ..so storing temp.
MSG msg;
CopyMemory(&msg, pMsg, sizeof(MSG));
HWND hWndParent = ::GetParent(msg.hwnd);
while (hWndParent && hWndParent != this->m_hWnd)
{
msg.hwnd = hWndParent;
hWndParent = ::GetParent(hWndParent);
}
if (pMsg->message==SETCOMBOTEXT && (pMsg->wParam == IDC_COMBO1))
SetDlgItemText(IDC_COMBO1, (LPCTSTR)pMsg->lParam);
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN && msg.hwnd ==m_ctrlCombo.m_hWnd )
{
OnCbnSelchangeCombo1();
}
}
return CDialog::PreTranslateMessage(pMsg);
}
I am able to achieve my requirement OnComboSelChange() and Arrow Keys event but on pressing enter key after using arrow keys in combo box, it is not showing formatted text in combo box.
I think the most reliable and easy to implement solution is to subclass the edit control of the combobox. Intercept the WM_SETTEXT message and change the text as you like before forwarding it to the rest of the chain (finally the original window proc).
Install the sub class proc in OnInitDialog():
COMBOBOXINFO cbi{ sizeof(cbi) };
if( m_ctrlCombo.GetComboBoxInfo( &cbi ) )
{
SetWindowSubclass( cbi.hwndItem, ComboEditSubClassProc, 0, 0 );
}
ComboEditSubClassProc() could look like this:
LRESULT CALLBACK ComboEditSubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch( uMsg )
{
case WM_SETTEXT:
{
CString text = reinterpret_cast<LPCTSTR>( lParam );
// Extract the name (everything before "-").
CString name = text.SpanExcluding( _T("-") );
name.TrimRight();
// Forward the modified text to any other sub class procs, aswell
// as the original window proc at the end of the chain.
return DefSubclassProc( hWnd, uMsg, 0, reinterpret_cast<LPARAM>( name.GetString() ) );
}
case WM_NCDESTROY:
{
// We must remove our subclass before the subclassed window gets destroyed.
// This message is our last chance to do that.
RemoveWindowSubclass( hWnd, ComboEditSubClassProc, uIdSubclass );
break;
}
}
return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}
Notes:
Contrary to my original solution of processing CBN_SELCHANGE, the current solution also works correctly if the combobox drop-down list is closed by pressing Return or is dismissed.
I think it is in general more reliable because we don't have to rely on the order of the notifications. The combobox has to finally call WM_SETTEXT to change the content of the edit control so this message will always be received.
There will also be no flickering as in the original solution where the text was first changed by the combobox and then modified by our code only after the fact.

RDP connection hook called twice for every connection

I want my application to hook remote connect and disconnect events for all sessions on the specified computer.
According to the documentation, I should call WTSRegisterSessionNotification function first:
WTSRegisterSessionNotification(hWnd, NOTIFY_FOR_ALL_SESSIONS);
Then in the messages processor callback I should do the required stuff on WM_WTSSESSION_CHANGE message type:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_WTSSESSION_CHANGE:
{
// do stuff
break;
}
}
}
It works very strange however. Every time I connect or disconnect to/from the computer where my application running, WTS_REMOTE_CONNECT or WTS_REMOTE_DISCONNECT called twice -- in one of these calls I can get user name and in another I can't (it's just an empty string).
case WM_WTSSESSION_CHANGE:
{
const int reason = (int)wParam;
const DWORD sessionId = (DWORD)lParam;
switch (reason)
{
case WTS_REMOTE_CONNECT:
{
DWORD bytesReturned = 0;
LPSTR pData = NULL;
if (WTSQuerySessionInformationA(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, &pData, &bytesReturned) == 0)
{
// Handle error
break;
}
const std::string username = pData;
WTSFreeMemory(pData);
// do other stuff
I found this question but it has very strange accepted answer:
Hmm, the answer appears to be that it's fairly normal for these fields
to be empty on a terminal services/RDP session
I don't understand why it actually works this way.

crash on ShowWindow()

I am developing a Notepad++ plugin.
I have a simple dialog box, which is created using CreateDialogParam(). It is initially hidden.
After a menu click, I call ShowWindow() with SW_SHOW on its handle, which results in this exception:
c000041d
Upon debugging , I found that these two messages are sent to my dialog just before crash
WM_SHOWWINDOW
WM_WINDOWPOSCHANGING
Both of them, I am not handling. Here is my dlgproc code. Any idea why it would happen ?
BOOL CALLBACK StaticDialog::dlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG :
{
// Get the additional init data
StaticDialog *pStaticDlg = (StaticDialog *)(lParam);
// Store the handle in the object
pStaticDlg->_hSelf = hwnd;
::SetWindowLongPtr(hwnd, GWL_USERDATA, (long)lParam);
// Store the co-ordinates in the object
::GetWindowRect(hwnd, &(pStaticDlg->_rc));
// Forward the message for further processing
pStaticDlg->run_dlgProc(message, wParam, lParam);
// TRUE if it processed the message
return TRUE;
}
default :
{
// Retrieve the user data
StaticDialog *pStaticDlg = (StaticDialog *)(::GetWindowLongPtr(hwnd, GWL_USERDATA));
if (!pStaticDlg)
return FALSE;
// Send the message for further processing
return pStaticDlg->run_dlgProc(message, wParam, lParam);
// return FALSE if it processed the message
}
}
}
BOOL CALLBACK MarkDownViewDialog::run_dlgProc( UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_INITDIALOG:
{
EmbedBrowserObject(this->_hSelf);
DisplayHTMLPage(this->_hSelf,L"http://www.microsoft.com");
DisplayHTMLStr(this->_hSelf, L"<H2><CENTER>HTML string test</CENTER></H2><P><FONT COLOR=RED>This is a <U>HTML string</U> in memory.</FONT>");
break;
}
default:
break;
}
return FALSE;
}
The dialog box is created through plugin mechanism of Notpead++. I am putting the source code from its file StaticDialog.cpp. The control goes through else block in code below.
void StaticDialog::create(int dialogID, bool isRTL, bool isModeles)
{
if (isRTL)
{
DLGTEMPLATE *pMyDlgTemplate = NULL;
HGLOBAL hMyDlgTemplate = makeRTLResource(dialogID, &pMyDlgTemplate);
_hSelf = ::CreateDialogIndirectParam(_hInst, pMyDlgTemplate, _hParent, (DLGPROC)dlgProc, (LPARAM)this);
::GlobalFree(hMyDlgTemplate);
}
else
_hSelf = ::CreateDialogParam(_hInst, MAKEINTRESOURCE(dialogID), _hParent, (DLGPROC)dlgProc, (LPARAM)this);
//int i=GetLastError();
if (!_hSelf)
{
//systemMessage(_T("StaticDialog"));
return;
}
if (isModeles) {
_isModeles = isModeles;
::SendMessage(_hParent, NPPM_MODELESSDIALOG, MODELESSDIALOGADD, (WPARAM)_hSelf);
}
}
My entire solution source code is hosted here:
https://github.com/madhur/Npp-Markdown-Viewer
Issue Resolution
Here is what I have done to troubleshoot the problem:
Created a Win32 project in which I create a dialog box and put exactly same source code to embed browser control in WM_INITDIALOG and it works. This Win32 project links with the same static library, which the original source code is linking with. This is the source of the sample project mail file:
https://github.com/madhur/Npp-Markdown-Viewer/blob/master/dll/example/example.c
The static library I referenced above, is nothing but it contains functions to embed browser control in HWND and to render either an URL or string. I have taken the source from here: http://www.codeguru.com/Cpp/I-N/ieprogram/article.php/c4379
This is the source of static library main file:
https://github.com/madhur/Npp-Markdown-Viewer/blob/master/dll/dll.c
If I comment this single line, the plugin works, basically it loads the dialog box without the Browser control in it:
EmbedBrowserObject(this->_hSelf);
The actual exception in Visual Studio is raised on this line:
return pStaticDlg->run_dlgProc(message, wParam, lParam);

Cannot add items to Win32 List Box Control

Backstory: I'm creating an Extension for Game Maker, a popular game development suite. An extension is a DLL that adds new functions to the built in scripting language, but is written in C or Pascal or whatever. Typically, it's used to allow games to use external libraries.
In my case, I'm adding FMOD support. This isn't relevant. What's relevant is that for debugging purposes, I am also adding a dialog that I display at runtime that shows me the internal state of my library. I need help with this window. I have literally done absolutely no raw Win32 forms programming before today (.NET WinForms 4eva), so I'm probably doing something really clueless.
Anyway. I have a listbox, and I want to add things to the list box, but when I try to add them, it fails. My code:
extern DebugDialog * debugDialog;
DebugDialog::DebugDialog(HWND owner, HINSTANCE hInst) {
this->hWnd = 0;
HWND hWnd = CreateDialogParam(hInst,
MAKEINTRESOURCE(IDD_DEBUGDIALOG),
owner,
DialogProc,
reinterpret_cast<LPARAM>(this));
ShowWindow(hWnd, SW_SHOW);
}
DebugDialog::~DebugDialog(void) {
DestroyWindow(this->getHWnd());
debugDialog = NULL;
}
BOOL CALLBACK DebugDialog::DialogProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
DebugDialog * self;
if(message == WM_INITDIALOG) {
self = reinterpret_cast<DebugDialog *>(lParam);
self->hWnd = hWnd;
SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(self));
} else {
self = reinterpret_cast<DebugDialog*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));
}
if(self) {
return self->HandleMessage(message, wParam, lParam);
} else {
return FALSE;
}
}
BOOL DebugDialog::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch(uMsg) {
case WM_INITDIALOG:
MessageBox(this->getHWnd(), "Okay!", "Debug", 0);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam)) {
case ID_CLOSE:
case IDOK:
case IDCANCEL:
delete this;
return TRUE;
default:
return FALSE;
}
return TRUE;
}
return false;
}
void DebugDialog::loadedSound(FMODGM_Sound * sound) {
HWND hwndList = GetDlgItem(this->getHWnd(), IDC_LIST);
LPARAM sound_text = (LPARAM)sound->file.c_str();
LRESULT lResult = SendMessage(hwndList, LB_ADDSTRING, NULL, sound_text);
SendMessage(hwndList, LB_SETITEMDATA, lResult, (LPARAM)sound);
}
DebugDialog is a simple class that wraps the window, and lets me manipulate it from the outside. Basically, at some other point, I do this:
debugWindow = new DebugDialog(owner, hInst);
And then as I execute and do interesting things, I do this:
FMODGM_Sound * sound = ...;
if(debugWindow) debugWindow->loadedSound(sound);
In loadedSound, I send a message to the list box saying "Hey, here's an item. Go ahead and make with the adding.", and it doesn't return an error. However, it also doesn't add anything to the box. It returns 0 each and every time I call it. According to the documentation, 0 means that it added an item, whose index is 0. However, that item doesn't exist.
I have a theory as to why it's not working. I have no control over the message pump that Game Maker runs, so if it's doing anything funky, I don't know about it, nor can I change it. That said, everything else about the dialog works, including moving it, clicking on my Close button, and drawing the marquee thing inside the listbox with the mouse.
Someone, please tell me what I'm doing horribly wrong :(
Edit: Someone asked about the FMODGM_Sound struct, so here it is:
struct FMODGM_Sound {
FMOD::Sound * sound;
std::vector<FMOD::Channel*> channels;
std::string file;
public:
FMODGM_Sound() {
sound = NULL;
}
};
Nothing particularly fancy.
Can you show a declaration of FMODGM_Sound structure and file field?
What happen if replace
LRESULT lResult = SendMessage(hwndList, LB_ADDSTRING, NULL, sound_text);
with ?
LRESULT lResult = SendMessage(hwndList, LB_ADDSTRING, NULL, "some constant text");
Does the your DLL compiled as Unicode version or multibytes version?
If it is Unicode, the sound_text should be an Unicode string. I guess the file is a std::string, so file.c_str() will return a multibytes string.
I had a very similar problem, which was solved. Basically, you have to pass it as a c-style string instead (str.c_str()). Though I am a complete newbie, after googling around how to use that, it worked.
Though the code I'm using serves an entirely different function than yours, maybe it will be a good example.
int i = res->getInt("ID");
std::string str = boost::lexical_cast<std::string>(i);
char *cstr = new char[10];
strcpy_s(cstr, 10, str.c_str());
SendDlgItemMessage(hwnd, IDC_lbList, LB_ADDSTRING, 0, (LPARAM)cstr);
EDIT: Wow, I did not even look at the dates. I'm a necromancer...