GetOpenFileName在64位系统上失败,但在32位系统上可以工作?

4
我有以下代码,用于使用Win32 API打开文件打开对话框。它在32位上运行良好,但在64位(在DLL中)使用时失败。我做错了什么?
 char Filestring[256];
Filter = "OBJ files\0*.obj\0\0";
char* returnstring = NULL;



OPENFILENAME opf;
opf.hwndOwner = mainHWND;
opf.lpstrFilter = Filter;
opf.lpstrCustomFilter = 0;
opf.nMaxCustFilter = 0L;
opf.nFilterIndex = 1L;
opf.lpstrFile = Filestring;
opf.lpstrFile[0] = '\0';
opf.nMaxFile = 256;
opf.lpstrFileTitle = 0;
opf.nMaxFileTitle=50;
opf.lpstrInitialDir = Path;
opf.lpstrTitle = "Open Obj File";
opf.nFileOffset = 0;
opf.nFileExtension = 0;
opf.lpstrDefExt = "*.*";
opf.lpfnHook = NULL;
opf.lCustData = 0;
opf.Flags = (OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT) & ~OFN_ALLOWMULTISELECT;
opf.lStructSize = sizeof(OPENFILENAME);

if(GetOpenFileName(&opf))
{
    returnstring = opf.lpstrFile;
    if (returnstring) {
        result = returnstring;
    }

}

编辑:失败意味着打开文件对话框没有出现。代码仍然返回 0 没有任何错误。

编辑 2:我已经调用了 CommDlgExtendedError(),它返回了 1。从 MSDN 参考中可以看出,这是否意味着对话框具有无效的 lStructSize?我已经检查了 sizeof(OPENFILENAME),它返回了 140 字节。

更新:在我的项目设置中,在代码生成下,"结构成员对齐" 设置为 4 字节(/Zp4)。我将其改回默认值,就神奇地解决了问题。请查看答案及其下面的评论以获取更多信息。


1
你是否碰巧先调用了 Wow64DisableWow64FsRedirection 函数(http://msdn.microsoft.com/en-us/library/aa365743.aspx)? - Cody Gray
你能给我们提供更多的上下文吗?这是什么样的一个项目?你在哪里使用这个DLL?它是否已经编译成64位,还是只是在64位Windows版本上运行32位DLL? - Cody Gray
2
你说“它失败了”,但事情不仅仅是失败,它们会报告错误。请养成习惯,调查这些错误并在发布时完整地包含它们。 - David Heffernan
实际上,我确实尝试获取任何错误。但是GetOpenFileName返回零。但我没有看到任何文件打开对话框。我不知道是否可以获得其他类型的错误消息。谢谢。 - rwb
此外,我确保mainHWND是有效的。当我使用该父HWND时,其他模态窗口可以正常工作。 - rwb
显示剩余2条评论
5个回答

6
您没有初始化lpTemplateName,因此它包含随机的堆栈噪音。这反过来会导致引用包含堆栈噪音的'hInstance'。
在调用此类函数时,应首先将结构体清零,然后仅填写非零字段。可以像这样操作:
OPENFILENAME opf={0};
opf.lStructSize = sizeof(OPENFILENAME);
opf.hwndOwner = mainHWND;
opf.lpstrFilter = Filter;
opf.nFilterIndex = 1L;
opf.lpstrFile = Filestring;
opf.lpstrFile[0] = '\0';
opf.nMaxFile = 256;
opf.lpstrInitialDir = Path;
opf.lpstrTitle = "Open Obj File";
opf.lpstrDefExt = "*.*";
opf.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;

既然一开始就没有包含OFN_ALLOWMULTISELECT,那么就没有必要明确排除它!

编辑

您在评论中指出此方法无效。调用CommDlgExtendedError是个好主意,可以告诉您为什么它失败了。

您也可以尝试运行最简单的GetOpenFileName,代码如下:

char Filestring[MAX_PATH] = "\0";
OPENFILENAME opf={0};
opf.lStructSize = sizeof(OPENFILENAME);
opf.lpstrFile = Filestring;
opf.nMaxFile = MAX_PATH;
GetOpenFileName(&opf);

@Cody 传递随机未初始化的堆栈噪声可能会解释这一点。 - David Heffernan
@gutsblow OPENFILENAME 结构体有两个版本(请参阅 MSDN)。你使用的是哪一个版本? - David Heffernan
是的,我认为你是对的。我的项目出了点问题。我创建了一个纯净的x64控制台项目,并将sizeof(OPENFILENAME)返回为152字节。我讨厌这种情况发生。 - rwb
我同意。但说实话,除了默认的那些宏之外,我的项目中没有任何奇怪的宏。我会查一下。再次感谢! - rwb
1
@gutsblow 控制结构体对齐方式和打包使用的设置。如果您不熟悉这些术语,请阅读维基百科文章:http://en.wikipedia.org/wiki/Data_structure_alignment - David Heffernan
显示剩余13条评论

3
我遇到了与上述内容相同的问题,并提供了一个部分解决方案: 这个简单的例子(如上所示)在x64模式下无法正常工作。 我将编译选项“成员对齐”从1字节/Zp1更改为默认值,这解决了问题(同时引入了其他问题!!!)。
以下是需要翻译的内容:

char Filestring[MAX_PATH] = "\0"; OPENFILENAME opf={0}; opf.lStructSize = sizeof(OPENFILENAME); opf.lpstrFile = Filestring; opf.nMaxFile = MAX_PATH; GetOpenFileName(&opf);


2

要了解更多信息,您应该调用CommDlgExtendedError来获取出现的错误代码。此外,我建议使用以下代码初始化结构体的所有成员变量为0:

ZeroMemory(&opf, sizeof(opf));

由于文件打开对话框实际上是一个COM组件,因此值得检查您的线程公寓状态是否在64位下不同。

if( RPC_E_CHANGED_MODE == CoInitialize(NULL) )
   ASSERT(FALSE); // MTA Apartment found
CoUnitialize()

您好,Alois Kraus先生:


1
"OPENFILENAME opf={0}" 相当于 "ZeroMemory"。建议使用 CommDlgExtendedError。 - David Heffernan
我不认为COM在这里是相关的。如果我没记错,GetOpenFileName 不是一个 COM 接口,而是可以追溯到 Windows 3.1!无论如何,我相信现代版本确实是用 COM 实现的,但如果你通过 GetOpenFileName 进入,Windows 就会启动许多其他线程并在这些线程中运行 COM。它必须为您处理 COM 初始化,因为最初设计 GetOpenFileName 是不需要 COM 初始化的。 - David Heffernan
你好,我调用了CommDlgExtendedError()函数并返回了1。根据MSDN的参考资料,这是否意味着对话框具有无效的lStructSize? - rwb
虽然这不应该有影响,但你可以尝试将ofn.lStructSize设置为sizeof(ofn)。此外,我建议你尝试一下示例代码http://msdn.microsoft.com/en-us/library/ms646829%28v=vs.85%29.aspx#open_file,看看是否出现相同的问题。如果示例代码也出现了相同的问题,那么可能是在64位系统下存在某些内存损坏。 - Alois Kraus
他已经执行了 ofn.lStructSize = sizeof(ofn); 但问题在于这将其设置为 140 而实际应该是 136。 - David Heffernan

1

我通过在包含头文件之前适当设置打包方式来解决了这个问题。这样,在这个函数的目的下,我们使用了“默认”的16字节对齐方式,但不必为程序的其余部分更改打包对齐方式:

#ifdef _WIN64
  #pragma pack( push )
  #pragma pack( 16 ) 
  #include "Commdlg.h"
  #pragma pack( pop )
#else
  #include "Commdlg.h"
#endif   // _WIN64

1

在 Microsoft Office 2010 64 位中,我们放弃了并使用了内部包装器,因为结构变成了 140 字节,而我们不确定如何更改对齐方式。

Application.GetOpenFilename(FileFilter,FilterIndex,Title,ButtonText,MultiSelect)和 Application.GetSaveAsFilename(InitialFilename,FileFilter,FilterIndex,Title,ButtonText)

http://msdn.microsoft.com/en-us/library/ff834966.aspx

http://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel._application.getopenfilename.aspx

不用说,我们认为所有在Excel中使用相当重的应用程序的个人都应该开始考虑其他选项,因为在多个客户端和平台上维护未来版本可能会变得非常疯狂!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接