|
Okay, here is a long-winded post, so bear with me.
I am in midst of writing a real-time data monitoring program using Visual C++ .NET (originally started out in VC++ 6 - only upgraded in the past couple months).
Anyway, the program includes a plot view showing points representing data collected at a given time. The plot view is a heavily modified version of Mark C. Malburg's OScope/Strip Chart control cribbed from this very site (http://www.codeproject.com/miscctrl/oscope.asp).
One of the major modifications is the ability to "pause/play", "rewind", and "fast-forward" the plot using a set of VCR-style button controls (with the typical "||", ">>", and "<<" type symbology) and a slider. Along with the ability to pause the plot is the ability to select and highlight the points in the plot (now THAT was fun to implement! ). As a result, I am now implementing the ability for the user to right-click on a point in the plot to view the actual data. Since it is possible that more than one "data item" can be represented by a single point in the plot (it is at one-second granularity), I need the ability to dynamically generate the pop-up menu, with each menu item represented as a single data item to examine.
This is what I have so far:
In the function "OnContextMenu" in the CPlotView class:
// Convert the point where the right-click occurs
// to client coordinates
::ScreenToClient (this->m_hWnd, &clClickPoint);
// Setup a vector that will contain all the
// data points found at the coordinates of the
// right-click
CStripChartCtrl::pclDataPointVec pclDataPointsVec;
// Populate the vector to contain all the
// data points found at the place where the
// user right-clicked in the plot.
m_clStripChart.vGetDataPoint (clClickPoint, pclDataPointsVec);
// Check if the vector is empty (which would
// indicate no points found were the user
// right-clicked (a blank area of the plot,
// for example).
if (pclDataPointsVec.empty () == false)
{
// code to highlight the points (removed
// for brevity sake - basically just
// passed the data vector to a vHighlight
// method of the strip chart control class)
// Dynamically setup the menu showing the
// selectable data items.
CMenu clDataMenu;
vSetupViewDataMenu (pclDataPointsVec, clDataMenu);
// Append the data sub-menu to the
// menu loaded from resource under the
// "view data" option
HMENU hDataMenu = clDataMenu.Detach ();
clPopupMenu.AppendMenu (MF_STRING | MF_POPUP | MF_ENABLED, (UINT)hDataMenu, "View Data");
}
else
{
// if no data items found, then simply add
// a greyed out "view data" option
::GetCursorPos(&clMenuPoint);
clPopupMenu.AppendMenu (MF_STRING | MF_GRAYED, 0, "View Data");
}
// Code follows to setup and then make the menu appear using the "TrackPopupMenu" method of the clPopupMenu object.
The function "vSetupViewDataMenu" appears below in its entirty. Basically, it loops through the passed in data vector and "sorts" them by event type (which is represented by letters - future enhancement is to actually give them a name, but this program is still in its very early alpha/prototype stage). It then loops through the "sort" map and builds the cascading "view data" menus, the first level by event/data type, followed by an entry for each data item. As the menu is being built, each item is put into a map, indexed by menu ID (starting with "ID_PLOT_VIEWDATA"), so that the program will know which piece of data is represted by each menu item.
//--------------------------------------
void
CPlotView::vSetupViewDataMenu (
CStripChartCtrl::pclDataPointVec &pclDataVec,
CMenu &clMenu)
//--------------------------------------
// Description:
// This private function will dynamically
// setup the pop-up menu item to allow
// the user to view the data for the
// selected point.
//--------------------------------------
{
map<char, vector <CDataPoint *> > clDataMenuMap;
map<char, vector <CDataPoint *> >::iterator clDataMenuIter;
clMenu.CreatePopupMenu ();
m_pclMenuDataMap.clear ();
// loop through the passed in data vector
for (UINT i = 0 ; i < pclDataVec.size (); i++)
{
char cLetter = pclDataVec[i]->cGetPointChar();
clDataMenuMap[cLetter].push_back(pclDataVec[i]);
} // end for loop vector
// Now, iterate through the menu map and add each of the submenus
// into the passed in menu object
UINT uiMenuID = ID_PLOT_VIEWDATA;
for (clDataMenuIter = clDataMenuMap.begin ();
clDataMenuIter != clDataMenuMap.end ();
clDataMenuIter++ )
{
CString strMenuItem;
if (clDataMenuIter->first == 0)
strMenuItem = "View EP Information";
else
strMenuItem.Format ("View Event Type -> '%c'", clDataMenuIter->first);
CMenu clSubMenu;
clSubMenu.CreatePopupMenu ();
for (UINT i = 0; i < clDataMenuIter->second.size(); i++)
{
CString strSubItem;
strSubItem.Format ("Data Item %d", (i + 1));
clSubMenu.AppendMenu (MF_STRING | MF_ENABLED, uiMenuID, (LPCTSTR)strSubItem);
m_pclMenuDataMap[uiMenuID] = clDataMenuIter->second[i];
uiMenuID++;
}
// Detach the newly created sub-menu from the CMenu class and
// then append it to the passed in menu class
HMENU hSubMenu = clSubMenu.Detach ();
clMenu.AppendMenu (MF_STRING | MF_POPUP | MF_ENABLED, (UINT)(hSubMenu), (LPCTSTR)strMenuItem);
} // end for loop map
} // end private function vSetupViewDataMenu
Since there is no message map generated for this, I was going to override the "OnCmdMsg" as shown below. Right now, I was going to have the program simply display a message box to indicate that an item was succesfully selected and found (before getting into the technicalities of the actual data retrieval - that is whole 'nother topic with this program - yeesh! This program is to process many megabytes of data per SECOND, thus all kinds of crazy caching schemes and storing to temp files and such in an effort to make it efficient in both memory and speed as possible)
This is what I currently have in the "OnCmdMsg" function:
//--------------------------------------
BOOL
CPlotView::OnCmdMsg(
UINT nID,
int nCode,
void* pExtra,
AFX_CMDHANDLERINFO* pHandlerInfo)
//--------------------------------------
// Description:
// This function handles the command messages
// that are sent when the user selects an
// item from the dynamically built view
// data menus.
//--------------------------------------
{
// TODO: Add your specialized code here and/or call the base class
if (pHandlerInfo == NULL)
{
if ( (nID >= ID_PLOT_VIEWDATA) &&
(nID < ID_PLOT_VIEWDATA +
m_pclMenuDataMap.size ()) )
{
if (nCode == CN_COMMAND)
{
CString strMessage;
strMessage.Format ("Selected Menu Item: %d", nID);
AfxMessageBox ((LPCTSTR)strMessage);
}
else if (nCode == CN_UPDATE_COMMAND_UI)
{
CCmdUI* pCmdUI = (CCmdUI*)pExtra;
pCmdUI->Enable (TRUE);
}
return TRUE;
}
}
return CFormView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
After all that, the problem is that all of the items in the sub-menus appear as greyed out. I tried several things such as using the ON_COMMAND_RANGE macro in a message map (with a block of command ID's 1000 apart, since it is very unlikely there will be that many data items represtented by one plot point in realistic usage), and no such luck. I determined it is because all those ID's are not part of the resources of this application, thus Windows does not "know" about them, since they are generated at run time. How can I get Windows to recognize those ID's at runtime, thus enabling those dynamically added menu items so the user can click on them?
When Windows won't do it, use Unix!
|
|
|
|
|
skonopa wrote:
After all that, the problem is that all of the items in the sub-menus appear as greyed out.
That is an MFC feature, menu items and toolbar buttons that don't have associated handlers are disabled. You can set the m_bAutoMenuEnable member of your main frame to FALSE to turn off this feature.
--Mike--
Personal stuff:: Ericahist | Homepage
Shareware stuff:: 1ClickPicGrabber | RightClick-Encrypt
CP stuff:: CP SearchBar v2.0.2 | C++ Forum FAQ
----
Actual sign at the laundromat I go to: "No tinting or dying."
|
|
|
|
|
Thank you very much! That seemed to do the trick! Now, just to get it to actually handle the message is another story.
Chalk another one up to good'ol Code Project! Only if you guys know how often this site has saved my @$$. It has been long quest for this old-time Unix developer to learn and develop programs under Windows.
|
|
|
|
|
Hi all,
so im writing a program that install itself as a process, to do this I need the path to process that is running, because my code installs the running process as the service.
Problem:
I cant seem to figure out a way to get the path the running process.
If I call GetCurrentDirectory, it will give me the directory from where the process was run which may not necessarily be the path to the process. furthermore it doesnt include the name of the executable in the path.
Also if I use argv[0], it will only work if I refer to the full path of the executable, which I dont always want to do!
Can someone help me find a way to find the path at which my process is running so that I can use that path to register the running process as a service?
|
|
|
|
|
|
Using MFC:
<br />
CFile file;<br />
file.Open(_T("."),CFile::modeRead);<br />
<br />
MessageBox(file.GetFilePath());<br />
<br />
file.Close();<br />
Ivan Cachicatari
www.latindevelopers.com
|
|
|
|
|
char *dir_buff = (char*)malloc(2048);
GetCurrentDirectory(2048,dir_buff);
MessageBox(0,dir_buff,dir_buff,0);
free(dir_buff);
Ivan Cachicatari
www.latindevelopers.com
|
|
|
|
|
Hi,
My application is keeping track of the coordinates of the cursor through a hook that fires a WM_MOUSEMOVE message and I get the mouse coordinates with GetCursorPos. When I later attempt to reproduce this movement with SetCursorPos, the movement is very jumpy, as very few points are recorded. Just calling GetCursorPos every 1 ms doesn't improve the resolution at all. How can I reproduce a smooth movement of the cursor at a later time?
Thanks,
Aaron
modified 12-Jul-20 21:01pm.
|
|
|
|
|
Hi,
You might want to check and make sure your hook is properly set up to receive all WM_MOUSEMOVE messages from the target. I'm not sure why you mentioned a timer, because when you have a hook properly installed, the message handler should get called automatically whenever the message comes in.
Take a look at this article: http://www.codeproject.com/atl/MouseGestures.asp[^]. It implements a hook to capture mouse messages as well as then performing calculations on the data stored from the mouse messages.
Hope that helps!
Sincerely,
Alexander Wiseman
Est melior esse quam videri
It is better to be than to seem
|
|
|
|
|
The hook seems to be fine, it catches all the other messages. But it appears, and I think I've read, that a mouse move message doesn't get sent for every fraction the mouse is moved. So I'm wondering how to make it smooth.
modified 12-Jul-20 21:01pm.
|
|
|
|
|
Why use GetCursorPos() at all, when the lParam of the WM_MOUSEMOVE message provides the same information?
Since Windows isnt an RTOS, you should not use your current tech. Instead, use the pox passed in WM_MOUSEMOVE, which should be good as long as you get enough of the W_MM messages.
Aaron Stubbendieck wrote:
But it appears, and I think I've read, that a mouse move message doesn't get sent for every fraction the mouse is moved
This may be a possibility, since windows is really designed to be functional, more than "perfect". It may have been one of those design trade-offs done to improve things in general...
Bikram Singh
|
|
|
|
|
I just changed it to use the lParam values, and the performance is identical. Its hard to tell from Spy++, but I'm guessing that there isn't a message sent for each movement. I think it can be done, cause apps like VNC product a smooth movement on the client, and I just want to do that locally. I've looked at the vnc source code and can't find anything magic that they did.
modified 12-Jul-20 21:01pm.
|
|
|
|
|
Jumpy cos no data, possible.
This is interesting, needless to say, i will try this in the next couple of days.
What is VNC? And if you have the sources for it, and it works as you desire, could i see some of it?
Bikram Singh
|
|
|
|
|
That'd be nice if you could take a look at it.
VNC stands for Virtual Network Computing and allows you control and see the screen of a remote computer. You can get the sources here[^]. When a remote user moves the mouse, the movement is smoothly reproduced on the host machine. The code in the server that set the cursor pos doesn't seem any more sophisticated than SetCursorPos.
modified 12-Jul-20 21:01pm.
|
|
|
|
|
I tried to quickly implement a basic dialog-based application which recorded mouse movement for about 2 seconds and then replayed the result. Oddly enough, the movement was smooth and exactly what I did during the recording period. Granted this wasn't through hooking, but I think it proves that one should be able to get every mouse movement, or at least a sufficient number to reproduce a smooth motion.
Here is the main code for recording and replaying ('points' is a CArray of CPoints and 'pt' is a CPoint, both defined in the class definition):
<br />
void CMouseMoveDlg::OnMouseMove(UINT nFlags, CPoint point) <br />
{<br />
if(bStarted)<br />
{<br />
GetCursorPos(&pt);<br />
points.Add(pt);<br />
}<br />
<br />
CDialog::OnMouseMove(nFlags, point);<br />
}<br />
<br />
void CMouseMoveDlg::OnReplay() <br />
{<br />
for(int h = 0; h < points.GetSize(); h++)<br />
{<br />
pt = points.GetAt(h);<br />
SetCursorPos(pt.x, pt.y);<br />
Sleep(10);<br />
}<br />
}<br />
If you would like I could have a look at your code. If you wish to send it to me (or just the relevant portions) my email address is Alexander@wisemanweb.com. I will try to implement this logic inside a hook as soon as I have a chance.
Sincerely,
Alexander Wiseman
Est melior esse quam videri
It is better to be than to seem
|
|
|
|
|
Okay wow. I was just sitting here, reading MY OWN CODE, and something hit me. Did you actually check the data stored in your data structure to see whether all of it was there, or were you just going by what it looked like? If you look at my post immediately before this, I added a call to the Sleep function in order to give a little bit of spacing between the SetCursorPos() calls. It could be that all of the data is there but you don't see smooth mouse movement because you are calling SetCursorPos too quickly. Try adding something like Sleep(10) and see if the motion is smooth. If that doesn't work, check your data structure (print it to the screen or something) and check the results against a Spy++ on the Window.
If all else fails, I can look at your code if you would care to send it to me.
Sincerely,
Alexander Wiseman
Est melior esse quam videri
It is better to be than to seem
|
|
|
|
|
Wow! that was it! Just adding a Sleep(1) call after every SetCursorPos call, made it work just fine. I guess all it needed was a moment to draw it.
Thanks!!
modified 12-Jul-20 21:01pm.
|
|
|
|
|
Cool! Glad I could help.
Sincerely,
Alexander Wiseman
Est melior esse quam videri
It is better to be than to seem
|
|
|
|
|
try using some sleeps
or threads
gabby
|
|
|
|
|
I believe its jumpy because there isn't enough data.
modified 12-Jul-20 21:01pm.
|
|
|
|
|
mmm
i think you're right
gabby
|
|
|
|
|
Hello,
This time the question if about threading... hope you'll appreciate it.
Well then, I have a worker thread being created at some point in my App. It happens that this [new] thread creates about 5 modeless dialogs which continuously output data. This thread was derived from CWinThread ; Everything fine so far...
Now, this is where it gets tricky: inside the InitInstance I initialise a class, which has no links whatsoever to a message pump nor to the main frame window. Moreover, it has to execute in a kind of a loop. Here's an example:
BOOL CClass::InitInstance()
{
CG1 theG1;
theG1.Run();
return FALSE;
}
This was my first (frustrated) attempt and I quickly learned that whenever the instruction execution reached inside theG1.Run , there were no Windows Messages being received/processed.
So I made some changes. Instead of having it run inside InitInstance , I implemented a solution with OnIdle . I don't really like it, but let's see how I did it:
CClass::OnIdle(long nCount)
{
theG1.ExecuteNext();
return TRUE;
}
Now my question:
Is is possible to make the class run in a real multi-threading environment rather than in OnIdle ? I know I would have to somehow make a connection with the thread's Message Pump... but how?
Any suggestions would be greatly appreciated,
David
PS: In case you got confused with the methods I'm using above (Run ; ExecuteNext ) , I am working on an academic project aimed at emulating a processor on a x86 machine.
|
|
|
|
|
dNimrod#X wrote:
I have a worker thread being created at some point in my App. It happens that this [new] thread creates about 5 modeless dialogs...
This raises a lot of red flags, namely that worker threads should not create UI components. If you must create a UI component in a thread other than the primary thread, it should be a UI thread (i.e., thread with a message pump). See here for more.
"When I was born I was so surprised that I didn't talk for a year and a half." - Gracie Allen
|
|
|
|
|
Hi,
I've been reading on the subject and decided to follow your cue. You're absolutely right -- there is the need to have a message pump thread.
Thanks very much for the reply. You have been very helpful indeed.
David
|
|
|
|
|
Hi,
I'm developing a database application using CDatabase & CRecordSet classes. My SQL Server database
is sitting on other machine and it's effecting application performance when I tried to insert large number of records
and retrieving the records for generating some reports(i.e. to fetch 1000000 records at a time for creating a report). Application is getting too slow.
Need some suggestions in this regard.
Thanks,
|
|
|
|
|