How to obtain the PIDL of an IShellFolder - c++

If I have an IShellFolder interface pointer. How might I obtain its PIDL?
I can see how to enumerate its children, and I can see how to use it to compare any two children. But how might I get its own pidl?
I ask because I'd like to know:
Is this IShellFolder == Another IShellFolder
I can use IShellFolder::CompareIDs(), but I have to have the IDs of both folders.

What either Chris or Mordechai writes on #1 is anyway not to the point. The question is not about objects in the shell namespace but about objects that have an IShellFolder interface. Possession of an IShellFolder interface does not itself imply a presence in the shell namespace. The original question is ill-formed, inasmuch as it assumes that an object with an IShellFolder interface must have "its own PIDL".
The best you can do, I think, is as Mordechai suggests:
see if the object also has an IPersistFolder2 interface
The purpose of this interface is to fix the object in the shell namespace, which is in turn what makes the folder persistable. Rather than infer from any absence of published documentation, look at what Microsoft actually does say of the IPersistFolder and IPersistFolder2 interfaces and the Initialize and GetCurFolder methods. Most notably:
you need to implement this interface so that the Shell folder object's ITEMIDLIST can be retrieved.
On #2, I'm afraid Chris is definitely not correct. An IShellFolder certainly can be obtained without a PIDL. The Control Panel, which Chris introduced for #1, provides a ready counter-example for #2. Just feed CLSID_ControlPanel and IID_IShellFolder to CoCreateInstance. You get a perfectly usable instantiation of the Control Panel without ever "having knowledge of a PIDL".
There are a handful of other creatable shell folders implemented in SHELL32, and any DLL can set up any number of others.

I found that you can query an IShellFolder for its IPersistFolder2, which has GetCurFolder(), which returns its absolute PIDL. I could then simply use the IShellFolder for the desktop to CompareIDs() to determine if they're equal. I found the outlines of this while looking at SHGetIDListFromObject. I couldn't just use that function, because its Vista, and I need XP compatibility.
Here's a sketch of how it works (assuming you have an ifolder_desktop, and ifolder_other, which are IShellFolder pointers. Pidl is a simple helper that ensures that the IDLISTs are deallocated properly):
CComQIPtr<IPersistFolder2> ipf2_desktop(ifolder_desktop);
CComQIPtr<IPersistFolder2> ipf2_folder(ifolder_other);
Pidl pidl_desktop, pidl_folder;
VERIFY(SUCCEEDED(ipf2_desktop->GetCurFolder(pidl_desktop)));
VERIFY(SUCCEEDED(ipf2_folder->GetCurFolder(pidl_folder)));
HRESULT hr = ifolder_desktop->CompareIDs(NULL, pidl_desktop, pidl_folder);
pCmdUI->Enable(SUCCEEDED(hr) && HRESULT_CODE(hr) != 0);
In case anyone is interested in my simple Pidl class:
class Pidl
{
public:
// create empty
Pidl() : m_pidl(NULL) { }
// create one of specified size
explicit Pidl(size_t size) : m_pidl(Pidl_Create(size)) {}
// create a copy of a given PIDL
explicit Pidl(const ITEMIDLIST * pidl) : m_pidl(Pidl_Copy(pidl)) {}
// create an absolute PIDL from a parent + child
Pidl(const ITEMIDLIST_ABSOLUTE * pParent, const ITEMIDLIST_RELATIVE * pChild) : m_pidl(Pidl_Concatenate(pParent, pChild)) { }
// return our PIDL for general use (but retain ownership of it)
operator const ITEMIDLIST * () { return m_pidl; }
// return a pointer to our pointer, for use in functions that assign to a PIDL
operator ITEMIDLIST ** ()
{
free();
return &m_pidl;
}
// release ownership of our PIDL
ITEMIDLIST * release()
{
ITEMIDLIST * pidl = m_pidl;
m_pidl = NULL;
return pidl;
}
void free()
{
if (m_pidl)
//Pidl_Free(m_pidl);
ILFree(m_pidl);
}
// automatically free our pidl (if we have one)
~Pidl()
{
free();
}
private:
ITEMIDLIST * m_pidl;
};

I forgot to mention the SHGetIDListFromObject function.
It's only available in Windows Vista and higher. It has the advantage of being documented, albeit tersely. You get more detail, of course, from my own documentation. This shows that Microsoft knows two more ways of getting a PIDL for an arbitrary interface pointer to an object in the shell namespace.

Mordachai's answer might be correct, but to me this query makes no sense on two fronts:
I don't believe there is a published document saying that an IShellFolder can have only one parent. There might be multiple ways to any particular shell folder. the control panel is accessible via My Computer, via the Start Menu, and anywhere in the file system you create a
junction point to it. It seems the shell teams oringinal intention was, given an IShellFolder instance, it should not matter to external users what its arbitrary location happened to be.
Plus, any application that instantiates an IShellFolder surely did so FROM a having knowledge of a PIDL. If your app cared about the path to an IShellFolder it already HAD that information. How did you loose it? (And why should the shell team add a method to help apps keep track of their own data?)

As said before there may be lots of issues with special folders like Control Panel (I still donĀ“t understand it fully) but here is a simple solution for "normal" folders:
HRESULT get_pidl(IShellFolder * sf, LPITEMIDLIST * pidl)
{
if (!sf || !pidl) return E_FAIL;
wchar_t FolderName[MAX_PATH] = {0};
STRRET strDispName;
sf->GetDisplayNameOf(NULL, SHGDN_FORPARSING, &strDispName);
StrRetToBuf(&strDispName, NULL, FolderName, (UINT)MAX_PATH);
IShellFolder * desktop = nullptr;
SHGetDesktopFolder(&desktop);
ULONG cbEaten, atrib = 0;
HRESULT hr = desktop->ParseDisplayName(NULL, nullptr, FolderName, &cbEaten, pidl, &atrib);
desktop->Release();
return hr;
}

Related

Can you have 2 identical COM objects?

A general question that could help answer another question I asked before. I believe that COM objects only return a pointer to the created object. So when I try to initialize a COM object twice, do I make two COM objects in my app, or do I make two different pointers to the same object? or perhaps there are two copies of the object that the pointers point to their respective objects?
// Pointer to WebViewController
static wil::com_ptr<ICoreWebView2Controller> webviewController;
// Pointer to WebView window
static wil::com_ptr<ICoreWebView2> webviewWindow;
// Locate the browser and set up the environment for WebView
CreateCoreWebView2EnvironmentWithOptions(nullptr, nullptr, nullptr,
Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(
[hWnd, url](HRESULT result, ICoreWebView2Environment* env) -> HRESULT {
// Create a CoreWebView2Controller and get the associated CoreWebView2 whose parent is the main window hWnd
env->CreateCoreWebView2Controller(hWnd, Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(
[hWnd, url](HRESULT result, ICoreWebView2Controller* controller) -> HRESULT {
_com_error err4(result);
MessageBoxW(hWnd, L"Controller setup", 0, MB_OK);
MessageBoxW(hWnd, err4.ErrorMessage(), 0, MB_OK);
if (controller != nullptr) {
webviewController = controller;
webviewController->get_CoreWebView2(&webviewWindow);
}
// Add a few settings for the webview
// The demo step is redundant since the values are the default settings
ICoreWebView2Settings* Settings;
webviewWindow->get_Settings(&Settings);
Settings->put_IsScriptEnabled(TRUE);
Settings->put_AreDefaultScriptDialogsEnabled(TRUE);
Settings->put_IsWebMessageEnabled(TRUE);
// Resize WebView to fit the bounds of the parent window
RECT bounds;
GetClientRect(hWnd, &bounds);
webviewController->put_Bounds(bounds);
// Schedule an async task to navigate to Bing
webviewWindow->Navigate(url);
if (GetWindow(hWnd, GW_CHILD) != NULL) {
MessageBoxW(hWnd, L"not a NULL child window", 0, MB_OK);
};
// Step 4 - Navigation events
// Step 5 - Scripting
// Step 6 - Communication between host and web content
return S_OK;
}).Get());
return S_OK;
}).Get());
I called
CoInitialize(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); earlier in the code only once. My question is what happens when I run this block twice?
My question is what happens when I run this block twice?
In this specific case, executing the code twice will produce two distinct WebView2 instances. Both instances are then assigned to the same static variable, causing one instance to be irrevocably leaked.
Being an XY Problem this information isn't terribly useful. The real issue you are trying to solve is knowing when the WebView2 control is fully constructed. That question has been answered elsewhere.
So when I try to initialize a COM object twice, do I make two COM objects in my app, or do I make two different pointers to the same object? or perhaps there are two copies of the object that the pointers point to their respective objects?
That really depends on the implementation of the COM object. If it is implemented as a singleton, then instantiating it multiple times will return a pointer to the existing instance (creating a new instance if it doesn't exist yet). But, if the COM object is not implemented as a singleton, then each instantiation will return a pointer to a new instance.

MSHTML: Callback on image insertion

Currently I am working on embedding a HTML editor into a C++ WinAPI application using the MSHTML component.
I got everything set up (activating editing mode, changing font face, etc.), but now I have to support inserting images. MSHTML already has support for it built in, but this support is - to my knowledge - not enough.
Somehow I need a way to intercept the insertion of images into the HTML-editor, since I have to create a list of images in the UI of our application. So, whenever the user uses the default-dialog of the MSHTML-component to insert an image or updates its source (e.g. from file://xyz.jpg to file://abc.jpg), I want my code to be notified.
I already looked at the conecpt of "Edit Designers", the implementation of IHTMLEditHost, or the DWebBrowserEvents2 interface. But nothing seems to do the trick.
Perhaps someone can give me a hint?
Okay,
it looks like you cannot explicitly subscribe for specific changes of the document. What you can do is to create a so-called "Change Sink".
Everytime you change the document, either by user input or programmatically, you can get a notification that "something" changed in your document. This can be done by implementing the IHTMLChangeSink interface and attaching it to the IMarkupContainer2 of the IHTMLDocument2.
Example code (not complete):
class CHTMLChangeSink : public IHTMLChangeSink
{
public:
// Implement IUnknown: QueryInterface, AddRef, Release
// Implement IHTMLChangeSink:
HRESULT STDMETHODCALLTYPE Notify()
{
// Notify() is called everytime the document changes
return S_OK;
}
};
CHTMLChangeSink * pChangeSink = new CHTMLChangeSink();
IHTMLDocument2 * pDoc; // lets suppose we already have it
IMarkupContainer2 * pMarkupContainer = nullptr;
if (pDoc->QueryInterface(IID_IMarkupContainer2, reinterpret_cast<void **>(&pMarkupContainer)) == S_OK) {
DWORD dwCookie = 1;
// registration is done here
pMarkupContainer->RegisterForDirtyRange(pChangeSink, &dwCookie);
}
Please be aware, that the document has to be loaded completely (register for DIID_DWebBrowserEvents2::DocumentComplete).
From now on, whenever a change in the document occurs, your Notify-method will be called and you can do further processing to find out what has changed (e.g. process the list of images in the document).
Have fun!

Should I call Release on a ID3D10Device interface after using CreateTexture2D? why?

I've this code in a directx10 project.
ID3D10Texture2D *depthStencilBuffer;
UINT a = m_device->Release();
if(FAILED(hr = m_device->CreateTexture2D( &descDepth, NULL, &depthStencilBuffer ))) {
DXGI_D3D10_ErrorExit(hr, L"CreateTexture2D");
return hr;
}
a = m_device->Release();
Now if I stop the debugger at the third line and check the value of a it says 2. And when I stop it at the line after the last one it says 3. I can't understand why. Is the CreateTexture2D function adding references to the ID3D10Device interface? And apparently it's not even adding one reference but two of them since Release() decrements one.
My problem is the documentation for ID3D10Device::CreateTexture2D doesnt specify it adds references to the ID3D10Device object. Same goes for ID3D10Device::CreateRenderTargetView for instance. How am I supposed to guess when to call Release?
I don't have the ability to have the DirectX SDK installed atm to test this, but, in general principals, when it comes to COM you should follow the COM rules and trust that other objects follow the rules too.
i.e. you shouldn't know the implementation of Texture2D but you should trust that if it needs add references to device, that it will also remove them when it is done. You shouldn't be attempting to make additional calls to Release().
Your code should read:
ID3D10Texture2D *depthStencilBuffer = NULL;
if(FAILED(hr = m_device->CreateTexture2D( &descDepth, NULL, &depthStencilBuffer ))) {
DXGI_D3D10_ErrorExit(hr, L"CreateTexture2D");
return hr;
}
depthStencilBuffer->Release();
depthStencilBuffer = NULL;
i.e. you should expect that the call returns a texture2d with a reference count of 1, this is all you need to know. When you're done, you should call release only on the depthStencilBuffer and expect it to clean up itself entirely. If during the implementation the stencil buffer needed references to the device, you should trust that it will also call release on those references correctly.

How to write simple background thread in CWorkerThread

I'm trying to asynchronously run function in my add-on for Internet Explorer (I'm writing BHO in VC++). As suggested here I'm trying to use CWorkerThread.
I've been trying to figure it out for hours but still have no idea how to do it. I don't have much experience in ATL. The lack of a good documentations or tutorials on Internet is killing me.
I'm creating class by Add->Class and choosing ATL Simple Object (that's how you add classed to ATL project right?). But how to implement this IWorkerThreadClient? I thought that choosing Add->Implement Interface in Class View would be good but there is no IWorkerThreadClient on the list.
I think I don't know ATL or COM enaugh but can't find good resource for learning this (esspessialy newest ATL7).
I even tried winapi CreateThread approach but it isn't working. I'm passing this class pointer to run static method but something is corrupting with memory later. Nevertheless if It had worked I still would rather use something else than CreateThread.
Right now I have something like this. In OnDocumentComplete there's RemoveImages(sptmlDoc) and I just want to run it asynchronously.
EDIT: What I did with CreateThread:
I tried running RemoveImages function (from here) asynchronously. I created static function in my class with signature like here. RemoveImages has parameter so I copied it to a member of a class:
if (htmlDoc2 != NULL)
{
m_tmpHtmlDocument2 = htmlDoc2;
m_hThread = CreateThread( NULL, 0, MyThreadFunction, this, 0, &m_threadId);
}
and MyThreadFunction:
static DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
CHelloWorldBHO* myClass = (CHelloWorldBHO*)lpParam;
myClass->RemoveImages(myClass->m_tmpHtmlDocument2);
return 0;
}
I get "Unhandled exception at 0x60c0da05 in iexplore.exe: 0xC0000005: Access violation reading location 0x000001b8." here in the bold line:
void CHelloWorldBHO::DontDisplayElement(CComPtr htmlElement)
{
CComPtr style;
HRESULT hr = htmlElement->get_style(&style);
if (hr == S_OK && style != NULL)
{
static const CComBSTR strNone(L"none");
style->put_display(strNone);
}
}
Your performing a naughty by trying to use a COM handle allocated in 1 thread in another. BHO environment is STA (Single Threaded Apartment) so you should be marshalling the m_tmpHtmlDocument2 object for use in your thread.
Experiance has shown that in some cases IE may let you get away with passing the Browser com object from 1 thread to another and then getting the document and elements afterwards may work. This is entirely unreliable.
Depending on IE 6/7/8 you will have different target threads to execute your actions on, thinking at the levels of per security level/frame/tab/window. basically any time IE creates a new 'Site'
Also to prevent your app from holding the pages active even after navigation away from the page, in FireFox you would use an nsWeakPointer<> , I've never found the equivelant in IE.
Suggestion: Perhaps instead of marshalling com to another thread because your interaction with the page is slow, trying to improve the way you interact with the page and improve performance in process might be a better aim.
Here is an outline using the CThreadPool which will queue up requests, and then execute them when the pool has space.
I use pvWorkerParam to tie the threads back to the site.
I have different types of ActionRequests, you could of course simplify and just pass null for the request.
Note: This doesn't resolve marshalling issues you already have
class ActionRequest{
DontDisplayElement();// define your do stuff in here
};
class ScriptWorker
{
public:
ScriptWorker(void);
virtual ~ScriptWorker(void);
public:
BOOL Initialize(void* pvWorkerParam);
void Execute(ActionRequest *request, void* pvWorkerParam, OVERLAPPED* pOverlapped){
try{
std::auto_ptr<ActionRequest> cleanupRequest(request);
request.DontDisplayElement();
} catch(...) {}
}
void Terminate(void* pvWorkerParam);
private:
boolean m_bCoUninit;
};
Site{
CThreadPool<ScriptWorker> m_scriptWorkerThread;
Site() {
void *pvWorkerParam = this;// or whatever you want to have passed to every script worker and execute in the pool.
m_scriptWorkerThread.Initialize( pvWorkerParam, 1 );
}
OnDocumentComplete() {
m_scriptWorkerThread.QueueRequest( new ActionRequest() );
}
}
and sptmlDoc - is it an IHTMLDocumet* ?
IWorkerThreadClient - never heard of it
"I even tried winapi CreateThread approach but it isn't working. I'm passing this class pointer to run static method but something is corrupting with memomory later"
Keeping it simple is the best design pattern of them all. So stick with CreateThread unless you have good reasons not to. Now, my guess is that the crash occurs because of sptmlDoc being passed to the thread for later processing. The thing is such pointers are only valid from the BeforeNavigate event until DocumentComplete event. Try to do that processing on the spot (inside your event handler) and see if it stil crashes. Some code posting would help too

Application crashes when creating an object

I have a class which inherits from IDirectInputA interface.
here: http://pastebin.com/QuHP02ai
so, when i try to create object of this, application crashes (calls CorExitProcess from somewhere). What i did wrong?
p.s. Direct input v. 7
p.p.s.
this code creates object. I deleted some code from it, except the main part
IDirectInputA** ppDI;
HRESULT hr = _DirectInputCreateA(hinst, dwVersion, ppDI, punkOuter);
xDirectInputA xDI = new xDirectInputA((IDirectInputA*)(*ppDI));
When you create your instance, you pass a pointer to IDirectInputA, right? What pointer do you pass? If you pass an uninitialized or a null pointer, you will get undefined behavior.
TBH what you are trying to do is more complicated than you think. The problem arises in what exactly you are trying to do. Are you trying to wrap IDirectInputA OR are you trying to completely re-implement it.
If you are trying to wrap it do the following:
IDirectInputA* pDI = NULL;
HRESULT hr = _DirectInputCreateA( hinst, dwVersion, &pDI, NULL );
Then create your derived class as follows:
class xDirectInputA : public IDirectInputA
{
protected:
IDirectInputA* mpInternal;
public:
xDirectInputA( IDirectInputA* pInternal ) :
mpInternal( pInternal )
HRESULT CreateDevice( REFGUID rguid, IDirectInputDevice** ppDirectInputDevice, IUknown* pOuter )
{
// Do what ever processing you need.
return mpInternal->CreateDevice( rguid, ppDirectInputDevice, pOuter );
}
// Implement other functions.
};
Now you pass your xDirectInputA pointer around instead of the normal pointer returned from DirectInputCreate. You can now intercept every message that goes through the class.
If you are trying to do your own full re-implementation it is a LOT more complicated. You are going to need to fully implement the COM object. You'll be best off putting a DInput.DLL alongside the executable that contains your implementation. All in though this is only something you should try if you REALLY know what you are doing.
If you wish to learn COM fully I suggest purchasing Essential COM by Don Box. Its a VERY helpful book.