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);
Related
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
I have an ATL/COM Outlook-AddIn that is activated when the user tries to send a mail. If the send button is clicked a DialogBox appears. There I want to set the subject of the mail in a TextBox and rewrite this to the subject if the OK Button is clicked. This should be done in the callback function of the DialogBox. Here is what I got so far:
static BSTR myGlobalVar;
void __stdcall OnItemSend(IDispatch* Item, VARIANT_BOOL* Cancel)
{
CComPtr<Outlook::_MailItem> mail;
HRESULT hr = Item->QueryInterface(__uuidof(Outlook::_MailItem), (void**)&mail); // get the mail item
if (hr == S_OK)
{
IUnknown* mapiItem;
if (mail->get_MAPIOBJECT(&mapiItem) == S_OK)
{
mail->get_Subject(&myGlobalVar); // get the current subject
DialogBox(GetModuleHandle(L"MyDllName.dll"), (LPCWSTR)IDD_DIALOG1, 0, (DLGPROC)About); // open the DialogBox
// here I want to set the subject with the value from the TextBox
mail->put_Subject(&myGlobalVar);
}
}
}
// this is the callback function of the DialogBox
static LRESULT CALLBACK About( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
switch( message )
{
case WM_INITDIALOG:
{
// I want to set the value of IDC_MYTEXT to the subject,
// but I cannot access any global variables from here
SetDlgItemText(hDlg, IDC_MYTEXT, myGlobalVar);
return TRUE;
}
case WM_COMMAND:
if (LOWORD(wParam) == IDOK)
{
TCHAR szBuf[128];
GetDlgItemText(hDlg, IDC_MYTEXT, szBuf, 128 - 1);
// here I want to set an global accessable variable
myGlobalVar = (BSTR)szBuf;
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
In the callback function I cannot access any global variables that would be available in OnItemSend as well, because I get a linker error LNK2001. What would be a good approach to achieve this? Thanks in advance!
I finally got it working by wrapping the whole dialog handling in a class and create an instance of this class in my OnItemSend function. Then I can get and set public properties from inside my function and the static callback function as well.
I am using Visual Studio C++ 2010, and I need to detect current active page of Property Sheet. I expect there should be send some message to tell when user activated the Page. But I did not found that message and where to place it in dialog process.
In the program which I work with, there is this main dialog process:
INT_PTR CALLBACK MainDlgProc(HWND hSheet, UINT msg, WPARAM wParam, LPARAM lParam)
{
INT_PTR ret = FALSE;
switch (msg)
{
// bla bla
case WM_COMMAND:
ret = 0; //processing message
if (!Sheet_HandleCommand(hSheet, HIWORD(wParam), LOWORD(wParam), (HWND)lParam))
CALLPROC();
break;
case WM_DESTROY:
// bla bla
return CALLPROC();
default:
return CALLPROC();
}
return ret;
}
I know how to get the handle but I don't know where to "install" it in the code. What's the name of the message to include it into the MainDlgProc?
Use PSM_GETTABCONTROL (or the PropSheet_GetTabControl macro) to get a handle to the tab control, and then use the TCM_GETCURSEL message (or the TabCtrl_GetCurSel macro) to query the currently selected tab index.
Note this is documented not to work if using the Aero wizard style (PSH_AEROWIZARD), presumably because there is no tab control in that case.
In one of my dialog based MFC application, I've used two dialogs of similar look. The requirement is when user populates an edit box of one dialog with some data the same to be replicated to similar edit box of another dialog instantly. I'm trying to implement it with EN_CHANGE event of the edit control; where when any change is detected application post a message with updated data to other dialog to update the content of its own edit box. The problem is when the second dialog is setting its edit box content with the received data from first dialog, EN_CHANGE event is getting triggered from the second dialog, which is obvious, resulting in an endless back and forth message exchange. Could anybody please suggest me some solution for instant replicating user inputs between edit boxes of two MFC dialogs while keeping MFC application type as dialog based?
In my implementation both the Dialogs are CDialog derived and have the following CEdit event handler and Message handler methods:
For CScreen1 class:
void CScreen1::OnEnChangeEditUser()
{
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen2,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen2 is the HWND of 2nd dlg
}
LRESULT CScreen1::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
delete msg;
break;
}
return 0;
}
For CScreen2 class:
void CScreen2::OnEnChangeEditUser()
{
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen1,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen1 is the HWND of 1st dlg
}
LRESULT CScreen2::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
delete msg;
break;
}
return 0;
}
Simply use a boolean variable to play around. I have updated your code here.
For CScreen1 class:
BOOL postchanges = TRUE; //always TRUE
void CScreen1::OnEnChangeEditUser()
{
if (!postchanges)
return;
static CString msg;
m_username.GetWindowText(msg);
::PostMessage(m_mScreen2,WM_INTER_LOGIN,10,(LPARAM)&msg); //m_mScreen2 is the HWND of 2nd dlg
}
LRESULT CScreen1::OnInterLoginMsg(WPARAM wParam, LPARAM lParam)
{
CString *msg=(CString*)lParam;
switch((int)wParam)
{
case 10:
postchanges = FALSE; // do not post msg
m_username.SetWindowText(msg->GetString()); //m_username is CEdit Ctrl
postchanges = TRUE; // revert back
delete msg;
break;
}
return 0;
}
For CScreen2 class: do the same
As the requirement is replicating user input between two Edit controls of different Dialogs; it can be handled through processing keystroke messages.
I am wondering how to catch the fact that a view (CView in a CMDIChildWnd frame) has been resized, and that the user just released the left mouse button.
None of OnSize, OnSizing and OnLButtonUp work.
I have tried the solution in http://www.codeguru.com/forum/showthread.php?t=59476 and it doesn't work.
I am working on VC2010 with W7.
Thanks in advance.
Try WM_NCLBUTTONUP. I don't know if a view can be resized other than by the mouse, but if it can you probably also want to respond to WM_EXITSIZEMOVE as in the link you gave.
I recently needed to do this. I used a combination of OnSysMessage to capture the SC_SIZE and SC_MOVE event and WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE within the CMDIChildWnd derived class. Within the OnSysMessage handler I set a public variable for IsSizing. I can then check the variable within the OnEnterSizeMove function and act accordingly. Within the OnExitSizeMove function, it resets the variable to FALSE.
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_WM_SYSCOMMAND()
ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
And for the handlers:
void CChildFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
switch(nID&0xfff0)
{
case SC_SIZE:
m_bIsSizing = TRUE;
break;
case SC_MOVE:
m_bIsSizing = FALSE;
break;
}
CMDIChildWnd::OnSysCommand(nID, lParam);
}
LRESULT CChildFrame::OnEnterSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnEnterSizeMove\n");
}
return 0; //don't flag we processed the message
}
LRESULT CChildFrame::OnExitSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnExitSizeMove\n");
m_bIsSizing = FALSE; // set to false before calling OnSizing
CRect dlgRect;
pView->GetClientRect(dlgRect);
pView->InvalidateRect(NULL, FALSE);
pView->SendMessage(WM_SIZE, WPARAM(SIZE_RESTORED), MAKELONG(dlgRect.Width(), dlgRect.Height()));
}
return 0; //don't flag we processed the message
}
Then within your View code, you can check if the Frame is sizing within your OnSize handler.
void CMyView::OnSize(UINT nType, int cx, int cy)
{
CChildFrame* pFrame=(CChildFrame*)GetParentFrame();
if(!pFrame->m_bIsSizing) {
CWaitCursor cur;
//DO UPDATE CODE
}
}