├── .gitattributes
├── install
├── 7z.dll
├── reg.cmd
├── unreg.cmd
├── RegAsm.exe
├── SevenZipSharp.dll
└── ZipInfoShell.dll
├── Resources
└── zip.png
├── ZipInfoShell.pfx
├── packages.config
├── ZipInfoShell.csproj.user
├── README_zh.md
├── ZipFileInfo.cs
├── README.md
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
└── Resources.resx
├── ZipInfoShell.csproj
├── LICENSE
├── ShellExtLib.cs
└── FileContextMenuExt.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/install/7z.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/install/7z.dll
--------------------------------------------------------------------------------
/install/reg.cmd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/install/reg.cmd
--------------------------------------------------------------------------------
/Resources/zip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/Resources/zip.png
--------------------------------------------------------------------------------
/ZipInfoShell.pfx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/ZipInfoShell.pfx
--------------------------------------------------------------------------------
/install/unreg.cmd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/install/unreg.cmd
--------------------------------------------------------------------------------
/install/RegAsm.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/install/RegAsm.exe
--------------------------------------------------------------------------------
/install/SevenZipSharp.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/install/SevenZipSharp.dll
--------------------------------------------------------------------------------
/install/ZipInfoShell.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VrezenStrijder/ZipInfoShell/HEAD/install/ZipInfoShell.dll
--------------------------------------------------------------------------------
/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ZipInfoShell.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
1 | # ZipInfoShell
2 | [[English]](./README.md)
3 |
4 | 一个windows右键菜单扩展应用.
5 | 当我在windows上要解压一个压缩包时,我常常需要先打开看看这个压缩包的最外层是一个目录还是包含很多个文件,如果是一个目录,那么我可以直接解压至当前目录,否则我可能需要先新建一个目录然后解压至其中,这个操作不复杂但是常常浪费我很多时间,因此我制作了这个工具能让我在压缩文件上点击右键时直接看到最外层的文件结构信息,并且给出推荐的解压方式,这对我很有用,希望你也能喜欢它.
6 |
7 | **安装:**
8 | - 下载源码
9 | - 打开目录 "install"
10 | - 运行 reg.cmd, 需要以管理员权限运行
11 |
12 | **卸载:**
13 | - 打开目录 "install"
14 | - 运行 unreg.cmd, 需要以管理员权限运行
15 |
--------------------------------------------------------------------------------
/ZipFileInfo.cs:
--------------------------------------------------------------------------------
1 | namespace ZipInfoShell
2 | {
3 | public class ZipFileInfo
4 | {
5 | public uint FileCount { get; set; }
6 |
7 | public int FirstLevelFileCount { get; set; }
8 |
9 | public int FirstLevelDirectoryCount { get; set; }
10 |
11 | public bool Unzip2NewFolder
12 | {
13 | get
14 | {
15 | if (FirstLevelDirectoryCount == 1 && FirstLevelFileCount == 0)
16 | {
17 | return false;
18 | }
19 | return true;
20 | }
21 | }
22 |
23 | public override string ToString()
24 | {
25 | return $"总文件数: {FileCount} (首层文件数: {FirstLevelFileCount},目录数: {FirstLevelDirectoryCount})";
26 | }
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ZipInfoShell
2 | [[中文]](./README_zh.md)
3 |
4 | A tiny windows shell extension app.
5 |
6 | When I need to extract a compressed file on Windows, I often have to open it first to see if the outermost layer is a single directory or contains multiple files. If it's a single directory, I can extract it directly to the current location. Otherwise, I might need to create a new folder first and then extract the contents into it. This operation isn't complicated, but it often wastes a lot of my time. That's why I created this tool that allows me to right-click on a compressed file and immediately see the file structure information of the outermost layer, as well as get a recommended extraction method. This is very useful for me, and I hope you'll like it too.
7 |
8 | **install:**
9 | - download this
10 | - open folder "install"
11 | - just run reg.cmd, need administrators
12 |
13 | **uninstall:**
14 | - open folder "install"
15 | - just run unreg.cmd, need administrators
16 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // 有关程序集的一般信息由以下
5 | // 控制。更改这些特性值可修改
6 | // 与程序集关联的信息。
7 | [assembly: AssemblyTitle("ZipInfoShell")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("ZipInfoShell")]
12 | [assembly: AssemblyCopyright("Copyright © 2024")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // 将 ComVisible 设置为 false 会使此程序集中的类型
17 | //对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
18 | //请将此类型的 ComVisible 特性设置为 true。
19 | [assembly: ComVisible(false)]
20 |
21 | // 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
22 | [assembly: Guid("1d621415-b53c-4403-b548-b2f719f7cdbc")]
23 |
24 | // 程序集的版本信息由下列四个值组成:
25 | //
26 | // 主版本
27 | // 次版本
28 | // 生成号
29 | // 修订号
30 | //
31 | //可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
32 | //通过使用 "*",如下所示:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
36 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // 此代码由工具生成。
4 | // 运行时版本:4.0.30319.42000
5 | //
6 | // 对此文件的更改可能会导致不正确的行为,并且如果
7 | // 重新生成代码,这些更改将会丢失。
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace ZipInfoShell.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// 一个强类型的资源类,用于查找本地化的字符串等。
17 | ///
18 | // 此类是由 StronglyTypedResourceBuilder
19 | // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
20 | // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
21 | // (以 /str 作为命令选项),或重新生成 VS 项目。
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// 返回此类使用的缓存的 ResourceManager 实例。
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ZipInfoShell.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// 重写当前线程的 CurrentUICulture 属性,对
51 | /// 使用此强类型资源类的所有资源查找执行重写。
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// 查找 System.Drawing.Bitmap 类型的本地化资源。
65 | ///
66 | internal static System.Drawing.Bitmap zip {
67 | get {
68 | object obj = ResourceManager.GetObject("zip", resourceCulture);
69 | return ((System.Drawing.Bitmap)(obj));
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/ZipInfoShell.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {1D621415-B53C-4403-B548-B2F719F7CDBC}
8 | Library
9 | Properties
10 | ZipInfoShell
11 | ZipInfoShell
12 | v4.8
13 | 512
14 | true
15 |
16 |
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 | false
25 | true
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 | true
37 |
38 |
39 | ZipInfoShell.pfx
40 |
41 |
42 |
43 |
44 |
45 |
46 | ..\packages\SevenZipSharp.Net45.1.0.19\lib\net45\SevenZipSharp.dll
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | True
69 | True
70 | Resources.resx
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | ResXFileCodeGenerator
84 | Resources.Designer.cs
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\zip.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
123 |
124 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/ShellExtLib.cs:
--------------------------------------------------------------------------------
1 | /********************************** Module Header **********************************\
2 | Module Name: ShellExtLib.cs
3 | Project: CSShellExtContextMenuHandler
4 | Copyright (c) Microsoft Corporation.
5 |
6 | The file declares the imported Shell interfaces: IShellExtInit and IContextMenu,
7 | implements the helper functions for registering and unregistering a shell context
8 | menu handler, and declares the Win32 enums, structs, consts, and functions used by
9 | the code sample.
10 |
11 | This source is subject to the Microsoft Public License.
12 | See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
13 | All other rights reserved.
14 |
15 | THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
16 | EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF
17 | MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
18 | \***********************************************************************************/
19 |
20 | #region Using directives
21 | using Microsoft.Win32;
22 | using System;
23 | using System.Runtime.InteropServices;
24 | using System.Runtime.InteropServices.ComTypes;
25 | using System.Text;
26 | #endregion
27 |
28 |
29 | namespace CSShellExtContextMenuHandler
30 | {
31 | #region Shell Interfaces
32 |
33 | [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
34 | [Guid("000214e8-0000-0000-c000-000000000046")]
35 | internal interface IShellExtInit
36 | {
37 | void Initialize(
38 | IntPtr /*LPCITEMIDLIST*/ pidlFolder,
39 | IntPtr /*LPDATAOBJECT*/ pDataObj,
40 | IntPtr /*HKEY*/ hKeyProgID);
41 | }
42 |
43 |
44 | [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
45 | [Guid("000214e4-0000-0000-c000-000000000046")]
46 | internal interface IContextMenu
47 | {
48 | [PreserveSig]
49 | int QueryContextMenu(
50 | IntPtr /*HMENU*/ hMenu,
51 | uint iMenu,
52 | uint idCmdFirst,
53 | uint idCmdLast,
54 | uint uFlags);
55 |
56 | void InvokeCommand(IntPtr pici);
57 |
58 | void GetCommandString(
59 | UIntPtr idCmd,
60 | uint uFlags,
61 | IntPtr pReserved,
62 | StringBuilder pszName,
63 | uint cchMax);
64 | }
65 |
66 | #endregion
67 |
68 |
69 | #region Shell Registration
70 |
71 | internal class ShellExtReg
72 | {
73 | ///
74 | /// Register the context menu handler.
75 | ///
76 | /// The CLSID of the component.
77 | ///
78 | /// The file type that the context menu handler is associated with. For
79 | /// example, '*' means all file types; '.txt' means all .txt files. The
80 | /// parameter must not be NULL or an empty string.
81 | ///
82 | /// The friendly name of the component.
83 | public static void RegisterShellExtContextMenuHandler(Guid clsid,
84 | string fileType, string friendlyName)
85 | {
86 | if (clsid == Guid.Empty)
87 | {
88 | throw new ArgumentException("clsid must not be empty");
89 | }
90 | if (string.IsNullOrEmpty(fileType))
91 | {
92 | throw new ArgumentException("fileType must not be null or empty");
93 | }
94 |
95 | // If fileType starts with '.', try to read the default value of the
96 | // HKCR\ key which contains the ProgID to which the file type
97 | // is linked.
98 | if (fileType.StartsWith("."))
99 | {
100 | using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(fileType))
101 | {
102 | if (key != null)
103 | {
104 | // If the key exists and its default value is not empty, use
105 | // the ProgID as the file type.
106 | string defaultVal = key.GetValue(null) as string;
107 | if (!string.IsNullOrEmpty(defaultVal))
108 | {
109 | fileType = defaultVal;
110 | }
111 | }
112 | }
113 | }
114 |
115 | // Create the key HKCR\\shellex\ContextMenuHandlers\{}.
116 | string keyName = string.Format(@"{0}\shellex\ContextMenuHandlers\{1}",
117 | fileType, clsid.ToString("B"));
118 | using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(keyName))
119 | {
120 | // Set the default value of the key.
121 | if (key != null && !string.IsNullOrEmpty(friendlyName))
122 | {
123 | key.SetValue(null, friendlyName);
124 | }
125 | }
126 | }
127 |
128 | ///
129 | /// Unregister the context menu handler.
130 | ///
131 | /// The CLSID of the component.
132 | ///
133 | /// The file type that the context menu handler is associated with. For
134 | /// example, '*' means all file types; '.txt' means all .txt files. The
135 | /// parameter must not be NULL or an empty string.
136 | ///
137 | public static void UnregisterShellExtContextMenuHandler(Guid clsid,
138 | string fileType)
139 | {
140 | if (clsid == null)
141 | {
142 | throw new ArgumentException("clsid must not be null");
143 | }
144 | if (string.IsNullOrEmpty(fileType))
145 | {
146 | throw new ArgumentException("fileType must not be null or empty");
147 | }
148 |
149 | // If fileType starts with '.', try to read the default value of the
150 | // HKCR\ key which contains the ProgID to which the file type
151 | // is linked.
152 | if (fileType.StartsWith("."))
153 | {
154 | using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(fileType))
155 | {
156 | if (key != null)
157 | {
158 | // If the key exists and its default value is not empty, use
159 | // the ProgID as the file type.
160 | string defaultVal = key.GetValue(null) as string;
161 | if (!string.IsNullOrEmpty(defaultVal))
162 | {
163 | fileType = defaultVal;
164 | }
165 | }
166 | }
167 | }
168 |
169 | // Remove the key HKCR\\shellex\ContextMenuHandlers\{}.
170 | string keyName = string.Format(@"{0}\shellex\ContextMenuHandlers\{1}",
171 | fileType, clsid.ToString("B"));
172 | Registry.ClassesRoot.DeleteSubKeyTree(keyName, false);
173 | }
174 | }
175 |
176 | #endregion
177 |
178 |
179 | #region Enums & Structs
180 |
181 | internal enum GCS : uint
182 | {
183 | GCS_VERBA = 0x00000000,
184 | GCS_HELPTEXTA = 0x00000001,
185 | GCS_VALIDATEA = 0x00000002,
186 | GCS_VERBW = 0x00000004,
187 | GCS_HELPTEXTW = 0x00000005,
188 | GCS_VALIDATEW = 0x00000006,
189 | GCS_VERBICONW = 0x00000014,
190 | GCS_UNICODE = 0x00000004
191 | }
192 |
193 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
194 | internal struct CMINVOKECOMMANDINFO
195 | {
196 | public uint cbSize;
197 | public CMIC fMask;
198 | public IntPtr hwnd;
199 | public IntPtr verb;
200 | [MarshalAs(UnmanagedType.LPStr)]
201 | public string parameters;
202 | [MarshalAs(UnmanagedType.LPStr)]
203 | public string directory;
204 | public int nShow;
205 | public uint dwHotKey;
206 | public IntPtr hIcon;
207 | }
208 |
209 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
210 | internal struct CMINVOKECOMMANDINFOEX
211 | {
212 | public uint cbSize;
213 | public CMIC fMask;
214 | public IntPtr hwnd;
215 | public IntPtr verb;
216 | [MarshalAs(UnmanagedType.LPStr)]
217 | public string parameters;
218 | [MarshalAs(UnmanagedType.LPStr)]
219 | public string directory;
220 | public int nShow;
221 | public uint dwHotKey;
222 | public IntPtr hIcon;
223 | [MarshalAs(UnmanagedType.LPStr)]
224 | public string title;
225 | public IntPtr verbW;
226 | public string parametersW;
227 | public string directoryW;
228 | public string titleW;
229 | POINT ptInvoke;
230 | }
231 |
232 | [Flags]
233 | internal enum CMIC : uint
234 | {
235 | CMIC_MASK_ICON = 0x00000010,
236 | CMIC_MASK_HOTKEY = 0x00000020,
237 | CMIC_MASK_NOASYNC = 0x00000100,
238 | CMIC_MASK_FLAG_NO_UI = 0x00000400,
239 | CMIC_MASK_UNICODE = 0x00004000,
240 | CMIC_MASK_NO_CONSOLE = 0x00008000,
241 | CMIC_MASK_ASYNCOK = 0x00100000,
242 | CMIC_MASK_NOZONECHECKS = 0x00800000,
243 | CMIC_MASK_FLAG_LOG_USAGE = 0x04000000,
244 | CMIC_MASK_SHIFT_DOWN = 0x10000000,
245 | CMIC_MASK_PTINVOKE = 0x20000000,
246 | CMIC_MASK_CONTROL_DOWN = 0x40000000
247 | }
248 |
249 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
250 | public struct POINT
251 | {
252 | public int X;
253 | public int Y;
254 | }
255 |
256 | internal enum CLIPFORMAT : uint
257 | {
258 | CF_TEXT = 1,
259 | CF_BITMAP = 2,
260 | CF_METAFILEPICT = 3,
261 | CF_SYLK = 4,
262 | CF_DIF = 5,
263 | CF_TIFF = 6,
264 | CF_OEMTEXT = 7,
265 | CF_DIB = 8,
266 | CF_PALETTE = 9,
267 | CF_PENDATA = 10,
268 | CF_RIFF = 11,
269 | CF_WAVE = 12,
270 | CF_UNICODETEXT = 13,
271 | CF_ENHMETAFILE = 14,
272 | CF_HDROP = 15,
273 | CF_LOCALE = 16,
274 | CF_MAX = 17,
275 |
276 | CF_OWNERDISPLAY = 0x0080,
277 | CF_DSPTEXT = 0x0081,
278 | CF_DSPBITMAP = 0x0082,
279 | CF_DSPMETAFILEPICT = 0x0083,
280 | CF_DSPENHMETAFILE = 0x008E,
281 |
282 | CF_PRIVATEFIRST = 0x0200,
283 | CF_PRIVATELAST = 0x02FF,
284 |
285 | CF_GDIOBJFIRST = 0x0300,
286 | CF_GDIOBJLAST = 0x03FF
287 | }
288 |
289 | [Flags]
290 | internal enum CMF : uint
291 | {
292 | CMF_NORMAL = 0x00000000,
293 | CMF_DEFAULTONLY = 0x00000001,
294 | CMF_VERBSONLY = 0x00000002,
295 | CMF_EXPLORE = 0x00000004,
296 | CMF_NOVERBS = 0x00000008,
297 | CMF_CANRENAME = 0x00000010,
298 | CMF_NODEFAULT = 0x00000020,
299 | CMF_INCLUDESTATIC = 0x00000040,
300 | CMF_ITEMMENU = 0x00000080,
301 | CMF_EXTENDEDVERBS = 0x00000100,
302 | CMF_DISABLEDVERBS = 0x00000200,
303 | CMF_ASYNCVERBSTATE = 0x00000400,
304 | CMF_OPTIMIZEFORINVOKE = 0x00000800,
305 | CMF_SYNCCASCADEMENU = 0x00001000,
306 | CMF_DONOTPICKDEFAULT = 0x00002000,
307 | CMF_RESERVED = 0xFFFF0000
308 | }
309 |
310 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
311 | internal struct MENUITEMINFO
312 | {
313 | public uint cbSize;
314 | public MIIM fMask;
315 | public MFT fType;
316 | public MFS fState;
317 | public uint wID;
318 | public IntPtr hSubMenu;
319 | public IntPtr hbmpChecked;
320 | public IntPtr hbmpUnchecked;
321 | public UIntPtr dwItemData;
322 | [MarshalAs(UnmanagedType.LPTStr)]
323 | public string dwTypeData;
324 | public uint cch;
325 | public IntPtr hbmpItem;
326 | }
327 |
328 | [Flags]
329 | internal enum MIIM : uint
330 | {
331 | MIIM_STATE = 0x00000001,
332 | MIIM_ID = 0x00000002,
333 | MIIM_SUBMENU = 0x00000004,
334 | MIIM_CHECKMARKS = 0x00000008,
335 | MIIM_TYPE = 0x00000010,
336 | MIIM_DATA = 0x00000020,
337 | MIIM_STRING = 0x00000040,
338 | MIIM_BITMAP = 0x00000080,
339 | MIIM_FTYPE = 0x00000100
340 | }
341 |
342 | internal enum MFT : uint
343 | {
344 | MFT_STRING = 0x00000000,
345 | MFT_BITMAP = 0x00000004,
346 | MFT_MENUBARBREAK = 0x00000020,
347 | MFT_MENUBREAK = 0x00000040,
348 | MFT_OWNERDRAW = 0x00000100,
349 | MFT_RADIOCHECK = 0x00000200,
350 | MFT_SEPARATOR = 0x00000800,
351 | MFT_RIGHTORDER = 0x00002000,
352 | MFT_RIGHTJUSTIFY = 0x00004000
353 | }
354 |
355 | internal enum MFS : uint
356 | {
357 | MFS_ENABLED = 0x00000000,
358 | MFS_UNCHECKED = 0x00000000,
359 | MFS_UNHILITE = 0x00000000,
360 | MFS_GRAYED = 0x00000003,
361 | MFS_DISABLED = 0x00000003,
362 | MFS_CHECKED = 0x00000008,
363 | MFS_HILITE = 0x00000080,
364 | MFS_DEFAULT = 0x00001000
365 | }
366 |
367 | internal enum MF : uint
368 | {
369 | MF_BYCOMMAND = 0x00000000,
370 | MF_BYPOSITION = 0x00000400,
371 | MF_POPUP = 0x00000010
372 | }
373 |
374 | #endregion
375 |
376 |
377 | internal class NativeMethods
378 | {
379 | ///
380 | /// Retrieve the names of dropped files that result from a successful drag-
381 | /// and-drop operation.
382 | ///
383 | ///
384 | /// Identifier of the structure that contains the file names of the dropped
385 | /// files.
386 | ///
387 | ///
388 | /// Index of the file to query. If the value of this parameter is 0xFFFFFFFF,
389 | /// DragQueryFile returns a count of the files dropped.
390 | ///
391 | ///
392 | /// The address of a buffer that receives the file name of a dropped file
393 | /// when the function returns.
394 | ///
395 | ///
396 | /// The size, in characters, of the pszFile buffer.
397 | ///
398 | /// A non-zero value indicates a successful call.
399 | [DllImport("shell32", CharSet = CharSet.Unicode)]
400 | public static extern uint DragQueryFile(
401 | IntPtr hDrop,
402 | uint iFile,
403 | StringBuilder pszFile,
404 | int cch);
405 |
406 | ///
407 | /// Free the specified storage medium.
408 | ///
409 | ///
410 | /// Reference of the storage medium that is to be freed.
411 | ///
412 | [DllImport("ole32.dll", CharSet = CharSet.Unicode)]
413 | public static extern void ReleaseStgMedium(ref STGMEDIUM pmedium);
414 |
415 | ///
416 | /// Insert a new menu item at the specified position in a menu.
417 | ///
418 | ///
419 | /// A handle to the menu in which the new menu item is inserted.
420 | ///
421 | ///
422 | /// The identifier or position of the menu item before which to insert the
423 | /// new item. The meaning of this parameter depends on the value of
424 | /// fByPosition.
425 | ///
426 | ///
427 | /// Controls the meaning of uItem. If this parameter is false, uItem is a
428 | /// menu item identifier. Otherwise, it is a menu item position.
429 | ///
430 | ///
431 | /// A reference of a MENUITEMINFO structure that contains information about
432 | /// the new menu item.
433 | ///
434 | ///
435 | /// If the function succeeds, the return value is true.
436 | ///
437 | [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)]
438 | [return: MarshalAs(UnmanagedType.Bool)]
439 | public static extern bool InsertMenuItem(
440 | IntPtr hMenu,
441 | uint uItem,
442 | [MarshalAs(UnmanagedType.Bool)] bool fByPosition,
443 | ref MENUITEMINFO mii);
444 |
445 | ///
446 | /// Inserts a new menu item into a menu, moving other items down the menu.
447 | ///
448 | ///
449 | /// A handle to the menu to be changed.
450 | ///
451 | ///
452 | /// The menu item before which the new menu item is to be inserted, as
453 | /// determined by the uFlags parameter.
454 | ///
455 | ///
456 | /// Controls the interpretation of the uPosition parameter and the content,
457 | /// appearance, and behavior of the new menu item.
458 | ///
459 | ///
460 | /// The identifier of the new menu item or, if the uFlags parameter has the
461 | /// MF_POPUP flag set, a handle to the drop-down menu or submenu.
462 | ///
463 | ///
464 | /// The content of the new menu item.
465 | ///
466 | ///
467 | /// If the function succeeds, the return value is true.
468 | ///
469 | [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)]
470 | [return: MarshalAs(UnmanagedType.Bool)]
471 | public static extern bool InsertMenu(
472 | IntPtr hMenu,
473 | uint uItem,
474 | uint uFlags,
475 | uint itemId,
476 | [MarshalAs(UnmanagedType.LPTStr)] string itemText);
477 |
478 | [DllImport("user32")]
479 | public static extern IntPtr CreateMenu();
480 |
481 | [DllImport("user32")]
482 | public static extern IntPtr CreatePopupMenu();
483 |
484 | ///
485 | /// The DeleteObject function deletes a logical pen, brush, font, bitmap,
486 | /// region, or palette, freeing all system resources associated with the
487 | /// object. After the object is deleted, the specified handle is no longer
488 | /// valid.
489 | ///
490 | ///
491 | /// A handle to a logical pen, brush, font, bitmap, region, or palette.
492 | ///
493 | ///
494 | /// If the function succeeds, the return value is true.
495 | ///
496 | [DllImport("gdi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
497 | public static extern bool DeleteObject(IntPtr hObject);
498 |
499 |
500 | public static int HighWord(int number)
501 | {
502 | return ((number & 0x80000000) == 0x80000000) ?
503 | (number >> 16) : ((number >> 16) & 0xffff);
504 | }
505 |
506 | public static int LowWord(int number)
507 | {
508 | return number & 0xffff;
509 | }
510 | }
511 |
512 | internal static class WinError
513 | {
514 | public const int S_OK = 0x0000;
515 | public const int S_FALSE = 0x0001;
516 | public const int E_FAIL = -2147467259;
517 | public const int E_INVALIDARG = -2147024809;
518 | public const int E_OUTOFMEMORY = -2147024882;
519 | public const int STRSAFE_E_INSUFFICIENT_BUFFER = -2147024774;
520 |
521 | public const uint SEVERITY_SUCCESS = 0;
522 | public const uint SEVERITY_ERROR = 1;
523 |
524 | ///
525 | /// Create an HRESULT value from component pieces.
526 | ///
527 | /// The severity to be used
528 | /// The facility to be used
529 | /// The error number
530 | /// A HRESULT constructed from the above 3 values
531 | public static int MAKE_HRESULT(uint sev, uint fac, uint code)
532 | {
533 | return (int)((sev << 31) | (fac << 16) | code);
534 | }
535 | }
536 | }
--------------------------------------------------------------------------------
/FileContextMenuExt.cs:
--------------------------------------------------------------------------------
1 | using CSShellExtContextMenuHandler;
2 | using SevenZip;
3 | using System;
4 | using System.Drawing;
5 | using System.IO;
6 | using System.Linq;
7 | using System.Runtime.InteropServices;
8 | using System.Runtime.InteropServices.ComTypes;
9 | using System.Text;
10 | using ZipInfoShell.Properties;
11 | using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
12 |
13 |
14 | namespace ZipInfoShell
15 | {
16 | [ClassInterface(ClassInterfaceType.None)]
17 | [Guid("E07DD184-C1C7-4315-90C0-9A8A6582E671"), ComVisible(true)]
18 | public class FileContextMenuExt : IShellExtInit, IContextMenu
19 | {
20 | private string selectedFile;
21 |
22 | private string menuText = "&压缩文件信息";
23 | private IntPtr menuBmp = IntPtr.Zero;
24 | private string verb = "zipFileInfo";
25 | private string verbCanonicalName = "GetZipFileInfo";
26 | private string verbHelpText = "ExtractZipFileInfo";
27 | private uint IDM_DISPLAY = 0;
28 |
29 |
30 | public FileContextMenuExt()
31 | {
32 | // Load the bitmap for the menu item.
33 | Bitmap bmp = Resources.zip;
34 | bmp.MakeTransparent(bmp.GetPixel(0, 0));
35 | this.menuBmp = bmp.GetHbitmap();
36 | }
37 |
38 | ~FileContextMenuExt()
39 | {
40 | if (this.menuBmp != IntPtr.Zero)
41 | {
42 | NativeMethods.DeleteObject(this.menuBmp);
43 | this.menuBmp = IntPtr.Zero;
44 | }
45 | }
46 |
47 |
48 | void Unzip2NewFolder(IntPtr hWnd)
49 | {
50 | SetLibraryPath();
51 |
52 | string zipFilePath = this.selectedFile;
53 | FileInfo fi= new FileInfo(zipFilePath);
54 | if (!fi.Exists)
55 | {
56 | return;
57 | }
58 |
59 | string pureFileName = Path.GetFileNameWithoutExtension(zipFilePath);
60 | string extractFolder = Path.Combine(fi.Directory.FullName,pureFileName);
61 | if (!Directory.Exists(extractFolder))
62 | {
63 | Directory.CreateDirectory(extractFolder);
64 | }
65 |
66 | using (var extractor = new SevenZipExtractor(zipFilePath))
67 | {
68 | extractor.ExtractArchive(extractFolder);
69 | }
70 |
71 | }
72 |
73 | void UnzipContent(IntPtr hWnd)
74 | {
75 | SetLibraryPath();
76 |
77 | string zipFilePath = this.selectedFile;
78 | FileInfo fi = new FileInfo(zipFilePath);
79 | if (!fi.Exists)
80 | {
81 | return;
82 | }
83 |
84 | string extractFolder = fi.Directory.FullName;
85 | using (var extractor = new SevenZipExtractor(zipFilePath))
86 | {
87 | extractor.ExtractArchive(extractFolder);
88 | }
89 | }
90 |
91 | private void SetLibraryPath()
92 | {
93 | //var filePath = @"E:\zyx\projects\ToolProject\ZipInfoShell\bin\Debug";
94 | var filePath = Environment.CurrentDirectory;
95 | SevenZipBase.SetLibraryPath(Path.Combine(filePath, "7z.dll"));
96 | }
97 |
98 | private ZipFileInfo SummaryZipInfo(string fileName)
99 | {
100 | var zipInfo = new ZipFileInfo();
101 |
102 | SetLibraryPath();
103 | using (var entries = new SevenZipExtractor(fileName))
104 | {
105 | zipInfo.FileCount = entries.FilesCount;
106 |
107 | var firstLevelEntries = entries.ArchiveFileData
108 | .Where(entry => !entry.FileName.Contains(Path.DirectorySeparatorChar))
109 | .ToList();
110 |
111 | zipInfo.FirstLevelFileCount = firstLevelEntries.Count(entry => !entry.IsDirectory);
112 | zipInfo.FirstLevelDirectoryCount = firstLevelEntries.Count(entry => entry.IsDirectory);
113 |
114 | }
115 | return zipInfo;
116 | }
117 |
118 |
119 | ///
120 | /// 关联的文件类型
121 | ///
122 |
123 | [ComRegisterFunction()]
124 | public static void Register(Type t)
125 | {
126 | try
127 | {
128 | ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".zip", "ZipInfoShell.FileContextMenuExt Class");
129 | ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".7z", "ZipInfoShell.FileContextMenuExt Class");
130 | ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".rar", "ZipInfoShell.FileContextMenuExt Class");
131 | ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".gz", "ZipInfoShell.FileContextMenuExt Class");
132 | ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".tar", "ZipInfoShell.FileContextMenuExt Class");
133 | }
134 | catch (Exception ex)
135 | {
136 | Console.WriteLine(ex.Message); // Log the error
137 | throw; // Re-throw the exception
138 | }
139 | }
140 |
141 | [ComUnregisterFunction()]
142 | public static void Unregister(Type t)
143 | {
144 | try
145 | {
146 | ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".zip");
147 | ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".7z");
148 | ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".rar");
149 | ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".gz");
150 | ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".tar");
151 | }
152 | catch (Exception ex)
153 | {
154 | Console.WriteLine(ex.Message); // Log the error
155 | throw; // Re-throw the exception
156 | }
157 | }
158 |
159 |
160 | ///
161 | /// Initialize the context menu handler.
162 | ///
163 | ///
164 | /// A pointer to an ITEMIDLIST structure that uniquely identifies a folder.
165 | ///
166 | ///
167 | /// A pointer to an IDataObject interface object that can be used to retrieve
168 | /// the objects being acted upon.
169 | ///
170 | ///
171 | /// The registry key for the file object or folder type.
172 | ///
173 | public void Initialize(IntPtr pidlFolder, IntPtr pDataObj, IntPtr hKeyProgID)
174 | {
175 | if (pDataObj == IntPtr.Zero)
176 | {
177 | throw new ArgumentException();
178 | }
179 |
180 | FORMATETC fe = new FORMATETC();
181 | fe.cfFormat = (short)CLIPFORMAT.CF_HDROP;
182 | fe.ptd = IntPtr.Zero;
183 | fe.dwAspect = DVASPECT.DVASPECT_CONTENT;
184 | fe.lindex = -1;
185 | fe.tymed = TYMED.TYMED_HGLOBAL;
186 | STGMEDIUM stm = new STGMEDIUM();
187 |
188 | // The pDataObj pointer contains the objects being acted upon. In this
189 | // example, we get an HDROP handle for enumerating the selected files
190 | // and folders.
191 | IDataObject dataObject = (IDataObject)Marshal.GetObjectForIUnknown(pDataObj);
192 | dataObject.GetData(ref fe, out stm);
193 |
194 | try
195 | {
196 | // Get an HDROP handle.
197 | IntPtr hDrop = stm.unionmember;
198 | if (hDrop == IntPtr.Zero)
199 | {
200 | throw new ArgumentException();
201 | }
202 |
203 | // Determine how many files are involved in this operation.
204 | uint nFiles = NativeMethods.DragQueryFile(hDrop, UInt32.MaxValue, null, 0);
205 |
206 | // This code sample displays the custom context menu item when only
207 | // one file is selected.
208 | if (nFiles == 1)
209 | {
210 | // Get the path of the file.
211 | StringBuilder fileName = new StringBuilder(260);
212 | if (0 == NativeMethods.DragQueryFile(hDrop, 0, fileName, fileName.Capacity))
213 | {
214 | Marshal.ThrowExceptionForHR(WinError.E_FAIL);
215 | }
216 | this.selectedFile = fileName.ToString();
217 | }
218 | else
219 | {
220 | Marshal.ThrowExceptionForHR(WinError.E_FAIL);
221 | }
222 |
223 | // [-or-]
224 |
225 | // Enumerate the selected files and folders.
226 | //if (nFiles > 0)
227 | //{
228 | // StringCollection selectedFiles = new StringCollection();
229 | // StringBuilder fileName = new StringBuilder(260);
230 | // for (uint i = 0; i < nFiles; i++)
231 | // {
232 | // // Get the next file name.
233 | // if (0 != NativeMethods.DragQueryFile(hDrop, i, fileName,
234 | // fileName.Capacity))
235 | // {
236 | // // Add the file name to the list.
237 | // selectedFiles.Add(fileName.ToString());
238 | // }
239 | // }
240 | //
241 | // // If we did not find any files we can work with, throw
242 | // // exception.
243 | // if (selectedFiles.Count == 0)
244 | // {
245 | // Marshal.ThrowExceptionForHR(WinError.E_FAIL);
246 | // }
247 | //}
248 | //else
249 | //{
250 | // Marshal.ThrowExceptionForHR(WinError.E_FAIL);
251 | //}
252 | }
253 | finally
254 | {
255 | NativeMethods.ReleaseStgMedium(ref stm);
256 | }
257 | }
258 |
259 |
260 | ///
261 | /// Add commands to a shortcut menu.
262 | ///
263 | /// A handle to the shortcut menu.
264 | ///
265 | /// The zero-based position at which to insert the first new menu item.
266 | ///
267 | ///
268 | /// The minimum value that the handler can specify for a menu item ID.
269 | ///
270 | ///
271 | /// The maximum value that the handler can specify for a menu item ID.
272 | ///
273 | ///
274 | /// Optional flags that specify how the shortcut menu can be changed.
275 | ///
276 | ///
277 | /// If successful, returns an HRESULT value that has its severity value set
278 | /// to SEVERITY_SUCCESS and its code value set to the offset of the largest
279 | /// command identifier that was assigned, plus one.
280 | ///
281 | public int QueryContextMenu(IntPtr hMenu, uint iMenu, uint idCmdFirst, uint idCmdLast, uint uFlags)
282 | {
283 | // If uFlags include CMF_DEFAULTONLY then we should not do anything.
284 | if (((uint)CMF.CMF_DEFAULTONLY & uFlags) != 0)
285 | {
286 | return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0);
287 | }
288 |
289 | var zipInfo = SummaryZipInfo(this.selectedFile);
290 | menuText = $"&{zipInfo.ToString()}";
291 | string subMenu1Text = zipInfo.Unzip2NewFolder ? $"解压至新目录(推荐)" : "解压至新目录";
292 | string subMenu2Text = zipInfo.Unzip2NewFolder ? $"解压至当前目录" : "解压至当前目录(推荐)";
293 |
294 | //string subMenu1Text = "test1";
295 | //string subMenu2Text = "test2";
296 |
297 |
298 | IntPtr hSubMenu = NativeMethods.CreatePopupMenu();
299 |
300 | // 向子菜单添加项
301 | uint idCmd = IDM_DISPLAY;
302 | NativeMethods.InsertMenu(hSubMenu, (uint)0, (uint)MF.MF_BYPOSITION | (uint)MFT.MFT_STRING, idCmdFirst + (idCmd++), subMenu1Text);
303 | NativeMethods.InsertMenu(hSubMenu, (uint)1, (uint)MF.MF_BYPOSITION | (uint)MFT.MFT_STRING, idCmdFirst + (idCmd++), subMenu2Text);
304 |
305 | // Use either InsertMenu or InsertMenuItem to add menu items.
306 | MENUITEMINFO mii = new MENUITEMINFO();
307 | mii.cbSize = (uint)Marshal.SizeOf(mii);
308 | mii.fMask = MIIM.MIIM_BITMAP | MIIM.MIIM_STRING | MIIM.MIIM_FTYPE | MIIM.MIIM_SUBMENU | MIIM.MIIM_ID | MIIM.MIIM_STATE;
309 | mii.wID = idCmdFirst + IDM_DISPLAY;
310 | mii.fType = MFT.MFT_STRING;
311 | mii.hSubMenu = hSubMenu;
312 | mii.dwTypeData = this.menuText;
313 | mii.fState = MFS.MFS_ENABLED;
314 | mii.hbmpItem = this.menuBmp;
315 | if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref mii))
316 | {
317 | return Marshal.GetHRForLastWin32Error();
318 | }
319 |
320 | // Add a separator.
321 | MENUITEMINFO sep = new MENUITEMINFO();
322 | sep.cbSize = (uint)Marshal.SizeOf(sep);
323 | sep.fMask = MIIM.MIIM_TYPE;
324 | sep.fType = MFT.MFT_SEPARATOR;
325 | if (!NativeMethods.InsertMenuItem(hMenu, iMenu + 1, true, ref sep))
326 | {
327 | return Marshal.GetHRForLastWin32Error();
328 | }
329 |
330 | // Return an HRESULT value with the severity set to SEVERITY_SUCCESS.
331 | // Set the code value to the offset of the largest command identifier
332 | // that was assigned, plus one (1).
333 | return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, idCmd);
334 | }
335 |
336 | ///
337 | /// Carry out the command associated with a shortcut menu item.
338 | ///
339 | ///
340 | /// A pointer to a CMINVOKECOMMANDINFO or CMINVOKECOMMANDINFOEX structure
341 | /// containing information about the command.
342 | ///
343 | public void InvokeCommand(IntPtr pici)
344 | {
345 | bool isUnicode = false;
346 |
347 | // Determine which structure is being passed in, CMINVOKECOMMANDINFO or
348 | // CMINVOKECOMMANDINFOEX based on the cbSize member of lpcmi. Although
349 | // the lpcmi parameter is declared in Shlobj.h as a CMINVOKECOMMANDINFO
350 | // structure, in practice it often points to a CMINVOKECOMMANDINFOEX
351 | // structure. This struct is an extended version of CMINVOKECOMMANDINFO
352 | // and has additional members that allow Unicode strings to be passed.
353 | CMINVOKECOMMANDINFO ici = (CMINVOKECOMMANDINFO)Marshal.PtrToStructure(pici, typeof(CMINVOKECOMMANDINFO));
354 | CMINVOKECOMMANDINFOEX iciex = new CMINVOKECOMMANDINFOEX();
355 | if (ici.cbSize == Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX)))
356 | {
357 | if ((ici.fMask & CMIC.CMIC_MASK_UNICODE) != 0)
358 | {
359 | isUnicode = true;
360 | iciex = (CMINVOKECOMMANDINFOEX)Marshal.PtrToStructure(pici, typeof(CMINVOKECOMMANDINFOEX));
361 | }
362 | }
363 |
364 | // Determines whether the command is identified by its offset or verb.
365 | // There are two ways to identify commands:
366 | //
367 | // 1) The command's verb string
368 | // 2) The command's identifier offset
369 | //
370 | // If the high-order word of lpcmi->lpVerb (for the ANSI case) or
371 | // lpcmi->lpVerbW (for the Unicode case) is nonzero, lpVerb or lpVerbW
372 | // holds a verb string. If the high-order word is zero, the command
373 | // offset is in the low-order word of lpcmi->lpVerb.
374 |
375 | // For the ANSI case, if the high-order word is not zero, the command's
376 | // verb string is in lpcmi->lpVerb.
377 | if (!isUnicode && NativeMethods.HighWord(ici.verb.ToInt32()) != 0)
378 | {
379 | // Is the verb supported by this context menu extension?
380 | if (Marshal.PtrToStringAnsi(ici.verb) == this.verb)
381 | {
382 |
383 | }
384 | else
385 | {
386 | // If the verb is not recognized by the context menu handler, it
387 | // must return E_FAIL to allow it to be passed on to the other
388 | // context menu handlers that might implement that verb.
389 | Marshal.ThrowExceptionForHR(WinError.E_FAIL);
390 | }
391 | }
392 |
393 | // For the Unicode case, if the high-order word is not zero, the
394 | // command's verb string is in lpcmi->lpVerbW.
395 | else if (isUnicode && NativeMethods.HighWord(iciex.verbW.ToInt32()) != 0)
396 | {
397 | // Is the verb supported by this context menu extension?
398 | if (Marshal.PtrToStringUni(iciex.verbW) == this.verb)
399 | {
400 |
401 | }
402 | else
403 | {
404 | // If the verb is not recognized by the context menu handler, it
405 | // must return E_FAIL to allow it to be passed on to the other
406 | // context menu handlers that might implement that verb.
407 | Marshal.ThrowExceptionForHR(WinError.E_FAIL);
408 | }
409 | }
410 |
411 | // If the command cannot be identified through the verb string, then
412 | // check the identifier offset.
413 | else
414 | {
415 | // Is the command identifier offset supported by this context menu
416 | // extension?
417 | if (NativeMethods.LowWord(ici.verb.ToInt32()) == IDM_DISPLAY)
418 | {
419 | Unzip2NewFolder(ici.hwnd);
420 | }
421 | else if (NativeMethods.LowWord(ici.verb.ToInt32()) == (IDM_DISPLAY + 1))
422 | {
423 | UnzipContent(ici.hwnd);
424 | }
425 | else
426 | {
427 | // If the verb is not recognized by the context menu handler, it
428 | // must return E_FAIL to allow it to be passed on to the other
429 | // context menu handlers that might implement that verb.
430 | Marshal.ThrowExceptionForHR(WinError.E_FAIL);
431 | }
432 | }
433 | }
434 |
435 | ///
436 | /// Get information about a shortcut menu command, including the help string
437 | /// and the language-independent, or canonical, name for the command.
438 | ///
439 | /// Menu command identifier offset.
440 | ///
441 | /// Flags specifying the information to return. This parameter can have one
442 | /// of the following values: GCS_HELPTEXTA, GCS_HELPTEXTW, GCS_VALIDATEA,
443 | /// GCS_VALIDATEW, GCS_VERBA, GCS_VERBW.
444 | ///
445 | /// Reserved. Must be IntPtr.Zero
446 | ///
447 | /// The address of the buffer to receive the null-terminated string being
448 | /// retrieved.
449 | ///
450 | ///
451 | /// Size of the buffer, in characters, to receive the null-terminated string.
452 | ///
453 | public void GetCommandString(UIntPtr idCmd, uint uFlags, IntPtr pReserved, StringBuilder pszName, uint cchMax)
454 | {
455 | if (idCmd.ToUInt32() == IDM_DISPLAY)
456 | {
457 | switch ((GCS)uFlags)
458 | {
459 | case GCS.GCS_VERBW:
460 | if (this.verbCanonicalName.Length > cchMax - 1)
461 | {
462 | Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER);
463 | }
464 | else
465 | {
466 | pszName.Clear();
467 | pszName.Append(this.verbCanonicalName);
468 | }
469 | break;
470 |
471 | case GCS.GCS_HELPTEXTW:
472 | if (this.verbHelpText.Length > cchMax - 1)
473 | {
474 | Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER);
475 | }
476 | else
477 | {
478 | pszName.Clear();
479 | pszName.Append(this.verbHelpText);
480 | }
481 | break;
482 | }
483 | }
484 | }
485 |
486 |
487 | }
488 |
489 | }
490 |
--------------------------------------------------------------------------------