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

文件校验和 Shell 菜单扩展 DLL

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (32投票s)

2006年12月15日

LGPL3

15分钟阅读

viewsIcon

270889

downloadIcon

6226

使用 ATL 和 Crypto++ 创建文件校验和 Shell 菜单扩展。

Introduction简介

文件校验和用于验证文件的完整性。校验和可以检测传输错误和篡改。要检测传输错误,我们可以使用 CRC(循环冗余校验)[1]。要检测篡改,我们通常会选择一种加密 哈希,因为其具有抗碰撞性的理想特性 [2]。

本文介绍了两个 Shell 扩展 DLL,它们可用于创建文件校验和和验证文件校验和。本文基于 Michael Dunn 的编写 Shell 扩展指南,第一部分[4]、Tom Archer 的使用剪贴板,第一部分:传输简单文本[5] 和 Crypto++。本文将讨论以下主题:

Microsoft 提供了一个命令行文件校验和工具,该工具发布在知识库文章 Q841290 文件校验和完整性验证程序实用工具的可用性和描述 [6]。Microsoft 工具支持 MD5 和 SHA1。

Shell 扩展 DLL 用法

如果读者希望使用扩展 DLL,应按照下文所述进行下载和安装。DLL 注册后,当用户在 Windows Explorer 中右键单击文件(或多个文件)时,它们将作为上下文菜单可用。

  • 下载 DLL
    • CtxCreateHashDll.zip
    • CtxVerifyHashDll.zip
  • 解压到 C:\Windows\System\
  • 注册 DLL
    • regsvr32.exe C:\Windows\System\CtxCreateHash.Dll
    • regsvr32.exe C:\Windows\System\CtxVerifyHash.Dll

Windows Vista 和用户帐户控制

要在启用了用户帐户控制的情况下在 Vista 上手动运行 regsvr32.exe,请从“开始”菜单导航到命令提示符。右键单击命令提示符并选择“以管理员身份运行”,如图 1 所示。

Run As Administrator
图 1:以管理员身份运行

如果不以提升的权限运行 regsvr32,通常会导致错误“DllRegisterServer 失败,错误代码为 0x80070005”,而成功则会显示熟悉的成功消息,如下所示。

DllRegisterServer Success
图 2:DllRegisterServer 成功

Visual C++ 运行时

编译的 DLL 的最新版本使用 Visual Studio 2008 构建和链接。如果需要,请下载 Microsoft Visual C++ 2008 可再发行组件包。需要该程序包的一个典型示例如 regsvr32 无法注册 DLL 导致错误“由于应用程序配置不正确,应用程序失败。重新安装应用程序可能可以解决此问题”。

校验和创建

选择文件或多个文件后,如果用户选择“创建校验和”,则会创建以下哈希并将它们放到剪贴板上:

  • MD5
  • RIPE MD-128, RIPE MD-160, RIPE MD-256, RIPE MD-320
  • SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
  • Whirlpool

然后会显示一个消息框,其中包含哈希文件或文件的摘要。为了使消息框的大小可控,将显示截断的版本。

Truncated Message Box

图 3:截断的消息框

完整的校验和会放入 Windows 剪贴板以供粘贴。从剪贴板粘贴到记事本将显示完整的文本。

Clipboard Text

图 4:剪贴板文本

校验和验证

验证文件的校验和同样简单。导航到校验和所在网页或文档,高亮显示并复制到剪贴板。

Copy to the Clipboard

图 5:将校验和复制到剪贴板

导航到文件,选择,右键单击,然后选择“验证校验和”。

Verify Checksums

图 6:验证校验和

支持的验证算法有:

  • CRC32
  • MD2, MD4, MD5
  • RIPE MD-128, RIPE MD-160, RIPE MD-256, RIPE MD-320
  • SHA-1, SHA-224, SHA-256, SHA-384, SHA-512
  • Whirlpool

将显示一个类似如下的消息框。消息框会将文件分为两类:已验证和未验证。已验证的文件将显示“已验证校验和”消息和校验和的摘要。未验证的文件将在“未验证校验和”标题下。

Verify Checksum Message Box

图 7:验证校验和消息框

验证时,DLL 会按从强到弱(SHA-512 到 CRC32)的顺序查找匹配的哈希值。匹配算法在第一次匹配时终止,因此只显示最强的哈希。

编译和集成 Crypto++

示例使用了各种 Crypto++ 对称加密算法。Crypto++ 可以从 Wei Dai 的 Crypto++ 页面下载。有关编译和集成问题,请访问将 Crypto++ 集成到 Microsoft Visual C++ 环境中。本文基于先前文章中提出的基本假设。对于那些对其他 C++ 加密库感兴趣的人,请参阅 Peter Gutmann 的Cryptlib 或 Victor Shoup 的NTL

Windows 剪贴板 API

请参阅 Tom Archer 的使用剪贴板,第一部分:传输简单文本,了解有关该主题的深入讨论。示例 1 演示了枚举剪贴板的数据格式。示例 2 演示了从剪贴板读取文本。最后,示例 3 演示了向剪贴板写入 Unicode 文本。

Writing Clipboard Text

图 8:写入剪贴板文本

创建 Shell 上下文菜单扩展 DLL

创建 Shell 上下文菜单主要基于 Michael Dunn 的编写 Shell 扩展指南,第一部分。有关讨论,请参阅 Michael 的文章。Michael 的技术非常适合为 Windows 2000 插入单个菜单项或多个菜单项。

组合

与 Michael 的文章不同,在 Windows XP、Server 2003 和 Vista 上调用 InsertMenu 时,我们需要使用一种略有不同的技术。这是由于组合。组合是 Windows XP 及更高版本用于将上下文菜单项分组的技术。在图 9 中,我们看到组合的两个示例。Adobe 提供了两个 DLL,它们被分组在一起,校验和 DLL 也被分组在一起。

Composition
图 9:组合

有关组合的讨论,请参阅 Raymond Chen 关于The Old New Thing的文章系列。感兴趣的两个章节是第 10 章:复合扩展 - 基础第 11 章:复合扩展 - 组合。请注意,Chen 的示例代码是从 Shell 的角度呈现的,而不是我们的角度。由于 Windows XP、Server 2003 和 Vista 中组合的副作用,我们必须按如下方式插入菜单项:

InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
  uidFirstCmd+1, _T("Create Checksums") );

// Return that we consumed two IDs rather than 1.
// This is because we skipped ID 0 (uidFirstCmd), and
// added ID 1 (uidFirstCmd+1)
//
// This means we must handle Command 1 in InvokeCommand(),
// rather than Command 0
return MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, 2 );

如果我们添加两个菜单项,我们的例程将如下所示:

InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
  uidFirstCmd+1, _T("Item 1") );
InsertMenu( hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION,
  uidFirstCmd+2, _T("Item 2") );

// Return that we consumed three IDs rather than one or two.
// This is because we skipped ID 0 (uidFirstCmd), and
// added ID 1 (uidFirstCmd+1) and added ID 2 (uidFirstCmd+2)
//
// This means we must handle Command 1 and Command 2
// in InvokeCommand(), rather than Command 0
return MAKE_HRESULT( SEVERITY_SUCCESS, FACILITY_NULL, 3 ); 

Shell 用来确定哪些项目应被组合(以及如何排列)的算法是不可用的。但似乎它取决于:

  • DLL 的注册表字符串名称
  • 字符串比较中匹配的前导字符数

似乎该算法位于 Shell 的 Shell32!_imp__DSA_InsertItem 函数中。观察到的经验法则是,如果注册表字符串中有两个或更多字符匹配,则项目将被组合。

Crypto++ ChannelSwitch 类

从磁盘多次读取可能会成为性能瓶颈。如果我们连续读取同一个文件,情况尤其如此。为了克服多次磁盘读取的问题,可以使用 Crypto++ ChannelSwitch 类。ChannelSwitch 类允许用户在单次磁盘读取后将数据流式传输到多个基于 HashFilter 的对象。更准确地说,可以将数据推送到任何 BufferedTransformation 派生对象。在执行字符串(内存中)操作时,这很简单;但基于磁盘的操作会影响响应时间。用户不想读取 1 MB 的文件 6 次以将其发送到 6 个不同的使用者对象。尽管可以编写代码来执行缓冲和委托,但最简单的解决方案是 ChannelSwitch

ChannelSwitch Overview

图 10:ChannelSwitch 概述

ChannelSwitch 提供 AddDefaultRoute(BufferedTransformation& destination) 函数来添加目标。HashFilter 派生自 BufferedTransformation

HashFilter Inheritance Diagram
图 11:HashFilter 继承

Create Checksum Shell Extension DLL 中 ChannelSwitch 对象的简要用法如下所示。

Create Checksum ChannelSwitch Usage

图 12:CreateChecksum DLL ChannelSwitch

Verify Checksum Shell Extension DLL 中 ChannelSwitch 对象的简要用法如下所示。

Verify Checksum ChannelSwitch Usage

图 13:VerifyChecksum DLL ChannelSwitch

下面显示了使用 ChannelSwitch 进行多个哈希处理的代码。创建了两个哈希:MD5 和 SHA-1。Wei 在 Crypto++ 库的test.cpp中提供了示例代码。

#include "channels.h"   // ChannelSwitch
#include "filters.h"    // HashFilter
#include "hex.h"        // HexEncoder
#include "md5.h"        // MD5
#include "sha.h"        // SHA-1, SHA-256, SHA-512
int main( )
{
    MD5 hashMD5;
    SHA1 hashSHA1;

    HashFilter filterMD5(hashMD5);    
    HashFilter filterSHA1(hashSHA1);

    std::auto_ptr<ChannelSwitch> channel(new ChannelSwitch );

    channel->AddDefaultRoute(filterMD5);
    channel->AddDefaultRoute(filterSHA1);
    ...
} 

前面提到了 fciv.exe(KB 841290),它创建和验证 MD5 和 SHA-1 校验和。通过将 StringSource 更改为 FileSource,读者可以获得一个新改进的 fciv.exe,但缺少 XML 编码。

下面显示了示例 4(使用 MD5、SHA-1、RIPEMD-160 和 SHA-256)的输出。

Sample 4

图 14:示例 4

命令行调试

如果使用 Visual Studio 进行调试太困难或不方便,可以使用 cdb.exe。它是 WinDbg 的命令行版本。CDB 可以从 Microsoft 的Debugging Tools for Windows 包下载。本练习中一些更有用的命令如下所示。

附加到 Explorer

要附加到 Windows Explorer,请发出 cdb -pid < pid> 命令,其中 pid 是 Explorer 的进程标识号。附加后,Explorer 将被挂起,因为调试器正在等待输入。输入 g 运行。

Attach To Explorer
图 15:附加到 Explorer

当 Explorer 运行时,按 CTRL-C 切换回调试器。在调试器中完成后,再次按 g。最后,ALT-Tab 在 Explorer 挂起时仍然有效。这对于将调试器窗口置于最前面很有用,尤其是在 Explorer 无法重绘(并且隐藏了某个视图)的情况下。

冻结的 Explorer 或调试器

由于此部分非常重要,因此从上面重申。如果在尝试处理桌面时 Explorer 冻结,请在调试器中键入 g。如果调试器不是具有焦点的窗口,请使用 ALT-Tab 将调试器带到最前面。最后,如果您无法在调试器中输入命令,请按 CTRL-C 中断。

加载的模块

附加到 Explorer 后,我们需要知道我们的 DLL 是否已加载,以便确定断点位置并设置断点。我们可以发出 ld 命令,指定一个完整路径名,或者右键单击一个文件来促使 Explorer 加载它。

断点

发出 x ctx*!*hash* 将显示两个 DLL 的符号位置。最后一个表达式(*hash*)细化了结果。这信息量太大,因为 Crypto++ 包含许多此类符号。

为了进一步细化位置搜索,请发出 x ctx*!*querycontextmenu。现在已知位置,请设置断点。运行 Explorer。bl 列出断点。当断点触发时,输入 p 一次以从 DebugBreak() 步出并进入函数。要进入一个函数,请键入 t

Locating Functions of Interest
图 16:定位感兴趣的函数

管理不稳定的 Windows Explorer

本文的这部分再次基于 Michael Dunn 的《编写 Shell 扩展指南,第一部分》。如果读者觉得处理过于肤浅,请参考 Michael 的文章。Michael 详细介绍了准备 Windows 环境以调试 Windows Explorer 的主题,以及允许上下文菜单 DLL 在组策略下执行。

调试前,请终止 Explorer 进程。当提示输入 Debug Executable 时,请指定 C:\Windows\Explorer.exe。调试开始时,Visual Studio 将启动一个 Explorer 实例。

explorer.exe

图 17:explorer.exe

经常保存您的工作。大多数问题只需要重新启动 Explorer,或者偶尔进行软重启。要重新启动 Explorer,请打开任务管理器并终止 Explorer 进程。

End Explorer Process

图 18:终止 Explorer 进程

接下来,切换到“任务”选项卡,然后单击“新建任务”。

New explorer.exe Task

图 19:新的 explorer.exe 任务

如果读者编译了 DLL,但在链接时无法打开 DLL 进行写入,请验证没有运行 Explorer.exe 实例。如果不存在实例,请重新启动 Explorer。

Explorer Instances

图 20:Explorer 实例

字符集注意事项

上下文菜单 DLL 使用宽字符编写,因为 NT 系列在当前占据主导地位。Crypto++ 库是窄的,而 Windows API 可以是两者的任意一种。

Checksum Dll Design Overview

图 21:校验和 DLL 设计概述

与 Crypto++ 相比,数据传输通常是从 Crypto++ 到 DLL。为了方便传输,DLL 对来自 Crypto++ 的窄数据调用 StringWiden()

Windows API 的情况则不同。如果定义了 _UNICODE UNICODE,则不进行转换。在使用 SBCSMBCS 时,在 Windows 和 DLL 之间移动字符串时会使用 StringWiden()StringNarrow()

转换将通过标准 C++ 库的 widen()narrow() 来处理。使用 Visual C++ 7.0 及更高版本的用户在使用标准库转换例程方面具有更大的灵活性。

// Courtesy of Tom Widmer (VC++ MVP)
std::wstring StringWiden( const std::string& narrow ) { 
    std::wstring wide;
    wide.resize( narrow.length() );
    typedef std::ctype<wchar_t> CT;
    CT const& ct = std::_USE(std::locale(), CT);
    // Non Portable
    // Iterators should not be used as pointers (works in VC++ 6.0)
    // wideness( narrow.begin(), narrow.end(), wide.begin() );
    // Portable
    // ct.widen(&narrow[0], &narrow[0] + narrow.size(), &wide[0]);
    // Portable
    ct.widen(narrow.data(), narrow.data() + narrow.size(), wide.data());    
    return wide;
}

创建和验证例程

示例 4(Channel Switch)和示例 5(CtxTest)提供了 DLL 所需的大部分代码。本节将讨论其余感兴趣的项目。创建和验证例程共享通用代码。这包括填充文件列表和 Fly By 帮助。

初始化

Initialize() 已按如下方式扩展。请注意,files 是一个宽 string 的向量。提取完所有文件后,将根据名称对向量进行排序。

STDMETHODIMP CCreateHash::Initialize( LPCITEMIDLIST pidlFolder,
                                      LPDATAOBJECT pDataObj, HKEY hProgID )
{
    pidlFolder;     // Suppress Warning C4100
    hProgID;        // Suppress Warning C4100
    FORMATETC fmt = { CF_HDROP,NULL,DVASPECT_CONTENT,-1,TYMED_HGLOBAL };
    STGMEDIUM stg = { TYMED_HGLOBAL };
    HDROP     hDrop;
    ...
    // Sanity check - make sure there is at least one filename.
    UINT uNumFiles = DragQueryFile( hDrop,
                         static_cast<UINT>(-1), NULL, 0 );    
    if( 0 == uNumFiles )
        { return E_INVALIDARG; }
    HRESULT hr = S_OK;
    // The author has encountered situations where
    //   MAX_PATH was a bit too small...
    TCHAR file[ MAX_PATH * 2 + 1 ];
    // Loop through all the files that were selected.
    for(UINT i = 0; i < uNumFiles; i++)
    {
        DragQueryFile( static_cast<HDROP>( stg.hGlobal ),
                       i, file, MAX_PATH * 2 );
        // If the file name is a directory, silently skip
        //   We should not encounter this...
        if (::GetFileAttributes( file ) & FILE_ATTRIBUTE_DIRECTORY)
            { continue; }
        // Add the file name to the end of the list.
        #ifdef UNICODE
          files.push_back( file );
        #else
          files.push_back( StringWiden( file ) );
        #endif
    }
    std::sort( files.begin(), files.end() );
    return hr;
}

Create InvokeCommand

HRESULT CCreateHash::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
   // If lpVerb really points to a std::string,
   //   ignore this function call and bail out.
   if ( 0 != HIWORD( pCmdInfo->lpVerb ) ) { return E_INVALIDARG; }

   unsigned int i = 0, j = 0;

   // Friendly Name: 'MD5'
   std::vector< std::wstring > hashnames;  
  
   // Hash: 47FD4214F5775826FB20FEC1091987A1 
   std::vector< std::wstring > hashvalues;

   std::wstring ClipboardText;
   std::wstring MsgBoxMessage;   

   MsgBoxMessage = L"The following was placed on the Windows Clipboard:\r\n\r\n";

   // We must look for Command 1 now
   switch( LOWORD( pCmdInfo->lpVerb ) )
   {
      case 1:
      {
         for( i = 0; i < files.size(); i++ )
         {
            CalculateFileHashes( files[ i ], hashnames, hashvalues );
            ClipboardText += FileName( files[ i ] ) + L"\r\n";
            MsgBoxMessage += FileName( files[ i ] ) + L"\r\n";
            for( j = 0; j < hashvalues.size(); j++ )
            {
               // Clipboard Text is easy...
               //   Just dump the information
               ClipboardText += hashnames[ j ] + L": " + hashvalues[ j ] + L"\r\n";

               ...

            } // End - Keep the Message Box size reasonable
         } // for( j = 0; j < hashnames.size(); j++ )
         // Pretty Print
         if( i + 1 < files.size() )
         {
            ClipboardText += L"\r\n";
            if( files.size() <= 2 )
            {
               MsgBoxMessage += L"\r\n"; 
         } // for( i = 0; i < files.size(); i++ )
         SetClipboardText( pCmdInfo->hwnd, ClipboardText );
         #ifdef _UNICODE
            MessageBox( pCmdInfo->hwnd, MsgBoxMessage.c_str(),
                        _T("File Checksum Results"), MB_ICONINFORMATION );
         #else
            MessageBox( pCmdInfo->hwnd, StringNarrow( MsgBoxMessage ).c_str(),
                        _T("File Checksum Results"), MB_ICONINFORMATION );
         #endif            
         return S_OK;
         break;
      }
      default:            
         break;
   }
   return E_INVALIDARG;
}

Verify InvokeCommand

Verify Checksum InvokeCommand() 使用与 Create Checksum 相同的基本代码和逻辑。此代码的区别在于引入了两个额外的向量:vector< wstring > verifiedfilesvector< wstring > unverifiedfiles 用于簿记。

HRESULT CVerifyHash::InvokeCommand ( LPCMINVOKECOMMANDINFO pCmdInfo )
{
    unsigned int i = 0, j = 0;
    UINT style = MB_ICONINFORMATION;
    bool found = false;
    wstring ClipboardText;
    wstring MsgBoxMessage;
    vector< wstring > hashnames;
    vector< wstring > hashvalues;
    vector< wstring > verifiedfiles;
    vector< wstring > verifiedhashes;
    vector< wstring > unverifiedfiles;

    // If lpVerb really points to a std::string,
    //   ignore this function call and bail out.
    if ( 0 != HIWORD( pCmdInfo->lpVerb ) ) { return E_INVALIDARG; }

    HWND hwnd = pCmdInfo->hwnd;

    // If there is no text on the Clipboard,
    //   inform the user and bail out.
    if( false == GetClipboardText( ClipboardText ) )
    {
          MessageBox( hwnd, _T("There is not text on the Clipboard."),
                      _T("File Checksum Verifier"), MB_ICONWARNING );
        return S_OK;
    }
    // We must respond to Command 1 now.
    switch( LOWORD( pCmdInfo->lpVerb ) )
    {
        case 1:
        {
            for( i = 0; i < files.size(); i++ )
            {
                // Hash found on the Clipboard?
                found = false;
                CalculateFileHashes( files[ i ], hashnames, hashvalues );
                // Start at size(): this has the effect of 
                //   verifying strongest (SHA-256) to weakest (MD4)
                for( j = 0; j < hashvalues.size() ; j++ )
                {
                    if( true == Find( hashvalues[ j ], ClipboardText ) )
                    {
                        found = true;
                        verifiedfiles.push_back( files[ i ] );
                        // Concatenate 'MD5: ' and
                        //   '82A80BE6F1E0E7766FAC3CA661089EE4', etc
                        verifiedhashes.push_back( hashnames[ j ] + L": " +
                                                  Snip( hashvalues[ j ] ) );
                        j = hashvalues.size();  // Break the loop
                    }
                } // for( j = 0; j < hashvalues.size(); j++ )
                if( false == found )
                {
                    unverifiedfiles.push_back( files[ i ] );
                }
            } // for( i = 0; i < files.size(); i++ )

            ...

            return S_OK;
            break;
        }
        default:
            break;
    }
    return E_INVALIDARG;
}

此版本的 InvokeCommand() 为简单起见,维护着并行数组。这些数组是为 files 向量中的每个文件创建的。数组可视化如下:

Hash Name - Hash Value Parallel Array
图 22:哈希名称 - 哈希值并行数组

数组是通过 CreateFileHashes() 创建的。函数原型如下。Create 和 Verify 都使用该函数。Create 仅将其转储到剪贴板,而 Verify 则遍历哈希值向量(从最强的到最弱的),在剪贴板上搜索哈希值。

bool CalculateFileHashes( const std::wstring& filename,
                          std::vector< std::wstring >& hashnames,
                          std::vector< std::wstring >& hashvalues )

现在显示 InvokeCommand()。大部分逻辑由维护两个字符串主导——一个用于消息框,另一个用于剪贴板。请记住,消息框的额外代码是必需的,以保持消息框的大小可控。

杂项

选择哈希时,应选择至少 160 位哈希。请注意,哈希长度并不一定等同于强度。例如,RIPEMD-128 与 RIPEMD-256 一样具有加密强度;RIPEMD-160 与 RIPEMD-320 一样具有加密强度。RIPEMD-256 和 RIPEMD-320 仅为给定的消息 M 生成更多的熵。读者应参考可选的 256 和 320 哈希结果扩展:RIPEMD-256 和 RIPEMD-320 获取详细信息。

摘自NIST 网站

有五 (5) 种 FIPS 批准的算法用于生成消息的压缩表示(消息摘要):SHA-1、SHA-224、SHA-256、SHA-384 和 SHA-512。
2006 年 3 月 15 日:SHA-2 系列哈希函数(即 SHA-224、SHA-256、SHA-384 和 SHA-512)可供联邦机构用于所有使用安全哈希算法的应用程序。联邦机构应尽快停止使用 SHA-1 进行数字签名、数字时间戳和其他需要抗碰撞性的应用程序,并在 2010 年后必须使用 SHA-2 系列哈希函数进行这些应用程序。2010 年之后,联邦机构只能将 SHA-1 用于以下应用程序:基于哈希的消息身份验证码 (HMAC)、密钥派生函数 (KDF) 和随机数生成器 (RNG)。无论如何使用,NIST 都鼓励应用程序和协议设计者为所有新应用程序和协议使用 SHA-2 系列哈希函数。

最后,摘自RIPE MD 网站

128 位哈希结果不再提供足够的保护。对 128 位哈希结果进行暴力碰撞搜索攻击需要 264 次或约 2 x 1019 次函数求值。1994 年,Paul van Oorschot 和 Mike Wiener 表明,这项暴力破解工作可以在不到一个月的时间内完成,投资为 1000 万美元(“Parallel Collision Search with Applications to Hash Functions and Discrete Logarithms”,第 2 届 ACM 计算机与安全会议,ACM Press,1994 年,第 210-218 页)。预计此成本每 18 个月减半。

Visual Studio 2005 和 Platform SDK

如果在编译过程中遇到以下问题:

error C2787: 'IContextMenu' : no GUID has been associated with this object

读者可以在 stdafx.h 中添加以下内容:

#ifndef IContextMenu
  struct __declspec(uuid("000214e4-0000-0000-c000-000000000046")) IContextMenu;
#endif

Microsoft MVP Doug Harrison 在Missing IContextMenu 中提供了其他修复方法。

摘要

文件校验和是一种经常被忽视的加密工具。有了这些 DLL,用户就可以轻松地将校验和功能集成到他们的文档或网站中。

下载

致谢

  • Wei Dai for Crypto++ 及其在 Crypto++ 邮件列表上的宝贵帮助
  • Dr. Brooke Stephens,他为我奠定了加密基础。

修订

  • 2008.05.24 添加了 Vista regsvr32.exe 错误
  • 2008.05.24 测试了升级到 Visual C++ 9.0 (VS2008)
  • 2008.03.07 添加了 VS2008 regsvr32.exe 错误
  • 2007.12.01 添加了 Windows XP 及更高版本的组合问题
  • 2007.08.03 添加了关于 IContextMenu 和 Visual Studio 2005 的注释
  • 2007.08.03 从 Visual C++ 7.1 升级到 Visual C++ 8.0
  • 2007.05.31 从 Visual C++ 6.0 升级到 Visual C++ 7.1
  • 2007.05.31 验证了与 Crypto++ 5.5.1 的兼容性
  • 2007.05.31 从 CtxVerifyHash.dll 中移除了 HAVAL
  • 2007.05.31 在 CtxVerifyHash.dll 中添加了其他 SHA-2 哈希
  • 2007.05.31 在 CtxVerifyHash.dll 中添加了 Whirlpool (512) 哈希
  • 2007.01.09 添加了对 NIST 哈希选择的引用
  • 2006.12.19 添加了“Crypto++ 字符串和文件哈希”部分
  • 2006.12.17 在 Verify Shell Extension DLL 中添加了 CRC32
  • 2006.12.16 更新了文章图形
  • 2006.12.15 更新了 ChannelSwitch
  • 2006.12.14 初始发布

校验和

  • CtxCreateHash.zip
    • MD5: D967CF24BEC8BF403B0F274B7908876E
    • RIPEMD-128: CF073CB397C912EE3395C63F5CCF93FE
    • RIPEMD-160: 324123B69596608381147F0F60C343B3F5C4B007
    • RIPEMD-256: 5A5A18771EAAF7A2E60C83D2FC8EC507389532A1E7F80FD2C38D5E949899A07F
    • RIPEMD-320: C8A01A5A8BA0323CEB25DA4FC45E155E47ACC5BFF7481AECF0C1E4250E5CF07DF7885A9CFD3D8E23
    • SHA-1: 53CFD2F493846CC49EC5209CA7E4C9939D371172
    • SHA-224: 70A8F1066BEAE431E4462E226A4293DB323A096EDE5163A47B83E64C
    • SHA-256: 75DA22C742752DBC73AB421F8A48BCCEC4BDF5486DFF16EB9395C6F499DF9221
  • CtxCreateHashDll.zip
    • RIPEMD-128: ABEF419E395A7A21CE9D65A5419842D4
    • RIPEMD-160: 22EF853EAE308F7B07CA674CC50BE6D9929F3649
    • RIPEMD-256
    • 19CC4D1BFD0B92B9E7D48CB7CE14C0C3F094E8E15EC8EB61C89632C5032D474C
    • RIPEMD-320: D06012A5A9EA47436AFD247B90D179081B64C62CEF3BA20DF35DB85021FFBB386BA7962A289BB224
  • CtxVerifyHash.zip
    • SHA-1: B54562A1EC047F31C75027C396CE44DFE6F2020D
    • SHA-224: CB413BAD801E4C9F8043BAF294FA29F0616F449B42E453B9EE1E5941
    • SHA-256: D10E2C892487CC6A17D5C673DE52974F7F0DB701342DD46B1821FD479DBCEC4F
  • CtxVerifyHashDll.zip
    • RIPEMD-256: 82BC7E086B8EDA7C1A8C5310F82F31DDEA13BF192482A6D1F362831E8FC2F23A
    • SHA-256: 1602BD0FF62E05F4B77428C5943FE37CB7CCE6367C5D79CCC004E125A2C6266A
  • Sample1.zip   
    • CRC32: BD50E874
  • Sample2.zip
    • MD5: 0CAE59412D3272C12A4985930F313DC1
  • Sample3.zip
    • SHA-1: 68274EDD7A70792057A379048F1FBA6E58048F7E
  • Sample4.zip
    • RIPEMD-160: 7434F4A3498A642A53FCC3AF6081718BE18FCE28
  • Sample5.zip
    • SHA-256: 3DA6D8C6D29DE726B96838273C5E9D7C558F725BF6574345C054E7FD1325DF42
© . All rights reserved.