LNK2019 示例来自 MSDN

LNK2019 with a sample from MSDN

试图从 Common File Dialog Sample got some LNK2019 中的 .cpp 文件复制代码。链接 3 个函数似乎有问题。 以下是错误:

Severity    Code    Description Project File    Line    Suppression State
Error   LNK2019 unresolved external symbol __imp_TaskDialog referenced in function "public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl CDialogEventHandler::ChooseFromFolder(void)" (?ChooseFromFolder@CDialogEventHandler@@QEAA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ)    Build-A-Font    C:\Users\nadav\Desktop\SFML\Build-A-Font\Build-A-Font\FileDialog.obj    1   
Error   LNK2019 unresolved external symbol __imp_PSGetPropertyDescriptionListFromString referenced in function "public: virtual long __cdecl CDialogEventHandler::OnTypeChange(struct IFileDialog *)" (?OnTypeChange@CDialogEventHandler@@UEAAJPEAUIFileDialog@@@Z) Build-A-Font    C:\Users\nadav\Desktop\SFML\Build-A-Font\Build-A-Font\FileDialog.obj    1   
Error   LNK2019 unresolved external symbol QISearch referenced in function "public: virtual long __cdecl CDialogEventHandler::QueryInterface(struct _GUID const &,void * *)" (?QueryInterface@CDialogEventHandler@@UEAAJAEBU_GUID@@PEAPEAX@Z)   Build-A-Font    C:\Users\nadav\Desktop\SFML\Build-A-Font\Build-A-Font\FileDialog.obj    1   

这是他们根据我的需要修改后的代码:

#pragma once
#define STRICT_TYPED_ITEMIDS
#include <shlobj.h>
#include <shlwapi.h>
#include <string>
#include <sstream>

#pragma comment(linker, "\"/manifestdependency:type='Win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
// Added for changing the entry point
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")

const COMDLG_FILTERSPEC c_rgSaveTypes[] =
{
    {L"Word Document (*.doc; *.docx)",  L"*.doc;*.docx"},
    {L"Powerpoint Presentation (*.ppt; *.pptx)",  L"*.ppt;*.pptx"},
    {L"Web Page (*.htm; *.html)",       L"*.htm;*.html"},
    {L"Text Document (*.txt)",          L"*.txt"},
    {L"All Documents (*.*)",            L"*.*"}
};

// Indices of file types
#define INDEX_WORDDOC 1
#define INDEX_PRPNTPR 2
#define INDEX_WEBPAGE 3
#define INDEX_TEXTDOC 4

// 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

/* 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 },
#pragma warning(suppress:4838)
        };
        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 OnOverwrite(IFileDialog*, IShellItem*, FDE_OVERWRITE_RESPONSE*) { return S_OK; };
    // 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.
    IFACEMETHODIMP 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();
                    }
                    break;
                }
            }
            pfsd->Release();
        }
        return hr;
    };
    // IFileDialogControlEvents methods

    // 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.
    IFACEMETHODIMP 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;
    };
    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;
};

// 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;
}

// This code snippet demonstrates how to work with the common file dialog interface
std::string 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(c_rgSaveTypes), c_rgSaveTypes);
                        if (SUCCEEDED(hr))
                        {
                            // Set the selected file type index to Word Docs for this example.
                            hr = pfd->SetFileTypeIndex(INDEX_WORDDOC);
                            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, &pszFilePath);
                                            if (SUCCEEDED(hr))
                                            {
                                                TaskDialog(NULL,
                                                    NULL,
                                                    L"CommonFileDialogApp",
                                                    pszFilePath,
                                                    NULL,
                                                    TDCBF_OK_BUTTON,
                                                    TD_INFORMATION_ICON,
                                                    NULL);
                                                CoTaskMemFree(pszFilePath);
                                            }
                                            psiResult->Release();
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                // Unhook the event handler.
                pfd->Unadvise(dwCookie);
            }
            pfde->Release();
        }
        pfd->Release();
    }
    TCHAR filepath[1024];
    if (hr == S_OK)
    {
        std::stringstream pff;
        pff << filepath;
        return pff.str();
    }
    return "";
}

我在从 github 下载的原始文件中更改了他们的代码,它工作得很好。我试图将它复制到另一个项目,但它不起作用(LNK2019 错误)

我想我会为此提供一种 meta-answer:Jerry 怎么知道你需要 link 使用哪些库? 而且,一如既往,答案就在文档中。

首先,让我们看看那些 linker 错误(为了清楚起见,我将它们删减了一些,使用模板通常会导致冗长/难以阅读的错误消息):

Unresolved symbol __imp_TaskDialog referenced in function <irrelevant>
Unresolved symbol __imp_PSGetPropertyDescriptionListFromString referenced in function <irrelevant>
Unresolved symbol QISearch referenced in function <irrelevant>

首先,您可以忽略 __imp_ 位。这只是告诉您该函数是从 DLL 导入的。所以这给我们留下了以下未解决的参考:

TaskDialog
PSGetPropertyDescriptionListFromString
QISearch

所以,是时候去谷歌搜索了。

TaskDialog 的文档是 here,如果您向下滚动到页面底部的 'requirements' 部分,您将看到:

Library Comctl32.lib

这就是那个(请注意,这里的大小写无关紧要,我不知道为什么 Microsoft 以如此奇怪的方式记录这些内容)。

同理,我们很容易发现PSGetPropertyDescriptionListFromString is in Propsys.lib, and QISearchShlwapi.lib中。故事结束。


我希望向您展示它是如何完成的。每个 Windows 开发人员都需要了解如何执行此操作以及如何查找和阅读 Microsoft 通常提供的(大量)文档。