Thumbnail Provider not working - c++

I'm trying to write a Windows Explorer thumbnail handler for our custom file type. I've got this working fine for the preview pane, but am having trouble getting it to work for the thumbnails.
Windows doesn't even seem to be trying to call the DllGetClassObject entry point.
Before I continue, note that I'm using Windows 7 and unmanaged C++.
I've registered the following values in the registry:
HKCR\CLSID\<my guid>
HKCR\CLSID\<my guid>\InprocServer32 (default value = path to my DLL)
HKCR\CLSID\<my guid>\InprocServer32\ThreadingModel (value = "Apartment")
HKCR\.<my ext>\shellex\{E357FCCD-A995-4576-B01F-234630154E96} (value = my guid)
I've also tried using the Win SDK sample, and that doesn't work. And also the sample project in this article (http://www.codemonkeycodes.com/2010/01/11/ithumbnailprovider-re-visited/), and that doesn't work.
I'm new to shell programming, so not really sure the best way of debugging this. I've tried attaching the debugger to explorer.exe, but that doesn't seem to work (breakpoints get disabled, and none of my OutputDebugStrings get displayed in the output window). Note that I tried setting the "DesktopProcess" in the registry as described in the WinSDK docs for debugging the shell, but I'm still only seeing one explorer.exe in the task manager - so that "may" be why I can't debug it??
Any help with this would be greatly appreciated!
Regards,
Dan.

I stumbled across this since you mentioned my blog ( codemonkeycodes.com ).
What problem are you having with my sample? Did you register you DLL using regsvr32? What version of Windows 7 are you on, 32 or 64?
Update:
I can't say what is or isn't working for you. I just downloaded the sample from my site, followed the directions and change the function
STDMETHODIMP CThumbnailProvider::GetThumbnail... to look like
{
*phbmp = NULL;
*pdwAlpha = WTSAT_UNKNOWN;
ULONG_PTR token;
GdiplusStartupInput input;
if (Ok == GdiplusStartup(&token, &input, NULL))
{
//gcImage.LogBuffer();
Bitmap * pBitmap = new Bitmap(188, 141);
if( pBitmap )
{
Color color(0, 0, 0);
pBitmap->GetHBITMAP(color, phbmp);
}
}
GdiplusShutdown(token);
if( *phbmp != NULL )
return NOERROR;
return E_NOTIMPL;
}
I registered the DLL and then created a new file with the proper extension, and tada, I had a nice black thumbnail.
I wish I could help you. Maybe you want to email me your code?

I've exactly the same problem. I cant make SDK or any sample works. I need COM sample because I must call Microsoft.Jet.OLEDB.4.0 which works only on 32 bits system.
I couldnt make this work: link
This works if AnyCPU is specified when compiling. Cant make it works for x86: link
This was nice under XP works like a charm: link
This show Adobe had problems with thumbnail An MS with Office 2007 (32 bits): link

Related

Why is my windows shell extension being invoked when I run an executable as administrator?

I'm working on a project that integrates with the Windows 10 file explorer to allow users to open selected files in our program. The shell extension I made works fine for the most part, but the problem I'm having is that my extension's IShellExtInit::Initialize(...) and IContextMenu::InvokeCommand(...) are being invoked when I right click an executable in my start menu results and click "Run as administrator". As far as I can tell, the only point in my code where I can confirm that my extension should actually be running when it is invoked is in DllGetClassObject(...) by checking that rclsid and my extension's GUID are equal.
For the basic setup of the shell extension, I followed this video series. The example extension in the videos only appeared for text files, but I changed mine to work on all file types.
Does anyone have any idea where this problem could be coming from? Here are the relevant parts of my code: https://gist.github.com/caevrobe/2865b5f472d668352a7a91fb5c66953a
I found a solution to this issue here. My fix was to return E_INVALIDARG if pici->lpVerb != NULL in my IContextMenu::InvokeCommand(...). When invoking "Run as administrator" pici->lpVerb was "runas". When using my extension through the context menu normally, it was null.
HRESULT MyContextMenuHandler::InvokeCommand(LPCMINVOKECOMMANDINFO pici) {
if (pici->lpVerb != NULL)
return E_INVALIDARG;
// rest of your code...
}
I'll leave the question up in case anyone else has the same issue.

Converting xls file into PDF in via cpp

I'm working on Visual Studio 2019 community, and I'm writing an MFC application in cpp. I want to add a functionality to the dialog box - such that when pressing a button it converts an existing xls file into pdf file. I've tried using the extension Aspose.Cells.Cpp and Aspose.PDF.Cpp with this code, but I get a lot of errors in the code of the DLL itself. I've also tried installing libHARU from here, but I didn't manage to make it work.
I've tried looking for another way to convert xls to pdf in MFC application in cpp, or another environment for creating desktop applications (which has some basic functionalities such as adding a pressing button in MFC, so I won't have to write everything from scratch with Win32 API), but I didn't find anything. Perhaps someone can explain to me how to perform this conversion in cpp so that I can use it when writing an MFC app, or could refer me to some useful information about another way of creating the application which supports this type of conversions?
You said in your comments to me that you can assume the user has Excel installed on his machine. If so, then you can use Excel's automation object model to manipulate Excel. You could do stuff like #import the type library and stuff...maybe that would make prettier C++. My experience, however, with driving Office apps is to stick more with just raw dispatch pointers. I've had less go wrong that way. This probably needs some more error checking and stuff, but that's up to you.
... and a big caveat. This is for an interactive app...which I halfway assume since you are using the MFC topic. With a service, Microsoft does not recommend you drive their Office applications in this manner. I tested this on one .xlsx file and it worked. Assume it will work for .xls as well. For other formats, you might need to use different import functions.
void ConvertFile(LPCWSTR lpszFileIn, LPCWSTR lpszFileOut)
{
CComDispatchDriver excel;
if (SUCCEEDED(excel.CoCreateInstance(_T("Excel.Application"), NULL, CLSCTX_LOCAL_SERVER)))
{
_variant_t vWorkbooks;
if (SUCCEEDED(excel.GetPropertyByName(L"Workbooks", &vWorkbooks)))
{
CComDispatchDriver workbooks(vWorkbooks.pdispVal);
_variant_t vWorksheet;
if (SUCCEEDED(workbooks.Invoke1(L"Open", &_variant_t(lpszFileIn), &vWorksheet)))
{
CComDispatchDriver worksheet(vWorksheet.pdispVal);
UINT xlTypePdf = 0;
HRESULT hr = worksheet.Invoke2(L"ExportAsFixedFOrmat", &_variant_t(xlTypePdf), &_variant_t(lpszFileOut), NULL);
if (FAILED(hr))
{
_com_error err(hr);
AfxMessageBox(err.ErrorMessage());
}
}
}
excel.Invoke0(L"Quit", NULL);
}
}
I'll give a solution, but this one doesn't use c++, just command line and LibreOffice app:
Use command line Libre Office tool: --convert-to
--convert-to output_file_extension[:output_filter_name] [--outdir output_dir] files
Batch convert files. If --outdir is not specified, then current working directory is used as output_dir.
Eg. --convert-to pdf *.doc
--convert-to pdf:writer_pdf_Export --outdir /home/user *.doc
More details about how to use this command you'll find here: https://help.libreoffice.org/Common/Starting_the_Software_With_Parameters
Later edit: Here is a sample of how to implement the above solution in a C++/MFC code:
STARTUPINFO si = { 0 };
si.cb = sizeof(si);
PROCESS_INFORMATION pi = { 0 };
sCommand.Format(_T("C:\\WINDOWS\\System32\\cmd.exe /c
C:\\LibreOffice\\program\\swriter.exe --convert-to pdf -outdir
c:\\temp\\ c:\\temp\\MyFile.doc"));
::CreateProcess(0, sCommand, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi);

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.

Hosting help content online

I'm trying to package some of my MFC applications as Windows 10 apps using Desktop Bridge.
I am having no end of trouble getting my HTML help file (CHM) included and working with the installed program (new versions of VS don't include the help file, and using workaround to include that file results in a file that I don't have the rights to access).
So it makes me wonder about hosting the online help on my website. A couple of the issues that arise is how best to host multiple help topics, and how to override (on a application-wide basis) the behavior of accessing help topics. (My app is dialog-based.)
So I just wondered if anyone else has done this already. I'd be curious to review how these issues were addressed. I was not able to find anything online.
I do host my html help in a single document, using html anchors to get to the topic of interest. If you have multiple pages, adapt MyHelp accordingly.
I didn't actually use the Desktop Bridge but wondered if you have tried something like this:
BOOL CMyDialog::OnHelpInfo(HELPINFO* pHelpInfo)
{
MyHelp(_T("HIDD_MYDIALOG")); // HTML anchor goes here
return CDialog::OnHelpInfo(pHelpInfo);
}
...
// a global helper function for showing help
void MyHelp(LPCTSTR anchor)
{
extern CMyApp theApp;
TCHAR *cp, buffer[1000];
// look for the html document in the program directory
strcpy(buffer, _T("file:///"));
DWORD dw = GetModuleFileName(theApp.m_hInstance, buffer + strlen(buffer), sizeof(buffer));
if (cp = strrchr(buffer, '\\'))
{
strcpy(cp+1, _T("MyHelpDocument.htm#"));
strcat(cp+1, anchor);
// for some reason, I don't want the default browser to open, just the Internet Explorer
ShellExecute(NULL, _T("open"), _T("iexplore"), buffer, NULL, SW_SHOWNORMAL);
// or, for real online help, use just '_T("http://myurl.com/myonlinehelpdocument.html#") + anchor'
// instead of 'buffer' and ommit all before ShellExecute()
}
}
I'm not sure if ShellExecute will behave the way it used to in the shop app though. But certainly there will be a way to open a URL somehow. You might want to try if the Internet Explorer ActiveX works to display your help pages inside the app.

Writing namespace extensions with Windows 7 integration

I'm new to the topic shell extensions and I'm looking for resources about namespace extensions. I would like to write a namespace extension which supports SFTP with all options to browse like FTP in the explorer.
I read the examples 1, 2 of zengxi from codeproject, but they don't compile right and seems to be old. I think that there were also many changes like the folder selection in the address bar.
Can somebody provide me some resources in the right direction or some working examples?
UPDATE:
It is important that the source is free. This is a non-profit project.
At the moment I found a good source on the MSDN called Explorer Data Provider Sample. This is up to date and provides some aliases Explorer Data Provider and Shell Data Source. What is yet missing is drop & drag support and a glue for supporting protocol links.
For future usage it would be great to find a way to associate a file extension with that shell data source like zip files.
Here is the full example of creating a namespace to mapping real files on Flickr.
However, only source codes available. No tutorial.
Hope it helps....
http://www.viksoe.dk/code/flickrdrive.htm
For Drag&Drop, this series of articles are a great point to start. After I understood that the files must have the flag can copy, can move, etc. I had almost the solution. The magic was to add one line in GetAttributesOf:
*rgfInOut |= SFGAO_CANCOPY|SFGAO_CANMOVE;
Also I had to publish the IDataObject in GetUIObjectOf like this:
if(riid == IID_IDataObject) {
PWSTR pszName;
hr = _GetName(apidl[0], &pszName);
hr = SHCreateDataObject(m_pidl, cidl, apidl,
new CFileDataObject(pszName), riid, ppv);
} else if(riid == IID_IDropTarget) {
// TODO publish
return E_NOINTERFACE;
}
That's all.
By the way what is the best practice for allocating CFileDataObject here?
Take a look at the EZNamespaceExtensionsMFC library which makes it very easy to develop namespace extensions. Check out its FileBrowser and RegBrowser samples which you can use a starting point.
DISCLAIMER: I work for LogicNP Software, the developer of EZNamespaceExtensionsMFC