I have an Single-document Interface(SDI) Microsoft Foundation Class(MFC) app that has to load a big document file (takes about 2 minutes). For that reason, my app stays unresponsive while I am opening the document.
However, I would like my app to be responsive while opening the document. The problem is that, if I try to load my document on a thread, my OnopenDocument function (in my doc) will return before I actually open the document:
BOOL CmodguiDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
start_tread(_open_doc); // just an example
return TRUE; // Will return before the document will actually be open
}
How can I do this to be non-locking but only returning after the document is actually open? Or how can I at least make my app responsive while I am loading the document?
Thanks
Returning after the thread has been created is OK. You should define a state for the document such as loading and loaded. When you start the thread the state should be loading. The view(s) should look at the state and display accordingly. When the thread has finished loading the document it should post a message to the document. In the handler, set the state to loaded and call UpdateAllViews() to give the views the chance to update with the new document data.
Example: This will print "loading" while the doc is loading and "loaded" and loaded after it finished in the view.
In resource.h:
#define IDD_NotifyDocumentFinished 101
In the document header:
public:
enum DocState
{
None,
Failed,
Loading,
Loaded
};
DocState GetDocState() const {return m_state;}
private:
DocState m_state;
void StartLoading();
In the document implementation:
BOOL CMFCDocViewAsyncDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if(!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
m_state = Loading;
StartLoading();
return TRUE;
}
UINT LongRunningFunction(LPVOID param)
{
Sleep(3000);
HWND hWnd = AfxGetApp()->m_pMainWnd->GetSafeHwnd();
NMHDR hdr = {hWnd, IDD_NotifyDocumentFinished, 0};
::SendMessage(hWnd, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&hdr));
return 0;
}
void CMFCDocViewAsyncDoc::StartLoading()
{
AfxBeginThread(&LongRunningFunction, nullptr);
}
BOOL CMFCDocViewAsyncDoc::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
if(HIWORD(nCode) == WM_NOTIFY)
{
WORD wCode = LOWORD(nCode);
AFX_NOTIFY * notify = reinterpret_cast<AFX_NOTIFY*>(pExtra);
if(notify->pNMHDR->idFrom == IDD_NotifyDocumentFinished)
{
m_state = Loaded;
UpdateAllViews(nullptr);
}
}
return TRUE;
}
In the view:
void CMFCDocViewAsyncView::OnDraw(CDC* pDC)
{
CMFCDocViewAsyncDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
CMFCDocViewAsyncDoc::DocState state = pDoc->GetDocState();
CString sstate;
switch(state)
{
case CMFCDocViewAsyncDoc::None:
sstate = "None";
break;
case CMFCDocViewAsyncDoc::Failed:
sstate = "Failed";
break;
case CMFCDocViewAsyncDoc::Loading:
sstate = "Loading";
break;
case CMFCDocViewAsyncDoc::Loaded:
sstate = "Loaded";
break;
}
pDC->TextOut(50, 50, sstate);
}
Update: Also look here for a similar, more detailed example http://www.codeproject.com/Articles/14706/Notifying-the-Document.
When it takes two minutes to just load something, then there are two reasons for that:
You are doing something stupid while loading, like e.g. running expensive algorithms on the data like indexing or graphical rendering. If you store the data in containers, the overhead of allocations could also become significant. If that is the case, only perform them on demand or perform these operations asynchronously in the background. You can notify the main thread (the one running the UI) when done in order to display the rendered graphics, for example.
The data is incredibly large. In that case, you will have to rethink how to access this data anyway, because any operation might be expensive. Your best bet will be to use an asynchronous model, like sending a request to the model to perform some operation. The model then sends a response to the UI to display the data. In that case, you need to think about how to make this transparent to the user though. One approach is to darken the display area after a change request and only brighten it up after the response or a progress bar.
Related
The first image shows what I want to do. The second one shows what I get.
I want the status bar to show the state of the special keys CAPS LOCK, NUM LOCK and SCROLL LOCK. This image is from an old project using the CStatusBar.
This snapshot is from CMainFrame class.
This image shows how I am doing it in Visual C++ 2015 MFC and the results I get. This snapshot is from CMainFrame class, too.
My question is : Can anyone tell me how my application can intercept the change from toggle buttons CAPS LOCK and NUM LOCK then update the user interface.
I tried to use this code but it doesn't work :
ON_UPDATE_COMMAND_UI(ID_INDICATOR_STYLE, &CMainFrame::OnUpdateStyle)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_FOLD, &CMainFrame::OnUpdateFold)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_OVR, &CMainFrame::OnUpdateInsert)
Thanks in advance.
First of all the standard behavior of CStatusBar is not supported by CMFCRibbonStatusBar.
The good news is that it is very easy to implement the same behavior.
Here is what you have to do in order to implement it in your application:
Add this to message map of your main frame class or child frame in case of MDI:
ON_UPDATE_COMMAND_UI(ID_INDICATOR_CAPS, &CMainFrame::OnUpdateButtonsState)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_NUM, &CMainFrame::OnUpdateButtonsState)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_SCRL, &CMainFrame::OnUpdateButtonsState)
The actual update handler should look like this:
void CMainFrame::OnUpdateButtonsState(CCmdUI* pCmdUI)
{
UINT nVK;
UINT flag = 0x0001;
switch (pCmdUI->m_nID)
{
case ID_INDICATOR_CAPS:
nVK = VK_CAPITAL;
break;
case ID_INDICATOR_NUM:
nVK = VK_NUMLOCK;
break;
case ID_INDICATOR_SCRL:
nVK = VK_SCROLL;
break;
case ID_INDICATOR_KANA:
nVK = VK_KANA;
break;
default:
TRACE(traceAppMsg, 0, "Warning: OnUpdateKeyIndicator - unknown indicator 0x%04X.\n",
pCmdUI->m_nID);
pCmdUI->ContinueRouting();
return; // not for us
}
pCmdUI->SetCheck(::GetKeyState(nVK) & flag);
}
I solved it by moving the code to CChildFrame class.
ON_UPDATE_COMMAND_UI(ID_INDICATOR_CAPS, &CChildFrame::OnUpdateIndicators)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_NUM, &CChildFrame::OnUpdateIndicators)
ON_UPDATE_COMMAND_UI(ID_INDICATOR_SCRL, &CChildFrame::OnUpdateIndicators)
And then
void CChildFrame::OnUpdateIndicators(CCmdUI *pCmdUI)
{
pCmdUI->Enable();
// ... the rest of the above code
CString text;
if (::GetKeyState(nVK) & flag)
text.LoadStringW(pCmdUI->m_nID);
pCmdUI->SetText(text);
}
I have a Silverlight 5.0 application and prvoide MS Word Automation functionality where the user can edit/add new document. I have gone through to MSDN pages but couldn't find any event that MS Word triggers after saving the document. The only event that talks about saving is the "DocumentBeforeSave" event that dosen't helps. I need to know when the MS Word finishes saving the document so that is it ready to save to server.
Can someone help me with this?
Any idea is very much approciated.
using MSWord = Microsoft.Office.Interop.Word;
namespace ConsoleApplication4
{
class Program
{
static void Main()
{
var app = new MSWord.Application();
var doc = app.Documents.Open(#"..\..\myDoc.docx");
app.DocumentBeforeSave += app_DocumentBeforeSave;
}
static void app_DocumentBeforeSave(MSWord.Document Doc, ref bool SaveAsUI, ref bool Cancel)
{
app.DocumentBeforeSave -= app_DocumentBeforeSave;
Cancel = true;
Doc.Save();
if(Doc.Saved){
//Now you know the document has saved
}
app.DocumentBeforeSave += app_DocumentBeforeSave;
}
}
}
The Save method doesn't run on a seperate thread, so it only returns once the Save is complete.
The DocumentBeforeSave event takes in a boolean call Cancel this is passed with the ref parameter, and setting it to true cancels the save that is about to take place.
You could set this to true, and then call Save yourself, this way you will know when the save has completed, as it runs on the same thread, something like this:
using MSWord = Microsoft.Office.Interop.Word;
namespace ConsoleApplication41
{
class Program
{
static void Main()
{
var app = new MSWord.Application();
var doc = app.Documents.Open(#"..\..\myDoc.docx");
app.DocumentBeforeSave += app_DocumentBeforeSave;
}
static void app_DocumentBeforeSave(MSWord.Document Doc, ref bool SaveAsUI, ref bool Cancel)
{
Cancel = true;
Doc.Save();
//Now you know the document has saved
}
}
}
I'd like to know more about how this system works, specifically when and how the framework actually decides to update a UI element.
My application has a 'tools' system where a single tool can be active at a time. I used the "ON_UPDATE_COMMAND_UI" message to 'check' the tool's icon/button in the UI, which affected both the application menu and the toolbars. Anyway, this was all working great until some point in the last couple of days, when the toolbar icons stopped getting highlighted properly.
I investigated a little and found that the update command was only being received when the icon was actually clicked. What's strange is this is only affecting the toolbars, not the menu, which is still working fine. Even when the buttons in the menu are updated the toolbar icon stays the same.
Obviously I've done something to break it - any ideas?
EDIT:
Never mind. I'd overwritten the Application's OnIdle() method and hadn't called the original base class method - that is, CWinApp::OnIdle() - which I guess is where the update gets called most of the time. This code snippet from https://msdn.microsoft.com/en-us/library/3e077sxt.aspx illustrates:
BOOL CMyApp::OnIdle(LONG lCount)
{
// CWinApp's original method is involved in the update message handling!
// Removing this call will break things
BOOL bMore = CWinApp::OnIdle(lCount);
if (lCount == 0)
{
TRACE(_T("App idle for short period of time\n"));
bMore = TRUE;
}
// ... do work
return bMore;
// return TRUE as long as there are any more idle tasks
}
Here's a good article that kinda explains how to do it. Don't use his code example with WM_KICKIDLE though, instead scroll down to the comments section. There are two code samples that explain how to do it better. I quote:
//Override WM_INITMENUPOPUP
void CDialog::OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu)
{
CDialog::OnInitMenuPopup(pPopupMenu, nIndex, bSysMenu);
// TODO: Add your message handler code here
if(pPopupMenu &&
!bSysMenu)
{
CCmdUI CmdUI;
CmdUI.m_nIndexMax = pPopupMenu->GetMenuItemCount();
for(UINT i = 0; i < CmdUI.m_nIndexMax; i++)
{
CmdUI.m_nIndex = i;
CmdUI.m_nID = pPopupMenu->GetMenuItemID(i);
CmdUI.m_pMenu = pPopupMenu;
// There are two options:
// Option 1. All handlers are in dialog
CmdUI.DoUpdate(this, FALSE);
// Option 2. There are handlers in dialog and controls
/*
CmdUI.DoUpdate( this, FALSE );
// If dialog handler doesn't change state route update
// request to child controls. The last DoUpdate will
// disable menu item with no handler
if( FALSE == CmdUI.m_bEnableChanged )
CmdUI.DoUpdate( m_pControl_1, FALSE );
...
if( FALSE == CmdUI.m_bEnableChanged )
CmdUI.DoUpdate( m_pControl_Last, TRUE );
*/
}
}
}
See if this helps - http://msdn.microsoft.com/en-us/library/essk9ab2(v=vs.80).aspx
I am trying to create a ID3D10RasterizerState with direct3D10, and then call
ID3D10Device::RSSetState()
with the proper information. However, whenever the window get rescaled, or when the app goes fullscreen, the rasterizerstate seems to reset to the default state.
I have tried to set the state with WM_SIZE messages, but awkwardly, nothing seems to happen...
It works properly when I call RSSetState() every frame, but that seems highly inefficient.
Does anyone know a solution to this?
It seems to be poorly documented on msdn.
Code:
bool TestGameApp::InitGame()
{
D3D10_RASTERIZER_DESC desc;
desc.AntialiasedLineEnable = TRUE;
desc.CullMode = D3D10_CULL_NONE;
desc.DepthBias = 0;
desc.DepthBiasClamp = 0.0f;
desc.FillMode = D3D10_FILL_SOLID;
desc.FrontCounterClockwise = false;
desc.MultisampleEnable = true;
desc.ScissorEnable = FALSE;
desc.SlopeScaledDepthBias = 0.0f;
m_pD3DDevice->CreateRasterizerState(&desc,m_pRSState);
m_pD3DDevice->RSSetState(m_pRSState);
//...more code
}
WndProc:
switch( message )
{
case WM_SIZE:
{
m_pD3DDevice->RSSetState(m_pRSState);
break;
}
}
Just set it every frame. In general you want to minimize the number of render state changes in a frame but you don't need to worry about the performance impact of setting the rasterizer state once a frame. Setting it every frame also lets you do things like enable and disable wireframe rendering for debugging.
I'm trying to write some code that discards all keyboard and mouse events when enabled on Mac OSX 10.6. My code runs as the root user. The approach I'm taking is to create an event tap that discards all events passed to it (while enabled). The event tap callback function looks like this:
CGEventRef MyTapCallback(CGEventTapProxy proxy,
CGEventType type,
CGEventRef event,
void *refcon)
{
return CKeyLocker::isEnabled() ? NULL : event;
}
And the code I'm using to enable and disable the event tap looks like this:
void CKeyLocker::enable(bool bEnable)
{
if (bEnable == m_bEnabled)
return;
if (bEnable)
{
// which events are we interested in?
CGEventMask evMask = kCGEventMaskForAllEvents;
CFMachPortRef mp = CGEventTapCreate(kCGHIDEventTap,
kCGHeadInsertEventTap,
kCGEventTapOptionDefault,
evMask,
MyTapCallback,
NULL);
if (mp)
{
qDebug() << "Tap created and active. mp =" << mp;
m_enabledTap = mp;
m_bEnabled = true;
}
}
else
{
CGEventTapEnable(m_enabledTap, false);
CFRelease(m_enabledTap);
m_enabledTap =0;
m_bEnabled = false;
qDebug() << "Tap destroyed and inactive";
}
}
This approach works very well while the event tap is active - I can hammer on the keyboard and mouse as much as I want and no events make it through the system. However, when the tap is disabled all the keys I pushed while the tap was active appear in the current window. It's like the event tap is just delaying the events, rather than destroying them, which is odd, since the Mac documentation clearly states:
If the event tap is an active filter, your callback function should return one of the following:
The (possibly modified) event that is passed in. This event is passed back to the event system.
A newly-constructed event. After the new event has been passed back to the event system, the new event will be released along with the original event.
NULL if the event passed in is to be deleted.
I'm returning NULL, but the event doesn't seem to be deleted. Any ideas?
The linked comment does not have an answer from what I see, so I'll dump some info from what I've seen when poking around with this stuff.
First, I have much better luck with CGEventTapCreateForPSN. It's as if the system gives you some leeway for restricting your tap. However, from this example it looks like this is not sufficient.
Next up - and this /may/ be all you need... In your call back, you probably want (and may need) to check for the following events:
switch (type)
{
case kCGEventTapDisabledByTimeout:
case kCGEventTapDisabledByUserInput:
{
CFMachPortRef *pTap = (CFMachPortRef*)refcon;
CGEventTapEnable( *pTap, true );
return NULL;
}
default:
break;
}
Regardless of what the various documentation does or doesn't say, it's been my observation that the OS feels like it's 'probing' for bad callbacks; basically disabling event tap callbacks that are universally eating events. If you re-register in these cases the OS seems to be ok with it, as if saying: OK, you seem to know what you're doing, but I'll probably poke you again in a bit to make sure.
It's really strange, we use event taps for the same purpose (input blocking in a given scenario) and works perfectly 10.4 - 10.8.2. excpet one thing, it should not block or receive events from a password dialog (which is not a big surprise)
What I can see now is different compared to you sample is:
we use kCGTailAppendEventTap instead of kCGHeadInsertEventTap (this should not matter)
we do some event logging in the installed callback
we have some user event data in some self injected events, that filtered out, but apart from this we simply return NULL to drop an unwanted event (like you do), I can confirm, not all events are ignorable!
we turn on/off the event tap this way:
bool SetInputFilter(bool bOn)
{
bool result = false;
CFRunLoopRef runLoopRef = CFRunLoopGetMain();
if (bOn) {
// Create an event tap.
CGEventMask eventMask = kCGEventMaskForAllEvents;
if ((m_eventTapInput = CGEventTapCreate(kCGHIDEventTap,
kCGTailAppendEventTap,
kCGEventTapOptionDefault,
eventMask, CGInputEventCallback, this)) == NULL) {
Log(L"Failed to create event tap");
return result;
}
// Create a run loop source.
m_runLoopEventTapSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapInput, 0);
CFRelease(m_eventTapInput); // CFMachPortCreateRunLoopSource retains m_eventTapInput
if (m_runLoopEventTapSource == NULL) {
Log(L"Failed to create run loop source for event tap");
return result;
}
// Add to the current run loop.
CFRunLoopAddSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
CFRelease(m_runLoopEventTapSource); // CFRunLoopAddSource retains m_runLoopEventTapSource
result = true;
}
else {
// Disable the event tap.
if (m_eventTapInput)
CGEventTapEnable(m_eventTapInput, false);
// Remove our run loop source from the current run loop.
if (runLoopRef && m_runLoopEventTapSource) {
CFRunLoopRemoveSource(runLoopRef, m_runLoopEventTapSource, kCFRunLoopCommonModes);//kCFRunLoopDefaultMode);
m_runLoopEventTapSource = NULL; // removing m_runLoopEventTapSource releases last reference of m_runLoopEventTapSource too
m_eventTapInput = NULL; // removing m_runLoopEventTapSource releases last reference of m_eventTapInput too
}
}
return result;
}
I can verify that returning NULL does effectively delete some events, but i have also seen times when it does not, exactly how it decides what deletions to permit is unclear but it looks like mass deletions seem to be prevented e.g.: when you delete more than 100 events or so in a row.