Create DataObject from Shell Run / Help (MFC C++) - mfc

I need help to create an IDataObject to enable drag and drop of these 2 Items (Run and Help)
For Example I need to do just like Windows Startmenu does.
to run them I use these
CComPtr<IShellDispatch2> pShellDisp;
if (SUCCEEDED(CoCreateInstance(CLSID_Shell,NULL,CLSCTX_SERVER,IID_IShellDispatch2,(void**)&pShellDisp)))
{
pShellDisp->Help(); //Help
pShellDisp->FileRun(); //Run
}
Can you guys help me out ?
PS: I need the drag with image Icons too
EDIT [SOLVED]
IShellFolder* desk = NULL;
HRESULT hr =SHGetDesktopFolder(&desk);
LPITEMIDLIST pidl2=NULL;
ULONG cbEaten;
DWORD dwAttribs = 0 ;
hr = desk->ParseDisplayName(NULL,
NULL,
L"shell:::{2559a1f3-21d7-11d4-bdaf-00c04f60b9f0}",
&cbEaten, // This can be NULL
&pidl2,
&dwAttribs);
hr = desk->GetUIObjectOf(parentHwnd, 1,
(PCITEMID_CHILD*)&pidl2, IID_IDataObject, 0, (LPVOID *)lpdataObj);
desk->Release();
return;
for run:
shell:::{2559a1f3-21d7-11d4-bdaf-00c04f60b9f0}

I assume you only want one of those items in the data object at any point in time, in that case:
If you already have a IShellItem then you can call IShellItem::BindToHandler(...,BHID_DataObject,...).
If you only have the pidl you can use SHCreateDataObject or CIDLData_CreateFromIDArray
If you want to drag&drop both items in the same operation then things get hard. I don't know if both of those objects have the same parent. If they do then even the old CIDLData_CreateFromIDArray can handle it. If they don't then you could try SHCreateShellItemArrayFromIDLists and then use IShellItemArray::BindToHandler(...,BHID_DataObject,...). To support < Vista I believe you have to create your own CFSTR_SHELLIDLIST and add it to the dataobject.
Drag images are not really related to this and should be asked in a separate question where you include information about your IDragSourceHelper etc.

Related

Name of process for active window in Windows 8/10

The following sample has reliably returned the name of the process that is associated with the active window, but does not work with the newer modern/universal apps because it returns the name of a helper process WWAHost.exe on Windows 8 and ApplicationFrameHost.exe on Windows 10 rather than the name of the app.
HWND active_window = GetForegroundWindow();
GetWindowThreadProcessId(active_window, &active_process_id);
HANDLE active_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, active_process_id);
GetProcessImageFileName(active_process, image_name, 512);
With Windows 10 the ApplicationFrameHost.exe is the process that creates the window handles and is what gets returned by GetWindowThreadProcessId(), is there another Win32 API that can be used to get the active process of universal app that is active?
Also tried using GetApplicationUserModelId() and GetPackageFullName() with no success as they return APPMODEL_ERROR_NO_APPLICATION and APPMODEL_ERROR_NO_PACKAGE respectively because the active_process handle is just the helper process and not the process of the active application.
Any other APIs to use to get the process name of a Modern/Universal application given the hwnd of the window, or otherwise figure out the process name of the universal app is active.
Thanks in advance!
Be sure to use the Spy++ utility when you want to reverse-engineer something like this. Included with Visual Studio, you need the 64-bit version in Common7\Tools\spyxx_amd64.exe. Use Search > Find Window and drag the bullseye to a UWP app, like Weather.
You'll see the window you'll find with GetForegroundWindow(), it has at least 3 child windows:
ApplicationFrameTitleBarWindow
ApplicationFrameInputSinkWindow
Windows.Core.UI.CoreWindow, that's the host window for the UWP app and the one you are interested in. Right-click it and select Properties, Process tab, click the Process ID. That takes you to the real owner process you want to know.
So you just need to make an extra step from the code you already have, you just have to enumerate the child windows and look for one with a different owner process. Some C code, trying to make it as universal as possible without making too many assumptions and not enough error checking:
#include <stdio.h>
#include <Windows.h>
typedef struct {
DWORD ownerpid;
DWORD childpid;
} windowinfo;
BOOL CALLBACK EnumChildWindowsCallback(HWND hWnd, LPARAM lp) {
windowinfo* info = (windowinfo*)lp;
DWORD pid = 0;
GetWindowThreadProcessId(hWnd, &pid);
if (pid != info->ownerpid) info->childpid = pid;
return TRUE;
}
int main()
{
Sleep(2000);
HWND active_window = GetForegroundWindow();
windowinfo info = { 0 };
GetWindowThreadProcessId(active_window, &info.ownerpid);
info.childpid = info.ownerpid;
EnumChildWindows(active_window, EnumChildWindowsCallback, (LPARAM)&info);
HANDLE active_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, info.childpid);
WCHAR image_name[MAX_PATH] = { 0 };
DWORD bufsize = MAX_PATH;
QueryFullProcessImageName(active_process, 0, image_name, &bufsize);
wprintf(L"%s\n", image_name);
CloseHandle(active_process);
return 0;
}
Output on the Weather program:
C:\Program Files\WindowsApps\Microsoft.BingWeather_4.5.168.0_x86__8wekyb3d8bbwe\
Microsoft.Msn.Weather.exe
Here is a small console app application that continuously (so you can test it easily selecting different windows on your desktop) display information about the current foreground window process and store process, if any.
Apps can have a window hierarchy that can span multiple processes. What I do here is search the first sub window that has the 'Windows.UI.Core.CoreWindow' class name.
This app uses the UIAutomation API (and also smart pointers, smart BSTRs and smart VARIANTs provided by the #import directive). I suppose you can do the same with standard Windows SDK, but I find the UIAutomation used this way quite elegant.
#include "stdafx.h"
#import "UIAutomationCore.dll"
using namespace UIAutomationClient;
int main()
{
// initialize COM, needed for UIA
CoInitialize(NULL);
// initialize main UIA class
IUIAutomationPtr pUIA(__uuidof(CUIAutomation));
do
{
// get the Automation element for the foreground window
IUIAutomationElementPtr foregroundWindow = pUIA->ElementFromHandle(GetForegroundWindow());
wprintf(L"pid:%i\n", foregroundWindow->CurrentProcessId);
// prepare a [class name = 'Windows.UI.Core.CoreWindow'] condition
_variant_t prop = L"Windows.UI.Core.CoreWindow";
IUIAutomationConditionPtr condition = pUIA->CreatePropertyCondition(UIA_ClassNamePropertyId, prop);
// get the first element (window hopefully) that satisfies this condition
IUIAutomationElementPtr coreWindow = foregroundWindow->FindFirst(TreeScope::TreeScope_Children, condition);
if (coreWindow)
{
// get the process id property for that window
wprintf(L"store pid:%i\n", coreWindow->CurrentProcessId);
}
Sleep(1000);
} while (TRUE);
cleanup:
CoUninitialize();
return 0;
}
Does taking snapshot of running processes and pulling the name out of it by comparing process id not work?
Full reference here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms686837(v=vs.85).aspx
But you fix the snapshot with CreateToolhelp32Snapshot().
Or from WTSEnumerateProcesses() and surrounding API?
Pretty sure it worked in Win 8. Is it broken in 10?
Starting from Win10 Anniversary Update ApplicationFrameHost child window return anything but UWP application. It worked only in Tablet mode after relogon.

Hide A Console C++ Program From Taskbar

I have a little console game that calls another console app. Something like Winamp's many windows (main and playlist). The thing is when I call two for example console windows the programs opened in the taskbar get too many, I don't need to open the windows separately, I want only the main window to stay in the taskbar and when I click on it, it and all its child apps to pop up.
P.S. I am familiar with ShowWindow ( GetConsoleWindow(), SW_HIDE );, but it hides the window as well and I want it to be hidden only from the taskbar.
Thanks to Captain Obvlious and some research, the following code:
ITaskbarList *pTaskList = NULL;
HRESULT initRet = CoInitialize(NULL);
HRESULT createRet = CoCreateInstance( CLSID_TaskbarList,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskbarList,
(LPVOID*)&pTaskList );
if(createRet == S_OK)
{
pTaskList->DeleteTab(GetConsoleWindow());
pTaskList->Release();
}
CoUninitialize();
with included ShObjIdl.h works great!
Note: You should get S_OK as a value in initRet and createRet!
The only way I am aware of to accomplish this on a console window is to use the shell interface ITaskbarList.
hr = CoCreateInstance(
CLSID_TaskbarList,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskbarList,
reinterpret_cast<void**>(&taskbar));
if(!FAILED(hr))
{
// Remove the icon from the task bar
taskbar->DeleteTab(GetConsoleWindow());
// Release it
taskbar->Release();
}

IWebBrowser2: how to force links to open in new window?

The MSDN documentation on WebBrowser Customization explains how to prevent new windows from being opened and how to cancel navigation. In my case, my application is hosting an IWebBrowser2 but I don't want the user to navigate to new pages within my app. Instead, I'd like to open all links in a new IE window. The desired behavior is: user clicks a link, and a new window opens with that URL.
A similar question was asked and answered here and rather than pollute that answered post, it was suggested I open a new discussion.
The members on the related post suggested I should be able to do this by trapping DISPID_BEFORENAVIGATE2, setting the cancel flag, and writing code to open a new window, but I've found out that the browser control gets lots of BeforeNavigate2 events that seem to be initiated by scripts on the main page. For example, amazon.com fires BeforeNavigate2 events like crazy, and they are not a result of link invocation.
Replies appreciated!
What I ended up doing was using IHTMLDocument directly rather than IWebBrowser. IWebBrowser is a superset of IHTMLDocument, and the navigation model implemented by IWebBrowser isn't customizable to the degree I wanted.
I actually got MS Developer Support involved and this approach was their recommendation. They say this is what Outlook uses for HTML-based email, which is the user experience I wanted to emulate. They also confirmed that there's no reliable way to filter the OnBeforeNavigate events that result from user action from those that result from script activity.
Hope this helps anybody facing the same issues. It wasn't too hard to port the code to use IHTMLDocument. If you end up doing this, you may also find yourself looking for a way to figure out when the document is done loading. To do that, hook HTMLDocumentEvents instead of DWebBrowserEvents, and look for the DISPID_HTMLDOCUMENTEVENTS_ONREADYSTATECHANGE event. It doesn't tell you what the ready state is; you need to call IHTMLDocument::get_readyState and parse the resulting string. Goofy, but there you go.
You can bind to onclick event before document is complete while creating browser in OnCreate() using IHTMLDocument2::put_onclick():
#include <comutil.h>
ClickEvents<RootFrame> clickEvents;
_variant_t clickDispatch;
clickDispatch.vt = VT_DISPATCH;
clickDispatch.pdispVal = &clickEvents;
CComQIPtr<IDispatch> dispatch;
hr = webBrowser2->get_Document(&dispatch);
ASSERT_EXIT(SUCCEEDED(hr), "webBrowser->get_Document(&dispatch)");
CComQIPtr<IHTMLDocument2> htmlDocument2;
hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");
htmlDocument2->put_onclick(clickDispatch);
ClickEvents class implements IDispatch, you only need to implement Invoke method, in rest return E_NOTIMPL:
HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
HRESULT hr;
CComQIPtr<IWebBrowser2> webBrowser2;
hr = rootFrame->GetDlgControl(rootFrame->rootview.GetDlgCtrlID(), IID_IWebBrowser2, (void**) &webBrowser2);
ASSERT_EXIT(SUCCEEDED(hr), "rootframe->GetDlgControl(IID_IWebBrowser2) failed");
CComQIPtr<IDispatch> dispatch;
hr = webBrowser2->get_Document(&dispatch);
ASSERT_EXIT(SUCCEEDED(hr), "webBrowser2->get_Document(&dispatch)");
CComQIPtr<IHTMLDocument2> htmlDocument2;
hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");
CComQIPtr<IHTMLWindow2> htmlWindow2;
hr = htmlDocument2->get_parentWindow((IHTMLWindow2**) &htmlWindow2);
ASSERT_EXIT(SUCCEEDED(hr), "htmlDocument2->get_parentWindow(&htmlWindow2)");
CComQIPtr<IHTMLEventObj> htmlEvent;
hr = htmlWindow2->get_event(&htmlEvent);
ASSERT_EXIT(SUCCEEDED(hr), "htmlWindow2->get_event(&htmlEvent)");
CComQIPtr<IHTMLElement> htmlElement;
hr = htmlEvent->get_srcElement(&htmlElement);
ASSERT_EXIT(SUCCEEDED(hr), "htmlEvent->get_srcElement(&htmlElement)");
CComBSTR hrefAttr(L"href");
VARIANT attrValue;
VariantInit(&attrValue);
hr = htmlElement->getAttribute(hrefAttr, 0 | 2, &attrValue); // 0 = case insensitive, 2 = return BSTR
ASSERT_EXIT(SUCCEEDED(hr), "htmlElement->getAttribute()");
wchar_t href[2084]; // maximum url length in IE, http://support.microsoft.com/kb/208427
wcsncpy_s(href, _countof(href), attrValue.bstrVal, _TRUNCATE);
if (!rootFrame->IsURLAllowed(href)) {
VARIANT variant;
variant.vt = VT_BOOL;
variant.boolVal = VARIANT_FALSE;
htmlEvent->put_returnValue(variant);
ShellExecute(0, L"open", href, 0, 0, SW_SHOWNORMAL);
}
return S_OK;
}
As you can see after querying some interfaces I finally have the element that got clicked, then I call IsURLAllowed() defined in my root frame to check whether to allow opening url in current webbrowser window or whether to open it using default browser on user's computer.
This handles all links even if they were appended to document using javascript.
The same should be done with "onsubmit" events for forms.
I also think I have a solution for "window.location" redirects in javascript, I haven't tested it yet, but I will soon test it and I will update this answer then. You could use a combination of "onunload" and "onbeforeunload" events along with DWebBrowserEvents2::BeforeNavigate2(), after onunload/onbeforeunload are called you will know that user is leaving current page so now in BeforeNavigate2() you can cancel it. You can attach unload events using IHTMLWindow2::put_onunload() and IHTMLWindow2::put_onbeforeunload().
See sources of a complete solution for the "onclick" below.
AttachClickEvents in BrowserFrame:
http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/browser_frame.h?r=709d00b991b5#125
Invoke in ClickEvents(IDispatch):
http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/click_events.h?r=a5b0b350c933#132
I'm hypothesising here but yet another approach could be to maintain a count of navigation events, incrementing the counter on DISPID_BEFORENAVIGATE2 and decrementing it on occurrences of DISPID_NAVIGATECOMPLETE2 and DISPID_NAVIGATEERROR. With that in place, you could speculate that whenever you get DISPID_BEFORENAVIGATE2 and your counter is at zero, it is actual user navigation / link invocation.
I have no idea whether this approach would work, or whether those are the right events you'd need to make it work, but it could be worth investigating.
You could try a different approach instead and physically add the attribute target="_blank"
to all <a> tags in the rendered document.
This approach would involve waiting for DISPID_DOCUMENTCOMPLETE and then using IHTMLDocument3::getElementsByTagName() to fetch all of the anchor elements. You would then use IHTMLElement::setAttribute() to set target="_blank" on each of them.
It seems to me, that it you want "to open all links in a new IE window", it means that you want that the opening of new windows must be done in another process. The easiest way to do so: using CreateObject("InternetExplorer.Application") way (see another question which solve a problem, which is opposite to your question: InternetExplorer.Application object and cookie container). With this way you will receive the best isolation from your application and the user who clicks on the link receive all possibilities which exist in IE. You should of cause continue usage of BeforeNavigate2 events to find out the moment when "a new IE window" should be opened.

What is the "Shell Namespace" way to create a new folder?

Obviously this is trivial to do with win32 api - CreateDirectory(). But I'm trying to host an IShellView, and would like to do this the most shell-oriented way. I would have thought that there would be a createobject or createfolder or some such from an IShellFolder. But neither IShellView nor IShellFolder nor even IFolderView seem to have anything quite like this.
Is there a Shell-programming way to create a new folder? Or do I need to create a folder using a pathname, the old-fashioned way?
If I have to do it via CreateDirectory(), then my next question might be: any ideas as to how to get the IShellView / IFolderView to actually see this new object and display it to the user?
Motivation: Creating my own File Dialog replacement and I want to provide the "new folder" toolbar icon functionality of the standard XP-style file dialog.
EDIT: I went ahead and created something that basically works, using CreateDirectory. However, I'm still hoping that there's a better way to do this, but so that you can see how that works, and to offer better ideas as to solve this issue better:
PidlUtils::Pidl pidl(m_folder);
CFilename folderName(GetDisplayNameOf(pidl), "New Folder");
for (int i = 2; folderName.Exists(); ++i)
folderName.SetFullName(FString("New Folder (%d)", i));
if (!CPathname::Create(folderName, false))
throw CContextException("Unable to create a new folder here: ");
// get the PIDL for the newly created folder
PidlUtils::Pidl pidlNew;
#ifdef UNICODE
const wchar_t * wszName = folderName.c_str();
#else
wchar_t wszName[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, folderName.GetFullName(), -1, wszName, MAX_PATH);
#endif
m_hresult = m_folder->ParseDisplayName(NULL, NULL, wszName, NULL, pidlNew, NULL);
if (FAILED(m_hresult))
throw CLabeledException(FString("Unable to get the PIDL for the new folder: 0x%X", m_hresult));
// upgrade our interface so we can select & rename it
CComQIPtr<IShellView2> sv2(m_shell_view);
if (!sv2)
throw CLabeledException("Unable to obtain the IShellView2 we need to rename the newly created folder.");
// force it to see thew new folder
sv2->Refresh();
// select the new folder, and begin the rename process
m_hresult = sv2->SelectAndPositionItem(pidlNew, SVSI_EDIT|SVSI_DESELECTOTHERS|SVSI_ENSUREVISIBLE|SVSI_POSITIONITEM, NULL);
if (FAILED(m_hresult))
throw CLabeledException(FString("Unable to select and position the new folder item: 0x%X", m_hresult));
Yes, you can get IContextMenu and look for sub menus, but why bother, just call SHChangeNotify after you call CreateDirectory
Shell folders usually implement the IStorage interface, so this is pretty simple. For example, the following creates a folder named "abcd" on the desktop:
CComPtr<IShellFolder> pDesktop;
HRESULT hr = SHGetDesktopFolder(&pDesktop);
if (FAILED(hr)) return;
CComQIPtr<IStorage> pStorage(pDesktop);
if (!pStorage) return;
CComPtr<IStorage> dummy;
hr = pStorage->CreateStorage(L"abcd", STGM_FAILIFTHERE, 0, 0, &dummy);
The Win32 API function CreateDirectory seems to be the "correct way". At least, I can find nothing better - and the answers here don't really shed any light on a better way to do it.

QueryInterface method for the IID_IPersistStreamInit quit working

I have an application that I've been using to parse data from an HTML document. The application has been working for a few years until this week when the QueryInterface method for the IID_IPersistStreamInit started failing. The call to QueryInterface is returning -2147467262 which fails the SUCCEEDED(hr) test. Any ideas why this quit working?
Thanks,
Wade
if (!myIE->IsValid())
return;
HRESULT hr;
LPDISPATCH lpDispatch = NULL;
LPOLECOMMANDTARGET lpOleCommandTarget = NULL;
LPPERSISTSTREAM lpPersistStream = NULL;
lpDispatch = myIE->GetHtmlDocument();
ASSERT(lpDispatch);
if (lpDispatch == NULL)
AfxMessageBox("Couldn't get IHTMLDocument2 interface!");
else
{
hr = lpDispatch->QueryInterface(IID_IPersistStreamInit, (void**) &lpPersistStream);
if (SUCCEEDED(hr) && lpPersistStream != NULL)
At what point are you executing the code above? In case it's not done, you should execute it only after the followings:
Navigating to about:blank to have mshtml loaded properly
Make sure the DocumentComplete event is called, meaning the navigation has completed, before you move on.
Only then is it safe to ask for the stream interface. For more, see Loading HTML content from a Stream.
Now, if all this is known and taken care of, you might pursue the solution from the other direction. The error code means "No such interface supported". I'd try finding out what is the component that contains that interface, and then re-register it. But given this is IE stuff you're dealing with, I kind of doubt it's installation got screwed.