调用GetFileSaveName时CommDlgExtendedError返回65535错误咨询
问题描述
我方通过C代码调用GetFileSaveName函数,该C代码由PowerBuilder调用,经评估该因素与当前问题无关。目前问题仅在1台已安装全部更新的Windows 10设备上复现:函数返回0,调用CommDlgExtendedError返回错误码65535(CDERR_DIALOGFAILURE),官方文档说明该错误含义为“对话框无法创建,通用对话框函数调用DialogBox失败,例如传入无效窗口句柄时会触发该错误”。
已确认该用户设备的系统通用对话框未完全损坏,可正常唤起记事本的文件打开、保存对话框。此前我方代码曾因传入GetOpenFileName的OPENFILENAME结构中hWndOwner指向已销毁窗口句柄触发同错误码,但本次场景中该参数设为0,可排除该原因。同版本代码在数千名用户设备上均正常运行。
可能触发原因
- OPENFILENAME结构大小不匹配:代码中
lStructSize赋值用的是ll_sizeof变量,首先要确认该变量在目标Windows 10设备上计算的大小是否正确。Vista及以上版本的Windows对OPENFILENAME结构有扩展,若PowerBuilder运行时计算的结构大小和当前系统预期的大小不匹配,会直接触发DialogBox创建失败。仅单设备复现的话,可能是该设备存在罕见的shell扩展修改了通用对话框的结构校验逻辑。 - 筛选器字符串格式错误:通用对话框要求筛选器字符串是双零结尾的格式,每组说明和后缀之间用单个零分隔。代码中的
of_Parse和of_StringToChar如果在处理特殊字符(比如该用户场景下的筛选器里包含\0字符、多字节特殊符号)时没有正确补零,会导致结构校验失败,单设备场景下可能是该用户的筛选器配置和其他用户不同。 - lpstrFile缓冲区参数异常:虽然代码调用了LocalAlloc带LMEM_ZEROINIT,但要确认
MAX_LENGTH的取值是否超过该设备上通用对话框允许的最大路径长度限制。部分Windows 10设备如果关闭了长路径支持,且传入的初始路径+文件名长度接近260字符阈值时,会偶发对话框创建失败。 - 第三方shell扩展注入冲突:仅单设备复现的最高概率原因是该设备安装了特殊的资源管理器shell扩展,会注入到所有调用通用对话框的进程里。记事本可以正常调用是因为记事本作为系统签名程序,部分安全类shell扩展会跳过对它的注入,而你的程序没有微软签名,被扩展注入后修改了对话框初始化逻辑导致创建失败,可让用户禁用所有非微软的shell扩展后重试验证。
- 权限配置异常:如果你的程序以管理员权限运行,而当前登录用户是标准用户,UAC虚拟化开启的状态下,部分Win10小版本会存在通用对话框创建失败的兼容性问题,可检查该设备的UAC配置和程序运行权限是否和其他用户不同。
- 字符编码不匹配:代码里固定用CHARSIZE=2对应Unicode编码,如果该设备的系统默认编码被修改为非Unicode的特殊编码,且PowerBuilder运行时的字符编码和系统预期不符,会导致传入的字符串参数乱码,触发结构校验失败。
所用OPENFILENAME结构初始化代码(PowerScript)
OPENFILENAME iOFN CONSTANT long CHARSIZE = 2 aul_flags = OFN_HIDEREADONLY + OFN_EXPLORER + OFN_FILEMUSTEXIST+ OFN_NOCHANGEDIR + OFN_DONTADDTORECENT RtlZeroMemory(iOFN, ll_sizeof) // initialize structure iOFN.lStructSize = ll_sizeof iOFN.nFilterIndex = 1 iOFN.nMaxFile = MAX_LENGTH iOFN.hWndOwner = il_hWnd iOFN.Flags = aul_flags + OFN_ENABLESIZING /* needed when using hook procedure */ // allocate memory and copy title ll_length = Len(as_title) * CHARSIZE iOFN.lpstrTitle = LocalAlloc(LMEM_ZEROINIT, ll_length + 2) RtlMoveMemory(iOFN.lpstrTitle, as_title, ll_length) // allocate memory and copy filter this.of_Parse(",", as_filter, ls_filter) li_max = UpperBound(ls_filter) /* count of 1-based array elements */ For li_cnt = 1 To li_max ll_length = this.of_StringToChar(Trim(ls_filter[li_cnt]), lc_filter) Next ll_length = UpperBound(lc_filter) * CHARSIZE iOFN.lpstrFilter = LocalAlloc(LMEM_ZEROINIT, ll_length) RtlMoveMemory(iOFN.lpstrFilter, lc_filter, ll_length) // allocate memory and copy default extension (if given) If as_extension <> "" Then ll_length = Len(as_extension) * CHARSIZE iOFN.lpstrDefExt = LocalAlloc(LMEM_ZEROINIT, ll_length) RtlMoveMemory(iOFN.lpstrDefExt, as_extension, ll_length) End If // allocate memory and copy initialdir (if given) If as_initdir <> "" Then ll_length = Len(as_initdir) * CHARSIZE iOFN.lpstrInitialDir = LocalAlloc(LMEM_ZEROINIT, ll_length) RtlMoveMemory(iOFN.lpstrInitialDir, as_initdir, ll_length) End If // allocate memory for returned data lc_pathname = Space(MAX_LENGTH) iOFN.nMaxFile = MAX_LENGTH iOFN.lpstrFile = LocalAlloc(LMEM_ZEROINIT, MAX_LENGTH) If as_initialfile <> "" Then ll_length = Len(as_initialfile) * CHARSIZE RtlMoveMemory(iOFN.lpstrFile, as_initialfile, ll_length) End If // display dialog box lb_return = GetOpenFileName(iOFN)
内容的提问来源于stack exchange,提问作者Dan Cooperstock
相关产品推荐
相关产品推荐

