Implement Overlay Icon? - c++

I read this article in http://www.codeproject.com/KB/shell/overlayicon.aspx.
I have some questions that I cannot answer. Please help me?
When I build the project to COM dll. When I use other program to call this dll. Which method could I will call to display overlay icon on the selected file? I think I will call
GetOverlayInfo() first and call IsMemberOf()? Just 2 functions?
In the GetOverlayInfo(). Will I pass what value in the first parameter? the path of the overlay icon? or the path of the file which will be setted overlay icon on it? Could u give me an example?
In the below function:
STDMETHODIMP CMyOverlayIcon::GetOverlayInfo(
LPWSTR pwszIconFile,
int cchMax,int* pIndex,
DWORD* pdwFlags)
{
GetModuleFileName(_AtlBaseModule.GetModuleInstance(),pwszIconFile,cchMax);
*pIndex =0;
*pdwFlags = ISIOI_ICONFILE | ISIOI_ICONINDEX;
return S_OK;
}
Could u tell me could I pass what value to the function when I call it from outside program.
Could u give me an example how to call it and transfer value to it?
pwszIconFile
cchMax
pIndex
pdwFlags (Could I pass which value when I call it from outside. Ex C# program)

Yes, you can get away with just the two functions. But I would call GetPriority() anyway, even if you don't use the return value. You'll never know whether the overlay handler relies on that call or not (if you haven't written it yourself). And you only need to call GetOverlayInfo() once, but IsMemberOf() for every file you want to show the overlay icon for.
You have to pass an (empty) buffer and in cchMax the length of the buffer. The Overlay handler will then fill the buffer with the path of the icon file. But you also have to check the pdwFlags and pIndex (both are filled by the overlay handler too) - depending on the flags set in pdwFlags, you either get a path to an icon file in pwszIconFile or a path to an exe file which contains the icon in its resources. If the latter, then the pIndex value is the resource index of the icon in the exe file you have to load.
You didn't say why you want to call the overlay handlers yourself. If you simply want to show the icons as explorer does in some list view or dialog, you can use a much easier route where you don't need to read the registry where all the overlay handlers are registered (and some of the system overlays aren't even registered there!).
Have a look at KB192055, maybe that's a better way for what you need?

Related

How to eliminate the MessageBeep from the RICHEDIT control?

The RichEdit control has this very annoying feature. It beeps every time the user tries to move the cursor past its "end point". For instance, you can test it with the WordPad that also implements RICHEDIT. Open it up, type in some text, then hit the Home key. If the cursor was not in the beginning of the line:
hitting Home key will move it there, but then hitting the Home key again will produce this beep.
At first glance it seemed like overriding WM_KEYDOWN and WM_KEYUP messages and blocking the situations when RICHEDIT can produce that beep was a solution ... until I actually started implementing it. Unfortunately though, it's not as simple as it sounds, as that control beeps in a LOT OF CASES! So my keystroke blocking code literally ballooned to over 300+ lines, and I still see that there are some key-presses that I either didn't account for, or worse, I might have overridden some useful behavior with. (Read below for more details.)
Then I decided to look inside the implementation of the RICHEDIT control itself. And sure enough, for instance if we look at the implementation of the Home key press, the C:\WINDOWS\SysWOW64\msftedit.dll on my Windows 10 OS, has the function called ?Home#CTxtSelection##QAEHHH#Z (or public: int __thiscall CTxtSelection::Home(int,int) demangled) at the mapped offset 0x3FC00, that is hard-coded to call the MessageBeep(MB_OK), or exactly what I'm trying to eliminate:
And if you look at the address 0x6B64FD38 in the screenshot above, there's a built-in way to bypass it, with what looks to be flag 0x800.
So having dug into msftedit.dll a little bit more, there appears to be a function called ?OnAllowBeep#CTxtEdit##QAEJH#Z (or public: long __thiscall CTxtEdit::OnAllowBeep(int) demangled) that can modify this flags:
After a bit more research I found out that there are COM interfaces built into RICHEDIT control, such as ITextServices and ITextHost that reference that flag as TXTBIT_ALLOWBEEP in ITextServices::OnTxPropertyBitsChange method.
Unfortunately though, I can't seem to find the way how I can directly change that TXTBIT_ALLOWBEEP flag (COM is not my forte.) I tried looking into implementing ITextHost, but it has a lot of virtual methods that have nothing to do with what I'm trying to achieve that I don't know how to implement.
Does anyone have any idea how to clear that TXTBIT_ALLOWBEEP flag?
PS. Here's why I didn't go the route of overriding key-presses:
Just to give you an example. Say, if I override the VK_HOME key press. I need to make sure that the cursor is not at the beginning of the line, but also that there's no selection. Yet, I need to make sure that Ctrl key is not down in a situation when the cursor is at the very top of the window. Then the same with the Shift key, and I'm not even sure what Alt does with it ... and so forth. Oh, and this is just the Home key. There's also Up, Down, Left, Right, PageUp, PageDown, End, Delete, Backspace. (And that's what I was aware of. There may be more, plus I'm not even talking about IME or other keyboard layouts, etc.) In other words, it becomes a mess!
So, eventually I realized that anticipating a keystroke is not the way to go.
first we need send EM_GETOLEINTERFACE message to rich edit window - this is Retrieves an IRichEditOle object that a client can use to access a rich edit control's Component Object Model (COM) functionality.
then for retrieve an ITextServices pointer, call QueryInterface on the private IUnknown pointer returned by EM_GETOLEINTERFACE.
here exist interesting point - the IID_ITextServices not well known but need get in runtime from Msftedit.dll
from About Windowless Rich Edit Controls
Msftedit.dll exports an interface identifier (IID) called IID_ITextServices that you can use to query the IUnknown pointer for the ITextServices interface.
after we got ITextServices pointer - we simply can call OnTxPropertyBitsChange with TXTBIT_ALLOWBEEP mask
code example:
#include <textserv.h>
if (HMODULE hmodRichEdit = LoadLibrary(L"Msftedit.dll"))
{
// create richedit window
if (HWND hwndRich = CreateWindowExW(0, MSFTEDIT_CLASS, ...))
{
if (IID* pIID_ITS = (IID*) GetProcAddress(hmodRichEdit, "IID_ITextServices"))
{
IUnknown* pUnk;
if (SendMessageW(hwndRich, EM_GETOLEINTERFACE, 0, (LPARAM)&pUnk))
{
ITextServices* pTxtSrv;
HRESULT hr = pUnk->QueryInterface(*pIID_ITS, (void**)&pTxtSrv);
pUnk->Release();
if (0 <= hr)
{
pTxtSrv->OnTxPropertyBitsChange(TXTBIT_ALLOWBEEP, 0);
pTxtSrv->Release();
}
}
}
}
}

How to get the text value of a control through its parent window

I have the following wxDialog parent window:
I have created that parent window by the following code:
settingsFrm settingsWindow(this, "Settings");
settingsWindow.ShowModal();
I have tried to use FindWindowByName to get the value of the first text ctrl as follow:
wxLogMessage(dynamic_cast<wxTextCtrl*>(settingsWindow->FindWindowByName("keywords_txt"))->GetValue());
But unfortunately, it doesn't work and gives me a runtime error.
I don't know if that method suitable to do what I want.
How to get the value/other of a control through its parent window?
From your comments, it seems like you expect the function to find the control from the name of the variable in your code which is not how it works and would be pretty much impossible.
FindWindowByName() uses the window name (and, as a fallback, label, but this is irrelevant here because text controls don't have labels anyhow), so for it to work you need to set the window name when creating the control, using the corresponding parameter of its ctor. This is very rarely useful in C++ code, however, as it's simpler to just store a pointer to the control in some variable and use this instead.
FindWindowByName() can often be useful when creating controls from XRC, however. If you do this, then you should specify names for your controls in XRC and pass the same name to this function.
How did you create the TextCtrl instance? You should have something like wxTextCtrl m_textCtrl1 = new wxTextCtrl(/// arguments); Accessing the value should be very easy, as wxString text = m_textCtrl1->GetValue(); You definitely don't need FindWindowByName just for what you are trying to do here.

Is there a way to get IShellBrowser from a dialog?

I know that windows common dialog have a IShellBrowser interface and its class name is 'SHELLDLL_DefView'. Refer this.
How to get the IShellBrowser interface of a file open dialog?
(The file open dialog is outside's, not my application's.)
This is the same as my question but is delphi(?) ver.
I don't know delphi code. How to solve this problem as C++ or MFC code?
Here is a C/C++ translation of the Delphi code:
const UINT CWM_GETISHELLBROWSER = WM_USER + 7;
IShellBrowser *ShellBrowser = (IShellBrowser*) SendMessage(aDialog, CWM_GETISHELLBROWSER, 0, 0);
if (ShellBrowser) {
ShellBrowser->AddRef();
// use ShellBrowser as needed...
ShellBrowser->Release();
}
You might also want to read the following article, which provides a slightly more official (ie, more reliable, but more complicated) way to get an IShellBrowser for a given HWND:
Querying information from an Explorer window
Start with the ShellWindows object which represents all the open shell windows. You can enumerate through them all with the Item property... From each item, we can ask it for its window handle and see if it's the one we want... Okay, now that we have found the folder via its IWebBrowserApp, we need to get to the top shell browser. This is done by querying for the SID_STopLevelBrowser service and asking for the IShellBrowser interface.

Does anyone know which relation may exist between registration-free COM and drag/drop functionality?

Does anyone know which relation may exist between registration-free COM and drag/drop functionality?
Specifically, we have a huge C++ CAD/CAM application comprising a number of EXEs and several hundreds DLLs. Many of them serve as COM servers (both in-proc and out-of-proc) and/or clients, and also implement ActiveX controls.
The most of ActiveX controls and the main CMDIFrameWnd-based window of one of EXEs implement drag/drop functionality. ActiveX controls implement the both drop source and drop target, and the main window is only drop target, in particular, for files from Windows Explorer.
The drag/drop implementation is pretty standard and based on two data members derived from COleDataSource and COleDropTarget for drop source and drop target respectively. The COleDropTarget-derived member is registered with respective window in the window's OnCreate method. It also overrides OnDragEnter, OnDragOver and OnDrop methods in a similar way. Namely, the system-supplied COleDataObject parameter is asked for specific format (in particular, CF_HDROP), and in the case of positive answer, the data (e.g., file path) is extracted from the clipboard. The code looks like the following:
static FORMATETC g_FileFmt = {CF_HDROP, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL};
....
// Inside OnDragEnter, OnDragOver or OnDrop method
STGMEDIUM stgmedium = {0,0,0};
if (pDataObject->IsDataAvailable(g_FileFmt.cfFormat))
{
HRESULT hr = pDataObject->GetData(g_FileFmt.cfFormat, &stgmedium);
HDROP hdrop = (HDROP)GlobalLock(stgmedium.hGlobal);
if (hdrop != 0)
{
int FilesCount = DragQueryFile(hdrop, (UINT)-1, 0, 0);
if (FilesCount != 0)
{
TCHAR FileName[_MAX_PATH];
DragQueryFile(hdrop, 0, FileName, _MAX_PATH);
// Check file extension and store the file name for farther use.
}
GlobalUnlock(hdrop);
}
}
The drop source implementation is also straightforward and looks like the following:
void CDmDocListCtrl::OnBeginDrag(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
if (pNMListView->iItem != -1 && m_pOleDataSource && prv_BeginDrag())
{
DROPEFFECT DE = m_pOleDataSource->DoDragDrop(
DROPEFFECT_COPY | DROPEFFECT_MOVE | DROPEFFECT_LINK, 0);
}
*pResult = 0;
}
where prv_BeginDrag() function collects dragged data, packs it and puts on the clipboard by calling SetData method from the m_pOleDataSource object's IDataObject interface.
The all this stuff worked perfectly until it was decided to make the whole application registration-free. It took me three months to force the application run isolated (without registration of COM components) by embedding manifests, launching out-of-proc COM servers on demand and altering CLSID of some classes in order to separate instances of the same server launched from different folders. At last it begins to work - but without drag/drop functionality, despite it wasn't even touched by my changes.
On the drop target side, when I drag file from Windows Explorer, depicted above call to COleDataObject::IsDataAvailable returns false, although before my changes returned true. At the same time, if I add a single line of code "DragAcceptFiles();" to the main window's OnCreate method, drag/drop begins working via the standard CFrameWnd's WM_DROPFILE message handler.
On the drop source side, the dragged data are successfully packed and placed on the clipboard, but COleDataSource::DoDragDrop method fails, because a call to ::DoDragDrop API inside MFC implementation returns REGDB_E_CLASSNOTREG "Class not registered" result.
It means, that COM activation changes somehow influence drag/drop behavior. How?
P.S. 1) The EXE, to which I drag files from Windows Explorer, has in its project properties "UAC Execution Level = asInvoker". As far as I understand, it tells that the EXE will run at the same UAC level as Windows Explorer when launched by double-click on the file.
2) Quite surprisingly, although drag/drop stopped working with symptoms described above, Copy/Paste continues work well, despite the both technologies have similar implementation.
3) I believe, that if find out when ::DoDragDrop API returns "Class not registered" error, and which class it is looking for, it would be possible to solve the problem.
Thanks for help,
Ilia.
Following to MartinBa advice, I solved the problem with the help of Process Monitor. The Process Monitor showed me that while I drag an item in the ActiveX control (mentioned in the question), the system unsuccessfully tries get access to a class ID in the Registry. Looking for that ID, I found that it is really not class ID, but IDataObject interface ID. It was referenced in one of my manifest files.
The most of manifests I have written by hand, but a few, especially at the beginning of the project having no experience in the area, I generated automatically by Visual Studio from existing type library. In one of them Studio included the comInterfaceExternalProxyStub statement for a couple of system interfaces, in which proxyStubClsid32 element was (erroneously) equal to the interface ID.
I'm still not sure whether those system interfaces should present in the manifest; for example, the IDataObject is only mentioned as a method's parameter in one of IDL definitions. Anyway, I corrected only the proxyStubClsid32 value, and the problem disappeared...
The moral of this very painful for me story is to always check output of automatic tools...

Hardcoding the resources in application

I have some code which shows a simple dialog box and handles user action (written using plain WinAPI).
// Display dialog and handle user action
LRESULT choice = DialogBoxParam(NULL, MAKEINTRESOURCE(AP_IDD_DIALOG), NULL, (DLGPROC)DialogCallback, NULL);
Is there any way to hardcode the resource file dialog.rc, which is used to build the dialog ?(I would like to get rid of .rc files and I'm pretty sure there is a way, yet I don't know what it is :)
Edit
Also, does someone have any ideas on converting existing .rc files into hardcoded resources? Is this possible?
*.rc (resource) files are source code, they are compiled with the resource compiler and linked into your object (.exe/.dll)
You don't need to ship the resource file or have it present with your app to run it.
If you want to move to programmatically defined windows rather than templates then you might want to be looking at QT/wxWidgets. But thats a fair chunk of overhead for 1 dialog!
I'm surprised I couldn't find an existing app to do this sort of thing, enough hits on google with people trying to do this.
Ok, so the DLGTEMPLATE is a variable length blob of data, normally you let the dialog function pull it from the resource bundle for you, but instead you want to store it in your program.
You need to change your static lib to have a new function to decode some 'blob' back into the dlgtemplate, and you need to generate the blob. (or add the blob in your code without decoding which I don't want to think about right now)
The following code will give you the DLGTemplate data you need to imbed in your app. (cut from larger project)
HGLOBAL LoadResourceImpl(wchar_t *resource, wchar_t *type)
{
HRSRC handle = FindResource(hInstance, resource,type);
if (handle)
{
HGLOBAL hResource = LoadResource(hInstance, handle);
if (hResource)
return LockResource(hResource);
}
return 0;
}
DLGTEMPLATE * LoadDialog(wchar_t *resource)
{
return (DLGTEMPLATE *) LoadResourceImpl(resource,RT_DIALOG);
}
DLGTEMPLATE * LoadDialog(int resource)
{
return (DLGTEMPLATE *) LoadResourceImpl(MAKEINTRESOURCE(resource),RT_DIALOG);
}
Make an app that includes your resource - use the appropriate LoadDialog to get the data.
Now "write out" that blob in a format to include in your app -
step 1 - find out how much data there is by traversing the structure to find the total size including all the controls (control count is in DLGTEMPLATE::cdit)
step 2 - convert the data to something you can compile into your code - like HEX
Add to your static library a new 'HEX' to DLGTEMPLATE method and the hex string you made using the other app.
Can we hard code the .res file into the program?
the resource compiler converts .rc into .res
use a hex dump tool (eg. winhex) to translate the .res into bytes array
(represented in C source code).
add the source code file in the project and compile in the executable.
locate the dialog resource position from the array and use DialogBoxIndirect.
DialogBoxParamIndirect can be used instead. It takes as a parameter the dialog template. Raymond Chen's blog has an example of building a dialog box at runtime rather than from a resource using the DialogBox*Indirect API's.
Per MSDN, dialog box resources are basically composed of the DLGTEMPLATE and DLGITEMTEMPLATE structures. So you should be able to use the resource API's (FindResource, LoadResource, and LockResource) to get at the underlying bits of an existing dialog resource, and embed that within your code.
Note that this is a lot more painful than using the .rc file. It's much more difficult to make changes to your layout, and it's also much less localizable, since localization would now require a code change to update the template in code.
If it's a simple dialog, why use the DLGTEMPLATE at all?
Nothing stops you from simply doing ::CreateWindow'ing those controls directly. If it's a simple dialog with 2-3 buttons and a couple text fields, simply call ::CreateWindow, passing in the window class of whatever common control you're using.
This is essentially what the DialogXxxxx functions do anyway. DLGTEMPLATE is a convenience for declaratively laying out your forms, and having the boilerplate make the appropriate CreateWindow calls, etc.