I've been struggling with creating a COM server for IContextMenu for the last couple of days and have hit an impasse. Explorer should call IShellExtInit:Initialize directly after the QueryInterface however it is not being called. Debugging this is very difficult since the error is generated inside explorer though the error is somewhere in my implementation of the interface. I have attached my source etc... if anyone has an idea why it's not working I would appreciate the input.

Note: GoAsm syntax
Posted on 2007-11-18 11:14:27 by donkey
Well, here's the final version of the IContextMenu example, I'll be testing it over the next few days then posting it on the website next weekend. Everything seems to work fine, it will do the following...

Register and unregister a Shell extension using DllRegisterServer/DllUnregisterServer
Create a class object
Create an instance of IShellExtInit and initialize the extension
Send Explorer a pointer to an instance of IContextMenu
Add to the context menu for EXE and DLL files
Extract the selected filename using the IDataObject interface and DragQueryFile
Display the filename

From this point you can do whatever you like with the file, I have simply displayed the name for the purpose of the demo. The demo can be downloaded from the GoAsm board...

http://www.masm32.com/board/index.php?topic=8194.msg59770#msg59770

Donkey
Posted on 2007-11-18 16:23:54 by donkey
Now that's a miracle - I'm learning COM at the moment, so your app was very helpful in explaining/verifying a few problems I had.
(And yes - the context menu seems to work flawlessly ^^ )

Two things I'd like to ask/mention:
1. You're registering the handles in "Classes_Root", while Platform SDK states that registry writes should be done in either HKLM, HKCU, or impersonated user's key. Is there any particular reason for that?
2. You're returning S_OK in a call to IClassFactory::LockServer, but you're actually not locking the class. This may lead to the library being unloaded prematurely. You should either return 80004005h (E_FAIL), or actually suport locking, which is simply maintaing one additional reference counter and taking this counter into consideration in DllCanUloadNow :)


Anyway, thank you for sharing ^^
Posted on 2007-11-18 21:53:09 by ti_mo_n
Hi ti_mo_n,

HKEY_CLASSES_ROOT was chosen because it is the key suggested on MSDN's tutorial

http://msdn2.microsoft.com/en-us/library/bb776881.aspx

For LockServer (and GetCommandString etc..), I have not implemented all of the interfaces yet, I still have a couple I will work on next time I get a couple of days off work. That said I don't believe it is necessary in this application as Explorer will reload the DLL as needed, it may however serve to speed up the GUI fractionally.

Notes to Callers
Most clients do not need to call this function. It is provided only for those clients that require special performance in creating multiple instances of their objects.


Donkey
Posted on 2007-11-18 22:20:58 by donkey
Yes, it's true that clients usually DON'T call this function, but I think (and unfortunately I can't verify that, because I could not find any clarification for that) that if a method is not implemented, it should return E_FAIL, or S_FAIL, because S_OK means, that the function processed successfully, and a client which actually USES the function, might behave incorrectly. But the truth is that I don't even know which error (S_FAIL or E_FAIL - they have different codes) should be returned, because there is an inconsistency in COM errors: some function return S* errors, some functions return E* errors, and some functions return E* errors and S_OK for no error o_O So that's why I just implemeted LockServer for the sake of sanity and I can happily return S_OK :P And it's very easy to implement: It requires an addiional reference counter, or 'lock counter'. LockServer(TRUE) increases that counter, LockServer(FALSE) decreases it. DllCanUnloadNow returns TRUE if and only if BOTH the normal reference counter (the one controlled by QueryInterface/AddRef/Release) is 0 AND lock counter is 0. So it was just a few lines to add and now I can feel much better :P

About HKEY_CLASSES_ROOT:
To change the settings for the interactive user, store the changes under HKEY_CURRENT_USER\Software\Classes rather than HKEY_CLASSES_ROOT.

To change the default settings, store the changes under HKEY_LOCAL_MACHINE\Software\Classes. If you write keys to a key under HKEY_CLASSES_ROOT, the system stores the information under HKEY_LOCAL_MACHINE\Software\Classes. If you write values to a key under HKEY_CLASSES_ROOT, and the key already exists under HKEY_CURRENT_USER\Software\Classes, the system will store the information there instead of under HKEY_LOCAL_MACHINE\Software\Classes.

http://msdn2.microsoft.com/en-us/library/ms724475.aspx

It's true that I may have misinterpreted it ^^' It just says that it's not guaranteed where the value will actually be stored.
Posted on 2007-11-19 08:24:14 by ti_mo_n
Hi ti_mo_n,

I will make the change to lockserver, you have convinced me that the msdn example I took it from was wrong. For the registry key I will keep it the same because as I understand it the OS will actually place the key where it belongs, I am more worried about W98+ compatibility than following esoteric Win v5+ guidelines when there is no obvious impairment to functionality.

Donkey

New upload to my website, LockServer now returns E_FAIL, thanks ti_mo_n  :thumbsup:
Posted on 2007-11-19 20:43:08 by donkey