IProgressDialog .NET






4.76/5 (10投票s)
一个 .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_ProgressDialog
和 IID_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
完成操作时值的属性。
事件
我决定只创建两个事件——OnUserCancelled
和 OnBeforeProgressUpdate
。
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 控件,拥有所有炫酷的设计师功能 >:)。