I have been trying to figure this out for the last 8 hours but I can find absolutely nothing on the problem anywhere that I have looked.
I am attempting to create an application that can use the "Microsoft Way", as described in this MSDN Article, to open a Windows Vista and higher file dialog window. However I am having problems adapting the code to my project due to the following linker error.
Here is the full linker error that I am getting:
main.obj : error LNK2019: unresolved external symbol _QISearch@16 referenced in function "public: virtual long __stdcall CDialogEventHandler::QueryInterface(struct _GUID const &,void * *)" (?QueryInterface@CDialogEventHandler@@UAGJABU_GUID@@PAPAX@Z)
And here is the function declaration and definition in question:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
Finally here is my entire code from main.cpp, note that a lot of the code was taken from this Microsoft Sample:
#include <windows.h> // For common windows data types and function headers
#define STRICT_TYPED_ITEMIDS
#include <shlobj.h>
#include <objbase.h> // For COM headers
#include <shobjidl.h> // for IFileDialogEvents and IFileDialogControlEvents
#include <shlwapi.h>
#include <knownfolders.h> // for KnownFolder APIs/datatypes/function headers
#include <propvarutil.h> // for PROPVAR-related functions
#include <propkey.h> // for the Property key APIs/datatypes
#include <propidl.h> // for the Property System APIs
#include <strsafe.h> // for StringCchPrintfW
#include <shtypes.h> // for COMDLG_FILTERSPEC
#include <new>
#include "resource.h"
#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='7.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
const COMDLG_FILTERSPEC rgFExt[] =
{
{L"SQLite Database (*.sqlite)", L"*.sqlite"}
};
HWND ghMainWnd = 0;
HWND ghTBTN = 0;
HINSTANCE ghAppInst = 0;
RECT winRect;
PWSTR fName = NULL;
// Controls
#define CONTROL_GROUP 2000
#define CONTROL_RADIOBUTTONLIST 2
#define CONTROL_RADIOBUTTON1 1
#define CONTROL_RADIOBUTTON2 2 // It is OK for this to have the same ID as CONTROL_RADIOBUTTONLIST,
// because it is a child control under CONTROL_RADIOBUTTONLIST
// IDs for the Task Dialog Buttons
#define IDC_BASICFILEOPEN 100
#define IDC_ADDITEMSTOCUSTOMPLACES 101
#define IDC_ADDCUSTOMCONTROLS 102
#define IDC_SETDEFAULTVALUESFORPROPERTIES 103
#define IDC_WRITEPROPERTIESUSINGHANDLERS 104
#define IDC_WRITEPROPERTIESWITHOUTUSINGHANDLERS 105
/* File Dialog Event Handler *****************************************************************************************************/
class CDialogEventHandler : public IFileDialogEvents,
public IFileDialogControlEvents
{
public:
// IUnknown methods
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
{
static const QITAB qit[] = {
QITABENT(CDialogEventHandler, IFileDialogEvents),
QITABENT(CDialogEventHandler, IFileDialogControlEvents),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
IFACEMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
IFACEMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
// IFileDialogEvents methods
IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) { return S_OK; };
IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnSelectionChange(IFileDialog *) { return S_OK; };
IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; };
IFACEMETHODIMP OnTypeChange(IFileDialog *pfd);
IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; };
// IFileDialogControlEvents methods
IFACEMETHODIMP OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem);
IFACEMETHODIMP OnButtonClicked(IFileDialogCustomize *, DWORD) { return S_OK; };
IFACEMETHODIMP OnCheckButtonToggled(IFileDialogCustomize *, DWORD, BOOL) { return S_OK; };
IFACEMETHODIMP OnControlActivating(IFileDialogCustomize *, DWORD) { return S_OK; };
CDialogEventHandler() : _cRef(1) { };
private:
~CDialogEventHandler() { };
long _cRef;
};
// IFileDialogEvents methods
// This method gets called when the file-type is changed (combo-box selection changes).
// For sample sake, let's react to this event by changing the properties show.
HRESULT CDialogEventHandler::OnTypeChange(IFileDialog *pfd)
{
IFileSaveDialog *pfsd;
HRESULT hr = pfd->QueryInterface(&pfsd);
if (SUCCEEDED(hr))
{
UINT uIndex;
hr = pfsd->GetFileTypeIndex(&uIndex); // index of current file-type
if (SUCCEEDED(hr))
{
IPropertyDescriptionList *pdl = NULL;
switch (uIndex)
{
/*case INDEX_WORDDOC:
// When .doc is selected, let's ask for some arbitrary property, say Title.
hr = PSGetPropertyDescriptionListFromString(L"prop:System.Title", IID_PPV_ARGS(&pdl));
if (SUCCEEDED(hr))
{
// FALSE as second param == do not show default properties.
hr = pfsd->SetCollectedProperties(pdl, FALSE);
pdl->Release();
}
break;
case INDEX_WEBPAGE:
// When .html is selected, let's ask for some other arbitrary property, say Keywords.
hr = PSGetPropertyDescriptionListFromString(L"prop:System.Keywords", IID_PPV_ARGS(&pdl));
if (SUCCEEDED(hr))
{
// FALSE as second param == do not show default properties.
hr = pfsd->SetCollectedProperties(pdl, FALSE);
pdl->Release();
}
break;
case INDEX_TEXTDOC:
// When .txt is selected, let's ask for some other arbitrary property, say Author.
hr = PSGetPropertyDescriptionListFromString(L"prop:System.Author", IID_PPV_ARGS(&pdl));
if (SUCCEEDED(hr))
{
// TRUE as second param == show default properties as well, but show Author property first in list.
hr = pfsd->SetCollectedProperties(pdl, TRUE);
pdl->Release();
}*/
default:
break;
}
}
pfsd->Release();
}
return hr;
}
// IFileDialogControlEvents
// This method gets called when an dialog control item selection happens (radio-button selection. etc).
// For sample sake, let's react to this event by changing the dialog title.
HRESULT CDialogEventHandler::OnItemSelected(IFileDialogCustomize *pfdc, DWORD dwIDCtl, DWORD dwIDItem)
{
IFileDialog *pfd = NULL;
HRESULT hr = pfdc->QueryInterface(&pfd);
if (SUCCEEDED(hr))
{
if (dwIDCtl == CONTROL_RADIOBUTTONLIST)
{
switch (dwIDItem)
{
case CONTROL_RADIOBUTTON1:
hr = pfd->SetTitle(L"Longhorn Dialog");
break;
case CONTROL_RADIOBUTTON2:
hr = pfd->SetTitle(L"Vista Dialog");
break;
}
}
pfd->Release();
}
return hr;
}
// Instance creation helper
HRESULT CDialogEventHandler_CreateInstance(REFIID riid, void **ppv)
{
*ppv = NULL;
CDialogEventHandler *pDialogEventHandler = new (std::nothrow) CDialogEventHandler();
HRESULT hr = pDialogEventHandler ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
hr = pDialogEventHandler->QueryInterface(riid, ppv);
pDialogEventHandler->Release();
}
return hr;
}
/* Common File Dialog Snippets ***************************************************************************************************/
// This code snippet demonstrates how to work with the common file dialog interface
HRESULT BasicFileOpen()
{
// CoCreate the File Open Dialog object.
IFileDialog *pfd = NULL;
HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
if (SUCCEEDED(hr))
{
// Create an event handling object, and hook it up to the dialog.
IFileDialogEvents *pfde = NULL;
hr = CDialogEventHandler_CreateInstance(IID_PPV_ARGS(&pfde));
if (SUCCEEDED(hr))
{
// Hook up the event handler.
DWORD dwCookie;
hr = pfd->Advise(pfde, &dwCookie);
if (SUCCEEDED(hr))
{
// Set the options on the dialog.
DWORD dwFlags;
// Before setting, always get the options first in order not to override existing options.
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr))
{
// In this case, get shell items only for file system items.
hr = pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
if (SUCCEEDED(hr))
{
// Set the file types to display only. Notice that, this is a 1-based array.
hr = pfd->SetFileTypes(ARRAYSIZE(rgFExt), rgFExt);
if (SUCCEEDED(hr))
{
// Set the selected file type index to Word Docs for this example.
hr = pfd->SetFileTypeIndex(0);
if (SUCCEEDED(hr))
{
// Set the default extension to be ".doc" file.
hr = pfd->SetDefaultExtension(L"doc");
if (SUCCEEDED(hr))
{
// Show the dialog
hr = pfd->Show(NULL);
if (SUCCEEDED(hr))
{
// Obtain the result, once the user clicks the 'Open' button.
// The result is an IShellItem object.
IShellItem *psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr))
{
// We are just going to print out the name of the file for sample sake.
PWSTR pszFilePath = NULL;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &fName);
if (SUCCEEDED(hr))
{
MessageBox(0,"File Name: " + *fName, "Success",MB_OK);
CoTaskMemFree(pszFilePath);
}
psiResult->Release();
}
}
}
}
}
}
}
// Unhook the event handler.
pfd->Unadvise(dwCookie);
}
pfde->Release();
}
pfd->Release();
}
return hr;
}
void createWnd(HINSTANCE hInst, HWND* ghMainWnd);
void createBtns(HINSTANCE hInst, HWND* ghTBtn, HWND main);
// Application entry point
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
switch(LOWORD(wParam))
{
case ID_FILE_OPENDB:
BasicFileOpen();
MessageBox(0,"Open Database option works","Msg",MB_OK);
break;
default:
break;
}
break;
case WM_LBUTTONDOWN:
MessageBox(0, "WM_LBUTTONDOWN message.", "Msg", MB_OK);
return 0;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(ghMainWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR cmdLine, int showCmd)
{
ghAppInst = hInstance;
createWnd(ghAppInst, &ghMainWnd);
if (ghMainWnd == 0)
{
::MessageBox(0, "CreateWindow - Failed", 0, 0);
return false;
}
ShowWindow(ghMainWnd, showCmd);
UpdateWindow(ghMainWnd);
createBtns(ghAppInst, &ghTBTN, ghMainWnd);
if (ghTBTN == 0)
{
::MessageBox(0,"CreateButton - Failed", 0,0);
}
ShowWindow(ghTBTN, showCmd);
UpdateWindow(ghTBTN);
UpdateWindow(ghMainWnd);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
void centerWnd(HWND parent_window)
{
GetClientRect(parent_window, &winRect);
winRect.left = (winRect.right/2) - (1280/2);
winRect.top = (winRect.bottom/2) - (720/2);
}
void createWnd(HINSTANCE hInst, HWND* ghMainWnd)
{
HMENU mMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_MENU1));
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghAppInst;
wc.hIcon = ::LoadIcon(0, IDI_APPLICATION);
wc.hCursor = ::LoadCursor(0, IDC_ARROW);
wc.hbrBackground = (HBRUSH) ::GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = "MyWndClassName";
RegisterClass(&wc);
centerWnd(GetDesktopWindow());
*ghMainWnd = ::CreateWindow("MyWndClassName", "Space Crusade Database Editor V1.0", WS_OVERLAPPEDWINDOW, winRect.left, winRect.top, 1280, 720, 0, mMenu, ghAppInst, 0);
}
void createBtns(HINSTANCE hInst, HWND* ghTBtn, HWND main)
{
*ghTBtn = ::CreateWindow(WC_BUTTON, "Test Button", WS_TABSTOP|WS_VISIBLE|WS_CHILD|BS_DEFPUSHBUTTON,10,10,100,100,main,NULL,(HINSTANCE)GetWindowLong(main, GWL_HINSTANCE),NULL);
}
I got it. What was going on was that I had not added a library to the linker additional dependencies settings. I had tried this several times before but when the project would build the program would fail to run because of a configuration error.
The sample project I linked to in the OP has two other libraries besides the shlwapi.lib (propsys.lib and comctl32.lib) which were causing that problem. What I did not identify right away was that shlwapi was required for QISearch and that it was not causing the problem, I had not tried adding in each lib one at a time.
After trawling through MSDN articles about QISearch I had the idea to try just shlwapi on its own; success.