OnDrop: how to determine which mouse button is being used? - c++

I'm trying to implement a "drag and drop" functionality into a program (for Windows, written in C++, with MFC).
A class is derived from COleDropTarget, it has the OnDragOver and OnDrop functions overridden.
In OnDragOver, a parameter dwKeyState tells me which mouse button is being used to "drag", and what "interesting" keys are pressed on the keyboard.
In OnDrop, this parameter is not passed (neither is it in OnDropEx).
Now, the question is: if I need to know this information "at droptime", how do I determine it?
The only way I found out is to store it in a static variable in OnDragOver and then retrieve it in OnDrop.
But I'm not satisfied (i.e. I can't believe I'm doing it the right way) because:
Is it guaranteed that the last OnDragOver reflects the final
situation at the "drop" time? (for the mouse button: if it's
possible that it may change, like if the user presses a button,
starts dragging, then presses the other button, releases the first
one, and then "drops"... don't know. But for the keys, the situation
is surely able to change when the user presses or releases Ctrl, Alt
and Shift while dragging)
If the answer is "yes":
If I have to remember information from OnDragOver anyway, why does
the system pass all the other data (pWnd, pDataObject, dropEffect,
point) again to OnDrop? What's the point?
Why does the documentation say that "typically, the application
overrides OnDropEx in the view class to handle right mouse-button
drag and drop"? If OnDropEx can't even tell which mouse button has
just been used?!?
If the answer to 1. is "no": so, what's the correct way to
determine the mouse button just used and the pressed keys?
Thanks for any help...
Functions prototypes:
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropDefault, DROPEFFECT dropList, CPoint point);
Documentation.

Here is how the thing appear to actually work (at least, with the MFC impementation found in Visual C++ version 10, alias 2010).
How the user IInspectable remarked, "The IDropTarget::Drop interface passes a grfKeyState argument. [...] the MFC implementation of COleDropTarget decided to drop this argument."
Well, it's possible to debug the part where the implementation receives this parameters (and doesn't pass it to OnDrop): it's in the source C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\oledrop2.cpp (or similar path).
The relevant part is in the function:
STDMETHODIMP COleDropTarget::XDropTarget::Drop(THIS_ LPDATAOBJECT lpDataObject, DWORD dwKeyState, POINTL pt, LPDWORD pdwEffect)
A quick debug shows that dwKeyState is actually passed, but (here) it has already lost the information about the mouse button.
Another fact: this COleDropTarget::XDropTarget::Drop calls still once COleDropTarget::OnDragover (that, this time, will receive the last update about the status of Ctrl, Alt and Shift keys, but no info about the mouse button), and then COleDropTarget::OnDropEx or COleDropTarget::OnDrop (if the -Ex has not been overridden).
So it's unimportant to try to access dwKeyState from COleDropTarget::OnDrop: the only way to determine the mouse button at droptime is to remember it from a previous COleDropTarget::OnDragover.
The info about mouse button will come from the last-but-one call (if not even an earlier one?), so it has to be stored separately (storing just the last dwKeyState and using it at droptime would not work).
That's what I understood. If an expert can confirm, or wants to point out mistakes, (s)he's welcome.

Related

How to eliminate the MessageBeep from the RICHEDIT control?

The RichEdit control has this very annoying feature. It beeps every time the user tries to move the cursor past its "end point". For instance, you can test it with the WordPad that also implements RICHEDIT. Open it up, type in some text, then hit the Home key. If the cursor was not in the beginning of the line:
hitting Home key will move it there, but then hitting the Home key again will produce this beep.
At first glance it seemed like overriding WM_KEYDOWN and WM_KEYUP messages and blocking the situations when RICHEDIT can produce that beep was a solution ... until I actually started implementing it. Unfortunately though, it's not as simple as it sounds, as that control beeps in a LOT OF CASES! So my keystroke blocking code literally ballooned to over 300+ lines, and I still see that there are some key-presses that I either didn't account for, or worse, I might have overridden some useful behavior with. (Read below for more details.)
Then I decided to look inside the implementation of the RICHEDIT control itself. And sure enough, for instance if we look at the implementation of the Home key press, the C:\WINDOWS\SysWOW64\msftedit.dll on my Windows 10 OS, has the function called ?Home#CTxtSelection##QAEHHH#Z (or public: int __thiscall CTxtSelection::Home(int,int) demangled) at the mapped offset 0x3FC00, that is hard-coded to call the MessageBeep(MB_OK), or exactly what I'm trying to eliminate:
And if you look at the address 0x6B64FD38 in the screenshot above, there's a built-in way to bypass it, with what looks to be flag 0x800.
So having dug into msftedit.dll a little bit more, there appears to be a function called ?OnAllowBeep#CTxtEdit##QAEJH#Z (or public: long __thiscall CTxtEdit::OnAllowBeep(int) demangled) that can modify this flags:
After a bit more research I found out that there are COM interfaces built into RICHEDIT control, such as ITextServices and ITextHost that reference that flag as TXTBIT_ALLOWBEEP in ITextServices::OnTxPropertyBitsChange method.
Unfortunately though, I can't seem to find the way how I can directly change that TXTBIT_ALLOWBEEP flag (COM is not my forte.) I tried looking into implementing ITextHost, but it has a lot of virtual methods that have nothing to do with what I'm trying to achieve that I don't know how to implement.
Does anyone have any idea how to clear that TXTBIT_ALLOWBEEP flag?
PS. Here's why I didn't go the route of overriding key-presses:
Just to give you an example. Say, if I override the VK_HOME key press. I need to make sure that the cursor is not at the beginning of the line, but also that there's no selection. Yet, I need to make sure that Ctrl key is not down in a situation when the cursor is at the very top of the window. Then the same with the Shift key, and I'm not even sure what Alt does with it ... and so forth. Oh, and this is just the Home key. There's also Up, Down, Left, Right, PageUp, PageDown, End, Delete, Backspace. (And that's what I was aware of. There may be more, plus I'm not even talking about IME or other keyboard layouts, etc.) In other words, it becomes a mess!
So, eventually I realized that anticipating a keystroke is not the way to go.
first we need send EM_GETOLEINTERFACE message to rich edit window - this is Retrieves an IRichEditOle object that a client can use to access a rich edit control's Component Object Model (COM) functionality.
then for retrieve an ITextServices pointer, call QueryInterface on the private IUnknown pointer returned by EM_GETOLEINTERFACE.
here exist interesting point - the IID_ITextServices not well known but need get in runtime from Msftedit.dll
from About Windowless Rich Edit Controls
Msftedit.dll exports an interface identifier (IID) called IID_ITextServices that you can use to query the IUnknown pointer for the ITextServices interface.
after we got ITextServices pointer - we simply can call OnTxPropertyBitsChange with TXTBIT_ALLOWBEEP mask
code example:
#include <textserv.h>
if (HMODULE hmodRichEdit = LoadLibrary(L"Msftedit.dll"))
{
// create richedit window
if (HWND hwndRich = CreateWindowExW(0, MSFTEDIT_CLASS, ...))
{
if (IID* pIID_ITS = (IID*) GetProcAddress(hmodRichEdit, "IID_ITextServices"))
{
IUnknown* pUnk;
if (SendMessageW(hwndRich, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk))
{
ITextServices* pTxtSrv;
HRESULT hr = pUnk->QueryInterface(*pIID_ITS, (void**)&pTxtSrv);
pUnk->Release();
if (0 <= hr)
{
pTxtSrv->OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0);
pTxtSrv->Release();
}
}
}
}
}

Simulate keyboard input inside app in C++ MFC app

I created a dialog MainDialog.cpp with 2 edit controls whose IDs are IDC_EDITCONTROL_A and IDC_EDITCONTROL_B, and have variables defined as m_editControlA and m_editControlB, respectively.
Also, I have 2 buttons whose IDs are IDC_MFCBUTTON_KEY_X and IDC_MFCBUTTON_KEY_Y, and variables are m_buttonKeyX and m_buttonKeyY, respectively.
Below is the code in the source file
#include "afxdialogex.h"
IMPLEMENT_DYNAMIC(CMainDialog, CDialogEx)
CMainDialog::CMainDialog(CWnd* pParent): CDialogEx(IDD_MAIN_DIALOG, pParent)
{
}
CMainDialog::~CMainDialog()
{
}
void CMainDialog::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_EDITCONTROL_A, m_editControlA);
DDX_Control(pDX, IDC_EDITCONTROL_B, m_editControlB);
DDX(Control(pDX, IDC_MFCBUTTON_KEY_X, m_buttonKeyX);
DDX(Control(pDX, IDC_MFCBUTTON_KEY_Y, m_buttonKeyY);
}
BEGIN_MESSAGE_MAP(CMainDialog, CDialogEx)
ON_EN_CHANGE(IDC_EDITCONTROL, &CMainDialog::OnEnChangeEditA)
ON_BN_CLICKED(IDC_MFCBUTTON_KEY_X, &CMainDialog::OnBnClickedButtonX)
ON_BN_CLICKED(IDC_MFCBUTTON_KEY_Y, &CMainDialog::OnBnClickedButtonY)
END_MESSAGE_MAP()
void CMainDialog::OnBnClickedButtonX()
{
m_editControlA.SetWindowTextW(_T("X")); // test
}
void CMainDialog::OnBnClickedButtonX()
{
m_editControlA.SetWindowTextW(_T("Y")); // test
}
I am trying to understand how I can have each button send their respective character (i.e. X or Y in this example) to the selected edit control if one is selected. Essentially, I would like to simulate keyboard input.
I have read the docs about how to simulate keyboard events and also the sendMessage but I could not understand how to implement it since my C++ knowledge is very basic. Also, following my previous question I have found that the GetFocus would be useful but still my main issue currently is sending the input.
Any example code or useful link could be very useful for me to learn how I can simulate a keyboard input inside an app.
The characters are sent from the OS to the edit controls using the WM_CHAR message.
In reality it is a bit more complex than that, but you do not need to emulate the entire WM_KEYUP WM_KEYDOWN message sequence, since its end result is to generate a WM_CHAR message.
You can use CWnd::PostMessage to send characters directly to your edit controls, even when they do not have the focus.
You have probably already found the documentation for WM_CHAR here: https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms646276(v=vs.85).aspx
oops.. excuse my french, ths english doc is here
https://msdn.microsoft.com/en-us/library/windows/desktop/ms646276(v=vs.85).aspx
(just changing the fr-fr to en-us does the trick, it probably works for all other languages, neat!
wParam holds the character you want to send. Either an plain ASCII character, or one of the VK_ constants... I suggest you use the unicode version WM_CHARW, as most windows software uses unicode nowadays. The notation for wide chars is either L'X' or _T('X'), the unicode (UTF-16) character type is wchar_t.
lParam contains other keystroke details, 0 should be fine for what you want to do.
to send X, simply call
m_editControlA.PostMessage(WM_CHAR, _T('X'));
When using the _T() notation, the character (or string) literal between the parenthesis will be automatically converted to the right character width for your app's unicode setting (you should set that to UNICODE, since that's what the OS is using, and is also the only valid encoding for Windows CE, for example, and you should get used to manipulating this type.
the _T() macros and _t* overrides for almost all C library functions operating on strings are defined in tchar.h, which is included by Visual Studio in stdafx.h. Under MFC, you'll mostly use CString, but it's good to know where these things are.
[EDIT] When you get that running, you should start playing with WM_KEYDOWN. You will discover that PostMessage(WM_CHAR, VK_ESCAPE) directly to your dialog does not close it, while a PostMessage(WM_KEYDOWN, VK_ESCAPE) does. And that m_editBox.PostMessage(WM_KEYDOWN, _T('X')) will send a lower key 'x' to your edit box. But that's another topic to ivestigate.
Have fun with MFC!
For your last question:
Sure, but it gets a bit more complicated, as your button will gain focus, as soon as you click on it. You'd have to create handlers for EN_SETFOCUS for eeach of your edit boxes, and add a CWnd* data member to keep track of the last edit box that had focus.
Your EN_SETFOCUS handlers should look something like this
void CdlgDlg::OnEnSetfocusEdit1()
{
m_pWndLastFocus = &m_edit1;
}
Don't forget to set the pointer to NULL in your constructor and to chjeck it's valid before calling m_pWndLastFocus->PostMessage() though.
The way to synthesize input in MFC is by using the SendInput method.

MFC Control "CVSListBox" - How to use the interface "AddButton"?

Is there anybody could tech me how to use the interface "AddButton" of the MFC control? "CVSListBox"? I wrote the below code, and the new buttons have showed on the control successful, but I can't respond its ON_BN_CLICKED event. Could you tell me why? thanks.
ON_BN_CLICKED(IDC_BTN_AWSPORTIMPORT, &CPgTestAwsPortfolio::OnBnClickedBtnAwsportimport)
void CPgTestAwsPortfolio::OnBnClickedBtnAwsportimport()
{
int xx = 100;
}
CPortCaseListBox m_lbAwsPortCases;
m_pgTestAwsPort.m_lbAwsPortCases.AddButton(IDB_AFXBARRES_NEW, _T("Import"), 0, 0, IDC_BTN_AWSPORTIMPORT);
The idea is different here.
All Buttons are handled internally in the CVSListBox class.
See CVSListBoxBaseBase::OnCommand override.
When a button sends a WM_COMMAND it is intercepted by CVSListBoxBaseBase::OnCommand
When the id is member of the internal button list of the ist Control the virtual function OnClickButton is executed.
GetButtonID might help you to convert the Position into the ID.
Note OnClickButton receives the number of the button, not the id.
So the parent never receives any notification of this Buttons. It is all handled in the virtual functions of CVSListBox.
The documentation is incomplete because the Base class isn't described and document.

What is the difference between ON_WM_CTLCOLOR_REFLECT() and ON_WM_CTLCOLOR() in VS2008?

The question is like this:
I write a windows application in VS2005, building success. Then I transfer it to VS2008, when I build the program, there will be an error messsage shows:
error C4867: 'CGroupBox::CtlColor': function call missing argument list; use '&CGroupBox::CtlColor' to create a pointer to member
the message map is as follows:
BEGIN_MESSAGE_MAP(CGroupBox, CButton)
//{{AFX_MSG_MAP(CGroupBox)
ON_WM_PAINT()
ON_WM_CTLCOLOR_REFLECT()
//ON_WM_CTLCOLOR()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
the function is is as follows:
HBRUSH CGroupBox::CtlColor(CDC* pDC, UINT nCtlColor)
{
GetParent()->Invalidate();
return NULL;
}
but if I change to the ON_WM_CTLCOLOR() ,I can get a successful build.
The error message is because the signature of the message handler doesn't match the signature you're using. Look up the signatures for ON_WM_CTLCOLOR_REFLECT() and ON_WM_CTLCOLOR() on MSDN to get the correct ones.
As to the question in your title, some controls normally send their notification messages to their parent. Which is inconvenient, because every time you re-use that control, you need to change its parent window. Therefore MFC has 'reflection' support, which is basically a way of saying 'when you receive a notification from a child control, first try bouncing it back to that control to see if that control knows how to deal with it itself.'
Read the details on https://web.archive.org/web/20101229015404/http://msdn.microsoft.com/en-us/library/eeah46xd(v=vs.80).aspx .
I just had the same problem, and as you can see from the link Roel provided, the original poster DID have the correct signature.
Turns out this is a bug in MFC itself. Find ON_WM_CTLCOLOR_REFLECT in afxmsg_.h and you'll notice that CtlColor is missing the "&" before it. I guess the older compilers didn't mind. Add the "&" to make it "&CtlColor" and the error is fixed.
Alternatively, if you don't like changing the official headers, you could #under ON_WM_CTLCOLOR_REFLECT in your own code and define it again properly.
I notice many other (maybe all?) reflected messages in that file have the same problem too.

Creating HyperLink in Notepad(textEdit)[MFC]

I am building a textEdit application with MFC. Is there a way to create a hyperlink automatically when a user write web address? It's like when you write a web address "www.google.com" the application detects web address and create a hyperlink right away. I have searched documents that explains about this, but couldn't find it..
and i couldn't make it..
i already have made notepad but i couldn't add the function of hyperlink on the notepad.
the following sentences are functions of hyperlink.
Clicking the text needs to open a browser window to the location specified by the text.
The cursor needs to change from the standard arrow cursor to a pointing index finger when it moves over the control.
The text in the control needs to be underlined when the cursor moves over the control.
A hyperlink control needs to display text in a different color—black just won't do.
The features that I added are:
5.A hyperlink control once visited needs to change color.
6.The hyperlink control should be accessible from the keyboard.
7.It should install some kind of hooks to allow the programmer to perform some actions when the control has the focus or when the cursor is hovering over the control.
Among the functions, What I mostly want to complete is the first one.
If I click a Hyperlink text, it should be linked to a browser window on the Internet.
Please answer and help me. Thanks.
Just use a CRichEditCtrl control (remember to call AfxInitRichEdit2 in your InitInstance). Call SetAutoURLDetect. Done.
Unfortunately this is not enough to make it work. It will display text that resembles URL as blue underlined but it will not invoke the link.
This will have to be handled by additional code. This will set needed event mask:
long lMask = m_RichEditCtrl.GetEventMask();
m_RichEditCtrl.SetEventMask(lMask | ENM_LINK);
m_RichEditCtrl.SetAutoURLDetect();
Also reflected EN_LINK will has to be handled to follow the link. For example:
void CHyperLinkInEditView::OnEnLink(NMHDR *pNMHDR, LRESULT *pResult)
{
ENLINK *p_Link = reinterpret_cast<ENLINK *>(pNMHDR);
if(p_Link && p_Link->msg == WM_LBUTTONDOWN)
{
//int iRange = m_RichEditCtrl.GetTextRange(p_enLinkInfo->chrg.cpMin, p_enLinkInfo->chrg.cpMax);
m_RichEditCtrl.SetSel(p_Link->chrg);
CString szLinkString = m_RichEditCtrl.GetSelText ();
ShellExecute(m_hWnd, L"Open", szLinkString, NULL, NULL, SW_MAXIMIZE);
}
*pResult = 0;
}
All of the above will solve requirement 1, 2, 3 (partially –text is underlined always), and 4.
I do not quite understand 5, 6 and 7.
Could you elaborate?