I'm new to shell programming and having trouble getting the filepath (or really, any information) about which item is being clicked on in a window (desktop or otherwise). I'm following the general path laid out by the answer to Can i use Global System Hooks to capture which file was clicked on? but I'm not having any luck.
The clicking is the smaller issue here, so I've just substituted random values (where I know the desktop is and where a file should be located) for the mouse position. (Regardless, it doesn't work even when I'm trying this out on my mouse's current position).
LVHITTESTINFO hitTest = { 0 };
hitTest.pt.x = 55;
hitTest.pt.y = 230;
hitTest.flags = LVHT_ONITEM;
currWindow = WindowFromPoint(pt);
int index = ListView_HitTest(currWindow, &hitTest);
//cout << index + " index";
//cout << hitTest.iItem + " iltem ";
if (index != -1) {
//char* itemText = new char[256];
std::vector<wchar_t> itemText(1024);
ListView_GetItemText(window, index, 0, &itemText[0], 256);
PIDLIST_ABSOLUTE filepidl;
SFGAOF out;
std::wstring strtext = std::wstring(itemText.begin(), itemText.end());
//cout << " ";
//cout << *(strtext.c_str()) + " ";
HRESULT parse = SHParseDisplayName(strtext.c_str(), NULL, &filepidl, SFGAO_CANDELETE, &out);
if (filepidl != NULL) {
LPTSTR filePath = new TCHAR[MAX_PATH];
BOOL getPath = SHGetPathFromIDList(filepidl, filePath);
cout << *filePath ;
}
}
This is part of my code. I think there's something wrong with how I'm getting the index of the file because it keeps returning 0 but I've been hacking at this for days and am stuck. The MSDN documentation is confusing to me at best.
Any help or insight would be appreciated! I can't find any example code of this online. Thanks!
Using the listview directly like this is not a good idea because Explorer is free to implement the shell view in any way it wants and in Windows 7 and later a Explorer window no longer uses a listview, it uses a custom control by default!
If you only care about the display name and invoking the default action you can use UI Automation, it should work on other types of windows/controls as well, not just a shell file list.
If you need to know the full path and other details you can use the IShellWindows interface. Examples can be found on Raymond Chens blog here and here...
Related
We have created a program that monitors domain/url info of the Firefox browser, so every few seconds
this program will grab the browser address bar information using UI Automation (similar to here
Get active Tab URL in FireFox with C++). When this
happens, it causes a performance slowdown for the user when that person attempts interaction with
Firefox, such as typing in characters in a control field, switching between tabs (if multiple tabs
open), etc. Using Task Manager Resource Monitor, I can see sustained "plateau spikes" in CPU usage
when the UI Automation "fires" (attempts to grab the URL info). So my question: Is this slow down
in performance likely just caused by the UI Automation iterating thru the browser
page, or is there some sort of conflict with web page interaction (by the user) and the UI Automation
(running within the monitor program)?
Example UI Auto code:
QString firefoxqstr = "Search with Google or enter address";
if(title.contains("mozilla firefox", Qt::CaseInsensitive))
{
addressBarIdentifier = (wchar_t*)firefoxqstr.utf16();
//addressBarIdentifier = (LPWSTR)(L"Search with Google or enter address");
getBrowserDomain(3,topWindow, addressBarIdentifier, title, domain, errqstr);
}
void getBrowserDomain(int xtrytype, HWND topWindow, LPWSTR addressBarIdentifier, QString title, QString& domain, QString& errqstr)
{
errqstr = "blank";
// It's a Non IE browser. Get the URL using addressBarIdentifier
// main UIAutomation interface
IUIAutomation *uiauto;
// initial value assigned to UIAutomation
uiauto = NULL;
// Initialing COM functionality
// My approach uses UI Automation
CoInitialize(NULL);
HRESULT hr =
CoCreateInstance(__uuidof(CUIAutomation),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IUIAutomation),
(void**)&uiauto);
// Normally, this shouldn't happen but just in case
if(FAILED(hr))
{
errqstr = "ERROR UIAutomation init failed";
qWarning() << "Get Window Domain: Cannot initialize UIAutomation.";
if (uiauto != NULL) uiauto->Release();
CoUninitialize();
return;
}
// Convert handle to UIAutomation Element to access address bar.
IUIAutomationElement *windElem;
hr = uiauto->ElementFromHandle(topWindow, &windElem);
if(FAILED(hr))
{
errqstr = "ERROR Cannot access Window in focus";
qWarning() << "Get Window Domain: Cannot access Window in focus.";
if (windElem != NULL) windElem->Release();
uiauto->Release();
CoUninitialize();
return;
}
VARIANT idVar;
IUIAutomationCondition *cond;
IUIAutomationCondition *cond1;
IUIAutomationCondition *cond2;
// First we store identifier in a VARIANT
// This is required for finding the Address Bar Component
// Create condition to access Address Bar
if (xtrytype == 3) //SearchFireFoxCase
{
idVar.vt = VT_I4;
idVar.lVal = UIA_EditControlTypeId;
// Create condition to make sure it's an edit box we're talking about (specially with firefox)
hr = uiauto->CreatePropertyCondition(UIA_ControlTypePropertyId,
idVar,
&cond);
//find the top editboxes
CComPtr<IUIAutomationElementArray> editboxes;
//if(FAILED(windElem->FindAll(TreeScope_Children, cond, &editboxes)) || !editboxes)
if(FAILED(windElem->FindAll(TreeScope_Descendants, cond, &editboxes)) || !editboxes) //use Descendants instead of Children
return;
int editboxes_count = 0;
editboxes->get_Length(&editboxes_count);
domain = QString::number(editboxes_count);
for(int icnt = 0; icnt < editboxes_count; icnt++)
{
//domain = domain + "icnt:" + QString::number(icnt);
CComPtr<IUIAutomationElement> editbox;
if(FAILED(editboxes->GetElement(icnt, &editbox)) || !editbox)
continue;
VARIANT urlVar;
if(FAILED(editbox->GetCurrentPropertyValue(UIA_ValueValuePropertyId, &urlVar)))
{
continue;
}
else
{
// convert URL from BSTR to QString
domain = QString::fromStdWString(std::wstring(
urlVar.bstrVal, SysStringLen(urlVar.bstrVal)));
icnt = editboxes_count; //terminate for loop
}
}
// Cleanup
cond->Release();
windElem->Release();
uiauto->Release(); // Cleanup code for UIAutomation
CoUninitialize(); // Uninitialize COM
return;
}
}
It seems that if the matching of the address bar string (i.e "Search with Google or enter address") fails, then the continued search of controls within the HTML can be lengthy (depending on the web page). So the solution is to abandon the UI Automation search after several iterations, as supposedly the address bar edit box should be very early within the search. In this case Firefox was using the Bing search engine instead of Google, so the correct address bar string to match should have been "Search with Bing or enter address"
I am new to C++ gui design and I am not too familiar with using pointers. Recently I have run into a few problems when trying to get an OpenCV Mat Image to display in a PictureBox in a constructed gui. I have searching online and even found a very similar post to my question but when trying to implement the guidance I ran into an exception when trying to operate the gui.
A very similar post
Displaying webcam feed in cv::Mat format in a picturebox
Code I grabbed from this post to save you a click:
void DrawCVImage(System::Windows::Forms::Control^ control, cv::Mat& colorImage)
{
System::Drawing::Graphics^ graphics = control->CreateGraphics();
System::IntPtr ptr(colorImage.ptr());
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(colorImage.cols,colorImage.rows,colorImage.step,System::Drawing::Imaging::PixelFormat::Format24bppRgb,ptr);
System::Drawing::RectangleF rect(0,0,control->Width,control->Height);
graphics->DrawImage(b,rect);
delete graphics;
}
Now I am trying to display a "video" feed (really an array of cv::Mat Objects) but I am having a
"Source Not Available"/System.ArgumentException: 'Parameter is not valid.'
screen come up when I attempt to call the function that houses that playback. I also know specific line of code that throws the issue is the line
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(colorImage.cols,colorImage.rows,colorImage.step,System::Drawing::Imaging::PixelFormat::Format24bppRgb,ptr);
Now for specifics, the form has an event (currently the click on the picture box, but I want to move to a "play" button in the future) and on this event, the code:
private: System::Void leftEyeImage_Click(System::Object^ sender, System::EventArgs^ e) {
std::cout << "Click Received" << std::endl;
cv::Mat& frame = cv::imread("Desktop/testcapture.png");
std::cout << "Import successful" << std::endl;
drawLeftEye(this, frame);
}
This code does get execute on click and starts the function drawLeftEye. The function is a modified version of the code from the other post and is below:
This is on the ShowResults.cpp file
namespace DevProject {
void ShowResults::drawLeftEye(System::Windows::Forms::Control^ control, cv::Mat& framebmp) {
System::Drawing::Graphics^ graphics = control->CreateGraphics();
std::cout << "Now Here" << std::endl;
System::IntPtr ptr(framebmp.data);
// Issue line
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(framebmp.cols, framebmp.rows, framebmp.step, System::Drawing::Imaging::PixelFormat::Format32bppRgb, ptr);
std::cout << "Converted successfully" << std::endl;
System::Drawing::RectangleF rect(0, 0, control->Width, control->Height); //No issue
graphics->DrawImage(b, rect);
std::cout << "Now Here before delete" << std::endl;
delete graphics;
//delete b;
}
}
I know based on my cout statements that I do make it into my function and I know my code compiles and runs through the function if I comment out the line:
System::Drawing::Bitmap^ b = gcnew System::Drawing::Bitmap(framebmp.cols, framebmp.rows, framebmp.step, System::Drawing::Imaging::PixelFormat::Format32bppRgb, ptr);
//As well as (but only because b is defined in the line above^
graphics->DrawImage(b, rect);
I am not quite sure how to fix this, my friend mentioned this could be a memory issue but I don't know how I would go about fixing it. It is also very possible I have made a simple mistake in the design of this or where my pointers are going and I am just not competent enough to know the error.
By the way the pointer for "this" that is called in the function is linked to the Windows Autogenerated code (from gui construction) of:
this->leftEyeImage->Anchor = System::Windows::Forms::AnchorStyles::None;
this->leftEyeImage->BorderStyle = System::Windows::Forms::BorderStyle::FixedSingle;
this->leftEyeImage->Location = System::Drawing::Point(11, 836);
this->leftEyeImage->Name = L"leftEyeImage";
this->leftEyeImage->Size = System::Drawing::Size(991, 646);
this->leftEyeImage->TabIndex = 4;
this->leftEyeImage->TabStop = false;
this->leftEyeImage->Click += gcnew System::EventHandler(this, &ShowResults::leftEyeImage_Click);
Any and all advice or tests would be very appreciated as I am both interested in the answer and the reasoning. Thanks in advance!
When trying to retrieve a leaderboard from Xbox Live, the stat event type get_leaderboard_complete returns the error code 404. I'm using Xbox Live in a UWP game in the Creators Program.
I'm am able to set and retrieve the stat for the user. This part works without issue:
xbox_live_result<stat_value> serverStat = m_statsManager->get_stat(m_user, L"score");
auto result = serverStat.payload();
if (result.as_integer() < score) {
setStatForUser(m_user, L"score", score);
}
My code is adopted from the leaderboard example in the Xbox Live Samples. So to retrieve my leaderboard I'm calling getLeaderboard(m_user, L"score"); and every frame I'm calling statsManager->do_work();.
// Process events from the stats manager
// This should be called each frame update
auto statsEvents = m_statsManager->do_work();
std::wstring text;
for (const auto& evt : statsEvents)
{
switch (evt.event_type())
{
case stat_event_type::local_user_added:
text = L"local_user_added";
break;
case stat_event_type::local_user_removed:
text = L"local_user_removed";
break;
case stat_event_type::stat_update_complete:
text = L"stat_update_complete";
break;
case stat_event_type::get_leaderboard_complete:
text = L"get_leaderboard_complete";
auto getLeaderboardCompleteArgs = std::dynamic_pointer_cast<leaderboard_result_event_args>(evt.event_args());
processLeaderboards(evt.local_user(), getLeaderboardCompleteArgs->result());
break;
}
stringstream_t source;
source << _T("StatsManager event: ");
source << text;
source << _T(".");
log("%S", source.str().c_str());
}
Because I'm able to set and retrieve the stat without issue, I wonder if maybe it's an issue with the Xbox Live backend? However, I'm not very familiar with the xbox live 2017 data platform, and I may be calling something incorrectly.
I discovered a solution:
Create a new stat/leaderboard in Dev Center.
Press the “Test” button. This is important because it publishes the service configuration.
I’m unsure why the original stat didn’t work. Perhaps because I used the word “score” as the statID.
I just can't understand what i did. How it works? It works just by half, but there is identical pieces of code(almost).
I have client-server application. It sends any requests, and getting response, either list, that i will transform to vector, or string that contains HTML code. So, I will try to explain, but you are welcome to ask as much questions as you want.
1) I request file from server, by sending command "2" and path. Here is code
void Connection::requestFile(string path)
{
//string cookedPath = "./" + path;
string reply = this->sendCommand("2\n" + path);
vector<string> response = this->divideString(reply);
// set as files list we got. First is a helper, so we will not add it to files list.
if (response[0] == "directories") {
// remove "directories" entry so it will not be listed then
response.erase(response.begin());
this->files = response;
this->displayHtml = false;
} else {
//else server sent string with html
this->html = reply;
this->displayHtml = true;
}
}
displayHtml here is kinda switch that will help to determine what to do.
So in this method i've used sendCommand() method, which by my opinion is origin of all troubles.
Here it comes
string Connection::sendCommand(std::string command)
{
// send command
zmq::message_t request(command.length());
memcpy (request.data(), command.c_str(), command.length());
Connection::socket.send(request);
// get reply
zmq::message_t reply;
Connection::socket.recv(&reply);
// make string out of reply
std::string rpl = std::string(static_cast<char*>(reply.data()), reply.size());
return rpl;
}
Sorry about this formatting.
Then i use these methods here
void MainWindow::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
{
// set our current folder to ./folder/ + selected file
connection->currentPath = connection->currentPath + item->text().toStdString();
// update file list. as argument we give path we just got
// std::cout << connection->currentPath << endl;
ui->listWidget->clear();
connection->requestFile(connection->currentPath);
if (connection->displayHtml == true) {
// webview->updateHtml(connection->html);
cout << "html";
} else {
this->updateFilesList(connection->files);
cout << "fileslist";
}
}
This method will be called when i double click on item in Qt widget list.
Say i've double clicked on item. It partially works fine, but it isnt prints it.
But if i close application, it prints.
So i guess problem is in blocking on 'recv' zmq function, but why it works for a half then? Maybe rewrite it anyhow? Thanks.
I am trying to make my webcam Win32 application save more than one photo at a time i.e. save one photo then another then another etc.
To do this, I am appending a string to an integer variable so that each new photo name can be unique and conform to the format of the second argument of CreateBMPFile. This would usually be a case of writing TEXT("string literal"), but I need to keep modifying the filename as each new photo is created.
PBITMAPINFO pbi = CreateBitmapInfoStruct(hwnd, hbm);
int i = 1;
std::string image = "..\\..\\..\\..\\WebCam\\frame" + std::to_string(i) + ".bmp";
while (!exists(image)) {
LPTSTR filename = (LPTSTR)image.c_str();
CreateBMPFile(hwnd, filename, pbi, hbm, hdcMem);
i++;
}
This compiles and executes, however, when I click the "Grab Frame" button and it attempts to save it, the application crashes i.e. I cannot see the GUI anymore and it becomes a stagnant process.
I am using an exists() function to see whether the file exists in the system:
inline bool exists(const std::string& name) {
struct stat buffer;
return (stat(name.c_str(), &buffer) == 0);
}
I've also tried using sprintf_s() with the same result of crashing the application.