65.9K
CodeProject 正在变化。 阅读更多。
Home

IProgressDialog .NET

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.76/5 (10投票s)

2005年1月24日

CPOL

4分钟阅读

viewsIcon

81881

downloadIcon

1206

一个 .NET 包装器,用于 IProgressDialog 接口和一个 COM 对象,允许使用带有 AVI 动画和内置剩余时间计算的标准 Windows 进度对话框。

引言

本文的目的是展示一个“纯粹”的 .NET 包装器,用于 IProgressDialog [^],该接口由 sytelus 在他的 在你的应用程序中使用 Windows Explorer 进度对话框 文章中发布。

虽然原文写得很好,我也非常喜欢这个想法,但我发现它通过 .tlb 导入“太慢太重”。我也没能找到其他关于 .NET 实现的文章/示例,所以我决定自己试试。

所以,这是我提出的解决方案。

.NET 的接口和 COM 声明

首先,我搜索了 MSDN、Google 及其群组以获取有关接口的线索。我发现我需要 C# 版本

IProgressDialog * ppd;
CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, 
                                IID_IProgressDialog, (void **)&ppd);

经过一番谷歌搜索,我偶然发现了 MSDN 上的一篇文章 - COM 互操作第一部分:C# 客户端教程 [^],这让我开始了

[ComImport]
[Guid("F8383852-FCD3-11d1-A6B9-006097DF5BD4")] 
internal class ProgressDialog {
}

[ComImport]
[Guid("EBBC7C04-315E-11d2-B62F-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IProgressDialog {
    ...
}

其中对应的 GUID 分别是 CLSID_ProgressDialogIID_IProgressDialog

下一步是将接口定义从 C++ 翻译到 C#。对于有兴趣的人来说,它定义在 ShlObj.h 中。

长话短说,它完成了,并且一个测试应用程序在没有任何严重问题的情况下被组装起来,只有一个问题——将 bool 封送到 BOOL 以及反之亦然的问题。事实证明,这个问题有两个方面——首先,C++ 中有两种不同的 BOOL ——大小分别为 2 和 4 字节,而 bool 只有 1 字节(我不确定具体数字,也找不到我看到它的页面)。其次,VS.NET 2003 中有一个 bug

尝试了各种封送,但都没有成功,直到 **Wraith,** 建议使用 [PreserveSig] 属性。不出所料,它确实适用于 HasUserCancelled() 方法,这是最具问题的方法。

Wraith, 还通过与我们分享他关于 LoadLibraryEx 的研究成果,为我节省了一些时间,该函数允许将 DLL 作为资源加载,而无需处理 DllMain()

.NET 包装器

方法

在克服了这些障碍之后,就开始以一种用户友好的方式包装它们(尤其是 MS 在 ShlObj.h 中提供了接口定义的示例)。

我只暴露了三个接口方法给最终用户(即开发者),而不是全部暴露

  • public void Start( ProgressOperationCallback callback )

    回调函数将负责工作(例如,文件复制、数据挖掘等)并将完成的进度报告给进度对话框。

    此函数显示进度对话框、设置标题、取消消息和动画。当 Complete 达到 Total 时,它通过调用 Stop() 函数关闭对话框。

  • public void Stop()

    停止处理并隐藏对话框。

  • public void SetLine( IPD_Lines line, string text, bool compactPath )

    为指定的行设置文本。

属性

接口的其他方法都由属性替代

  • 动画

    获取或设置将在对话框中运行的 AVI 剪辑。

  • CancelMessage

    获取或设置如果用户取消操作将显示的取消消息。

  • Complete

    获取或设置应用程序定义的指示方法调用时操作已完成比例的值。

  • Complete64

    获取或设置应用程序定义的指示方法调用时操作已完成比例的值。

  • 标志

    获取或设置控制进度对话框操作的标志。

  • 标题

    获取或设置进度对话框的标题。

  • 总计

    获取或设置应用程序定义的指定 Complete 完成操作时值的属性。

  • Total64

    获取或设置应用程序定义的指定 Complete 完成操作时值的属性。

事件

我决定只创建两个事件——OnUserCancelledOnBeforeProgressUpdate

  • OnBeforeProgressUpdate 在对话框即将调用回调函数之前触发,以便程序可以根据需要更新文本消息。
  • OnUserCancelled 在用户单击“取消”按钮时触发。

使用代码

使用 .NET 包装器非常简单(我希望如此)。

WinProgressDialog pDialog;
private uint _max, _step = 0;

private void button1_Click(object sender, System.EventArgs e) {
    this.pDialog = new WinProgressDialog( this.Handle );

    if( this.pDialog != null ) {
        this.pDialog.Title            = "Hello world!";
        this.pDialog.CancelMessage    = "hold on a sec...";
        this.pDialog.Flags = 
            WinProgressDialog.IPD_Flags.Normal | 
            WinProgressDialog.IPD_Flags.Modal |
            WinProgressDialog.IPD_Flags.NoMinimize |
            WinProgressDialog.IPD_Flags.AutoTime
            ;
        this.pDialog.Animation = WinProgressDialog.IPD_Animations.FileMove;

        this.pDialog.Complete        = ( this._step = 0 );
        this.pDialog.Total            = ( this._max = DoCalc() );

        this.pDialog.OnUserCancelled += 
            new WinProgressDialog.UserCancelledHandler( 
                        pDialog_OnUserCancelled 
                        );
        this.pDialog.OnBeforeProgressUpdate += 
            new WinProgressDialog.BeforeProgressUpdateHandler( 
                        pDialog_OnBeforeProgressUpdate 
                        );

        WinProgressDialog.ProgressOperationCallback progressUpdate =
            new WinProgressDialog.ProgressOperationCallback( this.DoStep );

        this.pDialog.Start( progressUpdate );
    }

    this.pDialog.Dispose();
}

private uint DoCalc() {
    // pretend to do some calc 
    System.Threading.Thread.Sleep( 2000 );
    // get some biggish number
    Random rand = new Random();
    return (uint)rand.Next( 150, 500 );
}

private uint DoStep() {
    // pretend to do some calc
    System.Threading.Thread.Sleep( 250 );
    this._step += 13;
    return this._step;
}

关注点

有几件事情可以/需要完成,包括加载自定义资源而不是硬编码的 shell32.dll 的方法。之后,它可以被转换成一个真正的 .NET 控件,拥有所有炫酷的设计师功能 >:)。

© . All rights reserved.