FileOpenDialog 生成的 CDN_SELCHANGE 通知消息适用于 32 位构建而不是 64 位构建

Posted

技术标签:

【中文标题】FileOpenDialog 生成的 CDN_SELCHANGE 通知消息适用于 32 位构建而不是 64 位构建【英文标题】:CDN_SELCHANGE notification message generated by the FileOpenDialog work for 32bit build not for 64bit build 【发布时间】:2015-11-08 12:25:58 【问题描述】:

我从 Microsoft 编写的网站下载“可扩展对话框源”(C# 使用 P/Invoke),以展示如何将 Windows 窗体控件放置在常用文件对话框之一中。 (如:添加预览功能)。 本项目有测试客户端代码,即打开一个对话框,点击一张图片,可以在对话框右侧预览图片。 测试客户端代码在 32 位构建 中运行良好,但在 64 位构建 中无法运行。

经过一些调试,我发现这是因为在 64 位构建中,来自

的 CDN_SELCHANGE 通知消息
    [DllImport("ComDlg32.dll", CharSet = CharSet.Unicode)]
    internal static extern bool GetOpenFileName( ref OpenFileName ofn );

在 c# 代码中无法识别或无法正确处理。

// WM_NOTIFY - we're only interested in the CDN_SELCHANGE notification message
// we grab the currently-selected filename and fire our event
case WindowMessage.Notify:

    IntPtr ipNotify = new IntPtr( lParam );
    OfNotify ofNot = (OfNotify)Marshal.PtrToStructure( ipNotify, typeof(OfNotify) );
    UInt16 code = ofNot.hdr.code;
    if( code == CommonDlgNotification.SelChange )
    
       // This is the first time we can rely on the presence of the content panel
       // Resize the content and user-supplied panels to fit nicely
       FindAndResizePanels( hWnd );

       // get the newly-selected path
       IntPtr hWndParent = NativeMethods.GetParent( hWnd );
       StringBuilder pathBuffer = new StringBuilder(_MAX_PATH);
       UInt32 ret = NativeMethods.SendMessage( hWndParent, CommonDlgMessage.GetFilePath, _MAX_PATH, pathBuffer );
       string path = pathBuffer.ToString();

       // copy the string into the path buffer
       UnicodeEncoding ue = new UnicodeEncoding();
       byte[] pathBytes = ue.GetBytes( path );
       Marshal.Copy( pathBytes, 0, _fileNameBuffer, pathBytes.Length );

       // fire selection-changed event
       if( SelectionChanged != null ) SelectionChanged( path );
     
     return IntPtr.Zero;

即使我在 OpenFileDialog 中选择了不同的文件,ofNot.hdr.code 始终为 0,因此,应用程序在if( code == CommonDlgNotification.SelChange ) 之后永远不会遇到代码块。 任何人都可以让这个测试样本在 64 位版本中工作吗?提前致谢!

示例代码下载链接:ExtensibleDialogsSource.msi

【问题讨论】:

这是扩展文件对话框的错误方法。使用 IFileDialogCustomize。 我的水晶球说 OfNotify 声明是错误的。 NMHDR.idFrom 很容易摸索,比如应该是 IntPtr。 NMHDR.code 可见错误,不是 UInt16。 是的,你是对的!非常感谢! 【参考方案1】:

感谢所有好的回复,我找到了解决方案。当应用于64位应用程序时,一些结构定义错误。 在 NativeMethods.cs(由 Microsoft 编写,可能不针对 64 位应用程序)中,它定义了

/// <summary>
/// Part of the notification messages sent by the common dialogs
/// </summary>
[StructLayout(LayoutKind.Explicit)]
internal struct NMHDR

    [FieldOffset(0)]    public IntPtr   hWndFrom;
    [FieldOffset(4)]    public UInt16   idFrom;
    [FieldOffset(8)]    public UInt16   code;
;

/// <summary>
/// Part of the notification messages sent by the common dialogs
/// </summary>
[StructLayout(LayoutKind.Explicit)]
internal struct OfNotify

    [FieldOffset(0)]    public NMHDR    hdr;
    [FieldOffset(12)]   public IntPtr   ipOfn;
    [FieldOffset(16)]   public IntPtr   ipFile;
;

由于 IntPtr 的大小已从 4 字节更改为 8 字节。 所以我们需要重新定义结构

[StructLayout(LayoutKind.Explicit)]
internal struct NMHDR

    [FieldOffset(0)]
    public IntPtr hWndFrom;
    [FieldOffset(8)]
    public IntPtr idFrom;
    [FieldOffset(16)]
    public UInt16 code;
;

/// <summary>
/// Part of the notification messages sent by the common dialogs
/// </summary>
[StructLayout(LayoutKind.Explicit)]
internal struct OfNotify

    [FieldOffset(0)]
    public NMHDR hdr;
    [FieldOffset(20)]
    public IntPtr ipOfn;
    [FieldOffset(28)]
    public IntPtr ipFile;
;

现在它适用于 64 位应用程序。

在我看来,如果可能,我们最好使用 .Net lib,这可以让生活更轻松。

【讨论】:

【参考方案2】:

NMHDR idFrom 不是 IntPtr,而是 MSDN2005 中的 UINT。后来它被记录为 UINT_PTR...

【讨论】:

以上是关于FileOpenDialog 生成的 CDN_SELCHANGE 通知消息适用于 32 位构建而不是 64 位构建的主要内容,如果未能解决你的问题,请参考以下文章

Delphi自定义组件如何在属性面板中实现打开文件的对话框?

在 WPF 中单击按钮执行文件操作

System.Windows.Forms.SaveFileDialog 不强制执行默认扩展名

crt的python脚本crt标红了

动软代码生成器的功能说明

全能代码生成器,自动生成前后端代码生成项目框架生成JavaBean生成数据库文档自动化部署项目(TableGo v8.0.0)