ShellExtension AddPages isn't called - c++

I am trying to implement a PropertySheetHandler shell extension, without much luck.
For some reason, the ContextMenu handlers are successfully called (Initialize() and QueryContextMenu()) but AddPages() is not called.
I have implemented the IShellPropSheetExt interface (AddPages() and ReplacePages()) and have the following declared:
BEGIN_COM_MAP(CShellExtension)
COM_INTERFACE_ENTRY(IShellExtInit)
COM_INTERFACE_ENTRY(IContextMenu)
COM_INTERFACE_ENTRY(IShellPropSheetExt)
END_COM_MAP()
Also, in the Registry I added the following entry:
HKEY_CLASSES_ROOT\*\shellex\PropertySheetHandlers\MyPropSheet
And of course also added to the CLSID:
HKEY_CLASSES_ROOT\CLSID\{CC669AD3-9F45-4C29-ADF7-F2F58E2DB2E9}\InprocServer32
With the DLL path and ThreadingModel Apartment (also tried Both).
What am I missing?
I also ran Process Monitor, and I can see my DLL is found, yet no call to Initialize or AddPages is made upon file properties.

Related

Can't find COM object from C++, although Guid it's registered

First of all happy new year to everyone, hope you're doing well!
I'm working on a C++ project in which I need to call a C# DLL I created following the first answer of this post. Once I have the DLL, I need to call it from Qt, so by using dumpcpp and the .tlb file generated by regasm, I managed to get the .cpp and .h files to use my classes. Just as a reference, the namespace of the classes is Wrapper, and the main class is Device with guid {DD4A4896-C105-4C60-839B-B18C99C8FE15}.
Once I have the generated files to use the DLL, if I try to create a Wrapper:: Device instance on Qt, I get the following error:
QAxBase::setControl: requested control {dd4a4896-c105-4c60-839b-b18c99c8fe15} could not be instantiated
QAxBase::qt_metacall: Object is not initialized, or initialization failed
It doesn't give any more information, so I tried to check if the guid was stored on the system registry (I used the regasm command explained on the previously quoted post, and It said that it was successful, but you never know). Opening Registry editor and searching for the Guid revealed that it's present at: Computer\HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{DD4A4896-C105-4C60-839B-B18C99C8FE15}, which, as far as I know, is the right route for these guids, and it points to the right DLL.
I though It may be due to some kind ActiveQt problem, and as the previously quoted post explained how to use that DLL from VS C++, I decided to give it a try, using this as an another reference. I've finished with this code, which is supposed to create an instance of my Device object
#include <iostream>
#include <atlstr.h>
#import "C:\Users\javie\Documents\Wrapper\Wrapper\bin\x86\Release\netstandard2.0\Wrapper.tlb" named_guids raw_interfaces_only
inline void TESTHR(HRESULT x) { if FAILED(x) _com_issue_error(x); };
int main()
{
try
{
TESTHR(CoInitialize(0));
Wrapper::IDevicePtr devPtr = nullptr;
TESTHR(devPtr.CreateInstance("{DD4A4896-C105-4c60-839B-B18C99C8FE15}"));
}
catch (const _com_error& e)
{
CStringW out;
out.Format(L"Exception occurred. HR = %lx, error = %s", e.Error(), e.ErrorMessage());
MessageBoxW(NULL, out, L"Error", MB_OK);
}
CoUninitialize();// Uninitialize COM
std::cout << "Hello World!\n";
}
However, this doesn't work either, the createInstance method throws an exception of Class not registered and HR=80040154. Again, according to Registry editor, the class is registered, so I don't understand the error. I've also tried with devPtr.CreateInstance("Wrapper.Device"), devPtr.CreateInstance("Wrapper::Device") or `devPtr.CreateInstance("Wrapper::CLSID_Device") as the links I posted suggest, but in those cases I get another exception with HR=800401f3 and message Invalid class string.
It doesn't matter whether VS or Qt Creator are opened as administrator or not, I get the exact same error.
I have run out of ideas, and I really need to be able to use that DLL from Qt using the files generated by dumpcpp.
Does any one know what could be happening? It feels quite strange to me.
If your C++ application is 64-bit, that's the answer right there, because your C# component is 32-bit (or MSIL but registered to the 32-bit hive). In situations like these, a simple test using VBScript is always useful.
Write a simple VB Script (test.vbs)
Dim obj
Set obj = CreateObject("Wrapper.Device") ' or whatever your ProgID is
MsgBox TypeName(obj)
Now, run this macro 2 ways: with 32-bit and 64-bit versions of VBScript:
32-bit > c:\windows\SysWow64\cscript.exe test.vbs
64-bit > c:\windows\system32\cscript.exe test.vbs
This is assuming your C# component is dispatch compatible. If it's not, then it will still give you differing results that you can use to debug.
Assuming automation/IDispatch compatible, one will work and one won't if you have registered your component correctly.
Have you registered correctly? When I use regasm, I always use the the switches /tlb /codebase when registering the C# component for COM.
Ok, in case someone find the same error, I'll explain the solution I found.
The problem was that in my case, the C# class I developed depended on another 32 bits dll which was not registered on my PC. Once I registered the other dll, everything worked fine.
I don't know why VS kept telling me that the class was not registered when my class itselft was registered, it was one of its dependencies that wasn't registered.
Anyway, I discovered this thanks to Joseph's comments, so thanks a lot for your help.

How to solve a function acting differently based on callsite?

Not sure how to word the title so feel free to rename, but the issue I'm having is that I've got a function that works in one project, but fails in another. Below is rough pseudocode to show that one call in LibraryProject works, whereas the call in GameProject doesn't.
In ChildClass::do_stuff, the win32_window HWND is valid, whereas the second one, failed_win32_window is null and glfw throws an error saying it isn't initialized, despite it already having been initialized (since the first glfw call was successful and I've manually stepped through to verify it was):
GLFWError #65537 Happen, The GLFW library is not initialized
Here's pseudocode showing the two projects, and the files. GLFW is set initialized properly since if I do all my glfw logic within LibraryProject, the window shows up as normal.
//LibraryProject
////library_header.h
class ParentClass {
GLFW* _mainWindow; //filled in elsewhere in the real code
HWND getWin32Window() { return glfwGetWin32Window(_mainWindow); }
}
//GameProject
////game_header.h
#include "library_header.h" //from other Project
class ChildClass : public ParentClass {
void do_stuff() {
HWND win32_window = this->getWin32Window(); //this works because it goes down into LibraryProject.dll's module
HWND failed_win32_window = glfwGetWin32Window(_mainWindow); //but literally the same call here doesn't because it happens within GameProject.exe
}
}
////game_body.cpp
void function_called_elsewhere_in_game() {
//called from GameProject.exe
auto child = ChildClass();
child.do_stuff();
}
I'm not sure if this is an issue with glfw and my setup, or just my misunderstanding how projects and dependencies work.
Things I've tried:
Downloading the latest glfw3
Rebuilding the entire solution
Toggling with References and Linking Dependency Inputs
Things to note:
This is happening in the main thread, nothing else is using glfw at the same time. Its 100% reproducible too.
glfw3.lib is always being created in my GameProject output folder, based on the one inside LibraryProject
Stepping through the disassembly for each of the two glfwGetWin32Window calls has different addresses in disassembly, leading me to believe they're two different copies of the same library, but I'm not sure.
This is not an issue with cocos2d, the game engine I'm using as starting a blank project and calling glfwGetWin32Window(..) returns a valid pointer, even in GameProject, so there's something that I'm doing wrong, but I don't know what.
Images showing off the actual behaviour. magnolia_cocos_proj is GameProject and is the exe I'm running, and libcocos2d is LibraryProject I'm using as a DLL (I'm unfamiliar with the details of how linking and dlls work).
win32_window has valid value
definition of getWin32Window() to be 100% sure. Notice the module is in libcocos2d.dll now.
after going over the second line, the error throws and the second window is null
As I understood from "glfw3.lib is always being created" you use static linking. Static linking of a lib to different dll and exe lead to duplicating of all static memory of the lib. You should use a dynamic library for GLFW in the case. It's glfw3dll.lib.
There are two main cases why this error could appear:
GLFWError #65537 Happen, The GLFW library is not initialised
Case One :
The mentioned error occurs if a GLFW function was called that mustn't be called unless the library is initialised. So, you need to initialise GLFW before calling any function that requires initialisation.
Read an API introduction for reference. Use if-else statement for handling glfwInit() and errors.
Reading Moving from GLFW 2 to 3 is also useful.
Case Two :
This error quite often occurs in the case you have previous versions of GLFW installed on your machine. GLFW3 doesn't like running along with previous version installed. So, delete all the GLFW libraries and linkers and reinstall the latest GLFW 3 from scratch.
Hope this helps.

C++ Builder XE2: Initializing a Data Module in a dll

I'm trying to create a dll that contains a VCL data module - the idea being that various applications can all load the same dll and use the same database code.
The data module itself is tested ok as part of an application - I've copied the form over to my dll project.
So in the dll entry point method, I need to initialize the data module:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
//if I don't call this, I get an exception on initializing the data module
CoInitialize(NULL);
//initialize a standard VCL form; seems to works fine
//I'm not using Application->CreateForm as I don't want the form to appear straight away
if(!MyForm) MyForm = new TMyForm(Application);
//this doesn't work - the thread seems to hang in the TDataModule base constructor?
//I've also tried Application->CreateForm; same result
if(!MyDataModule) MyDataModule = new TMyDataModule(Application);
}
I've also seen something about how I need to call Application->Initialize before creating the form but this doesn't seem to make any difference.
Any ideas?
Thanks
You really should not be doing very much work in your DllEntryPoint() at all. Certainly not calling CoInitialize(), anyway. It is not the DLL's responsibility to call that when loaded. It is the calling app's responsibility before loading the DLL.
You should either:
export an additional function to initialize your DLL and then have the app it after loading the DLL (same for uninitialing the DLL before unloading it)
don't create your TForm/TDataModule until the first time the DLL actually needs them.
move your TForm/TDataModule into their own worker thread inside the DLL. In this case, you would then call CoIniitalize().
And in all cases, don't relay on the DLL's Application object to manage the lifetime of your TForm/TDataModule. Free them yourself instead before the DLL is unloaded.

unable to successfully call function in dynamically loaded plugin in c++

I've successfully loaded a C++ plugin using a custom plugin loader class. Each plugin has an extern "C" create_instance function that returns a new instance using "new".
A plugin is an abstract class with a few non-virtual functions and several protected variables(std::vector refList being one of them).
The plugin_loader class successfully loads and even calls a virtual method on the loaded class (namely "std::string plugin::getName()".
The main function creates an instance of "host" which contains a vector of reference counted smart pointers, refptr, to the class "plugin". Then, main creates an instance of plugin_loader which actually does the dlopen/dlsym, and creates an instance of refptr passing create_instance() to it. Finally, it passes the created refptr back to host's addPlugin function. host::addPlugin successfully calls several functions on the passed plugin instance and finally adds it to a vector<refptr<plugin> >.
The main function then subscribes to several Apple events and calls RunApplicationEventLoop(). The event callback decodes the result and then calls a function in host, host::sendToPlugin, that identifies the plugin the event is intended for and then calls the handler in the plugin. It's at this point that things stop working.
host::sendToPlugin reads the result and determines the plugin to send the event off to.
I'm using an extremely basic plugin created as a debugging plugin that returns static values for every non-void function.
Any call on any virtual function in plugin in the vector causes a bad access exception. I've tried replacing the refptrs with regular pointers and also boost::shared_ptrs and I keep getting the same exception. I know that the plugin instance is valid as I can examine the instance in Xcode's debugger and even view the items in the plugin's refList.
I think it might be a threading problem because the plugins were created in the main thread while the callback is operating in a seperate thread. I think things are still running in the main thread judging by the backtrace when the program hits the error but I don't know Apple's implementation of RunApplicationEventLoop so I can't be sure.
Any ideas as to why this is happening?
class plugin
{
public:
virtual std::string getName();
protected:
std::vector<std::string> refList;
};
and the pluginLoader class:
template<typename T> class pluginLoader
{
public: pluginLoader(std::string path);
// initializes private mPath string with path to dylib
bool open();
// opens the dylib and looks up the createInstance function. Returns true if successful, false otherwise
T * create_instance();
// Returns a new instance of T, NULL if unsuccessful
};
class host
{
public:
addPlugin(int id, plugin * plug);
sendToPlugin(); // this is the problem method
static host * me;
private:
std::vector<plugin *> plugins; // or vector<shared_ptr<plugin> > or vector<refptr<plugin> >
};
apple event code from host.cpp;
host * host::me;
pascal OSErr HandleSpeechDoneAppleEvent(const AppleEvent *theAEevt, AppleEvent *reply, SRefCon refcon) {
// this is all boilerplate taken straight from an apple sample except for the host::me->ae_callback line
OSErr status = 0;
Result result = 0;
// get the result
if (!status) {
host::me->ae_callback(result);
}
return status;
}
void host::ae_callback(Result result) {
OSErr err;
// again, boilerplate apple code
// grab information from result
if (!err)
sendToPlugin();
}
void host::sendToPlugin() {
// calling *any* method in plugin results in failure regardless of what I do
}
EDIT: This is being run on OSX 10.5.8 and I'm using GCC 4.0 with Xcode. This is not designed to be a cross platform app.
EDIT: To be clear, the plugin works up until the Apple-supplied event loop calls my callback function. When the callback function calls back into host is when things stop working. This is the problem I'm having, everything else up to that point works.
Without seeing all of your code it isn't going to be easy to work out exactly what is going wrong. Some things to look at:
Make sure that the linker isn't throwing anything away. On gcc try the compile options -Wl -E -- we use this on Linux, but don't seem to have found a need for it on the Macs.
Make sure that you're not accidentally unloading the dynamic library before you've finished with it. RAII doesn't work for unloading dynamic libraries unless you also stop exceptions at the dynamic library border.
You may want to examine our plug in library which works on Linux, Macs and Windows. The dynamic loading code (along with a load of other library stuff) is available at http://svn.felspar.com/public/fost-base/trunk/
We don't use the dlsym mechanism -- it's kind of hard to use properly (and portably). Instead we create a library of plugins by name and put what are basically factories in there. You can examine how this works by looking at the way that .so's with test suites can be dynamically loaded. An example loader is at http://svn.felspar.com/public/fost-base/trunk/fost-base/Cpp/fost-ftest/ftest.cpp and the test suite registration is in http://svn.felspar.com/public/fost-base/trunk/fost-base/Cpp/fost-test/testsuite.cpp The threadsafe_store holds the factories by name and the suite constructor registers the factory.
I completely missed the fact that I was calling dlclose in my plugin_loader's dtor and for some reason the plugins were getting destructed between the RunApplicatoinEventLoop call and the call to sendToPlugin. I removed dlclose and things work now.

Changing the Total Number of Recent Files

I'd like the user to be able to edit the number of recent files shown in the File menu of my MFC application. I've used two very good references:
http://www.codeproject.com/KB/menus/changemru.aspx
http://www.microsoft.com/msj/0899/c/c0899.aspx
It involves deleting and recreating the CRecentFileList object stored in CWinApp::m_pRecentFileList. Unfortunately, I find that the menu is not updated properly after replacing the CRecentFileList. See code snippet below:
void CMyWinApp::SetMRUListSize( int size )
{
// size guaranteed to be between 1 and 16
delete m_pRecentFileList ;
LoadStdProfileSettings( size ) ;
}
What can I do to ensure that what is drawn into the File menu is synchronized with m_pRecentFileList after I recreate the object?
My CApp derives from CWinApp. In initInstance, you have this line:
LoadStdProfileSettings(10);
At the end of InitInstance, add this code:
m_pmf->m_pRecentFileList = m_pRecentFileList;
Here m_pmf is my MainFrame class and I created a member CMainFrame::m_pRecentFileList of type CRecentFileList which is in the MFC source file filelist.cpp. m_pRecentFileList on the right is protected and CMainFrame doesn't have access to it from outside InitInstance, but you can make a functional copy here.
At the end of CMainFrame::OnClose, force a registry update by:
m_pRecentFileList->WriteList();
// Force registry update on exit. This doesn't work without forcing.
I don't even have to rebuild m_pRecentFileList, the MRU mechanism updates it correctly. Example: 5 MRU items, the first is moved to another directory and can no longer be found. Stepping through the code in the debugger shows that the bad entry is removed from the list. For some reason, the updated list isn't saved correctly unless I force it as explained above. I originally thought the problem might have something to do with privileges (64-bit Win7), but running the app as admin didn't help.
Some of Microsoft's documentation suggest you should call CWinApp::LoadStdProfileSettings from within InitInstance. This suggests to me that it's something done once during initialisation rather than at run time.
Have you tried fully implementing the second of the two links you provided? My guess is you need to add the second part instead of the call to CWinApp::LoadStdProfileSettings:
m_pRecentFileList = new CRecentFileList(0, strSection, strEntryFormat, nCount);
if(m_pRecentFileList)
{
bReturn = TRUE;
// Reload list of MRU files from registry
m_pRecentFileList->ReadList();
}
[Edit] Apparently m_pRecentFileList points to an CRecentFileList Class . Have you tried calling CRecentFileList::UpdateMenu?
There's another CodeProject example which might help too.