这是我第一次尝试任何类型的 COM。我正在尝试在文件夹模式下启动“打开文件”对话框。代码可以编译,但应用程序无法启动,并且出现“无法在动态链接库中找到序号”错误。谁能查明是什么原因造成的?任何帮助将不胜感激。注意:该代码基于我对 MSDN 示例和文档的解释。现在我知道它不会做任何事情,我只是想让它显示对话框。
谢谢!
HRESULT OpenDirectory()
{
//Initialize the COM library
COINIT Init = COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE;
CoInitialize(NULL);
//CoCreate Folder Open Dialog Object
IFileDialog *pfd = NULL;
CLSID OpenFileID = CLSID_FileOpenDialog;
IID IFileDialog = IID_IFileDialog;
HRESULT hr = CoCreateInstance(&OpenFileID, NULL, CLSCTX_INPROC_SERVER, &IFileDialog, (void**) ( &pfd));
if (SUCCEEDED(hr))
{
IFileDialogEvents *pfde = NULL;
hr = OpenDirEventHandler(&pfde);
if (SUCCEEDED(hr))
{
//Hook the Event Handler
DWORD dwHook;
hr = pfd->lpVtbl->Advise(pfd, pfde, &dwHook);
if (SUCCEEDED(hr))
{
//flags
DWORD dwFlags;
hr = pfd->lpVtbl->GetOptions(pfd, &dwFlags);
if (SUCCEEDED(hr))
{
hr = pfd->lpVtbl->SetOptions(pfd, dwFlags | FOS_FORCEFILESYSTEM | FOS_PICKFOLDERS);
if (SUCCEEDED(hr))
{
hr = pfd->lpVtbl->Show(pfd, NULL);
if (SUCCEEDED(hr))
{
IShellItem *psiResult;
hr = pfd->lpVtbl->GetResult(pfd, &psiResult);
// We are just going to print out the
// name of the file for sample sake.
PWSTR pszFilePath = NULL;
hr = psiResult->lpVtbl->GetDisplayName(pfd, SIGDN_FILESYSPATH,
&pszFilePath);
if (SUCCEEDED(hr))
{
TaskDialog(NULL,
NULL,
L"CommonFileDialogApp",
pszFilePath,
NULL,
TDCBF_OK_BUTTON,
TD_INFORMATION_ICON,
NULL);
CoTaskMemFree(pszFilePath);
}
psiResult->lpVtbl->Release(psiResult);
}
}
}
}
pfd->lpVtbl->Unadvise(pfd, dwHook);
}
pfde->lpVtbl->Release(pfde);
}
pfd->lpVtbl->Release(pfd);
return 0;
}
HRESULT STDMETHODCALLTYPE FileOK(IFileDialog *pfd)
{
return 0;
}
HRESULT STDMETHODCALLTYPE SelectionChange(IFileDialog *pfd)
{
return 0;
}
HRESULT STDMETHODCALLTYPE DirOverwrite(IFileDialog *pfd)
{
return 0;
}
OpenDirEventHandler(IFileDialogEvents *pfde)
{
pfde->lpVtbl->OnFileOk = &FileOK;
pfde->lpVtbl->OnSelectionChange = &SelectionChange;
pfde->lpVtbl->OnOverwrite = &DirOverwrite;
}
“Ordinal 无法位于动态链接库”错误是由于 TaskDialog 需要链接到公共控件 dll,您可以按照此处的说明进行修复 https://stackoverflow.com/a/4308532 /403671,即将这一行添加到您的代码中:
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
或者使用
app.manifest
文件,如下所述:启用视觉样式
然后你必须完全实现一个暴露给世界的COM接口,你不能只实现你需要的方法,并且你必须正确初始化v表,例如,像这样:
IFileDialogEvents pfde;
// build the full v-table
pfde.lpVtbl = malloc(sizeof(IFileDialogEventsVtbl)); // make sure you free sometimes in the future
pfde.lpVtbl->AddRef = &OnAddRefOrRelease;
pfde.lpVtbl->Release = &OnAddRefOrRelease;
pfde.lpVtbl->QueryInterface = &OnQueryInterface;
pfde.lpVtbl->OnFolderChange = &OnFolderChange;
pfde.lpVtbl->OnFolderChanging = &OnFolderChanging;
pfde.lpVtbl->OnSelectionChange = &OnSelectionChange;
pfde.lpVtbl->OnOverwrite = &OnOverwrite;
pfde.lpVtbl->OnFileOk = &OnFileOk;
pfde.lpVtbl->OnTypeChange = &OnTypeChange;
pfde.lpVtbl->OnShareViolation = &OnShareViolation;
// hook the Event Handler
DWORD dwHook;
hr = pfd->lpVtbl->Advise(pfd, &pfde, &dwHook);
现在,对于每个方法,您必须声明接口定义中声明的所有参数,包括作为第一个参数的“this”指针,如下所示:
HRESULT STDMETHODCALLTYPE OnFolderChanging(IFileDialogEvents* this, IFileDialog* pfd, IShellItem* psiFolder)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnFolderChange(IFileDialogEvents* this, IFileDialog* pfd)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnShareViolation(IFileDialogEvents* this, IFileDialog* pfd, FDE_SHAREVIOLATION_RESPONSE* pResponse)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnTypeChange(IFileDialogEvents* this, IFileDialog* pfd)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnFileOk(IFileDialogEvents* this, IFileDialog* pfd)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnSelectionChange(IFileDialogEvents* this, IFileDialog* pfd)
{
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnOverwrite(IFileDialogEvents* this, IFileDialog* pfd, IShellItem* psi, FDE_OVERWRITE_RESPONSE* pResponse)
{
return S_OK;
}
ULONG STDMETHODCALLTYPE OnQueryInterface(IFileDialogEvents* this, REFIID riid, void** ppvObject)
{
if (riid == &IID_IFileDialogEvents)
{
*ppvObject = this;
return S_OK;
}
return E_NOINTERFACE;
}
ULONG STDMETHODCALLTYPE OnAddRefOrRelease(IFileDialogEvents* this)
{
// this is a hack, ok for sample, implement it properly
return 1;
}
然后,GetDisplayName 中存在一个错误,您必须始终传递“this”(此处为
psiResult
)作为第一个参数:
hr = psiResult->lpVtbl->GetDisplayName(psiResult, SIGDN_FILESYSPATH, &pszFilePath);