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

Explorer 范式 - 两阶段搜索

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2019年9月4日

CPOL

52分钟阅读

viewsIcon

6990

downloadIcon

205

GUI 关键字搜索中最不常见的字符偏移精确匹配算法

The String found by U2Charfunc, shown in it 's full context in yhe Viewer Panel

U2Charfunc 找到的字符串,在 Viewer Panel 中以完整上下文显示

引言:使用最低频率字符偏移的两阶段搜索的 Explorer 强制要求

什么是最低频率字符偏移?我为什么应该关心?

最低频率字符偏移 (LFC) 是要在模式中查找的 LFC 的位置。模式中的任何字符都可以使用,但使用 LFC,它会更少地出现。借助 SSE SIMD 指令,比较一次可以检查十六个字节。模式的第一个字符必须在 LFC 的正确前缀偏移处,否则当 SIMD 寄存器“AND”在一起时,它们都会消失。这意味着 SSE 内建函数可以以 16 字节为增量扫描文件,仅找到 LFC 中与搜索模式的第一个字符匹配的字符的正确偏移量的那些实例。找到 LFC 和“其他”字符后,将执行 `string` 比较来确认或否认是否找到匹配项。

这个程序 `U2Charfunc` 是为了在一台只有 SSE 指令或可能只有 SSE 头文件的机器上运行而编写的。我还不确定,而且我不再拥有那台机器,但我知道有人仍然拥有它,并且他们仍然可以使用这个程序。SSE2 包含一些额外的指令,使其速度稍快一些,特别是 `_mm_and_si128`,它允许将 16 个字节作为字节进行 AND 操作,而 SSE 只有 `_mm_and_ps`,它将它们作为 32 位浮点数进行 AND 操作,因此您不能使用它来“AND”两个 16 字节的 SSE 寄存器。换句话说,您必须将它们存储在中间的 `DWORD` 中,并使用“`&`”来“`and`”中间结果。这使得 SSE2 能够实现更紧密的循环和更快的搜索,但这仅在您拥有 SSE2 指令集时才有效。

LFC 算法使用控制字符串和简短的预处理例程来确定您提供的模式中的 LFC。控制字符串包含字母表中的字符,按照它们的频率排列,在我的计算机上的“C”和“C++”中,频率从高到低。它们由 `U2Charfunc` 计算。它将计算字符、字符对和字符串,最多测试长度为 64 个字符。预处理例程尝试在 `LFCARRAY Control string` 中查找搜索模式的最后一个字符。它向后扫描直到找到一个字符。这就是最低频率字符。它是 LFC 偏移量,用于扫描循环的搜索模式中该字符的偏移量。找到 LFC 及其偏移量后,将打开并读取文件。对于单字符、双字符和多于两个字符的模式,有单独的例程。单字符例程使用一个 SSE 寄存器并弹出出现次数。双字符例程使用两个寄存器,但它也弹出计数。这些例程都不需要比较来验证匹配。

在三个或更多字符的例程中,`(MEMORY)` 寄存器对齐在 16 字节边界上,并从当前文件位置 `nWts` 加载对齐。这 16 个字节与包含 16 个匹配模式的第一个字符副本的字节进行比较,称为 `fCtm`,使用 `_mm_cmpeq_epi8`,并将生成的掩码移动到匹配的第一个字符 `fCom`。现在从 `nWts` 加上 LFC 偏移量加载内存。将其与最低频率字符的 16 个副本进行比较,并将掩码移动到 `sCom`,即第二个要匹配的字符。现在对 `fCom` 和 `sCom` 进行 AND 操作,这将导致任何未匹配的 1 位消失。如果 AND 结果为零,则 `nWts` 增加 16,扫描窗口 `wTs` 从 nWts 加载。

当 AND 操作的结果不为零时,我们使用 `_BitScanForward` 来查找设置的最低有效位。这就是匹配索引或 mIdx,它被加到 `nWts` 上以获取可能匹配的地址。在这里,我们使用 `_memicmp` 函数进行比较,并可能增加计数。如果您只想知道文件是否包含一个 `string` 并且不关心它包含的次数,您可以只返回一个标志来指示它是否包含,或者在程序结束时返回一个标志来指示它不包含。

这是 U2Charfunc 作为独立程序的代码。'main()' 函数已被注释掉。

u2Charfunc 函数

// u2charFunc.c() loads the memory segments of the  'line ' buffer in
// sixteen byte increments in a 16 byte aligned SIMD Instruction and 
// compares each byte for equality with the least frequent character, 
// then it loads 16 bytes from an offset of the aligned address that 
// matches the offset of the second least frequent character from the
// first. The results are and-ed, eliminating any occurrence of either
// character which does not have an occurrence of the other at the proper
// offset. Since it jumps ahead 16 bytes at a time, it is fast. Also, 
// since it looks for the least frequent characters of the search string,
// it should be faster when the characters chosen are, indeed, less 
// frequently the other characters. The exact effect on the speed of a 
// search depends on the use of the proper control string and the specific
// string being searched for.
//

#include <intrin.h>
#include <stdio.h>

#define  MAXLINE   1024 * 4
#define  MAXPATH   260
#pragma warning( disable: 4706)

// unaligned two character scan function
int u2Charfunc ( CHAR * chBuf, char * pattern, int patlen ) {

  // Analyze search string and scan files in Listview 
  // Frequency Rank   -----------1---------2---------3---------4---------5-
  // most to least    0123456789012345678901234567890123456789012345678901 
  // frequent char    etroniaslcdpETSCmIRuLDAhOfPNgwMUbBFyWvGxVHkzKYXjQqZJ
  char lFcArray [ ] =  "etroniaslcdpETSCmIRuLDAhOfPNgwMUbBFyWvGxVHkzKYXjQqZJ ";
  int lfclen = ( int ) strlen ( lFcArray );
  int prefixLen;
  char lFcIndex = 0;
  char secLFcIndex = 0;
  char searchChar = 0;
  char searchChar2 = 0;
  char *p, *q, *r; p = q = r = 0;
  size_t j = 0;
  for ( int i = 1; i < lfclen; i++ ) {
    p = strrchr ( pattern, lFcArray [ lfclen - i ] );
    if ( p != 0 ) {
      lFcIndex = ( char ) ( p - pattern );
      searchChar = pattern [ lFcIndex ];
      j = ( size_t ) lfclen - i - 1;
      p = strchr ( pattern, searchChar );
      secLFcIndex = ( char ) ( p - pattern );
      searchChar2 = pattern [ secLFcIndex ];
      break;
    }
  }
  if ( secLFcIndex == lFcIndex ) {

    for ( size_t i = 0; i < j; i++ ) {
      p = strrchr ( pattern, lFcArray [ j - i ] );
      if ( p != 0 ) {
        secLFcIndex = ( char ) ( p - pattern );
        searchChar2 = pattern [ secLFcIndex ];
        break;
      }
    }
  }

  char  scanChar = searchChar;
  char scanChar2 = searchChar2;
  prefixLen = lFcIndex;
  int charOffset = -prefixLen;
  int maskOffset = secLFcIndex - lFcIndex;

  HANDLE  hSearch;
  char * line = NULL;
  char searchFile [ 260 ];
  int numfound = 0;
  scanChar = pattern [ lFcIndex ];
  scanChar2 = pattern [ secLFcIndex ];
  charOffset = -lFcIndex;
  maskOffset = secLFcIndex - lFcIndex;

  int totBytes = 0;
  p = strchr ( chBuf,  '\r ' );
  if ( p ) *p =  '\0 ';
  memset ( &searchFile, 0, sizeof ( searchFile ) );
  sprintf_s ( searchFile, 260,  "%s ", chBuf );
  numfound = 0;
  BOOL bResult = FALSE;
  DWORD nbytesToRead = 1024 * 64;
  DWORD nbytesRead = 0;
  hSearch = CreateFileA ( searchFile, GENERIC_READ, FILE_SHARE_READ, NULL,
              OPEN_EXISTING, FILE_ATTRIBUTE_READONLY | FILE_FLAG_SEQUENTIAL_SCAN, NULL );
  if ( hSearch == INVALID_HANDLE_VALUE ) {
    DWORD dwErr = GetLastError ( );
    if ( dwErr != 0 && dwErr != 5 && dwErr != 32 && dwErr != 2 ) {
      MessageBoxA ( 0, searchFile,  "Can 't Create File: ", MB_OK | MB_ICONEXCLAMATION );
      return -1;
    }
  }
  else {
    unsigned long maxLen = 0;
    LARGE_INTEGER  maxLenQ;
    PLARGE_INTEGER  maxLenL = &maxLenQ;
    maxLenL->QuadPart = 0;
    GetFileSizeEx ( hSearch, maxLenL );
    if ( maxLenL == 0 )
      return 0;
    maxLen = ( LONG ) maxLenL->QuadPart;
    if ( maxLen > 1024 * 1024 - 1024 ) {
      nbytesToRead = 1024 * 1024 - 1024;
      maxLen -= 1024 * 1024 - 1024;
    }
    else
      nbytesToRead = ( DWORD ) maxLenL->QuadPart;
    line = ( char * ) calloc ( ( size_t ) 
            nbytesToRead + 1024, 1 ); // Buffer the buffer - doubled buffered
    errno_t nErr;
    _get_errno ( &nErr );
    if ( patlen != 0 ) {
      bResult = ReadFile ( hSearch, &line [ 16 ], nbytesToRead, &nbytesRead, NULL );
      while ( bResult &&  nbytesRead != 0 ) {
        int numRead = nbytesRead;
        if ( numRead != 0 ) {
          totBytes += nbytesRead;
          numRead = nbytesRead;
          if ( patlen == 1 ) {
            // Next Window to Scan
            char * nWts = line + 16;
            //Match InDeX
            //unsigned long mIdx = 0ul;
            // The Window to Scan
            __m128i       wTs;
            // First Character To Match
            __m128i       fCtm = _mm_set1_epi8 ( *pattern );
            // Comparison flags to be moved to fcom
            __m128i       tempMask;

            // First Character of Match
            unsigned  fCom = 0;;
            // Scan Matches
            // Alignment Offset
            unsigned      aO = 15 & ( UINT_PTR ) nWts;
            nWts -= aO;        // align the buffer
            // Pointer To Matches 
            wTs = _mm_load_si128 ( ( __m128i const * )nWts );
            while ( 1 ) {
              tempMask = _mm_cmpeq_epi8 ( fCtm, wTs );
              fCom = _mm_movemask_epi8 ( tempMask );
              numfound += __popcnt ( fCom );
              if ( numRead <= 0 )
                break;
              nWts += 16;
              wTs = _mm_load_si128 ( ( __m128i const * )nWts );
              numRead -= 16;
            }
          }
          else if ( patlen > 1 ) {
            if ( numRead == 4096 ) {
              SetFilePointer ( hSearch, -( patlen - 1 ),
                       NULL, FILE_CURRENT );
            }
            if ( patlen == 2 ) {
              char * nWts = line + 16;  // Next Window to Scan
              __m128i       wTs;        // The Window to Scan
              // First Character To Match
              __m128i       fCtm = _mm_set1_epi8 ( scanChar );
              // Second Character To Match
              __m128i       sCtm = _mm_set1_epi8 ( scanChar2 );

              unsigned  fCom = 0;  // First Character of Match
              unsigned  sCom = 0;  // Second Character of Match
              unsigned    sM = 0;  // Scan Matches

              // Alignment Offset
              unsigned      aO = 15 & ( uintptr_t ) nWts;
              nWts -= aO;        // align the buffer
              wTs = _mm_load_si128 ( ( __m128i const * ) nWts );
              while ( 1 ) {
                //#pragma warning( disable: 4706)
                if ( ( fCom = _mm_movemask_epi8 ( _mm_cmpeq_epi8 ( fCtm, wTs ) ) != 0 ) ) {
                  wTs = _mm_loadu_si128 ( ( __m128i const * )( nWts + maskOffset ) );
                  //#pragma warning( disable: 4706 )
                  if ( ( sCom = _mm_movemask_epi8 ( _mm_cmpeq_epi8 ( sCtm, wTs ) ) != 0 ) ) {
                    sM = fCom & sCom;
                  }
                  if ( sM ) {
                    numfound += __popcnt ( sM );
                  }
                }
                if ( numRead <= 0 )
                  break;
                nWts += 16;
                wTs = _mm_load_si128 ( ( __m128i const * ) nWts );
                numRead -= 16;
              }
            }
            else if ( patlen > 2 ) {
              // Next Window to Scan
              char * nWts = line + 16;
              //Match InDeX
              unsigned long mIdx = 0ul;
              // The Window to Scan
              __m128i       wTs;
              // First Character To Match
              __m128i       fCtm = _mm_set1_epi8 ( scanChar );
              // Second Character To Match
              __m128i       sCtm = _mm_set1_epi8 ( scanChar2 );
              // Comparison flags to be moved to fcom and scom
              __m128i       tempMask;

              // First Character of Match
              unsigned  fCom = 0;;
              // Second Character of Match
              unsigned  sCom = 0;
              // Scan Matches
              unsigned      sM = 0;
              // Alignment Offset
              unsigned      aO = 15 & ( uintptr_t ) nWts;
              nWts -= aO;        // align the buffer
              // Pointer To Matches 
              char const* pTm = NULL;
              wTs = _mm_load_si128 ( ( __m128i const * )nWts );
              while ( 1 ) {
                tempMask = _mm_cmpeq_epi8 ( fCtm, wTs );
                fCom = _mm_movemask_epi8 ( tempMask );
                wTs = _mm_loadu_si128 ( ( __m128i const * )( nWts + maskOffset ) );
                tempMask = _mm_cmpeq_epi8 ( sCtm, wTs );
                sCom = _mm_movemask_epi8 ( tempMask );
                sM = fCom & sCom;
                while ( sM ) {
                  _BitScanForward ( &mIdx, sM );
                  pTm = nWts + mIdx + charOffset; //address of a possible match
                  if ( !_memicmp ( pTm, pattern, patlen ) ) {
                    numfound++;
                  }
                  sM &= sM - 1;
                }
                nWts += 16;
                if ( numRead <= 0 )
                  break;
                numRead -= 16;
                wTs = _mm_load_si128 ( ( __m128i const * )nWts );
              }
            }
          }
          memset ( &line [ 16 ], 0, nbytesRead );
          bResult = ReadFile ( hSearch, &line [ 16 ], nbytesToRead, &nbytesRead, NULL );
        }
      }
    }
  }
  CloseHandle ( hSearch );
  free ( line );
  return numfound;
}
/* 
// To run u2charfunc as a stand-alone program, you need 
// a main function, similarto the one below but when you
// call it as a subroutine,  you must pass the pattern
// length as the third parameyer.
int main ( int argc, char *argv [ ] ) {
  char * path [ 260 ] = { 0 };
  char * pattern [ 64 ] = { 0 };
  int patlen = 0;
  // Verify correct number of args (exename filename string2search4
  if ( argc != 3 ) {
    printf_s (  "Usage: lfcMatch.exe  <path\file> <pattern>\n " );
    // Copy args 
    //strcpy ( pattern, tPattern );
    //strcpy ( path, tPath );
    return 0;
  }
  else {
    // Copy args
    patlen = sprintf_s ( path, 260,  "%s ", *++argv );
    if ( 260 < patlen ) {
      printf_s (  "Path is too long \n " );
    }
    patlen = sprintf_s ( pattern, 64,  "%s ", *++argv );
  }
  int retnum = u2Charfunc ( path, pattern, patlen );
  printf_s (  "Found %s %d times\n ", pattern, retnum );
  return retnum;
}
*/

这个程序 `U2Charfunc` 是本文的原因。但我意识到,只有那些对 `string` 匹配技术有预先兴趣的开发人员才会对此感兴趣。因此,我包含了一个简短的 Windows 程序来演示此程序的可能用途。

背景

为什么还要有一个查找实用程序?很简单。我不喜欢我现有的。我尝试了许多不同的产品,但找不到我需要的。有时我可能需要其中一个及其功能,但主要是我想要查找关键字及其使用上下文。大多数时候,我想看看我自己的代码是如何使用它的,或者别人是如何使用它的。当我阅读 Visual Studio 文档时,我需要这个,它说找不到请求的主题,或者有时它可以找到请求的主题。我需要它不要让我分心于我正在做的事情。我不想花 30 分钟来弄清楚如何使用它。我试图提供这些功能,同时尽量不让程序变得太大。

但这只是一个批处理程序!它与 Windows 有什么关系?

如果您取消注释 '`main()`' 函数,这只能是一个批处理程序。否则,它是 Windows 程序中的一个函数。`U2Charfunc` 不由 Windows 程序中的 '`main()`' 调用,而是由 `findStringInAllFiles()` 函数调用。此函数不接受任何参数并返回一个 `BOOL`。要查找的模式从环境变量 '`FSIAF`' 中获取,它是 '`Find string in all files`' 的缩写。文件名和路径从列表视图中当前选定的项检索。模式长度由 `_snprintf_s` 确定,作为从 `fsiaf` 复制到模式的字符数。它获取第一个路径,即 `ListView` 中的当前项。然后获取项计数,并循环 `ListView` 项计数次,获取下一个路径,设置主窗口和编辑框的窗口文本,并调用 `u2Charfunc(path, pattern, patlen)`。如果返回值大于零,则 `ListView` 中的 '`OCCURS`' 列将使用 `count` 更新。然后它获取下一项的路径,并窥探消息队列,这样用户就不会因为尝试多任务处理而导致程序无响应。然后我们循环直到所有项都被搜索完。循环结束后,`ListView` 中的当前项与开始时相同。我们将 F5 键发送到 `ListView`,使其搜索 '`Occurs`' 列,查找当前项之后的第一个包含出现的文件。

findStringInAllFiles() 函数

//////////////////////////////////////////////
//
// findStringInAllFiles() analyzes the Search String to find the Least Frequent
// and Second Least Frequent characters using a Control String that is specific
// to the C and C++ Language, based on the C++ files on My Computer. It then calls 
// U2CharFunc() on each file in the ListView File Set to count the occurrences
// of the Search String, if any. After each file is scanned, the message queue
// is peeked and dispatched to animate the progress bar and retain the windows 
// responsiveness.
//
////////////////////////////////////////////
BOOL findStringInAllFiles ( ) {
  # pragma region Initialize_Storage
  TCHAR searchCap [ 256 ]; // The Window Caption for Search Function
  TCHAR curntFname [ 256 ];
  TCHAR curntPath [ MAX_PATH ];
  int hits = 0;
  memset ( &curntFname, 0, sizeof ( curntFname ) );
  memset ( &curntPath, 0, sizeof ( curntPath ) );
  TCHAR curntHitCount [ 256 ];
  int result;
  CHAR chBuf [ 4096 ];
  memset ( &chBuf, 0, sizeof ( chBuf ) );
  char pattern [ MAX_PATH ];
  TCHAR fsiaf [ 256 ] = { 0 };  // Find this String In A File
  GetEnvironmentVariable ( L "FSIAF ", fsiaf, 255 );
  int patlen = _snprintf_s ( pattern, 255, 255,  "%S ", fsiaf );
  HWND hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
  HWND hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 );
  HWND hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );

  int startPos = -1, endPos = 0;
  SendMessage ( hEdit, EM_SETSEL, ( WPARAM ) &startPos, ( LPARAM ) &endPos );

  # pragma endregion findStringInAllFiles(...)  
  SetFocus ( hEdit );
  StringCchPrintf ( searchCap, 255, L "Searching for %s in ALL listed files. ", fsiaf );
  SetWindowText ( hMainWnd, searchCap );
  int iCount = ListView_GetItemCount ( hList );
  for ( int i = 0; i < iCount; ++i ) {
    memset ( &curntPath, 0, sizeof ( curntPath ) );
    memset ( &curntFname, 0, sizeof ( curntFname ) );
    // Get column one text string
    ListView_GetItemText ( hList, i, 0, curntFname, MAX_PATH - 1 );
    // Get column three text string
    ListView_GetItemText ( hList, i, 2, curntPath, MAX_PATH - 1 );
    memset ( &chBuf, 0, sizeof ( chBuf ) );
    // either of the following 2 lines can be used because u2Charfunc
    // changes backslash r to backslash zero
    sprintf_s ( chBuf, 4096,  "%S\\%S\r\n ", curntPath, curntFname );
    curntPath [ 259 ] = 0;
    SetWindowText ( hMainWnd, curntPath );
    SetWindowText ( hEdit, curntPath );
    int numbFound = u2Charfunc ( chBuf, pattern, patlen );

    if ( numbFound > 0 ) {
      hits++;
      StringCchPrintf ( curntHitCount, 255, L "%d occurrences of  '%s '. ", numbFound, fsiaf );
      LVITEM lvi;
      int ret = i;
      memset ( &lvi, 0, sizeof ( lvi ) );
      lvi.mask = LVIF_TEXT;
      lvi.iItem = ret;
      lvi.iSubItem = 1;
      lvi.cchTextMax = 255;
      lvi.pszText = curntHitCount;
      ListView_SetItem ( hList, &lvi );
      ListView_SetItemState ( hList, i, LVIS_SELECTED, LVIS_SELECTED );
      ListView_EnsureVisible ( hList, i, TRUE );
      MSG msg;
      while ( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE ) ) {
        DispatchMessage ( &msg );
      }
    }
  } //endfor
  result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
  if ( result == -1 )
    result = 0;
  ListView_SetItemState ( hList, result, LVIS_SELECTED, LVIS_SELECTED );
  StringCchPrintf
  ( searchCap, 255,
    L "Searched %d Files, Found %d files containing 1 or more occurrences of  '%s '. ",
    iCount, hits, fsiaf );
  SetWindowText ( hMainWnd, searchCap );
  SetWindowText ( hEdit, searchCap );
  ListView_SetItemState ( hList, -1, 0, LVIS_SELECTED );
  ListView_SetItemState ( hList, result, LVIS_SELECTED, LVIS_SELECTED );
  HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
  StringCchPrintf ( searchCap, 255, L "%d Files with matches. ", hits );
  SendMessage ( hwndStatus, SB_SETTEXT, 1 | SBT_POPOUT, ( LPARAM ) searchCap );
  sendMeMyKey ( hList, VK_F5 );
  return 1;
}

Putting the File Name in the ListView

选择或输入文件列表的扩展名。

将文件名和路径放入 ListView:recursivefileSearch() 函数

大多数开发人员都有一个主要语言,他们用这种语言编写他们的大部分工作。这表明他们可能想要一种“默认”类型的文件。我们无法提前知道是什么,因此初始“默认”设置为“*.cpp;*.c”。这将导致 `ListView` 被填充具有 '`cpp`' 和 '`c`' 扩展名的文件,根文件夹为 `%USERPROFILE%` 环境变量。这两个“默认值”都可以更改,并且新值将使用 `setx` 函数存储在注册表中。通过左侧的组合框选择或输入扩展名集。用户可以单击他们想要的下拉行或 `EditCtrl` 并键入他们的选择,然后按 **ENTER** 或单击左侧的 **START** 按钮。

在上方的屏幕截图中,组合框下拉列表显示了一长串扩展名,前面是星号和句点,并用分号分隔。如果使用多个扩展名,则必须使用分号。除非您正在搜索特定的标题或后缀,否则必须使用星号,例如“the Wind”可能会找到“Gone With the Wind”,但找不到“The Wind blows Cold”,因为它会查看参数的结尾。这三种情况由三个不同的递归例程处理。由于可以通过分号和星号的存在来区分这些情况,因此我们使用一个非递归函数来分派它们。这使得单个情况的逻辑更加简单,但确实需要更多的代码。这是非递归函数 '`recursiveFileSearch`' 的代码。

选择正确的递归文件搜索

recursivefileSearch 函数

// analyze search request string and dispatch recursivefileSearch functions accordingly
int recursivefileSearch ( TCHAR *name, BOOL hidden ) {
  TCHAR searchStr [ MAX_PATH ] = { 0 };
  GetEnvironmentVariable ( L "SEARCHSTR ", searchStr, 255 );
  if ( ( wcschr ( searchStr, L '* ' ) == NULL ) ) {
    recursivefileSearch1 ( name, searchStr, hidden );
    return 0;
  }
  else    if ( wcschr ( ( LPWSTR ) searchStr, L '; ' ) != NULL ) {
    recursivefileSearch2 ( name, searchStr, hidden );
    return 0;
  }
  recursivefileSearch3 ( name, searchStr, hidden );
  return 0;
}

查找匹配的后缀:recursivefileSearch1

现在,已经建立了建立 `ListView` 文件列表的标准。我们刚刚进入函数,但不知道我们已经来过多少次了。由于可能已经有一段时间了,我们窥探消息队列以确保我们保持响应。这在一个循环中,所以我们不需要循环。我们确保路径末尾有一个反斜杠,并在末尾添加一个“`*`”。我们使用 `FindFirstFileExW` 创建一个查找句柄。这会填充一个包含初始发现信息的结构。如果这是一个文件,我们将其文件名后缀与我们的参数进行比较。如果匹配,我们将其放入 `ListView`。如果它是点或双点,我们忽略它。当它是目录时,我们将目录添加到路径的末尾,并将路径放入主窗口和编辑框的标题中。然后我们调用 `recursiveFileSearch1`。我们这样做直到 `FindNextFile` 返回零而不是句柄 `hFind` 的新信息。这是 `recursivefileSearch1` 函数的代码。

recursivefileSearch1 函数

// FUNCTION: Find all files whose name contains the search argument.
// Does not search hidden directories. This results in a significant 
// increase in speed. Pattern is assumed to be a suffix. It can be any
// reasonable length, so it is not limited to extensions. It can not 
// contain wildcards. The pattern is compared to the end of the file
// name to identify files to list.
int recursivefileSearch1 ( TCHAR *name, TCHAR * searchStr, BOOL hidden ) {
  WIN32_FIND_DATA ffd;
  HANDLE hFind = INVALID_HANDLE_VALUE;
  DWORD dwError = 0, addcount = 0;
  TCHAR recursiveSearchPath [ MAX_PATH ], text [ 32 ];
  HWND hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
  HWND hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 );
  HWND hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
  MSG msg;
  while ( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE ) )
    DispatchMessage ( &msg );

  if ( name [ wcslen ( name ) - 1 ] != L '\\ ' ) {
    // Prepare string for use with FindFile functions.  First, copy the
    // string to a buffer, then append  "\* " to the directory name. 
    wsprintf ( recursiveSearchPath, L "%s\\* ", name );
  }
  else {
    wsprintf ( recursiveSearchPath, L "%s* ", name );
  }

  // Find the first file in the directory.
  hFind = FindFirstFileExW ( recursiveSearchPath, FindExInfoBasic, 
          &ffd, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH );

  // List all the files in the directory with some info about them.
  do {
    if ( hFind == INVALID_HANDLE_VALUE )
      // Done checking this folder
      // no files were found in this recursion
      return -1;

    int fnlen = ( int ) wcslen ( ffd.cFileName );
    int extlen = ( int ) wcslen ( searchStr );
    if ( ( wcscmp ( ffd.cFileName, L ". " ) ) == 0
       || ( wcscmp ( ffd.cFileName, L ".. " ) ) == 0 ) {
      ;  //If the file is a self-reference do nothing
    }
    else  if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN && !hidden ) {
      ;    // the file is hidden and hidden search is turned off
    }
    else if ( !( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) {
      if ( wcsncmp ( ffd.cFileName + fnlen - extlen, searchStr, extlen ) == 0 ) 
      { // it is a FILE containing the argument, put it in the list
        LVITEM item;
        memset ( &item, 0, sizeof ( item ) );
        item.mask = LVIF_TEXT;
        item.iItem = 0;
        item.iSubItem = 0;
        item.cchTextMax = 260;
        item.pszText = ffd.cFileName;
        ListView_InsertItem ( hList, &item );
        item.iSubItem = 2;
        item.pszText = name;
        ListView_SetItem ( hList, &item );
        wsprintf ( text, L "%I64u ", ( ( ULONGLONG ) 
        ( ffd.nFileSizeHigh ) * ( ( ULONGLONG ) MAXDWORD + 1 ) ) + ffd.nFileSizeLow );
        item.iSubItem = 3;
        item.pszText = text;
        ListView_SetItem ( hList, &item );
      }
    }
    else if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // it is a directory
      if ( name [ wcslen ( name ) - 1 ] != L '\\ ' ) {
        wsprintf ( recursiveSearchPath, L "%s\\%s ", name, ffd.cFileName );
      }
      else {
        wsprintf ( recursiveSearchPath, L "%s%s ", name, ffd.cFileName );
      }
      SetWindowText ( hEdit, recursiveSearchPath );
      addcount = ListView_GetItemCount ( hList );
      TCHAR tStr [ MAX_PATH ] = { 0 };
      StringCchPrintf ( tStr, MAX_PATH - 1, L "[%d] %s ", addcount, recursiveSearchPath );
      SetWindowText ( hMainWnd, tStr );
      recursivefileSearch1 ( recursiveSearchPath, searchStr, hidden ); // search this one too
    }
  }
  while ( FindNextFile ( hFind, &ffd ) != 0 );

  dwError = GetLastError ( );
  if ( dwError != ERROR_NO_MORE_FILES )
    if ( dwError != ERROR_ACCESS_DENIED ) {
      // set the text in IDC_EDIT2
      SetWindowText ( hEdit, L "Error in Find File Routine ! " );
    }

  FindClose ( hFind );
  return dwError;
}

Putting the File Name in the ListView

进度条覆盖组合框,因为 recursiveFileSearch 填充 ListView

列出多个扩展名:recursivefileSearch2()

`recursivefileSearch2()` 函数与 `recursivefileSearch1` 非常相似。但有一个主要区别。`recursivefileSearch1` 将参数与文件名末尾进行比较,而 `recursivefileSearch2` 使用 `PathMatchSpec` 函数来确定文件名扩展名是否在参数扩展名集中。如果是,则将其添加到文件列表中。由于其他所有内容都相同,因此未在此处显示代码块。

拆分递归:recursivefileSearch3

当只有一个扩展名并且参数中有一个星号时,我们可以告诉 `FindFirstFileExW` 函数我们只想查看具有此扩展名的文件,而没有目录,方法是在 `FINDEX_SEARCH_OPS` 字段中使用 `FindExSearchNameMatchflag`。然后我们循环遍历结果,将它们全部放入文件列表,直到 `FindNextFile` 返回零。然后我们再次调用 `FindFirstFileExW`,这次使用 `FindExSearchLimitToDirectories`,如 `FINDEX_SEARCH_OPS` 参数中所述,以递归遍历目录。由于逻辑差异很大,我将完整地展示它。这就是它...

recursivefileSearch3 函数

// FUNCTION: Find all files whose name contains the search argument.
// Does not search hidden directories. This results in a significant 
// increase in speed. The pattern may contain wildcards. The 
// FindExSearchNameMatchflag is used to identify files to list. 
// After FindExSearchNameMatchflag processing is completed then 
// the FindExSearchLimitToDirectories as is used to recursively search
// through the subdirectories.
int recursivefileSearch3 ( TCHAR *name, TCHAR * searchStr, BOOL hidden ) {
  WIN32_FIND_DATA ffd;
  HANDLE hFind = INVALID_HANDLE_VALUE;
  DWORD dwError = 0;
  TCHAR recursiveSearchPath [ MAX_PATH * 2 ], text [ 32 ];
  HWND hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
  HWND hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1);
  HWND hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
  MSG msg;
  while ( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE ) )
    DispatchMessage ( &msg );
  if ( name [ wcslen ( name ) - 1 ] != L '\\ ' ) {
    // Prepare string for use with FindFile functions.  First, copy the
    // string to a buffer, then append  "\* " to the directory name. 
    wsprintf ( recursiveSearchPath, L "%s\\*%s ", name, searchStr );
  }
  else {
    wsprintf ( recursiveSearchPath, L "%s* ", name );
  }

  // Find the first file in the directory.
  hFind = FindFirstFileExW ( recursiveSearchPath, FindExInfoBasic, &ffd,
                 FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH );

  if ( hFind != INVALID_HANDLE_VALUE ) {
    // List all the files in the directory with some info about them.
    do {
      LVITEM item;
      memset ( &item, 0, sizeof ( item ) );
      item.mask = LVIF_TEXT;
      item.iItem = 0;
      item.iSubItem = 0;
      item.cchTextMax = 260;
      item.pszText = ffd.cFileName;
      ListView_InsertItem ( hList, &item );
      item.iSubItem = 2;
      item.pszText = name;
      ListView_SetItem ( hList, &item );
      wsprintf ( text, L "%I64u ", ( ( ULONGLONG ) 
      ( ffd.nFileSizeHigh ) * ( ( ULONGLONG ) MAXDWORD + 1 ) ) + ffd.nFileSizeLow );
      item.iSubItem = 3;
      item.pszText = text;
      ListView_SetItem ( hList, &item );
    }
    while ( FindNextFile ( hFind, &ffd ) != 0 );

    dwError = GetLastError ( );
    if ( dwError != ERROR_NO_MORE_FILES )
      if ( dwError != ERROR_ACCESS_DENIED ) {
        // set the text in IDC_EDIT2
        SetWindowText ( hEdit, L "Error in Find File Routine ! " );
      }
    FindClose ( hFind );
  }

  if ( name [ wcslen ( name ) - 1 ] != L '\\ ' ) {
    // Prepare string for use with FindFile functions.  First, copy the
    // string to a buffer, then append  "\* " to the directory name. 
    wsprintf ( recursiveSearchPath, L "%s\\* ", name );
  }
  else {
    wsprintf ( recursiveSearchPath, L "%s* ", name );
  }
  // Find the first file in the directory.
  hFind = FindFirstFileExW ( recursiveSearchPath, FindExInfoBasic, &ffd,
                 FindExSearchLimitToDirectories, NULL, FIND_FIRST_EX_LARGE_FETCH );
  do {
    if ( hFind == INVALID_HANDLE_VALUE )
      // Done checking this folder
      // no files were found in this recursion
      return -1;

    if ( ( wcscmp ( ffd.cFileName, L ". " ) ) == 0
       || ( wcscmp ( ffd.cFileName, L ".. " ) ) == 0 ) {
      ;  //If the file is a self-reference do nothing
    }
    else  if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN && !hidden ) {
      ;    // the file is hidden and hidden search is turned off
    }
    else if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { // it is a directory
      if ( name [ wcslen ( name ) - 1 ] != L '\\ ' ) {
        wsprintf ( recursiveSearchPath, L "%s\\%s ", name, ffd.cFileName );
      }
      else {
        wsprintf ( recursiveSearchPath, L "%s%s ", name, ffd.cFileName );
      }
      SetWindowText ( hMainWnd, recursiveSearchPath );
      SetWindowText ( hEdit, recursiveSearchPath );

      recursivefileSearch3 ( recursiveSearchPath, searchStr, hidden ); // search this one too
    }
  }
  while ( FindNextFile ( hFind, &ffd ) != 0 );

  dwError = GetLastError ( );
  if ( dwError != ERROR_NO_MORE_FILES ) {
    if ( dwError != ERROR_ACCESS_DENIED ) {
      // set the text in IDC_EDIT2
      SetWindowText ( hEdit, L "Error in Find File Routine ! " );
    }
  }
  FindClose ( hFind );
  return dwError;
}

选择或输入要搜索的字符串:ComboBox2

The irratating meddagebox that you can get rid of. ...forever

更改目录、扩展名或要搜索的字符串时,将显示此消息框。

当您看到此消息框时,消息框的左下角有一个 `checkbox`。如果选中 `checkbox`,则不再显示消息框。恢复消息框的方法将在后面讨论。目前,这三个消息框都使用相同的 GUID 来控制 `Checkbox` 的状态。消息框纯粹是信息性的,因为使用的函数(`SETX`)即使失败也返回相同的结果。有额外的错误检查来告知用户数据是否未保存。此函数最有可能失败的时间是当天的第一次尝试,并且仅在最后一次尝试时才重要。因为只检索最后一个设置的值。

The not irratating meddagebox that you see whem the data is not saved.

更改目录、扩展名或要搜索的字符串时,并且数据未保存,将显示此消息框。

选择右侧的组合框 EditCtrl 并按 ENTER 将检索上次保存的值。单击下拉箭头将显示今天输入的值

Entering a string to search for or using the Dropdown

在 EditCtrl 中输入要搜索的字符串后,按 ENTER 的效果与单击“开始搜索”按钮相同。

两个 `ComboBox` 都在同一个 `comboBoxSubClassProc` 函数中进行了子类化。这是一个非常简单的函数。它首先获取主窗口和 `IDC_BTN1` 和 `IDC_BTN2` 的窗口句柄。当 `uMsg` 是 `WM_KEYDOWN` 并且 `wParam` 是 `VK_RETURN` 时,就知道它来自 `combobox` 的子项,因此它获取父 `combobox`,然后使用它来 `GetDlgCtrlID` 以区分两个 `ComboBox`,并向相应的 `Button` 发送 `BM_CLICK` 消息。这会单击按钮并启动请求的功能。它还处理 `WM_KEYUP` 和 `WM_CHAR`,对于 `WM_KEYUP` 返回零。所有内容都返回给 `DefSubclassProc` 函数。

处理 Enter 键:comboBoxSubClassProc

comboBoxSubClassProc 函数

// The Combobox was subclassed to make Buttons respond to EDITCTRL Enter Key 
// press as if Button was clicked. User can either click Button or, since they
// are typing in the EditCtrl,  they can just press Enter.
LRESULT CALLBACK comboBoxSubClassProc ( HWND hWnd, UINT uMsg, WPARAM wParam, 
  LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) {
  UNREFERENCED_PARAMETER ( uIdSubclass );
  UNREFERENCED_PARAMETER ( dwRefData );
  HWND hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
  HWND hBtn1 = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_BUTTON1 );
  HWND hBtn2 = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_BUTTON2 );
  
  switch ( uMsg ) {

  case WM_KEYDOWN:
    switch ( wParam ) {
    case VK_RETURN:
      HWND hComBo = GetParent ( hWnd );
      ULONG dlgId = GetDlgCtrlID ( hComBo );
      if ( dlgId == IDC_COMBO1 ) {
        SetActiveWindow ( hBtn1 );
        SendMessage ( hBtn1, BM_CLICK, 0, 0 );
        return 1;
      }
      if ( dlgId == IDC_COMBO2 ) {
        SetActiveWindow ( hBtn2 );
        SendMessage ( hBtn2, BM_CLICK, 0, 0 );
        return 1;
      }
    }
    break;

  case WM_KEYUP:
  case WM_CHAR:
    switch ( wParam ) {
    case  VK_RETURN:
      return 0;
    }
  default:
    return DefSubclassProc ( hWnd, uMsg, wParam, lParam );
  }
  return 0;
}

处理输入和生命周期数据:Comboboxproc 函数

The String found by U2Charfunc, shown in it 's full context in yhe Viewer Panel

U2Charfunc 找到的字符串,在 Viewer Panel 中以完整上下文显示

ComboBox1 中的扩展名,“要搜索的字符串”以及找到它的文件

`comboBoxProc` 函数处理 `TwoStageSearch` 的用户输入。在“`message`”开关之前,它确定消息不是 `WM_INITDIALOG`,以便它可以检索 `WM_INITDIALOG` 创建的所有窗口的句柄。这样做的原因是常量不能在 `case` 语句中使用两次。通过在“`if`”结构中使用 `WM_INITDIALOG`,它仍然可以在 `switch` case 中使用。

`ComboBox1` 的句柄 `hCombo1` 用于防止某些项的初始化,直到所有项都准备好为止。在 `WndProc` 中初始化 `hCombo1` 句柄后,可以完成所有其他操作。我们获取 `Combobox` 信息并为 proc 设置 `SetWindowSubclass`。这是子类化控件的新过程。如前所示,我们只处理 Return 键以生成 Button 的鼠标单击。第一个 Combo Box 将为“Start File List Build”按钮生成鼠标单击。接下来,我们用一组用于构建扩展名的可能选择填充 Combo Box 下拉列表,并初始化 Combo Box 的提示横幅。对于 `ComboBox2`,我们做同样的事情,除了下拉列表,我们将其留空。接下来,我们给控件一个初始大小并将它们移动到初始位置。进度条定位在组合框上方但被隐藏。

在 `WM_COMMAND` 消息中,我们处理 `wmId` 开关,第一个 case 是 `IDC_BUTTON1`。在这里,我们响应 `BM_CLICK` 消息,获取 `Combobox EditCtrl` 的文本。如果非空,则将其添加到下拉列表中,否则,检索上次使用的扩展名集并将其放入 `EditCtrl`,并返回零。换句话说,如果 `EditCtrl` 为空并且显示了 `CUE`,当用户单击 `EditCtrl` 或左侧的按钮时,将把先前的值放入 `EditCtrl`。如果 `EditCtrl` 中有值,则将其与先前扩展名集进行比较,如果不同,则通过调用 `persist_This` 函数将新值保存在注册表中。无论哪种情况,都会激活进度条以覆盖 `ComboBox` 并阻止用户进行多任务处理,更新状态栏并调用 `recursivefileSearch` 来构建文件列表。返回后,状态栏会更新,进度条会隐藏,并且当前文件会被选中。

同样在 `wmId` 开关中,下一个 case 是 `IDC_COMBO1`。我们响应的唯一事件是 `CBN_EDITUPDATE`。我们获取 `EditCtrl` 的文本并检查第一个字符,如果它是一个问号,我们就清空该字段并设置 `CUE` 横幅。`IDC_COMBO0` 的处理与 `IDC_COMBO1` 相同。`IDC_BUTTON2` 以与 `IDC_BUTTON1` 相同的方式处理 `BM_CLICK`,但调用 `findStringInAllFiles` 函数而不是 `recursivefileSearch` 函数。

`WM_SIE` 消息获取 `hFormView` 容器中所有控件的位置和大小,并将它们移动到正确的位置。唯一有问题(窗口)的是 `ComboBox2`。如果我使用 `GetWindowRect` 函数的大小,它有时会显示 `combobox` 按钮,有时不会。全屏时,它始终显示,但我认为要求用户全屏以单击下拉三角形是不合理的。为了确保下拉按钮始终显示,我选择将其设置为静态值,除非它是全屏的,在这种情况下,我使用 `GetWindowRect` 值。

comboBoxProc 函数

// Subclass the proc to handle vk_return to simulate button click.
// Input string2search4 and file list Extension Set. Persist environment
// variables on input invoke File list Build or Find string in all files.
INT_PTR CALLBACK comboBoxProc ( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) {
  UNREFERENCED_PARAMETER ( lParam );
  TCHAR searchStr [ 256 ] = { 0 };
  int wmId, wmEvent;
  static HWND hwndEdit1, hwndEdit2;
  HWND hEdit = NULL, hMainWnd = NULL, hCombo1 = NULL, hCombo2 = NULL, hProgress = NULL,
    hStrtBild = NULL, hStrtSrch = NULL, hList = NULL;
  RECT bRc1 = { 0 }, bRc2 = { 0 }, cRc1 = { 0 }, cRc2 = { 0 }, dRc = { 0 }, pRc = { 0 };
  RECT bRccl1 = { 0 }, bRccl2 = { 0 }, cRccl1 = { 0 }, 
                       cRccl2 = { 0 }, dRccl = { 0 }, pRccl = { 0 };

  if ( WM_INITDIALOG != message ) {

    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    if ( 0 == hMainWnd ) {  return 0;}
    hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
    if ( 0 == hEdit ) {    return 0;  }
    hCombo1 = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_COMBO1 );
    if ( 0 == hCombo1 ) {return 0;  }
    hCombo2 = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_COMBO2 );
    if ( 0 == hCombo2 ) {  return 0;}
    hProgress = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_PROGRESS1 );
    if ( 0 == hProgress ) {  return 0;}
  }

  switch ( message ) {

  case WM_INITDIALOG:
  {
    hCombo1 = getThisWindowHandle ( hDlg, IDC_COMBO1 );
    if ( 0 != hCombo1 )
    {
      hStrtBild = GetDlgItem ( hDlg, IDC_BUTTON1 );
      hCombo1 = GetDlgItem ( hDlg, IDC_COMBO1 );
      COMBOBOXINFO cbi = { 0 };
      cbi.cbSize = { sizeof ( COMBOBOXINFO ) };
      GetComboBoxInfo ( hCombo1, &cbi );
      hwndEdit1 = cbi.hwndItem;
      SetWindowSubclass ( hwndEdit1, comboBoxSubClassProc, ( UINT_PTR ) IDC_COMBO1, 0 );
      DWORD dwErr = GetLastError ( );
      // Add strings to combobox.
      TCHAR szComboText [ ] [ 64 ] = { L "*.cpp;*.c;*.cs;*.fs;*.fsx;*.htm;*.html;*.xaml;
      *.xml ",L "*.cs ",L "*.vb ",L "*.c ",
      L"*.fsx;*.fs",L"*.pdf",L"*.txt" };
      int nItemCount = 7;
      for ( int i = 0; i < nItemCount; i++ ) {
        SendMessage ( hCombo1, ( UINT ) CB_ADDSTRING, 
                    ( WPARAM ) 0, ( LPARAM ) szComboText [ i ] );
        dwErr = GetLastError ( );
      }
      // set the CUE Banner for the ComboBox
      SendMessage ( hCombo1, ( UINT ) CB_SETCUEBANNER, ( WPARAM ) 0,
        ( LPARAM ) L "Type any part of filename or 
        select extension from the drop down list below. " );
      dwErr = GetLastError ( );
      hStrtSrch = GetDlgItem ( hDlg, IDC_BUTTON2 );
      dwErr = GetLastError ( );

      hCombo2 = GetDlgItem ( hDlg, IDC_COMBO2 );
      GetComboBoxInfo ( hCombo2, &cbi );
      hwndEdit2 = cbi.hwndItem;
      SetWindowSubclass ( hwndEdit2, comboBoxSubClassProc, ( UINT_PTR ) IDC_COMBO2, 0 );
      dwErr = GetLastError ( );
      // set the CUE Banner for the ComboBox
      //SendMessage ( hCombo2, ( UINT ) CB_SETEXTENDEDUI, ( WPARAM ) 0, 0 );
      SendMessage ( hCombo2, ( UINT ) CB_SETCUEBANNER, ( WPARAM ) 0, 
      ( LPARAM ) L "Type a word to search for in the list of files shown below. " );
      dwErr = GetLastError ( );
      hProgress = GetDlgItem ( hDlg, IDC_PROGRESS1 );
      ShowWindow ( hProgress, SW_HIDE );
      GetWindowRect ( hDlg, &dRc );
      MoveWindow ( hDlg, 0, 10, dRc.right, 30, TRUE );
      MoveWindow ( hStrtBild, 10, 0, 104, 23, TRUE );
      MoveWindow ( hCombo1, 120, 0, 410, 23, TRUE );
      MoveWindow ( hStrtSrch, 540, 0, 104, 23, TRUE );
      MoveWindow ( hCombo2, 650, 0, dRc.right-650, 23, TRUE );
      MoveWindow ( hProgress, 0, 0, dRc.right, dRc.bottom, TRUE );
      return FALSE;
    }
  }
  break;

  case WM_COMMAND:
    wmId = LOWORD ( wParam );
    wmEvent = HIWORD ( wParam );
    switch ( wmId ) {

    case IDC_BUTTON1:
    {
      TCHAR  ListItem [ 256 ];
      TCHAR  searchCap [ 256 ];
      TCHAR  nodeCD [ 256 ];
      TCHAR * tStr;
      TCHAR cStr [ 255 ]; tStr = cStr;
      LRESULT itemIndex = SendMessage ( hCombo1, 
                   ( UINT ) CB_GETCURSEL, ( WPARAM ) 0, ( LPARAM ) 0 );
      if ( itemIndex < 0 )
        if ( ComboBox_GetText ( hCombo1, ListItem, 255 ) )
          itemIndex = SendMessage ( hCombo1, 
                   ( UINT ) CB_ADDSTRING, ( WPARAM ) 0, ( LPARAM ) ListItem );
        else {
          if (!*searchStr ) {
            GetEnvironmentVariable ( L "SEARCHSTR ", searchStr,255 );
            ComboBox_SetText ( hCombo1, searchStr );
          }
          return 0;
        }
      SendMessage ( hCombo1, ( UINT ) CB_SETCURSEL, ( WPARAM ) itemIndex, ( LPARAM ) 0 );
      SendMessage ( hCombo1, ( UINT ) CB_GETLBTEXT, 
                  ( WPARAM ) itemIndex, ( LPARAM ) ListItem );
      GetEnvironmentVariable ( L "SEARCHSTR ", searchStr,255 );

      if ( wcscmp(ListItem, searchStr)!=0 ) {
        StringCchPrintf ( searchStr, 255, L "%s ", ListItem );
        SetEnvironmentVariable ( L "SEARCHSTR ", searchStr );
        StringCchPrintf ( cStr, 255, L " /k Setx SEARCHSTR \ "%s\ " ", searchStr );
        persist_This ( tStr );
      }
      GetCurrentDirectory ( 255, nodeCD );
      StringCchPrintf ( searchCap, 255, L "Searching --- %s ", nodeCD );
      SetWindowText ( hMainWnd, searchCap );
      hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 );
      if ( 0 == hList ) {
        return 0;
      }
      ShowWindow ( hProgress, SW_NORMAL );
      SendMessage ( hProgress, PBM_SETMARQUEE, PBST_NORMAL, 0 );
      HWND hwndStatus = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_STATUS );
      StringCchPrintf ( tStr, MAX_PATH, L "Searching for %s in   ", searchStr );
      SendMessage ( hwndStatus, SB_SETTEXT, 0, ( LPARAM ) tStr );
      SendMessage ( hwndStatus, SB_SETTEXT, 1 | SBT_POPOUT, ( LPARAM ) nodeCD );
      recursivefileSearch ( nodeCD, FALSE );
      SendMessage ( hwndStatus, SB_SETTEXT, 0, ( LPARAM ) nodeCD );
      int iCount = ListView_GetItemCount ( hList );
      int result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
      StringCchPrintf ( tStr, MAX_PATH, L "Number of Files %d. ", iCount );
      SendMessage ( hwndStatus, SB_SETTEXT, 1 | SBT_POPOUT, ( LPARAM ) tStr );
      StringCchPrintf ( tStr, MAX_PATH, L "%d of %d Files. ", result + 1, iCount );
      SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) tStr );
      SetFocus ( hList );
      SendMessage ( hProgress, PBM_SETMARQUEE, PBST_PAUSED - 3, 0 );
      ShowWindow ( hProgress, SW_HIDE );
      SendMessage ( hwndStatus, SB_SETTEXT, 
                    3 | SBT_POPOUT, ( LPARAM ) L "Line: 0    Column: 0 " );
      return FALSE;
    }
    break;

    case IDC_COMBO1:
    {
      hCombo1 = ( HWND ) lParam;
      if ( wmEvent == CBN_EDITUPDATE ) {
        TCHAR  ListItem [ 256 ];
        ComboBox_GetText ( hCombo1, ListItem, 255 );
        if ( *ListItem == L '? ' ) {
          SetWindowText ( hEdit, L " " );
          // set the CUE Banner for the ComboBox
          Edit_SetCueBannerText ( hEdit,
            ( LPARAM ) L "Type last part of filename in the ComboBox or 
            select an extension from the drop down list below the ComboBox. " );
        }
      }
    }
    break;

    case IDC_BUTTON2:
    {
      TCHAR  ListItem [ 256 ];
      TCHAR  fsiaf [ 256 ] = { 0 };
      TCHAR  searchCap [ 256 ];
      TCHAR  nodeCD [ 256 ];
      TCHAR * tStr;
      TCHAR cStr [ 255 ]; tStr = cStr;
      LRESULT itemIndex = SendMessage 
                ( hCombo2, ( UINT ) CB_GETCURSEL, ( WPARAM ) 0, ( LPARAM ) 0 );
      if ( itemIndex == CB_ERR )
        if ( ComboBox_GetText ( hCombo2, ListItem, 255 ) )
          itemIndex = SendMessage 
                ( hCombo2, ( UINT ) CB_ADDSTRING, ( WPARAM ) 0, ( LPARAM ) ListItem );
        else {
          if ( !*fsiaf ) {
            GetEnvironmentVariable ( L "FSIAF ", fsiaf, 255 );
            ComboBox_SetText ( hCombo2, fsiaf );
          }
          return 0;
        }
      SendMessage ( hCombo2, ( UINT ) CB_SETCURSEL, ( WPARAM ) itemIndex, ( LPARAM ) 0 );
      SendMessage ( hCombo2, ( UINT ) CB_GETLBTEXT, ( WPARAM ) itemIndex, ( LPARAM ) ListItem );
      GetEnvironmentVariable ( L "FSIAF ", fsiaf, 255 );
      if ( wcscmp(ListItem,fsiaf)!=0 ) {
        StringCchPrintf ( fsiaf, 255, L "%s ", ListItem );
        SetEnvironmentVariable ( L "FSIAF ", fsiaf );
        StringCchPrintf ( cStr, 255, L " /k Setx FSIAF \ "%s\ " ", fsiaf );
        persist_This ( tStr );
      }

      GetCurrentDirectory ( 255, nodeCD );
      StringCchPrintf ( searchCap, 255, L "Searching --- %s ", nodeCD );
      SetWindowText ( hMainWnd, searchCap );
      hList = getThisWindowHandle ( hMainWnd, IDC_LIST1 );
      int result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
      int searchStartRow = result;
      ShowWindow ( hProgress, SW_NORMAL );
      SendMessage ( hProgress, PBM_SETMARQUEE, PBST_NORMAL, 0 );
      findStringInAllFiles ( );
      SendMessage ( hProgress, PBM_SETMARQUEE, PBST_PAUSED - 3, 0 );
      ShowWindow ( hProgress, SW_HIDE );
      HWND hRich = getThisWindowHandle ( hMainWnd, IDC_EDIT1 );
      #pragma region select_first_file_with_a_match
      if ( hRich && IsWindowVisible ( hRich ) ) {
        result = searchStartRow - 1;
        if ( searchStartRow == 0 ) {
          result++;
        }
        ListView_SetItemState 
             ( hList, -1, 0, LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
        ListView_SetItemState 
             ( hList, result, LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED,
                    LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
        ListView_EnsureVisible ( hList, result, TRUE );
        return 0; 
      }
      #pragma endregion and make sure it is visible
    }
    break;

    case IDC_COMBO2:
    {
      hCombo2 = ( HWND ) lParam;
      if ( wmEvent == CBN_EDITUPDATE ) {
        TCHAR  ListItem [ 256 ];
        ComboBox_GetText ( hCombo2, ListItem, 255 );
        if ( *ListItem == L '? ' ) {
          SetWindowText ( hEdit, L " " );
          // set the CUE Banner for the ComboBox
          Edit_SetCueBannerText ( hEdit,
            ( LPARAM ) L "Type a word to search for in the list of files 
             shown in the ListView below. " );
        }
      }
    }
    break;

    default:
      break;
    }
    break;

  case WM_SIZE:
  {
    hStrtBild = getThisWindowHandle ( hDlg, IDC_BUTTON1 );
    hStrtSrch = getThisWindowHandle ( hDlg, IDC_BUTTON2 );
    GetWindowRect ( hDlg, &dRc );
    GetClientRect ( hDlg, &dRccl );
    GetWindowRect ( hStrtBild, &bRc1 );
    GetClientRect ( hStrtBild, &bRccl1 );
    GetWindowRect ( hStrtSrch, &bRc2 );
    GetClientRect ( hStrtSrch, &bRccl2 );
    GetWindowRect ( hCombo1, &cRc1 );
    GetClientRect ( hCombo1, &cRccl1 );
    GetWindowRect ( hCombo2, &cRc2 );
    GetClientRect ( hCombo2, &cRccl2 );
    GetWindowRect ( hProgress, &pRc );
    GetClientRect ( hProgress, &pRccl );
    OffsetRect ( &bRc1, -dRc.left, -dRc.top );
    OffsetRect ( &cRc1, -dRc.left, -dRc.top );
    OffsetRect ( &bRc2, -dRc.left, -dRc.top );
    OffsetRect ( &cRc2, -dRc.left, -dRc.top );
    MoveWindow ( hStrtBild, bRc1.left, bRc1.top, bRc1.right-bRc1.left, bRc1.bottom, TRUE );
    MoveWindow ( hCombo1, cRc1.left, cRc1.top, cRc1.right-cRc1.left, cRc1.bottom, TRUE );
    MoveWindow ( hStrtSrch, bRc2.left, bRc2.top, bRc2.right-bRc2.left, bRc2.bottom, TRUE );
    HWND dtHwnd = GetDesktopWindow ( );//MoveWindow 
        // ( hCombo2, cRc2.left, cRc2.top, dRc.right - 720, cRc2.bottom, TRUE );
    RECT dtRc;  GetWindowRect ( dtHwnd, &dtRc );//
    if ( dtRc.right <= dRc.right ) {
      MoveWindow ( hCombo2, cRc2.left, cRc2.top, dRc.right - cRc2.left, cRc2.bottom, TRUE );
    }
    else
      MoveWindow ( hCombo2, cRc2.left, cRc2.top, 350, cRc2.bottom, TRUE );
    MoveWindow ( hProgress, 0, 0, pRccl.right, pRccl.bottom, TRUE );
    return 0;
  }break;
  }
  return ( INT_PTR ) FALSE;
}

使用 Viewer Panel:richEditProc

在 `WM_INITDIALOG` 消息处理程序中,`RichEdit` 控件位于一个名为 `hFormView3` 的窗口中,该窗口用于 `RichEdit` 控件的大小调整。窗口 `hFormView3` 位于一个名为 `hBottom` 的窗口中,该窗口用于调整 `hFormView1`、`hFormView2`、`hForView3` 和 `hMiddle` 的大小。为防止 `RichEdit` 在 `hBottom` 中创建但在 `hFormview3` 中不创建,我们获取主窗口和 `hDlg` 的句柄。因为 `hDlg` 可以是 `hBottom` 或 hFormView3,所以我们通过获取 `hDlg` 的父项来区分它们。如果父项是主窗口,我们返回 `0`。然后我们确保在加载包含 `RICHEDIT50W` 的“`MSFTEDIT”DLL 之前没有 `RichEdit`,它用于创建 `RichEdit` 控件。接下来,我们向 `RichEdit` 发送 `EM_SETEVENTMASK`,告知我们想要按键事件、鼠标事件和 `requestresize`。然后设置缩放比例,以便用户可以使用鼠标滚轮和 Ctrl 键缩放文本字体。

在 `WM_THEMECHANGED` 消息处理程序中,我们需要纠正一个非常糟糕(对我而言)的颜色组合,当主题在程序执行期间更改时。其他控件会将其颜色更改为可接受的组合,但 `RichEdit50w` 不会。我们向它发送一个 `EM_SETBKGNDCOLOR`,其中 `wParam` 设置为一,告诉它将颜色设置为系统颜色。当 `wParam` 设置为 `1` 时,它会忽略您发送的颜色,但您仍然必须发送颜色。我不知道这是否会发生在其他版本的 `RichEdit` 或其他控件上。

在 `WM_NOTIFY` 消息处理程序中,对于 `pMsgFilter` 成员 msg 等于 `WM_KEYUP`,我们获取 `CARET` 的位置,或者如果有选区,则获取选区的第一个字符。这通过 `EM_EXLINEFROMCHAR` 和 `EM_LINEINDEX` 消息转换为行号,并通过从行首减去 `CARET` 位置来转换为列号。然后使用行号和列号更新状态栏。

`WM_LBUTTONDOWN` 消息处理程序执行与 `WM_KEYUP` 相同的处理,即获取行号和列号以更新状态栏。但是对于 `WM_LBUTTONDOWN`,我们还获取鼠标位置,将其从客户区坐标转换为屏幕坐标,并将其保存在名为“`rpt”的 `static Point` 变量中。如果左键单击在一个单词上,则此 Point 可用于以编程方式选择该单词。

这会在 `pMsgFilter` 成员 msg 为 `WM_KEYDOWN` 且 `wParam` 为“`f`”且控件键被按下时处理。同样,我们使用 `EM_GETSEL`。这次,我们比较 `startsel` 和 `endsel` 来确定我们是否有选区。如果没有,我们就向 `Point`“`rpt”发送一个鼠标双击。当 `RichEdit` 收到鼠标双击时,它会选择鼠标下方的单词。然后我们向 `RichEdit` 发送一个“`f”,这会导致同一代码块被执行,因为控件键仍然被按下。这次有一个选区,因此执行“`else”块。现在向 `RichEdit` 发送一个 `EM_GETSELTEXT` 消息,并将文本复制到“`fsiaf”并设置环境变量。该值也放入 `ComboBox2`。`RichEdit` 中的单词随后会被高亮显示。

`pMsgFilter` 成员 `msg` 等于 `WM_KEYDOWN` 且 `wParam` 为 `VK_F3(F3)` 且 `static` 变量 `keyshifted` 设置的消息处理程序,我们将表项 `keyst[VK_SHIFT]` 设置为 `0x80`,设置键盘状态,将 `keyst` 复制到系统表。这样做是为了防止 `RichEdit` 将我们正在搜索的单词大写。

`pMsgFilter` 成员 `msg` 等于 `WM_KEYDOWN` 且 `wParam` 为 `VK_F3(F3)` 且 '`fsiaf' 中没有内容的消息处理程序,用户可能双击并按下 F3。在这里,我们获取选区并将其复制到 '`fsiaf'。现在我们确定 **SHIFT** 键是否被按下,如果是,我们获取键盘状态表并将其放入 `keyst`。将 `keyst` 中 `VK_SHIFT` 的条目设置为零,我们将键盘状态表设置为 `keyst` 并将 `static` 变量 `keyshifted` 设置为 `true`。然后我们找到该单词的先前出现并高亮显示它。如果 **SHIFT** 键未按下,我们只查找下一个出现并高亮显示它。如果找不到,则发送 `VK_F5` 到 `ListView` 请求查找下一个包含匹配项的文件。如果 `find` 函数成功,我们将在屏幕中间放置 **CARET**。这需要我们获取包含选区的行的第一个字符 `chline`,可见的第一行 `fvline`。向下滚动一页以获取页面大小,向上滚动一页,将页面大小除以 2 并加到 `fvline`,从 `chline` 中减去它以找到要滚动的行数。您还拥有更新状态栏的信息。轻而易举,小菜一碟!

`pMsgFilter` 成员 `msg` 等于 `WM_KEYDOWN` 且 `wParam` 为 `VK_F6(F6)` 的消息处理程序,它作为 Shift F3 处理的替代方案。它始终查找前一个,当失败时,它向 `ListView` 发送 `VK_F17` 以查找包含匹配项的前一个文件。当它成功时,`CARET` 会移动到屏幕中间,状态栏会更新。

`pMsgFilter` 成员 `msg` 等于 `WM_KEYDOWN` 且 `wParam` 为 `VK_F5(F5)` 的消息处理程序将简单地将键发送到 `ListView`。

`pMsgFilter` 成员 `msg` 为 `EN_REQUESTRESIZE` 的消息处理程序,如果它给出大小请求,我将给它空间。

richEditProc 函数

// The richEditProc offers the user a Viewer where the context of a code snippet
// can examined to assist in understanding the concept of it 's usage. The find
// function in the Viewer is  the built-in RichEdit find function, however, the 
// find function for the file search is U2Charfunc - an Exact Match algorithm.
// This means that it will miss some apparent matches and RichEdit will find more
// of the string than is indicated in the OCCURS Column of the ListView. For some
// languages, this could be undesirable but for  'C/C++ ' it filters out non-keywords
// in the file search but the point is to speed-up the process. RICHEDIT50W in MSFTEDIT.DLL
// has a feature that CAPITALIZES the first letter of a word 
// when the user types shift plus VK_F3.
// Common convention is to use this combination for  'find previous '. 
// To provide this functionality without the capitalization 
// it is necessary to manipulate the KEYSTATE Table to trick RichEdit.
// To extend  'find previous ' to the file search, richEditProc 
// sends VK_17( which is shift + F5) to the ListView. 
// The RichEdit Control is READ/WRITE to allow the user to type in a  'string to find '
// then select it and press Control plus  'F ' to begin a search, 
// instead of using the Combobox.
// The richEditProc adds the string to the Combobox Dropdown List and 
// saves it in the Environment
// Variables. It does not save the full list, only the last string entered. 
// The richEditProc also positions the Caret(EM_LINESCROLL)
// in the middle of the screen to reduce Eye-Strain. This Version
// of RichEdit appears to not repaint the client area when the THEME is changed by the user.
// This can result in a less than optimum color combination. 
// We send it a EM_SETBKGNDCOLOR with a wParam value
// of 1 to tell it to repaint the client area with the system(THEME) color.
INT_PTR CALLBACK richEditProc ( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) {
  UNREFERENCED_PARAMETER ( lParam );
  int wmId, wmEvent;
  static POINT rpt = { 0, 0 };
  static BOOL keyshifted = FALSE;
  LONG intRc = 0;
  BYTE keyst [ 256 ] = { 0 };
  static HTHEME thme = NULL;
  HWND hMainWnd = NULL, hRich = NULL, hTree = NULL, hList = NULL, 
                  hEdit = NULL, hFormView3 = NULL;
  if ( message != WM_INITDIALOG ) {
    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    hRich = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT1 );
  }

  switch ( message ) {
  case WM_INITDIALOG:
  {
    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    HWND hBottom = GetAncestor ( hDlg, GA_PARENT );
    if ( hMainWnd == hBottom ) {
      return 0;
    }
    hRich = FindWindowEx ( hDlg, 0, L "RICHEDIT50W ", 0 );
    if ( hRich == 0 ) {
      RECT rc = { 0, 0, 380, 380 };
      GetClientRect ( hMainWnd, &rc );
      HINSTANCE hLib;       // for Rich Edit
      hLib = LoadLibrary ( L "Msftedit.dll " );
      hRich = CreateWindowExW ( WS_EX_CLIENTEDGE,   // extended styles
                    L "RICHEDIT50W ",               //control  'class ' name
                    L " ",                          //control caption
                    //control style
                    WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL | 
                    ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | 
                    ES_NOHIDESEL | ES_WANTRETURN,
                    rc.left,                        //position: left
                    rc.top,                         //position: top
                    rc.right,                       //width
                    rc.bottom,                      //height
                    hDlg,                           //parent window handle
                    //control 's ID
                    ( HMENU ) ( IDC_EDIT1 ),
                    hInst,                          //application instance
                    0 );                            //user defined info
      SendMessage ( hRich, EM_SHOWSCROLLBAR, SB_VERT, TRUE );
      ShowWindow ( hRich, SW_SHOW );
      SendMessage ( hRich, EM_SETEVENTMASK, 0, ENM_KEYEVENTS | 
                    ENM_REQUESTRESIZE | ENM_MOUSEEVENTS );
      SendMessage ( hRich, EM_SHOWSCROLLBAR, SB_VERT, TRUE );
      SendMessage ( hRich, EM_SETZOOM, 3, 4 );
      SendMessage ( hRich, EM_SETEDITSTYLEEX, SES_EX_NOACETATESELECTION, 
                    SES_EX_NOACETATESELECTION );
      ShowWindow ( hFormView3, SW_SHOW );

      return ( INT_PTR ) TRUE;
    }
  }

  case WM_THEMECHANGED:
  {
    // RICHEDIT does not change the background color when HIGH CONTRAST 
    // is turned on . Turn it on progmattically using the new background color
    // by setting wparam to any non zero value. The hex lparam value is ignored.
    SendMessage ( hRich, EM_SETBKGNDCOLOR, 1, 0x008888ff );
    return ( HRESULT ) FALSE;
  }

  case WM_NOTIFY:
  {
    wmId = LOWORD ( wParam );
    wmEvent = HIWORD ( wParam );
    TCHAR fsiaf [ 256 ] = { 0 };  // Find this String In A File
    GetEnvironmentVariable ( L "FSIAF ", fsiaf, 255 );
    FINDTEXTEXW fndFrst;
    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 );
    hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
    hRich = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT1 );
    hTree = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_TREE1 );
    hFormView3 = hDlg;

    switch ( wmId ) {

    case IDC_EDIT1:
    {
      hRich = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT1 );
      MSGFILTER  *pMsgFilter = ( MSGFILTER * ) lParam;

      if ( pMsgFilter->msg == WM_KEYUP ) {
        TCHAR Text [ 256 ] = { 0 };
        DWORD dwSelStart = 0, dwSelEnd = 0;
        SendMessage ( hRich, EM_GETSEL, ( WPARAM ) &dwSelStart, ( LPARAM ) &dwSelEnd );
        int chLine = ( int ) SendMessage ( hRich, EM_EXLINEFROMCHAR, 0, dwSelStart );
        LONG strtLine = ( LONG ) SendMessage ( hRich, EM_LINEINDEX, chLine, 0 );
        LONG colPos = dwSelStart - strtLine;
        StringCchPrintf ( Text, 255, L "Line: %d  Column: %d ", chLine, colPos );
        HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
        SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, ( LPARAM ) Text );
      }
      if ( pMsgFilter->msg == WM_LBUTTONDOWN && pMsgFilter->wParam == MK_LBUTTON ) {
        #pragma region user_clicked_somewhere_in_richedit
        DWORD lbd = ( DWORD ) pMsgFilter->lParam;// NOTE this is the lParam of the lParam
        rpt.x = GET_X_LPARAM ( lbd );
        rpt.y = GET_Y_LPARAM ( lbd );
        ClientToScreen ( hRich, &rpt );
        TCHAR Text [ 256 ] = { 0 };
        DWORD dwSelStart = 0, dwSelEnd = 0;
        SendMessage ( hRich, EM_GETSEL, ( WPARAM ) &dwSelStart, ( LPARAM ) &dwSelEnd );
        int chLine = ( int ) SendMessage ( hRich, EM_EXLINEFROMCHAR, 0, dwSelStart );
        LONG strtLine = ( LONG ) SendMessage ( hRich, EM_LINEINDEX, chLine, 0 );
        LONG colPos = dwSelStart - strtLine;
        StringCchPrintf ( Text, 255, L "Line: %d  Column: %d ", chLine, colPos );
        HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
        SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, ( LPARAM ) Text );
        return 0;
        #pragma endregion save mouse position for control  'f '
      }

      if ( pMsgFilter->msg == WM_KEYDOWN && pMsgFilter->wParam == 0x46 ) {
        #pragma region control-f_was_pressed_by_ user
        short nVirtKey = GetKeyState ( VK_CONTROL );
        if ( nVirtKey & 0x8000 ) {
          CHARRANGE cr;
          DWORD dwSelStart = 0, dwSelEnd = 0;
          SendMessage ( hRich, EM_GETSEL, ( WPARAM ) &dwSelStart, ( LPARAM ) &dwSelEnd );
          if ( dwSelStart == dwSelEnd ) {
            SetCursorPos ( rpt.x, rpt.y );
            INPUT lbtnPr;
            lbtnPr.type = INPUT_MOUSE;
            lbtnPr.mi.dwFlags = MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_ABSOLUTE;
            lbtnPr.mi.dx = rpt.x;
            lbtnPr.mi.dy = rpt.y;
            SendInput ( 1, &lbtnPr, sizeof ( INPUT ) );
            lbtnPr.mi.dwFlags = MOUSEEVENTF_LEFTUP;
            SendInput ( 1, &lbtnPr, sizeof ( INPUT ) );
            lbtnPr.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
            SendInput ( 1, &lbtnPr, sizeof ( INPUT ) );
            lbtnPr.mi.dwFlags = MOUSEEVENTF_LEFTUP;
            SendInput ( 1, &lbtnPr, sizeof ( INPUT ) );
            INPUT keyPr;
            keyPr.type = INPUT_KEYBOARD;
            keyPr.ki.dwFlags = 0; // KEYEVENTF_KEYDOWN?;
            keyPr.ki.wScan = 0;
            keyPr.ki.time = 0;
            keyPr.ki.dwExtraInfo = 0;
            keyPr.ki.wVk = 0x46;
            SendInput ( 1, &keyPr, sizeof ( INPUT ) );
            keyPr.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput ( 1, &keyPr, sizeof ( INPUT ) );
          }
          else {
            cr.cpMin = dwSelStart;
            cr.cpMax = dwSelEnd;
            intRc = dwSelStart;
            SendMessage ( hRich, EM_GETSELTEXT, 0, ( LPARAM ) fsiaf );
            SetEnvironmentVariable ( L "FSIAF ", fsiaf );
            TCHAR  ListItem [ 256 ];
            StringCchPrintf ( ListItem, 255, L "%s ", fsiaf );
            HWND hCombo2 = getThisWindowHandle ( hMainWnd, IDC_COMBO2 );
            ComboBox_SetCurSel ( hCombo2, -1 );
            ComboBox_SetText ( hCombo2, ListItem );
            LRESULT itemIndex = SendMessage ( hCombo2, ( UINT ) CB_GETCURSEL, 
                                            ( WPARAM ) 0, ( LPARAM ) 0 );
            if ( itemIndex < 0 )
              if ( ComboBox_GetText ( hCombo2, ListItem, 255 ) )
                itemIndex = SendMessage ( hCombo2, ( UINT ) CB_ADDSTRING, 
                                        ( WPARAM ) 0, ( LPARAM ) ListItem );
              else
                itemIndex = 0;
            SendMessage ( hCombo2, ( UINT ) CB_SETCURSEL, 
                                   ( WPARAM ) itemIndex, ( LPARAM ) 0 );
            SendMessage ( hCombo2, ( UINT ) CB_GETLBTEXT, 
                        ( WPARAM ) itemIndex, ( LPARAM ) ListItem );
            CHARFORMAT cf;
            cf.cbSize = sizeof ( cf );
            cf.dwMask = CFM_BOLD | CFM_UNDERLINE;
            cf.dwEffects = CFE_BOLD | CFE_UNDERLINE;
            SendMessage ( hRich, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
            DWORD dwerr = GetLastError ( );
            dwerr = GetLastError ( );
          }
        }
        return TRUE;
        #pragma endregion send double click and get text for search string
      }

      if ( pMsgFilter->msg == WM_KEYUP && pMsgFilter->wParam == VK_F3 ) {
        #pragma region vk_f3-released
        if ( keyshifted ) {
          keyst [ VK_SHIFT ] = 0x80;//keysh [ VK_SHIFT ];
          SetKeyboardState ( keyst );
        }
        #pragma endregion so release shift key
      }

      // Richedit will capitalize an uncapitalized word, capitalize the
      // the entire word if the first letter is a capital and if the 
      // entire word is all caps it will make them all lower case when
      // the key combination is  VK_F3 and VK_SHIFT. To prevent this from happening
      // the user can hold down the control key, VK_CONTROL. To prevent it 
      // in code, one way is to copy the keyboard state table and unset 
      // the VK_SHIFT entry. When Richedit processes the VK_F3 message  it
      // will be unshifted but the previous occurrence has been found
      if ( pMsgFilter->msg == WM_KEYDOWN && pMsgFilter->wParam == VK_F3 ) {
        if ( !*fsiaf ) {
          #pragma region select_marked_text
          CHARRANGE cr;
          DWORD dwSelStart = 0, dwSelEnd = 0;
          cr.cpMin = dwSelStart;  cr.cpMax = dwSelEnd;
          SendMessage ( hRich, EM_GETSELTEXT, 0, ( LPARAM ) fsiaf );
          CHARFORMAT cf;
          cf.cbSize = sizeof ( cf );
          cf.dwMask = CFM_BOLD | CFM_UNDERLINE;
          cf.dwEffects = CFE_BOLD | CFE_UNDERLINE;
          SendMessage ( hRich, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
          DWORD dwerr = GetLastError ( );
          dwerr = GetLastError ( );
          #pragma endregion if search string is blank and text is high-lighted
        }
        short nVirtKeySh = GetKeyState ( VK_SHIFT );
        short nVirtKey = GetKeyState ( VK_CONTROL );
        WPARAM wp = 1;
        if ( nVirtKeySh & 0x8000 || nVirtKey & 0x8000 ) {
          #pragma region find_backwards
          if ( GetKeyboardState ( keyst ) == TRUE ) {
            #pragma region prevent_richedit_from _changing_case
            keyst [ VK_SHIFT ] = 0;
            SetKeyboardState ( keyst );
            keyshifted = TRUE;
            #pragma endregion along with find previous
            wp = 0;
            DWORD dwSelStart = 0, dwSelEnd = 0;
            SendMessage ( hRich, EM_GETSEL, ( WPARAM ) &dwSelStart, ( LPARAM ) &dwSelEnd );
            fndFrst.chrg.cpMin = dwSelStart - 1;
            fndFrst.chrg.cpMax = intRc;
            fndFrst.lpstrText = fsiaf;
            intRc = ( LONG ) SendMessage ( hRich, EM_FINDTEXTEXW, 
                                         ( WPARAM ) wp, ( LPARAM ) &fndFrst );
            CHARFORMAT cf;
            cf.cbSize = sizeof ( cf );
            cf.dwMask = CFM_BOLD | CFM_UNDERLINE;
            cf.dwEffects = CFE_BOLD | CFE_UNDERLINE;
            SendMessage ( hRich, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
            DWORD dwerr = GetLastError ( );
            dwerr = GetLastError ( );
          }
          else {
            // can 't get keyboard state table
            return FALSE;
          }
          #pragma endregion when vk_f3 is shifted
        }
        else {
          #pragma region find-forward
          keyshifted = FALSE;
          DWORD dwSelStart = 0, dwSelEnd = 0;
          SendMessage ( hRich, EM_GETSEL, ( WPARAM ) &dwSelStart, ( LPARAM ) &dwSelEnd );
          fndFrst.chrg.cpMin = dwSelEnd + 1;
          fndFrst.chrg.cpMax = -1;
          fndFrst.lpstrText = fsiaf;
          intRc = ( LONG ) SendMessage ( hRich, EM_FINDTEXTEXW, 
                                       ( WPARAM ) wp, ( LPARAM ) &fndFrst );
          CHARFORMAT cf;
          cf.cbSize = sizeof ( cf );
          cf.dwMask = CFM_BOLD | CFM_UNDERLINE;
          cf.dwEffects = CFE_BOLD | CFE_UNDERLINE;
          SendMessage ( hRich, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
          DWORD dwerr = GetLastError ( );
          dwerr = GetLastError ( );
          #pragma endregion when vk_f3 is not shifted
        }

        if ( intRc == -1 ) {
          #pragma region string_not_found
          if ( wp ) {
            return sendMeMyKey ( hList, VK_F5 );
          }
          else {
            if ( keyshifted ) {
              keyst [ VK_SHIFT ] = 0x80;//keysh [ VK_SHIFT ];
              SetKeyboardState ( keyst );
            }
            return sendMeMyKey ( hList, VK_F17 );
          }
          #pragma endregion so send request to listview
        }
        else {
          #pragma region put_caret_in_middle of screen
          SendMessage ( hRich, EM_SETSEL, fndFrst.chrgText.cpMin, fndFrst.chrgText.cpMax );
          SendMessage ( hRich, EM_SCROLLCARET, 0, 0 );
          int chLine = ( int ) SendMessage ( hRich, EM_EXLINEFROMCHAR, 0, intRc );
          int fvLine = ( int ) SendMessage ( hRich, EM_GETFIRSTVISIBLELINE, 0, 0 );
          DWORD lvLine = ( DWORD ) SendMessage ( hRich, EM_SCROLL, SB_PAGEDOWN, 0 );
          WORD pgSize = LOWORD ( lvLine );
          lvLine = ( DWORD ) SendMessage ( hRich, EM_SCROLL, SB_PAGEUP, 0 );
          DWORD desiredPos = pgSize / 2 + fvLine;
          int noffSet = chLine - desiredPos;
          SendMessage ( hRich, EM_LINESCROLL, 0, noffSet );
          SendMessage ( hRich, EM_SCROLLCARET, 0, 0 );
          TCHAR Text [ 255 ] = { 0 };
          HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
          LONG strtLine = ( LONG ) SendMessage ( hRich, EM_LINEINDEX, chLine, 0 );
          LONG colPos = intRc - strtLine;
          StringCchPrintf ( Text, 255, L "Line: %d  Column: %d ", chLine, colPos );
          SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, ( LPARAM ) Text );
          intRc = fndFrst.chrgText.cpMax + 1;
          #pragma endregion to reduce visual fatigue
        }
        return -1;
      }
      if ( pMsgFilter->msg == WM_KEYDOWN && pMsgFilter->wParam == VK_F6 ) {
        #pragma region find_previous_unshifted
        #pragma region find_previous_unshifted
        WPARAM wp = 0;
        DWORD dwSelStart = 0, dwSelEnd = 0;
        SendMessage ( hRich, EM_GETSEL, ( WPARAM ) &dwSelStart, ( LPARAM ) &dwSelEnd );
        fndFrst.chrg.cpMin = dwSelStart - 1;
        fndFrst.chrg.cpMax = intRc;
        fndFrst.lpstrText = fsiaf;
        intRc = ( LONG ) SendMessage ( hRich, EM_FINDTEXTEXW, 
                                     ( WPARAM ) wp, ( LPARAM ) &fndFrst );
        CHARFORMAT cf;
        cf.cbSize = sizeof ( cf );
        cf.dwMask = CFM_BOLD | CFM_UNDERLINE;
        cf.dwEffects = CFE_BOLD | CFE_UNDERLINE;
        SendMessage ( hRich, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
        DWORD dwerr = GetLastError ( );
        dwerr = GetLastError ( );
        #pragma endregion (just using vk_f6, no shift or control)
        if ( intRc == -1 ) {
          #pragma region when_not_found_backwards
          return sendMeMyKey ( hList, VK_F17 );
          #pragma endregion send vk_f5 to listview
        }
        else {
          #pragma region put_the_caret_in_middle of screen
          SendMessage ( hRich, EM_SETSEL, fndFrst.chrgText.cpMin, fndFrst.chrgText.cpMax );
          SendMessage ( hRich, EM_SCROLLCARET, 0, 0 );
          int chLine = ( int ) SendMessage ( hRich, EM_EXLINEFROMCHAR, 0, intRc );
          int fvLine = ( int ) SendMessage ( hRich, EM_GETFIRSTVISIBLELINE, 0, 0 );
          DWORD lvLine = ( DWORD ) SendMessage ( hRich, EM_SCROLL, SB_PAGEDOWN, 0 );
          WORD pgSize = LOWORD ( lvLine );
          lvLine = ( DWORD ) SendMessage ( hRich, EM_SCROLL, SB_PAGEUP, 0 );
          DWORD desiredPos = pgSize / 2 + fvLine;
          int noffSet = chLine - desiredPos;
          SendMessage ( hRich, EM_LINESCROLL, 0, noffSet );
          SendMessage ( hRich, EM_SCROLLCARET, 0, 0 );
          intRc = fndFrst.chrgText.cpMax + 1;
          #pragma endregion to reduce visual fatigue
        }
        return 1;
        #pragma endregion (just using vk_f6, then center caret)
      }
      if ( pMsgFilter->msg == WM_KEYDOWN && pMsgFilter->wParam == VK_F5 ) {
        #pragma region send_find_next_to_listview
        return sendMeMyKey ( hList, VK_F5 );
        #pragma endregion to find string in a file
      }
      if ( pMsgFilter->msg == EN_REQUESTRESIZE ) {
        #pragma region Richedit_requested_more_space
        REQRESIZE * resrc = ( REQRESIZE * ) lParam;
        MoveWindow ( hRich, resrc->rc.left, resrc->rc.top, resrc->rc.right, 
                     resrc->rc.bottom, TRUE );
        return 0;
        #pragma endregion lParam points to REQRESIZE structure with width and height
      }
    }
    break;
    }
    return 0;
  }
  break;

  case WM_NCCALCSIZE:
    if ( wParam ) {
      return WVR_ALIGNLEFT | WVR_ALIGNTOP | WVR_REDRAW;
    }
    else
      return DefWindowProc ( hRich, message, wParam, lParam );
    break;

  case WM_SIZE:
  {
    #pragma region  Richedit_parent_window_resized
    UINT width = GET_X_LPARAM ( lParam );
    UINT height = GET_Y_LPARAM ( lParam );
    if ( wParam ) {  
      MoveWindow ( hRich, 0, 0, width, height, TRUE );
    }
    return 0;
    #pragma endregion get size from richedit and make it so
  }break;

  case WM_ACTIVATE:
  {
    if ( LOWORD ( wParam ) & WA_ACTIVE ) {
      SendMessage ( hRich, EM_SHOWSCROLLBAR, SB_VERT, TRUE );
      if ( hRich ) {
        SetFocus ( hRich );
        return 0;
      }
      return 0;
    }
    else
      return -1;
  }break;
  }
  return ( INT_PTR ) FALSE;
}

查找下一个或上一个包含匹配项的文件:listViewProc 函数

在消息开关中,`WM_INITDIALOG` 消息分别被 `hBottom` 和 `hFormView2` 接收。如果是 `hBottom`,我们就直接退出。如果是 `hFormView2`,则创建一个字体以获得更大的文本大小。然后创建 `ListView` 控件并将列标题放入其中。创建 `ListView` 后,会向其发送 `WM_SETFONT` 消息以设置我们之前创建的字体。现在来谈谈 `hFormView2`。在资源视图中查看,您可以看到它有一个 RESIZING Border。这用于代替模拟分割条。`RESIZING` Border 可以为您提供 3x3 的网格,如果您想要的话,但我们想要该网格的 1x3 行。为了做到这一点,`hFormView2` 的顶部和底部边框必须禁用。`Cursor` 被剪裁以实现这一点。`hBottom` 中所有控件的大小调整由 `hFormView2` 控制,并在 `WM_SIZING` 消息处理程序中执行。使用 `RESIZING` Border 而不是分割条存在一个问题。问题是大小控制消息接收到的 `RECT` 的大小。我知道如何正确修复这个问题,但现在,我只使用硬编码的值来为它们提供正确的大小。

`WM_COMMAND` 消息用于处理 `WM_SETFOCUS` 消息。当选择一个项时,如果 `ListView` 获得焦点但未选择任何项,则在此处高亮显示,并选择并高亮显示 `ListView` 中的第一项。

在 `WM_NOTIFY` 消息处理程序中,我们从环境变量块中获取 `fsiaf` 的值。我们还获取我们可能需要的所有窗口句柄。在 `LPNMHDR lParam` 成员 `code` 的 `switch` 中,我们通过获取所选文件的路径来处理 `NM_CLICK` 的代码,将其加载到 `RichEdit` 中并更新状态栏,除非它大于 64MB,在这种情况下,我们使用“打开方式”对话框在其他应用程序中打开文件。

`NM_DBLCLK` 的代码通过在文件资源管理器中打开选定的文件夹或文件来处理。

`NM_RCLICK` 的代码通过调用“打开方式”对话框来处理。它不显示上下文菜单。

`LVN_KEYDOWN` 的代码通过创建指向 `NMLVKEYDOWN` 结构的指针并将其命名为 `pnkd`,然后将其初始化为 `lParam` 强制转换为 `LPNMLVKEYDOWN` 来处理。`pnkd` 成员 `wVKey` 将包含键入的虚拟键码。

`VK_DOWN` 的 `wVKey` 代码通过向下移动一项并将该文件加载到 `RichEdit` 中来处理,除非它大于 64MB。

`VK_TAB` 的 `wVKey` 代码通过将焦点设置在 `RichEdit` 控件上,模拟 Tab 键来处理。

`VK_UP` 的 `wVKey` 代码通过向上移动一项并将该文件加载到 `RichEdit` 中来处理,除非它大于 64MB。

`VK_F5` 和 `VK_F17*SHIFT F5)` 的 `wVKey` 代码通过获取当前项的第二列“出现次数”的文本来确定它是否包含 `fsiaf`。如果包含,则调用 `U2Charfunc` 进行确认并高亮显示 `ListView` 中的文件名,然后将高亮显示的行放在可见列表的中间,将文件加载到 `RichEdit` 中,并使用 `RichEdit` 查找函数将 `RichEdit CARET` 定位到匹配项并将其滚动到屏幕中间。

当在 `ListView` 的“Occurs”列中找不到下一个或上一个文件时(通过计算未命中次数直到等于或超过列表视图的行数来确定),如果行数大于,则显示一条消息框,告知下一个步骤可能需要一些时间。然后它调用 `findStringInAllFiles` 搜索文件列表本身以用计数填充“Occurs”列。接下来,它找到包含匹配项的下一个文件并将其加载到 `RichEdit` 中,将高亮显示放置在屏幕中间。请注意,这只会在用户在 `RichEdit` Viewer Panel 中输入要搜索的 `string` 时发生。

当命中次数超过 `ListView` 中的行数时,会显示一条消息框,告知用户未找到包含所请求 `STRING` 的文件,并建议检查拼写。

`VK_F3` 和 `VK_F6` 的 `wVKey` 代码通过将键发送到 `RichEdit` proc 来处理。

`VK_F4` 的 `wVKey` 代码通过显示文件的属性来处理。

`NM_RETURN` 是一个 `WM_NOTIFY` 消息。我们将其与 `NM_DBLCLK` 相同的方式处理,文件位置将在文件资源管理器中打开。

`NM_SETFOCUS` 是一个 `WM_NOTIFY` 消息。我们将其与 `WM_SETFOCUS` 相同的方式处理。如果 `ListView` 中有任何内容,我们会确保它被高亮显示。

对于 `WM_NCLBUTTONUP` 消息,我们通过将 `ClipCursor` 设置为 `NULL` 来取消剪裁,以便光标可以自由移动。

对于 `WM_SIZING` 消息,我们从 `lParam` 获取拖动矩形。我们还获取所有窗口句柄、窗口矩形和客户区矩形,并偏移窗口矩形以使其位置相对于主窗口。然后我们确定正在移动哪个边框。如果不是左边或右边,那么我们就剪裁光标以防止垂直移动。相同的公式适用于顶部和底部。光标只允许在任一方向上从左边框移动到右边框。对于右边框,我们进行剪裁以防止用户用 `ListView` 覆盖 `RichEdit` Viewer Panel。然后我们将 `RichEdit` 控件的容器 `hFormView3` 移动到其新位置并用 `RichEdit` 填充它。如果是左边框,我们剪裁光标以防止 `ListView` 覆盖整个 `treeview`,并将 `treeview` 移动到其新的大小和位置。对于左边框和右边框,我们将 `ListView Control` 移入 `hFormView2`。

listViewProc 函数

// Message handler for ListView. Processes WM_NOTIFY for the following messages: 
// NM_CLICK - select the current file and load it into RichEdit
// NM_RCLICK - select the current file and Invokes OpenWith Dialog
// NM_DBLCLK - opens selected file location in File Explorer with Mouse input
// NM_RETURN - opens selected file location in File Explorer with Keyboard input
// F3 is sent to RichEdit to find next string in the current file. 
// Shift with F3 searches backwards
// F4 Displays Properties for current file
// F5 finds next fie containing string. Shift with F5 searches backwards
// F6 is sent to RichEdit to find previous string or 
// last occurrence of string in current file
// F17 (shifted F5) is sent to ListView by Richedit when F6 reaches the begining of the file
// WM_NCLBUTTONUP will nullify the ClipCursor, thus freeing the Cursor to move freely
// WM_SIZING wil<span lang="en-us">l</span> re-size the TreeView control and RichEdit 
// when User drags Left or Right sizing grip. If User attempts to drag 
// the TOP or BOTTOM size grip the Cursor is Clipped preventing
// UP or Down movement. The Cursor is also clipped to 
// prevent the user from moving the left or 
// right side sizing grips too far, covering up the other windows and 
// causing a loss of function.
INT_PTR CALLBACK    listViewProc ( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
  PAINTSTRUCT ps;
  HDC hdc;
  TCHAR curntFname [ 256 ];
  TCHAR numbOfOccurs [ 256 ];
  TCHAR curntPath [ MAX_PATH ];
  TCHAR Text [ 256 ] = { 0 };
  TCHAR text [ 256 ] = { 0 };
  TCHAR teXt [ 256 ] = { 0 };
  TCHAR tStr [ MAX_PATH ];
  TCHAR searchCap [ 256 ]; // The Window Caption for Search Function
  static int currentRow = 0;
  CHAR chBuf [ 4096 ];
  HWND hMainWnd = NULL, hTree = NULL, hList = NULL, hEdit = NULL,
    hRich = NULL, hListView, hFormView2;
  HWND hWndFocus = NULL;

  switch ( message ) {
  case WM_INITDIALOG:
  {
    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    HWND hBottom = GetAncestor ( hDlg, GA_PARENT );
    if ( hMainWnd == hBottom ) {
      return 0;
    }
    hList = FindWindowEx ( hDlg, 0, L "SysListView32 ", 0 );
    if ( hList == 0 ) {

      HFONT hFont = CreateFont ( 32, 10, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, ANSI_CHARSET, \
                     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
                     DEFAULT_PITCH | FF_SWISS, L "Arial " );
      RECT sRc;
      GetClientRect ( hDlg, &sRc );
    
      hList = CreateWindowEx ( 0,
                      WC_LISTVIEW, NULL,
                      WS_CHILD | WS_VISIBLE | DS_3DLOOK | WS_BORDER |
                      WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP |
                      WS_EX_OVERLAPPEDWINDOW | WS_EX_STATICEDGE | 
                      WS_EX_COMPOSITED | WS_EX_NOINHERITLAYOUT |
                      LVS_REPORT | LVS_SHOWSELALWAYS,
                      sRc.left, sRc.top, sRc.right, sRc.bottom - 5, hDlg,
                      ( HMENU ) IDC_LIST1, hInst, 0 );
      LVCOLUMN lvCol;
      memset ( &lvCol, 0, sizeof ( lvCol ) );
      lvCol.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; // Type of mask
      lvCol.pszText = ( LPWSTR ) L "Name ";
      lvCol.cx = 0x96;          // width of first column
      ListView_InsertColumn ( hList, 0, &lvCol );
      lvCol.pszText = ( LPWSTR ) L "Number of Occurrences ";
      lvCol.cx = 0x96;        // width of second column
      ListView_InsertColumn ( hList, 1, &lvCol );
      lvCol.pszText = ( LPWSTR ) L "Path ";
      lvCol.cx = 0x64;        // width of third column
      ListView_InsertColumn ( hList, 2, &lvCol );
      lvCol.pszText = ( LPWSTR ) L "File Size ";
      lvCol.cx = 0x072;        // width of fourth column
      ListView_InsertColumn ( hList, 3, &lvCol );
      SendMessage ( hList, WM_SETFONT, WPARAM ( hFont ), TRUE );
      return 0;
    }
  }
  break;

  case WM_PAINT:

    if ( hMainWnd ) {
      hdc = BeginPaint ( hMainWnd, &ps );
      EndPaint ( hMainWnd, &ps );
    }
    return 0;
    break;

  case WM_COMMAND:
  {
    switch ( HIWORD ( wParam ) ) {
    case WM_SETFOCUS:
    {
      // if anything is in List make sure something is selected
      if ( hList && ListView_GetItemCount ( hList ) >= 1 ) {
        int selectedItem = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
        if ( selectedItem == -1 ) {
          // select first one
          ListView_SetItemState ( hList, 0, LVIS_SELECTED, LVIS_SELECTED );
          return 0;
        }
        else {
          ListView_SetItemState ( hList, selectedItem, LVIS_SELECTED, LVIS_SELECTED );
          return 0;
        }
      }
    }
    break;
    }
  }
  break;

  case WM_LBUTTONUP:
    return 0;
    break;

  case WM_LBUTTONDOWN:

    if ( hList ) {
      hWndFocus = hList;
    }
    return 0;
    break;

  case WM_NOTIFY:
  {
    LONG intRc = 0;
    TCHAR fsiaf [ 256 ] = { 0 };  // Find this String In A File
    GetEnvironmentVariable ( L "FSIAF ", fsiaf, 255 );
    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    if ( 0 == hMainWnd ) {
      return 0;
    }
    hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 );
    hTree = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_TREE1 );
    if ( 0 == hList ) {
      return 0;
    }
    hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
    if ( 0 == hEdit ) {  return 0;}
    hRich = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT1 );
    if ( 0 == hRich ) {  return 0;}
    hListView = GetParent ( hList );
    hFormView2 = hListView;
    
    switch ( ( ( LPNMHDR ) lParam )->code ) {

    case NM_CLICK:
    {
      #pragma region mouse_left_ckick
      // Left Click
      NMLISTVIEW * pnmlv;
      LVITEM lvi;
      INT ret;
      pnmlv = ( LPNMLISTVIEW ) lParam;
      memset ( &lvi, 0, sizeof ( lvi ) );
      ret = ( pnmlv )->iItem;
      if ( ret == -1 ) {
        ret = currentRow;
        lvi.iItem = ret;
      }
      else {
        currentRow = ret;
        lvi.iItem = ( pnmlv )->iItem;
      }
      lvi.iItem = ( pnmlv )->iItem;
      lvi.mask = LVIF_TEXT;
      lvi.cchTextMax = 255;
      lvi.pszText = Text;
      ListView_GetItem ( hList, &lvi );
      lvi.pszText = text;
      lvi.iSubItem = 2;
      ListView_GetItem ( hList, &lvi );
      wsprintf ( teXt, L "\ "%s\\%s\ " ", text, Text );
      SetWindowText ( hEdit, teXt );
      ListView_SetItemState ( hList, -1, 0, LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
      ListView_SetItemState ( hList, ret, LVIS_DROPHILITED, LVIS_DROPHILITED );
      ListView_SetItemState ( hList, ret, LVIS_SELECTED, LVIS_SELECTED );
      ListView_SetItemState ( hList, ret, LVIS_FOCUSED, LVIS_FOCUSED );
      int result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
      ListView_GetItemText ( hList, result, 3, text, MAX_PATH - 1 );
      text [ 255 ] = 0;
      int fileSize = _wtoi ( text );
      ListView_GetItemText ( hList, result, 0, curntFname, MAX_PATH - 1 );
      ListView_GetItemText ( hList, result, 2, curntPath, MAX_PATH - 1 );
      StringCchPrintf ( tStr, MAX_PATH, L "%s\\%s ", curntPath, curntFname );
      SetWindowText ( hMainWnd, tStr );
      if ( fileSize > 64 * 1024 * 1024 ) {
        MessageBox ( hMainWnd, L "File Size  is too much for me! 
        Can 't LOAD ", text, MB_OK | MB_ICONEXCLAMATION );
        OPENASINFO opWith;
        opWith.pcszFile = ( LPCTSTR ) &tStr;
        opWith.pcszClass = NULL;
        SHOpenWithDialog ( NULL, &opWith );
        return 0;
      }
      FillRichEditFromFile ( hRich, ( LPCTSTR ) &tStr );
      HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
      int iCount = 0;
      if ( hList ) iCount = ListView_GetItemCount ( hList );
      StringCchPrintf ( Text, 255, L "%d of %d Files ", result + 1, iCount );
      SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) Text );
      SetFocus ( hList );
      #pragma endregion select item and load richedit
    }break; // case NM_CLICK

    case  NM_DBLCLK:
    {
      #pragma region user_left_double_clicked_an_item
      // Left Double Click
      NMLISTVIEW * pnmlv;
      LVITEM lvi;
      int result = 0;
      struct _stat64 buf;
      pnmlv = ( LPNMLISTVIEW ) lParam;
      memset ( &lvi, 0, sizeof ( lvi ) );
      lvi.iItem = ( pnmlv )->iItem;
      if ( lvi.iItem == -1 ) return 0;
      lvi.mask = LVIF_TEXT;
      lvi.cchTextMax = 255;
      lvi.pszText = Text;
      ListView_GetItem ( hList, &lvi );
      lvi.pszText = text;
      lvi.iSubItem = 2;
      ListView_GetItem ( hList, &lvi );
      wsprintf ( teXt, L "%s\\%s ", text, Text );
      result = _wstat64 ( teXt, &buf );
      #pragma endregion get the full path and attributes
      if ( ( buf.st_mode & _S_IFDIR ) ) {
        #pragma region for_a_directory
        wsprintf ( teXt, L "\ "%s\\%s\ " ", text, Text );
        SetWindowText ( hEdit, teXt );
        ShellExecute ( NULL, L "explore ", teXt, NULL, NULL, SW_SHOWNORMAL );
        #pragma endregion open it with file explorer
      }
      if ( result == 0 ) {
        if ( ( buf.st_mode & _S_IFREG ) ) {
          #pragma region not_a_directory_but_a_regular_file
          wsprintf ( teXt, L "/select, \ "%s\\%s\ " ", text, Text );
          SetWindowText ( hEdit, teXt );
          ShellExecute ( NULL, L "open ", L "explorer.exe ", teXt, text, SW_SHOW );
          #pragma endregion open it with current program association
        }
      }
    }break; // case NM_DBLCLK

    case NM_RCLICK:
    {
      #pragma region mouse_right_click
      // Right Click
      NMLISTVIEW * pnmlv;
      LVITEM item;
      pnmlv = ( LPNMLISTVIEW ) lParam;
      memset ( &item, 0, sizeof ( item ) );
      item.iItem = ( pnmlv )->iItem;
      int result = item.iItem;
      item.mask = LVIF_TEXT;
      item.iSubItem = 0;
      item.cchTextMax = 260;
      item.pszText = Text;
      ListView_GetItem ( hList, &item );
      ListView_GetItemText ( hList, result, 0, curntFname, MAX_PATH - 1 );
      ListView_GetItemText ( hList, result, 2, curntPath, MAX_PATH - 1 );
      StringCchPrintf ( tStr, MAX_PATH, L "%s\\%s ", curntPath, curntFname );
      SetWindowText ( hMainWnd, tStr );
      OPENASINFO opWith;
      opWith.pcszFile = ( LPCTSTR ) &tStr;
      opWith.pcszClass = NULL;
      opWith.oaifInFlags = OAIF_EXEC;
      SHOpenWithDialog ( NULL, &opWith );
      return 0;
      #pragma endregion get fullpath and call Open-With dialog
    } break; // case  NM_RCLICK

    case LVN_KEYDOWN:
    {
      NMLVKEYDOWN * pnkd;
      pnkd = ( LPNMLVKEYDOWN ) lParam;
      if ( ( pnkd )->wVKey == VK_DOWN ) {
        #pragma region select_next_item
        LVITEM lvi = { 0 };
        int result = 0;
        int itemCount = 0;
        memset ( &lvi, 0, sizeof ( lvi ) );
        itemCount = ListView_GetItemCount ( hList );
        result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
        if ( result < itemCount - 1 )
          lvi.iItem = result + 1;
        else
          return 0;
        lvi.mask = LVIF_TEXT;
        lvi.cchTextMax = 255;
        lvi.pszText = Text;
        ListView_GetItem ( hList, &lvi );
        lvi.pszText = text;
        ListView_GetItemText ( hList, result, 0, Text, MAX_PATH - 1 );
        ListView_GetItemText ( hList, result, 2, text, MAX_PATH - 1 );
        wsprintf ( teXt, L "%s\\%s ", text, Text );
        SetWindowText ( hEdit, teXt );
        SetWindowText ( hMainWnd, teXt );
        ListView_SetItemState ( hList, -1, LVIS_SELECTED | LVIS_FOCUSED | 
        LVIS_DROPHILITED, LVIS_CUT | LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
        ListView_SetItemState ( hList, result, LVIS_FOCUSED, LVIS_CUT | 
        LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
             // select this one and scroll to it 's position
        ListView_SetItemState ( hList, lvi.iItem, LVIS_SELECTED, LVIS_SELECTED );
        ListView_EnsureVisible ( hList, lvi.iItem, TRUE );
        currentRow = lvi.iItem;
        ListView_GetItemText ( hList, currentRow, 3, curntFname, MAX_PATH - 1 );
        curntFname [ 255 ] = 0;
        int fileSize = _wtoi ( curntFname );
        if ( fileSize > 64 * 1024 * 1024 ) {
          MessageBox ( hMainWnd, L "File Size  is too much for me! 
          Can 't LOAD ", curntFname, MB_OK | MB_ICONEXCLAMATION );
          return 0;
        }
        ListView_GetItemText ( hList, currentRow, 0, curntFname, MAX_PATH - 1 );
        ListView_GetItemText ( hList, currentRow, 2, curntPath, MAX_PATH - 1 );
        StringCchPrintf ( tStr, MAX_PATH, L "%s\\%s ", curntPath, curntFname );
        SetWindowText ( hMainWnd, tStr );
        SetWindowText ( hEdit, tStr );
        FillRichEditFromFile ( hRich, ( LPCTSTR ) &tStr );
        HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
        int iCount = 0;
        if ( hList ) iCount = ListView_GetItemCount ( hList );
        StringCchPrintf ( Text, 255, L "%d of %d Files ", currentRow + 1, iCount );
        SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) Text );
        SetFocus ( hList );
        return 1;
        #pragma endregion and load it into richedit
      }

      if ( ( pnkd )->wVKey == VK_TAB ) {
        SetFocus ( hRich );
        return 0;
      }

      if ( ( pnkd )->wVKey == VK_UP ) {
        #pragma region select_previous_item
        LVITEM lvi;
        int result = 0;
        memset ( &lvi, 0, sizeof ( lvi ) );
        result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
        if ( result < 1 ) return 0;
        lvi.iItem = result - 1;
        lvi.mask = LVIF_TEXT;
        lvi.cchTextMax = 255;
        lvi.pszText = Text;
        ListView_GetItem ( hList, &lvi );
        lvi.pszText = text;
        ListView_GetItem ( hList, &lvi );
        wsprintf ( teXt, L "%s\\%s ", text, Text );
        SetWindowText ( hEdit, teXt );
        ListView_SetItemState ( hList, -1, LVIS_SELECTED | LVIS_FOCUSED | 
        LVIS_DROPHILITED, LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED | LVIS_CUT);
        // select this one and scroll to it 's position
        ListView_SetItemState ( hList, lvi.iItem, LVIS_SELECTED, LVIS_SELECTED );
        ListView_EnsureVisible ( hList, lvi.iItem, TRUE );
        currentRow = lvi.iItem;
        ListView_GetItemText ( hList, currentRow, 3, curntFname, MAX_PATH - 1 );
        curntFname [ 255 ] = 0;
        int fileSize = _wtoi ( curntFname );
        if ( fileSize > 64 * 1024 * 1024 ) {
          MessageBox ( hMainWnd, L "File Size  is too much for me! 
          Can 't LOAD ", curntFname, MB_OK | MB_ICONEXCLAMATION );
          return 0;
        }
        ListView_GetItemText ( hList, currentRow, 0, curntFname, MAX_PATH - 1 );
        ListView_GetItemText ( hList, currentRow, 2, curntPath, MAX_PATH - 1 );
        StringCchPrintf ( tStr, MAX_PATH, L "%s\\%s ", curntPath, curntFname );
        SetWindowText ( hMainWnd, tStr );
        FillRichEditFromFile ( hRich, ( LPCTSTR ) &tStr );
        HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
        int iCount = 0;
        if ( hList ) iCount = ListView_GetItemCount ( hList );
        StringCchPrintf ( Text, 255, L "%d of %d Files ", currentRow + 1, iCount );
        SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) Text );
        SetFocus ( hList );
        return 0;
        #pragma endregion and load it into richedit
      }

      if ( ( pnkd )->wVKey == VK_F5 || ( pnkd )->wVKey == VK_F17 ) {
        #pragma region cycle _thru_OCCURRENCES_Column
        LVITEM lvi;
        MSG msg;
        memset ( &lvi, 0, sizeof ( lvi ) );
        int numbFound = 0;
        int iCount = 0;
        if ( hList ) iCount = ListView_GetItemCount ( hList );
        int result = currentRow;;
        int misses = 0;
        short nVirtKey = GetKeyState ( VK_CONTROL );
        short nVirtKeySh = GetKeyState ( VK_SHIFT );
        // async key states
        nVirtKey = GetAsyncKeyState ( VK_CONTROL );
        if ( wcscmp ( fsiaf, L " " ) != 0 ) {
          do {
            if ( nVirtKeySh & 0x8000|| ( pnkd )->wVKey == VK_F17) {
              #pragma region shift_key_down
              // get previous file with matches
              result = ListView_GetNextItem ( hList, result, LVNI_ABOVE );
              if ( result == -1 ) {
                result = iCount - 1;
                ListView_SetItemState ( hList, -1, 0, 
                LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
                ListView_SetItemState ( hList, result, LVIS_DROPHILITED | 
                                        LVIS_SELECTED | LVIS_FOCUSED,
                            LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
                ListView_EnsureVisible ( hList, result, TRUE );
                ListView_Update ( hList, result );
                #pragma endregion select previous file
              }
            }
            else {
              #pragma region shift_key_up
              // get next file with matches
              result = ListView_GetNextItem ( hList, result, LVNI_BELOW );
              if ( result == -1 )
                result = 0;
              #pragma endregion select nextt file
            }
            #pragma region does_str-to-search-for_occur_in_occurs_text
            // Get column two text string
            ListView_GetItemText ( hList, result, 1, numbOfOccurs, 255 );
            numbOfOccurs [ 255 ] = 0;
            if ( wcsstr ( (LPWSTR)numbOfOccurs, (LPWSTR)fsiaf ) != 0 ) {
              // search string is a substring of Number of Occurrences column
              // Get column one text string
              ListView_GetItemText ( hList, result, 0, curntFname, MAX_PATH - 1 );
              // Get column three text string
              ListView_GetItemText ( hList, result, 2, curntPath, MAX_PATH - 1 );
              sprintf_s ( chBuf, 4096,  "%S\\%S\r\n ", curntPath, curntFname );
              char pattern [ 260 ] = { 0 };
              int patlen = sprintf_s ( pattern, 259,  "%S ", fsiaf );
              // daa-ble chek. Call u2Charfunc just to make sure it has a match
              if ( ( numbFound = u2Charfunc ( chBuf, pattern, patlen ) ) != 0 ) {
                ListView_SetItemState ( hList, -1, 0, LVIS_DROPHILITED | 
                                        LVIS_SELECTED | LVIS_FOCUSED );
                ListView_SetItemState ( hList, result, LVIS_DROPHILITED | 
                                        LVIS_SELECTED | LVIS_FOCUSED,
                            LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
                // Remove Highlight from all files that are highlighted
                result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
                ListView_SetItemState ( hList, -1, 0,
                            LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
                // Highlight all files with matches
                ListView_SetItemState ( hList, result,
                            LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED,
                            LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
                ListView_EnsureVisible ( hList, result, TRUE );
                ListView_Update ( hList, result );
                currentRow = result;
                int topLine = 0, linesPer = 0;
                topLine = ListView_GetTopIndex ( hList );
                linesPer = ListView_GetCountPerPage ( hList );
                ListView_Scroll ( hList, 0, ( ( ( size_t ) currentRow - topLine ) - 
                                ( linesPer / 2 )+3 ) * 16 );
                iCount = ListView_GetItemCount ( hList );
                ListView_GetItemText ( hList, currentRow, 3, curntFname, MAX_PATH - 1 );
                curntFname [ 255 ] = 0;
                int fileSize = _wtoi ( curntFname );
                if ( fileSize > 64 * 1024 * 1024 ) {
                  MessageBox ( hMainWnd, L "File Size  is too much for me! 
                  Can 't LOAD ", curntFname, MB_OK | MB_ICONEXCLAMATION );
                  ListView_GetItemText ( hList, result, 0, curntFname, MAX_PATH - 1 );
                  ListView_GetItemText ( hList, result, 2, curntPath, MAX_PATH - 1 );
                  curntPath [ 259 ] = 0;
                  wsprintf ( teXt, L "/select, \ "%s\\%s\ " ", curntPath, curntFname );
                  SetWindowText ( hEdit, teXt );
                  ShellExecute ( NULL, L "open ", 
                                 L "explorer.exe ", teXt, curntPath, SW_SHOW );
                  return 0;
                }
                ListView_GetItemText ( hList, currentRow, 0, curntFname, MAX_PATH - 1 );
                ListView_GetItemText ( hList, currentRow, 2, curntPath, MAX_PATH - 1 );
                StringCchPrintf ( Text, 255, L "[%d of %d]%s\\%s ", 
                                  result + 1, iCount, curntPath, curntFname );
                SetWindowText ( hMainWnd, Text );
                StringCchPrintf ( tStr, MAX_PATH, L "%s\\%s ", curntPath, curntFname );
                SetWindowText ( hEdit, tStr );
                FillRichEditFromFile ( hRich, ( LPCTSTR ) &tStr );
                HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
                StringCchPrintf ( Text, 255, L "%d of %d Files ", result + 1, iCount );
                SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) Text );
                SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, 
                            ( LPARAM ) L "Line: 0    Column: 0 " );
                SendMessage ( hwndStatus, WM_SIZE, 0, 0 );
                if ( fsiaf ) {
                  SetFocus ( hRich );
                  FINDTEXTEX fndFrst;
                  fndFrst.lpstrText = fsiaf;
                  if ( ( pnkd )->wVKey == VK_F17 ) {
                    GETTEXTLENGTHEX lastChar = { 0 };
                    lastChar.flags = GTL_DEFAULT;
                    lastChar.codepage = 1200;
                    intRc = ( LONG ) SendMessage ( hRich, EM_GETTEXTLENGTHEX, 
                                                 (WPARAM)&lastChar, ( LPARAM ) 0 );
                    fndFrst.chrg.cpMin = intRc;
                    fndFrst.chrg.cpMax = 0;
                    intRc = ( LONG ) SendMessage ( hRich, EM_FINDTEXTEXW, 0, 
                                                 ( LPARAM ) &fndFrst );
                  }
                  else {
                    fndFrst.chrg.cpMin = 0;
                    fndFrst.chrg.cpMax = -1;
                    intRc = ( LONG ) SendMessage ( hRich, EM_FINDTEXTEXW, 1, 
                                                 ( LPARAM ) &fndFrst );
                  }

                  CHARFORMAT cf;
                  cf.cbSize = sizeof ( cf );
                  cf.dwMask = CFM_BOLD | CFM_UNDERLINE;
                  cf.dwEffects = CFE_BOLD | CFE_UNDERLINE;
                  SendMessage ( hRich, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
                  DWORD dwerr = GetLastError ( );
                  dwerr = GetLastError ( );
                  if ( intRc == -1 ) {
                    #pragma region str-to-find was not found in this file 
                    if ( MessageBox ( hMainWnd, L "Could Not FIND 
                    Requested String!\rDo you want to Continue? ", fsiaf,
                        MB_YESNO | MB_ICONINFORMATION | MB_DEFBUTTON2 ) == IDOK ) {
                      StringCchPrintf ( searchCap, 255, L "Could Not 
                      FIND Requested String!\tDo you want to Continue? " );
                      SetWindowText ( hMainWnd, searchCap );
                      SetWindowText ( hEdit, searchCap );
                      SetFocus ( hRich );
                      return 1;
                    }
                    else {
                      intRc = 0;
                      SetFocus ( hRich );
                    }
                    #pragma endregion str-to-find was not found in this file 
                  }
                  else {
                    #pragma region str-to-find was found in this file 
                    hWndFocus = hRich;
                    SetFocus ( hWndFocus );
                    SendMessage ( hRich, EM_SETSEL, fndFrst.chrgText.cpMin, 
                                  fndFrst.chrgText.cpMax );
                    SendMessage ( hRich, EM_SCROLLCARET, 0, 0 );
                    int chLine = ( int ) SendMessage ( hRich, EM_EXLINEFROMCHAR, 0, intRc );
                    int fvLine = ( int ) SendMessage ( hRich, EM_GETFIRSTVISIBLELINE, 0, 0 );
                    LONG strtLine = ( LONG ) SendMessage ( hRich, EM_LINEINDEX, chLine, 0 );
                    LONG colPos = intRc - strtLine;
                    DWORD lvLine = ( DWORD ) SendMessage ( hRich, EM_SCROLL, SB_PAGEDOWN, 0 );
                    int pgSize = 0;
                    pgSize = LOWORD ( lvLine );
                    lvLine = ( DWORD ) SendMessage ( hRich, EM_SCROLL, SB_PAGEUP, 0 );
                    DWORD desiredPos = pgSize / 2 + fvLine;
                    int noffSet = chLine - desiredPos;
                    SendMessage ( hRich, EM_LINESCROLL, 0, noffSet );
                    SendMessage ( hRich, EM_SCROLLCARET, 0, 0 );

                    intRc = fndFrst.chrgText.cpMax + 1;
                    StringCchPrintf ( Text, 255, L "Line: %d  Column: %d ", chLine, colPos );
                    SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, ( LPARAM ) Text );
                    #pragma endregion make sure caret is centered in the middle of the screen 
                  }
                }
                SetFocus ( hRich );
              }
              if ( !( nVirtKeySh & 0x8000 ) ) {
                // Key-repeat can cause find-in-previous-file to appear
                // to skip files that have matches. This prevents that, whilr
                // remaining responsive for find-in-next-file
                PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE );
                DispatchMessage ( &msg );
              }
              nVirtKey = GetAsyncKeyState ( VK_CONTROL );
              if ( nVirtKey & -1 ) {
                nVirtKey = GetAsyncKeyState ( 0x43 );//  'c ' key
                if ( nVirtKey & -1 )
                  return 0; //user hit control-c. stop finding
              }
            }
            #pragma endregion load richedit and find string in file
            else {
              if ( misses++ == iCount ) {
                #pragma region when_entire_column_has_been_searched_and_no_match_found
                if ( iCount >= 5000 ) {
                  MessageBox ( NULL, L "NO Matches found in the presearched files!\rPress 
                  ENTER or SELECT OK to search file-list. 
                  This could take a while! ", fsiaf, MB_OK | MB_ICONHAND | MB_SYSTEMMODAL );
                }
                int searchStartRow = currentRow;
                ListView_SetItemState ( hList, -1, 0,
                            LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
                findStringInAllFiles ( );
                #pragma endregion search every file in listview
                SetFocus ( hRich );
                #pragma region select_first_file_with_a_match
                if ( hRich && IsWindowVisible ( hRich ) ) {
                  result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
                  if ( searchStartRow != 0 ) {
                    result = searchStartRow + 1;
                  }
                  ListView_SetItemState ( hList, result,
                              LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED,
                              LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
                  ListView_GetItemText ( hList, result, 3, curntFname, 255 );
                  curntFname [ 255 ] = 0;
                  int fileSize = _wtoi ( curntFname );
                  if ( fileSize > 64 * 1024 * 1024 ) {
                    MessageBox ( hMainWnd, L "File Size  is too much for me! 
                    Can 't LOAD ", curntFname, MB_OK | MB_ICONEXCLAMATION );
                    ListView_GetItemText ( hList, result, 0, curntFname, MAX_PATH - 1 );
                    ListView_GetItemText ( hList, result, 2, curntPath, MAX_PATH - 1 );
                    wsprintf ( teXt, L "/select, \ "%s\\%s\ " ", curntPath, curntFname );
                    SetWindowText ( hEdit, teXt );
                    curntPath [ 259 ] = 0;
                    ShellExecute ( NULL, L "open ", 
                                   L "explorer.exe ", teXt, curntPath, SW_SHOW );
                    return 0;
                  }
                  ListView_GetItemText ( hList, result, 0, curntFname, MAX_PATH - 1 );
                  ListView_GetItemText ( hList, result, 2, curntPath, MAX_PATH - 1 );
                  StringCchPrintf ( tStr, MAX_PATH, L "%s\\%s ", curntPath, curntFname );
                  SetWindowText ( hMainWnd, tStr );
                  SetWindowText ( hEdit, tStr );
                  FillRichEditFromFile ( hRich, ( LPCTSTR ) &tStr );
                  HWND hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
                  iCount = 0;
                  if ( hList ) iCount = ListView_GetItemCount ( hList );
                  StringCchPrintf ( Text, 255, L "%d of %d Files ", result + 1, iCount );
                  SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) Text );
                  ShowWindow ( hRich, SW_SHOW );
                  SetFocus ( hRich );
                  if ( fsiaf ) {
                    FINDTEXTEX fndFrst;
                    fndFrst.chrg.cpMin = 0;
                    fndFrst.chrg.cpMax = -1;
                    fndFrst.lpstrText = fsiaf;
                    intRc = ( LONG ) SendMessage 
                            ( hRich, EM_FINDTEXTEXW, 1, ( LPARAM ) &fndFrst );
                    CHARFORMAT cf;
                    cf.cbSize = sizeof ( cf );
                    cf.dwMask = CFM_BOLD | CFM_UNDERLINE;
                    cf.dwEffects = CFE_BOLD | CFE_UNDERLINE;
                    SendMessage ( hRich, EM_SETCHARFORMAT, SCF_SELECTION, ( LPARAM ) &cf );
                    DWORD dwerr = GetLastError ( );
                    dwerr = GetLastError ( );
                    if ( intRc == -1 ) {
                      if (1){
                        StringCchPrintf ( searchCap, 255, 
                        L "Could Not FIND Requested String!\tDo you want to Continue? " );
                        SetWindowText ( hMainWnd, searchCap );
                        SetWindowText ( hEdit, searchCap );
                        sendMeMyKey ( hList, VK_F5 );
                        intRc = ( LONG ) SendMessage ( hRich, EM_FINDTEXTEXW, 1, 
                                                     ( LPARAM ) &fndFrst );
                        if ( intRc == -1 ) {
                          intRc = 0;
                          SetFocus ( hRich );
                        }
                      }
                    }
                    else {
                      SendMessage ( hRich, EM_SETSEL, 
                                    fndFrst.chrgText.cpMin, fndFrst.chrgText.cpMax );
                      hwndStatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
                      int chLine = ( int ) SendMessage ( hRich, EM_EXLINEFROMCHAR, 0, intRc );
                      LONG strtLine = ( LONG ) SendMessage ( hRich, EM_LINEINDEX, chLine, 0 );
                      LONG colPos = intRc - strtLine;
                      StringCchPrintf ( Text, 255, L "Line: %d  Column: %d ", chLine, colPos );
                      SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, ( LPARAM ) Text );
                      return 1;
                    }
                  }
                }
                #pragma endregion and load it into richedit to find match
              }
              else if ( misses > iCount ) {
                MessageBox ( NULL, L "NO FILES containing Requested STRING were found!\rCheck 
                the spelling of STRING. ", fsiaf, MB_OK | MB_ICONHAND );
                return 0;// searched every file in listvie and found no matchesw
              }
            }
          }
          while ( !numbFound );
          SetFocus ( hList );
          return 0;
        }
        #pragma endregion looking for string-to-search-for in text
      }
      if ( ( pnkd )->wVKey == VK_F3 ) {
        #pragma region send_vk-f3
        sendMeMyKey ( hRich, VK_F3 );
        return 0;
        #pragma endregion to richedit
      }
      if ( ( pnkd )->wVKey == VK_F6 ) {
        #pragma region send_vk-f6
        sendMeMyKey ( hRich, VK_F6 );
        return 0;
        #pragma endregion to richedit
      }

      if ( ( pnkd )->wVKey == VK_F4 ) {
        short nVirtKey = GetKeyState ( VK_MENU );
        if ( nVirtKey & -128 ) {
          if ( hMainWnd ) {
            return DefWindowProc ( hMainWnd, message, wParam, lParam );
          }
          else {
            return 0;
          }
        }
        #pragma region get_full_path_and_file_attributes
        NMLISTVIEW * pnmlv;
        LVITEM lvi;
        int result = 0;
        struct _stat64 buf;
        pnmlv = ( LPNMLISTVIEW ) lParam;
        memset ( &lvi, 0, sizeof ( lvi ) );
        result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
        if ( result == -1 ) result = 0;
        lvi.iItem = result;
        lvi.mask = LVIF_TEXT;
        lvi.cchTextMax = 255;
        lvi.pszText = Text;
        if ( hList ) ListView_GetItem ( hList, &lvi );
        lvi.pszText = text;
        lvi.iSubItem = 2;// get the path
        ListView_GetItem ( hList, &lvi );
        wsprintf ( teXt, L "%s\\%s ", text, Text );
        result = _wstat64 ( teXt, &buf );
        if ( result == 0 ) {
          if ( ( buf.st_mode & _S_IFREG ) ) {
            wsprintf ( teXt, L "\ "%s\\%s\ " ", text, Text );
            SetWindowText ( hEdit, teXt );
            SHELLEXECUTEINFO ShExecInfo = { 0 };
            ShExecInfo.cbSize = sizeof ( SHELLEXECUTEINFO );
            ShExecInfo.fMask = SEE_MASK_INVOKEIDLIST;
            ShExecInfo.hwnd = hList;
            ShExecInfo.lpVerb = L "properties ";
            ShExecInfo.lpFile = Text; //can be a file as well
            ShExecInfo.lpParameters = L " ";
            ShExecInfo.lpDirectory = text;
            ShExecInfo.nShow = SW_SHOW;
            ShExecInfo.hInstApp = NULL;
            ShellExecuteEx ( &ShExecInfo );
          }
          if ( ( buf.st_mode & _S_IFDIR ) ) {
            wsprintf ( teXt, L "\ "%s\\%s\ " ", text, Text );
            if ( hEdit ) {
              SetWindowText ( hEdit, teXt );
            }
            ShellExecute ( NULL, L "properties ", teXt, NULL, NULL, SW_SHOWNORMAL );
          }
        }
        #pragma endregion and open with current file association
      }
    }
    break;

    case NM_RETURN:
    {
      #pragma region get_full_path_and_file_attributes
      // Keyboard RETuRN Key
      NMLISTVIEW * pnmlv;
      LVITEM lvi = { 0 };
      int result = 0;
      struct _stat64 buf;
      pnmlv = ( LPNMLISTVIEW ) lParam;
      memset ( &lvi, 0, sizeof ( lvi ) );
      result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
      if ( result == -1 ) return 0;
      lvi.iItem = result;
      lvi.mask = LVIF_TEXT;
      lvi.cchTextMax = 255;
      lvi.pszText = Text;
      ListView_GetItem ( hList, &lvi );
      lvi.pszText = text;
      lvi.iSubItem = 2;// get the path
      ListView_GetItem ( hList, &lvi );
      wsprintf ( teXt, L "%s\\%s ", text, Text );
      result = _wstat64 ( teXt, &buf );
      if ( result == 0 ) {
        if ( ( buf.st_mode & _S_IFREG ) ) {
          wsprintf ( teXt, L "/select, \ "%s\\%s\ " ", text, Text );
          SetWindowText ( hEdit, teXt );
          ShellExecute ( NULL, L "open ", L "explorer.exe ", teXt, text, SW_SHOW );
        }
        if ( ( buf.st_mode & _S_IFDIR ) ) {
          wsprintf ( teXt, L "\ "%s\\%s\ " ", text, Text );
          SetWindowText ( hEdit, teXt );
          ShellExecute ( NULL, L "explore ", teXt, NULL, NULL, SW_SHOWNORMAL );
        }
      }
      #pragma endregion and open with current file association
    }break; // case NM_RETURN 

    case NM_SETFOCUS:
    {
      int rslt = 0;
      rslt = ListView_GetItemCount ( hList );
      if ( rslt >= 1 ) {
        if ( ( rslt = ListView_GetNextItem ( hList, -1, LVNI_SELECTED ) ) == -1 ) {
          ListView_SetItemState ( hList, 0, LVIS_SELECTED | LVIS_DROPHILITED | LVIS_FOCUSED,
                      LVIS_SELECTED | LVIS_DROPHILITED | LVIS_FOCUSED ); // select this one
          return 1;
        }
      }
    } //  case NM_SETFOCUS
    break;
    } // switch (((LPNMHDR)lParam)->code)
  }break; // case WM_NOTIFY

    case WM_NCLBUTTONUP:
  {
    ClipCursor ( NULL );
    return 0;
  }break;

  case WM_SIZING:
  {
    RECT* dragRc = ( RECT* ) lParam;
    RECT mrc = { 0 }, wrc = { 0 }, wrccl = { 0 }, crc = { 0 }, lrc = { 0 }, lrccl = {0 },
      trc = { 0 }, trccl = { 0 },rrc = { 0 }, rrccl = {0 }, temprc = {0 },
      drc = { 0 }, src = { 0 }, srccl = { 0 }, brc = { 0 }, 
      brccl = { 0 }, toprc = { 0 }, toprccl = { 0 }, f0rc = { 0 },
      f0rccl = { 0 }, erc = { 0 }, erccl = { 0 },  oldCliprc = { 0 }, clipRc = { 0 };
    POINT xPos = { 0 };
    GetCursorPos ( &xPos );
    HWND hWnd = FindWindow ( L "TwoStageSearch ", 0 );
    GetWindowRect ( hWnd, &wrc );
    GetClientRect ( hWnd, &wrccl );
    HWND hTopView = FindWindowEx ( hWnd, 0, L "#32770 ", 0 );
    GetWindowRect ( hTopView, &toprc );
    GetClientRect ( hTopView, &toprccl );
    HWND hFormView0 = FindWindowEx ( hTopView, 0, L "#32770 ", 0 );
    GetWindowRect ( hFormView0, &f0rc );
    GetClientRect ( hFormView0, &f0rccl );
    hEdit = FindWindowEx (hFormView0, 0, L "Edit ", 0 );
    GetWindowRect ( hEdit, &erc );
    GetClientRect ( hEdit, &erccl );
    GetWindowRect ( hDlg, &lrc );
    HWND hBottomView = GetParent ( hDlg );
    HWND hFormView1 = GetNextWindow ( hDlg, GW_HWNDNEXT );
    HWND hFormView3 = GetNextWindow ( hDlg, GW_HWNDPREV );
    HWND hStatusView = GetNextWindow ( hFormView3, GW_HWNDPREV );
    hFormView2 = hDlg;
    hList = getThisWindowHandle ( hFormView2, IDC_LIST1 );
    hTree = getThisWindowHandle ( hFormView1, IDC_TREE1 );
    hRich = getThisWindowHandle ( hFormView3, IDC_EDIT1 );
    HWND hwndStatus = getThisWindowHandle ( hStatusView, IDC_STATUS );
    GetWindowRect ( hStatusView, &src );
    GetClientRect ( hwndStatus, &srccl );
    GetClientRect ( hTree, &trccl );
    GetClientRect ( hList, &lrccl );
    GetClientRect ( hRich, &rrccl );
    GetWindowRect ( hBottomView, &brc );
    GetClientRect ( hBottomView, &brccl );
    GetClientRect ( hWnd, &crc );
    GetWindowRect ( hWnd, &wrc );
    GetWindowRect ( hDlg, &drc );
    GetWindowRect ( hFormView1, &trc );
    GetWindowRect ( hFormView2, &lrc );
    GetWindowRect ( hFormView3, &rrc );
    OffsetRect ( &toprc, -wrc.left, -wrc.top );
    OffsetRect ( &f0rc, -wrc.left, -wrc.top );
    OffsetRect ( &erc, -wrc.left, -wrc.top );
    OffsetRect ( &brc, -wrc.left, -wrc.top );
    OffsetRect ( &trc, -wrc.left, -wrc.top );
    OffsetRect ( &lrc, -wrc.left, -wrc.top );
    OffsetRect ( &rrc, -wrc.left, -wrc.top );
    OffsetRect ( &src, -wrc.left, -wrc.top );
    CopyRect ( &temprc, dragRc );
    OffsetRect ( &temprc, -wrc.left, -wrc.top );

    if ( wParam != WMSZ_RIGHT &&
       wParam != WMSZ_LEFT )
    {
      GetClipCursor ( &oldCliprc );
      clipRc.left = drc.left;
      clipRc.top = xPos.y;
      clipRc.right = drc.right;
      clipRc.bottom = xPos.y + 1;
      ClipCursor ( &clipRc );
    }
    if ( wParam == WMSZ_RIGHT ) {
      GetClipCursor ( &oldCliprc );
      clipRc.left = drc.left+100;
      clipRc.top = xPos.y;
      clipRc.right = wrc.right-wrc.left - 20;
      clipRc.bottom = xPos.y + 1;
      ClipCursor ( &clipRc );
      MoveWindow ( hFormView3, lrc.right-8, 0, wrccl.right - lrc.right+8, 
                   lrc.bottom - lrc.top, TRUE );
      MoveWindow ( hRich, 0, 0, wrccl.right - lrc.right+8, lrc.bottom - lrc.top, TRUE );
    }
    if ( wParam == WMSZ_LEFT ) {
      GetClipCursor ( &oldCliprc );
      clipRc.left = wrc.left + 50;
      clipRc.top = xPos.y;
      clipRc.right = drc.right-30;
      clipRc.bottom = xPos.y + 1;
      ClipCursor ( &clipRc );
      MoveWindow ( hFormView1, 0, 0, temprc.left-4, temprc.bottom - temprc.top+4, TRUE );
      GetWindowRect ( hFormView1, &trc );
      OffsetRect ( &trc, -wrc.left, -wrc.top );
    }
    MoveWindow ( hList, 0, 0, lrc.right - lrc.left - 16, lrc.bottom - lrc.top - 16, TRUE );
    return 0;
  }break;

  case WM_SIZE:
  {
    UINT width = GET_X_LPARAM ( lParam );
    UINT height = GET_Y_LPARAM ( lParam );
    MoveWindow ( hList, 0, 0, width, height, TRUE );
    return 0;
  }
  break;

  case WM_ACTIVATE:
  {
    if ( LOWORD ( wParam ) & WA_ACTIVE ) {
      if ( hWndFocus ) {
        SetFocus ( hWndFocus );
        return 0;
      }
      else {
        hWndFocus = hList;
        SetFocus ( hList );
        if ( ListView_GetItemCount ( hList ) >= 1 ) {
          if ( ListView_GetNextItem ( hList, -1, LVNI_SELECTED ) == -1 ) {
            // select this one
            ListView_SetItemState ( hList, 0, LVIS_SELECTED, LVIS_SELECTED );
          }
        }
        return 0;
      }
    }
    else
      return -1;
  }
  break;

  default:
    break;
  }
  return 0;
}

如何更改文件列表的根文件夹:菜单和树视图

用户看到的 Windows 程序表面上类似于 Windows 文件资源管理器。它左侧有一个 `TreeView`,用于导航文件系统和选择当前目录路径,中间有一个 `ListView`,右侧有一个文件查看器。但它不用于文件管理。它的唯一目的是在文件中查找 `string` 并以其使用上下文显示它。它使用 PF3 向前查找,使用 Shift+PF3 向后查找。它也可以使用 PF6 向后查找。向后查找时,如果找不到更多出现的内容,它将查找下一个包含匹配项的文件中的第一个出现项,或上一个文件中的最后一个匹配项。您可以使用 PF5 立即转到下一个文件,或使用 Shift+PF5 转到上一个文件。程序中还有其他功能,但它们是为了支持 `find` 函数。

`TwoStageSearch` 菜单非常稀疏,这是故意的。在下面的屏幕截图中,您可以看到菜单中选择了“更改目录”。主窗口标题栏中的文本显示“**Two Stage Search - Build ...**”。在右侧稍小的屏幕截图中,我们看到标题文本现在显示“**使用光标键在...中导航...**”。在 `TreeView` 中,用户将选择从“`ASUS` ”移动到“`All Users”。编辑框包含“c:\Users\All Users”。状态栏仍然显示“c:\Users\asus”。最后一步是按“**ENTER**”接受更改。用户可以单击他们选择的文件夹,但这会删除 `ListView` 的内容,并用所选文件夹的子项填充它(如果存在)。要获取多个 `Root` 的文件,您必须使用 `Menu`。

标题文本的意义在于 `TreeView` Proc 使用它来检测更改目录的请求。当焦点在 `TreeView` 上并且用户按下“**ENTER**”时,`NM_RETURN` 处理程序会获取标题文本。它与几个不同的控件 `string` 进行比较。如果控件 `string` 匹配标题文本,则执行相应的请求。更改目录请求将更改*根*文件夹并将其保存在注册表中。第二个请求 `Temp_Change_CD` 将临时更改它,第三个请求 `CD_AND_Build` 将更改它并构建文件列表。

更改“开始于”目录:单击菜单

Click on the Menu and select Change Directory.

要更改根文件夹或“开始于”目录,请单击菜单并选择 Change_Directory

TreeView Proc 还处理发送到 TreeView 控件的其他消息

在 `WM_INITDIALOG` 消息处理程序中,我们确保我们不在 `hBottom` 本身,而是在 `hFormView1` 中。然后我们创建一个字体,创建一个 `treectrl` 并向其发送 `WM_SETFONT` 消息。然后我们调用 `SetTreeviewImagelist` 并从环境变量中获取搜索根 `startInDir` 以设置当前目录。我们将 `TreeView` 扩展样式设置为双缓冲,以减少闪烁。然后我们显示主窗口。

`WM_NOTIFY` 消息处理程序包含 `(LP)NMHDR lParam` 代码的开关,用于 `TVN_SELCHANGING`,它获取 `NODE` 的完整路径,返回到驱动器并确保它是可见的。

`NMHDR lParam` 代码 `TVN_SELCHANGED` 检查操作是否为 `TVC_UNKNOWN` 并且 `startInDir` 不为空,它会填充并展开树节点直到找到 `startInDir`。如果操作是 `TVC_BYKEYBOARD`,则选择并高亮显示当前项。如果操作是 `TVC_BYMOUSE` 并且当前项是一个文件夹,则文件夹的内容将替换 `ListView` 的内容。如果它不是文件夹,则将该项添加到 `ListView`。

`NMHDR lParam` 代码 `TVN_ITEMEXPANDING` 填充子节点,其中包含文件和文件夹,始终只深入一级。这创建了 `TreeView` 的按需填充行为。

`NMHDR lParam` 代码 `TVN_ITEMEXPANDED` 将会(如果 `startInDir` 不为空)填充并展开树节点直到找到 `startInDir`。这将显示文件列表构建的根文件夹作为选定的 `TreeView` 项。

`NMHDR lParam` 代码 `NM_CLICK` 当节点已被键盘选择然后通过鼠标单击时,将填充 `ListView`。

`NMHDR lParam` 代码 `NM_RETURN` 当节点被键盘选择然后按下 **Enter** 时,填充 `ListView`。当 `TreeView` 从菜单选择获得焦点时,它会从主窗口标题获取文本,以确定是临时保存 CD、永久保存还是仅用于执行文件列表构建,还会获取扩展集的环境变量。

`NMHDR lParam` 代码 `TVN_KEYDOWN` 将处理 `VK_TAB` 和 `VK_F5`,方法是将焦点设置到 `ListView`。

`WM_SIZE` 消息处理程序将在容器 `FormView1` 被控件左侧的 `ListView` 调整大小时调整 `TreeView` 控件的大小,根据两个控件的当前关系,使其填满容器窗口,但在它们之间留有狭窄的条带。

treeViewProc 函数

// Message handler for tree view. Processes WM_NOTIFY for the following messages: 
// TVN_SELCHANGING - Gets the NODE 's full path, back to the drive. 
// TVN_SELCHANGED - If action is TVC_UNKNOWN and startindir is not blank, 
// populates and expands tree until startin dir is located. 
// If action is TVC_BYKEYBOARD current item is selected and 
// highlighted If action is TVC_BYMOUSE and current item is a folder 
// the contents of the folder replaces the listview contents.
// If it's not a folder the item is added to the listview. 
// TVN_ITEMEXPANDING - populates the child nodes with files and folders, 
// always and only one level deeper. 
// TVN_ITEMEXPANDED - If startindir is not blank, populates and expands tree 
// until startin dir is located. 
// NM_CLICK - populates ListView when node is selected by keyboard and 
// then clicked by mouse. 
// NM_RETURN - populates ListView when node is selected by keyboard and 
// then Enter is pressed. When TreeView
// receives focus from Menu Selection it gets text from Title of MainWindow 
// to determine whether to save CD 
// temporarily, permanently or just to perform file list build, 
// also gets environment variable for extension set.
// WM_SIZE wil re-size the TreeView control when the container 
// FormView1 is moved by the ListView
// control 's sizing border on the LEFT side of the control 
// based on the current relationship of the 
// two controls, to fill the containing window but leaving a narrow strip 
// in between them. 
INT_PTR CALLBACK treeViewProc 
( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) {
  UNREFERENCED_PARAMETER ( wParam );
  TCHAR nodeCD [ MAX_PATH ] = { 0 };
  TCHAR startInDir [ MAX_PATH ] = { 0 };
  HWND hMainWnd = NULL, hTree = NULL, hList = NULL, hEdit = NULL;
  TV_ITEM tvi;
  DWORD sidLen = 0;

  if ( WM_INITDIALOG == message ) {
    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    HWND hBottom = GetAncestor ( hDlg, GA_PARENT );
    if ( hMainWnd == hBottom ) {
      return 0;
    }
    hTree = FindWindowEx ( hDlg, 0, L "SysTreeView32 ", 0 );
    if ( hTree == 0 ) {
      HFONT hFont = CreateFont ( 32, 10, 0, 0, FW_BOLD, FALSE, 
                     FALSE, FALSE, ANSI_CHARSET, \
                     OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, \
                     DEFAULT_PITCH | FF_SWISS, L "Arial " );
      RECT sRc;
      GetClientRect ( hDlg, &sRc );
      hTree = CreateWindowEx ( WS_EX_OVERLAPPEDWINDOW | WS_EX_STATICEDGE | 
                   WS_EX_COMPOSITED | WS_EX_NOINHERITLAYOUT,
                   WC_TREEVIEW, NULL,
                   WS_CHILD | WS_VISIBLE |
                   DS_3DLOOK | WS_BORDER | WS_CLIPSIBLINGS | 
                   WS_CLIPCHILDREN | WS_TABSTOP |
                   TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT,
                   sRc.left, sRc.top, sRc.right, sRc.bottom, hDlg,
                   ( HMENU ) IDC_TREE1, hInst, 0 );
      SendMessage ( hTree, WM_SETFONT, WPARAM ( hFont ), TRUE );
      GetClientRect ( hDlg, &sRc );
      SetTreeviewImagelist ( hTree );
      sidLen = GetEnvironmentVariable ( L "startInDir ", startInDir, 255 );
      SetCurrentDirectory ( startInDir );
      if ( _wcsicmp ( startInDir, nodeCD ) != 0 ) {
        StringCchPrintf ( nodeCD, 255, L "%s ", startInDir ); // define the 
                                                  // start-in-directory for XP
        SetCurrentDirectory ( nodeCD );
        GetCurrentDirectory ( 255, startInDir );
      }
      else {
        StringCchPrintf ( nodeCD, 255, L "%s ", startInDir ); // initialize 
            // in case user did not select anything before clicking  'search '
      }
      sidLen = GetEnvironmentVariable ( L "HOMEDRIVE ", startInDir, 255 );
      RECT wrc = { 0 };
      GetWindowRect ( hMainWnd, &wrc );
      TreeView_SetExtendedStyle ( hTree, TVS_EX_DOUBLEBUFFER, TVS_EX_DOUBLEBUFFER );
      MoveWindow ( hMainWnd, wrc.left, wrc.top, wrc.right - wrc.left, 
                   wrc.bottom - wrc.top, TRUE );
      ShowWindow ( hMainWnd, SW_NORMAL );
      return ( INT_PTR ) TRUE;
    }
  }
  else {
    if ( WM_NOTIFY == message ||
       WM_SIZE == message ) {
      if ( (  hMainWnd = FindWindow ( L "TwoStageSearch ", 0 )) == 0 ) return 0;
      if ( (  hTree = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_TREE1 )) == 0) 
         return 0;
      if ( ( hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 ) ) == 0 ) 
         return 0;
      if ( ( hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 )) == 0) 
         return 0;
    }
  }

  switch ( message ) {

  case WM_ERASEBKGND:
    return 1;

  case WM_NOTIFY:
  { // messages from child window common controls
    if ( ( hMainWnd = FindWindow ( L "TwoStageSearch ", 0 ) ) == 0 ) return 0;
    if ( ( hTree = getThisWindowHandle 
          ( hMainWnd, ( ULONG ) IDC_TREE1 ) ) == 0 ) return 0;
    if ( ( hList = getThisWindowHandle 
          ( hMainWnd, ( ULONG ) IDC_LIST1 ) ) == 0 ) return 0;
    if ( ( hEdit = getThisWindowHandle 
          ( hMainWnd, ( ULONG ) IDC_EDIT2 ) ) == 0 ) return 0;

    switch ( ( ( LPNMHDR ) lParam )->idFrom ) {
    case IDC_TREE1:
    {
      switch ( ( ( LPNMHDR ) lParam )->code ) {
      case  TVN_SELCHANGING:
      { // Gets the NODE 's full path, back to the drive. 
        NMTREEVIEW * pnmtv; // Handle to the NM TreeView STRUCTURE
        pnmtv = ( LPNMTREEVIEW ) lParam; // set the Handle to the 
                                         // NM TreeView STRUCTURE
        tvi = ( ( pnmtv )->itemNew );    // set the Handle to the tvitem 
                                     // STRUCTURE for the item that was just selected 
        getNodeFullPath ( hTree, tvi.hItem );
        TreeView_EnsureVisible ( hTree, tvi.hItem );
        return FALSE;
      }  break; // case  TVN_SELCHANGING

      case TVN_SELCHANGED:
      {
        // populates ListView when node is expanded by mouse. 
        // picks-up process to find startinDirectory when node is 
        // selected by code or by clicking  '+ ' button on node
        // updates  'HighLite ' status if node is selected by keyboard
        NMTREEVIEW * pnmtv;
        pnmtv = ( LPNMTREEVIEW ) lParam;
        HTREEITEM  nmtvi;
        sidLen = GetEnvironmentVariable ( L "STARTINDIR ", startInDir, 255 );
        nmtvi = TreeView_GetSelection ( hTree );
        TCHAR Text [ 256 ] = { 0 };
        TCHAR text [ 256 ] = { 0 };
        DWORD result = 0;
        memset ( &tvi, 0, sizeof ( tvi ) );
        tvi.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE;
        tvi.pszText = Text;
        tvi.cchTextMax = 255;
        tvi.hItem = nmtvi;
        if ( ( pnmtv )->action == TVC_UNKNOWN ) {
          if ( *startInDir ) {
            // is there a start-in-directory 
            GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
            if ( _wcsnicmp ( startInDir, nodeCD, wcslen ( nodeCD ) ) == 0 ) {
              // is it in the path
              if ( TreeView_GetItem ( hTree, &tvi ) ) {
                // could you get the item 
                if ( tvi.cChildren > 0 ) {
                  // does it have children
                  if ( tvi.state & TVIS_EXPANDEDONCE ) {
                    // node has children and has been expanded. 
                    // Go on to the next higher level
                    nmtvi = TreeView_GetChild ( hTree, nmtvi );
                    while ( nmtvi ) {
                      // loop to get the child 's label text
                      memset ( &tvi, 0, sizeof ( tvi ) );
                      memset ( &Text, 0, sizeof ( Text ) );
                      tvi.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE;
                      tvi.pszText = Text;
                      tvi.cchTextMax = 255;
                      tvi.hItem = nmtvi;
                      if ( TreeView_GetItem ( hTree, &tvi ) ) {
                        if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' ) {
                          // concat in text buffer
                          wsprintf ( text, L "%s\\%s ", nodeCD, tvi.pszText );
                        }
                        else {
                          // concat in text buffer
                          wsprintf ( text, L "%s%s ", nodeCD, tvi.pszText );
                        }
                        result = ( int ) wcslen ( text );
                        // when lengths are the same compare
                        if ( sidLen == result && ( _wcsicmp 
                          ( startInDir, text ) == 0 ) ) {
                          // when strings are same 
                          SetCurrentDirectory ( nodeCD );
                          memset ( startInDir, 0, sizeof ( startInDir ) );
                          TreeView_Select ( hTree, nmtvi, TVGN_CARET );
                          TreeView_Select ( hTree, nmtvi, TVGN_DROPHILITE );
                          // comment this out to position at bottom
                          TreeView_Select ( hTree, nmtvi, TVGN_FIRSTVISIBLE );
                          SetFocus ( hTree );
                          return 0;
                        }
                        else {
                          wsprintf ( text, L "%s\\ ", text ); // concat a back-slash
                          result = ( int ) wcslen ( text );   //take the length 
                                                              //again(it just changed)
                          if ( _wcsnicmp ( startInDir, 
                               text, result ) == 0 ) { // is folder in path
                            if ( tvi.cChildren > 0 ) {
                              if ( !( tvi.state & TVIS_EXPANDEDONCE ) ) 
                              { //node has children but has never been expanded. 
                                //TVN_EXPANDED will take over now.
                                TreeView_Expand ( hTree, tvi.hItem, 
                                     TVE_EXPAND ); // we are through here
                                return 0; //terminate the routine
                              }
                              else { // folder in path, go on to the 
                                     // next higher level
                                if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' ) {
                                  wsprintf ( nodeCD, L "%s\\%s ", 
                                             nodeCD, tvi.pszText );
                                }
                                else {
                                  wsprintf ( nodeCD, L "%s%s ", nodeCD, tvi.pszText );
                                }
                                nmtvi = TreeView_GetChild ( hTree, nmtvi );
                              }
                            }
                            else { // there are no children. end
                              return 0;
                            }
                          }
                          else {
                            // get the next child 's handle
                            nmtvi = TreeView_GetNextSibling ( hTree, nmtvi );
                          }
                        }
                      }
                    } // loop to get the child 's label text
                  }   // node has children and has been expanded. 
                      //Go on to the next higher level
                }     // does it have children
              }       // could you get the item 
            }         // is it in the path
            else {
              return 0; // don 't do  anything
            }
            return 0;
          } // is there a start-in-directory 
        }

        if ( ( pnmtv )->action == TVC_BYKEYBOARD ) {
          TreeView_Select ( hTree, nmtvi, TVGN_CARET );
          TreeView_Select ( hTree, nmtvi, TVGN_DROPHILITE );
          return 0;
        }

        if ( ( pnmtv )->action == TVC_BYMOUSE ) {
          TreeView_Select ( hTree, nmtvi, TVGN_DROPHILITE );
          TreeView_Select ( hTree, nmtvi, TVGN_CARET );
          tvi.hItem = nmtvi;
          if ( TreeView_GetItem ( hTree, &tvi ) ) {
            if ( tvi.iImage == 4 ) { // not a folder
              LVITEM item;
              memset ( &item, 0, sizeof ( item ) );
              GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
              item.mask = LVIF_TEXT;
              item.iItem = 0;
              item.iSubItem = 0;
              item.cchTextMax = 260;
              item.pszText = tvi.pszText;
              int ret = 0, nRet = 0;
              ret = ListView_InsertItem ( hList, &item );
              item.iItem = ret;
              item.iSubItem = 2;
              item.pszText = nodeCD;
              ListView_SetItem ( hList, &item );
              nRet = ret + 1;
              ListView_GetItemText ( hList, nRet, 0, text, 255 );
              while ( _wcsicmp ( text, tvi.pszText ) == 0 ) { //  'text ' is  
                                                              //'Name ' in listview
                ListView_GetItemText ( hList, nRet, 1, text, 255 );
                if ( _wcsicmp ( text, nodeCD ) == 0 ) { // now  'text ' is  
                                                        // 'Path ' in listview
                  ListView_DeleteItem ( hList, ret );   // it 's a duplicate. 
                                                        // Delete it. 
                  ret = nRet;
                }
                nRet = nRet + 1;
                ListView_GetItemText ( hList, nRet, 0, text, 255 ); // now it's 
                                                                    //'Name ' again 
              }
              ListView_SetItemState ( hList, -1, 0, 
                       LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
              ListView_SetItemState ( hList, ret, 
                       LVIS_DROPHILITED, LVIS_DROPHILITED );
              ListView_SetItemState ( hList, ret, LVIS_SELECTED, LVIS_SELECTED );
              ListView_SetItemState ( hList, ret, LVIS_FOCUSED, LVIS_FOCUSED );
              ListView_EnsureVisible ( hList, ret, TRUE );
              SetFocus ( hList );
            } // the item is not a folder, add it to the listview
            else if ( tvi.cChildren == 1 ) { // it is a folder, 
                 // clear listview and populate it with folder contents. 
              nmtvi = TreeView_GetChild ( hTree, nmtvi );
              memset ( &tvi, 0, sizeof ( tvi ) );
              memset ( &Text, 0, sizeof ( Text ) );
              tvi.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE;
              tvi.pszText = Text;
              tvi.cchTextMax = 255;
              tvi.hItem = nmtvi;
              if ( TreeView_GetItem ( hTree, &tvi ) ) {
                ListView_DeleteAllItems ( hList );
                GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
                do {
                  LVITEM item;
                  memset ( &item, 0, sizeof ( item ) );
                  item.mask = LVIF_TEXT;
                  item.iItem = 0;
                  item.iSubItem = 0;
                  item.cchTextMax = 260;
                  item.pszText = tvi.pszText;
                  int ret = ListView_InsertItem ( hList, &item );
                  item.iItem = ret;
                  item.iSubItem = 2;
                  item.pszText = nodeCD;
                  ListView_SetItem ( hList, &item );
                  if ( ret < 1 )
                    ret *= -1;
                  nmtvi = tvi.hItem;
                  nmtvi = TreeView_GetNextSibling ( hTree, nmtvi );
                  memset ( &tvi, 0, sizeof ( tvi ) );
                  memset ( &Text, 0, sizeof ( Text ) );
                  tvi.mask = TVIF_TEXT;
                  tvi.pszText = Text;
                  tvi.cchTextMax = 255;
                  tvi.hItem = nmtvi;
                }
                while ( TreeView_GetItem ( hTree, &tvi ) );
              }
            }
          }
        }
      } break; // case TVN_SELCHANGED 

      case TVN_ITEMEXPANDING:
      { // populates the child nodes with files and folders, always one level deeper. 
        NMTREEVIEW * pnmtv;
        HTREEITEM  nmtvi = { 0 };
        sidLen = GetEnvironmentVariable ( L "STARTINDIR ", startInDir, 255 );
        pnmtv = ( LPNMTREEVIEW ) lParam;
        TCHAR Text [ 256 ] = { 0 };
        tvi = ( ( pnmtv )->itemNew );
        nmtvi = TreeView_GetSelection ( hTree );
        if ( NULL != nmtvi && tvi.hItem != nmtvi ) {
          // user clicked on node button(+), node is not selected
          TreeView_SelectItem ( hTree, tvi.hItem );
        }
        if ( ( pnmtv )->action & TVE_COLLAPSE ) { // the folder is closing, 
               // set its image indexes  to show a closed folder
          tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
          tvi.iImage = 0; // Closed 
          tvi.iSelectedImage = 1; // Closed and selected 
          TreeView_SetItem ( hTree, &tvi );
          return FALSE;
        }
        // still here? Okay, then this node is expanding but it has no grandchildren yet. 
        // Give it 's CHILDREN some Children so that it 's CHILDREN will have BUTTONS.
        tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
        tvi.iImage = 2;// FOLDEROPEN 
        tvi.iSelectedImage = 3;// opened and selected
        TreeView_SetItem ( hTree, &tvi ); // set the image indexes to show open folders.
        if ( tvi.state & TVIS_EXPANDEDONCE )
          return FALSE; // don 't duplicate children
        tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
        tvi.pszText = Text;
        tvi.cchTextMax = 255;
        if ( TreeView_GetItem ( hTree, &tvi ) ) { // you 've got the requested attributes 
          if ( tvi.pszText [ 1 ] == L ': ' ) { //the second character is a colon. It 's a DRIVE 
            wsprintf ( nodeCD, L "%s ", Text );
            SetCurrentDirectory ( nodeCD );
            nmtvi = TreeView_GetChild ( hTree, tvi.hItem );
          }
          else { //the second character is not a colon. This is not a DRIVE 
            memset ( &nodeCD, 0, sizeof ( nodeCD ) );
            nmtvi = TreeView_GetChild ( hTree, tvi.hItem );
          } // either way, you might now have the first child of the node
          while ( nmtvi ) { // if you have the child, do the loop 
            memset ( &tvi, 0, sizeof ( tvi ) );
            memset ( &Text, 0, sizeof ( Text ) );
            tvi.hItem = nmtvi;
            tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
            tvi.pszText = Text;
            tvi.cchTextMax = 255;
            if ( TreeView_GetItem ( hTree, &tvi ) ) { // just in case the handle is corrupted 
              DWORD dwLen = GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
              if ( dwLen == 0 || dwLen > MAX_PATH - 2 )
                return 0;
              if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' ) 
              { // does not end with a back-slash
                wsprintf ( nodeCD, L "%s\\ ", nodeCD ); // concat a back-slash to nodeCD 
              }
              wsprintf ( nodeCD, L "%s%s ", nodeCD, tvi.pszText ); // concat to nodeCD 
              getDirectories ( tvi.hItem, nodeCD );
              nmtvi = TreeView_GetNextSibling ( hTree, tvi.hItem );
            }
          }
        }
        return FALSE;
      } break; // case TVN_ITEMEXPANDING

      case TVN_ITEMEXPANDED: // This folder has just been expanded. 
      { // Is it and any of it 's children in the srart-in-directory?
        NMTREEVIEW * pnmtv;
        HTREEITEM  nmtvi;
        sidLen = GetEnvironmentVariable ( L "STARTINDIR ", startInDir, 255 );
        pnmtv = ( LPNMTREEVIEW ) lParam;
        TCHAR Text [ 256 ] = { 0 };
        TCHAR text [ 256 ] = { 0 };
        if ( ( pnmtv )->action & TVE_COLLAPSE )
          return 0; // don 't do  anything
        tvi = ( ( pnmtv )->itemNew );
        tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
        tvi.pszText = Text;
        tvi.cchTextMax = 255;
        if ( TreeView_GetItem ( hTree, &tvi ) ) { // you 've got the requested attributes 
          if ( *startInDir ) { // is there a start-in-directory
            memset ( &nodeCD, 0, sizeof ( nodeCD ) );
            nmtvi = TreeView_GetChild ( hTree, tvi.hItem );
            while ( nmtvi ) { // if you have the child, do the loop 
              tvi.hItem = nmtvi;
              DWORD dwLen = GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
              if ( dwLen == 0 || dwLen > MAX_PATH - 2 )
                return 0;
              if ( TreeView_GetItem ( hTree, &tvi ) ) { // just in case the handle is corrupted 
                if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' ) 
                { // if it doesn 't have a backslash on the end, add one 
                  wsprintf ( nodeCD, L "%s\\ ", nodeCD );
                }
                wsprintf ( text, L "%s%s ", nodeCD, tvi.pszText );
                if ( _wcsnicmp ( startInDir, text, wcslen ( text ) ) == 0 ) {
                  if ( ( _wcsicmp ( ( startInDir ), ( text ) ) ) == 0 ) {
                    memset ( startInDir, 0, sizeof ( startInDir ) );
                    TreeView_Select ( hTree, nmtvi, TVGN_CARET );
                    TreeView_Select ( hTree, nmtvi, TVGN_DROPHILITE );
                    SetFocus ( hTree );
                    return FALSE;
                  }
                  wsprintf ( text, L "%s\\ ", text );
                  if ( _wcsnicmp ( startInDir, text, wcslen ( text ) ) == 0 ) {
                    if ( tvi.cChildren > 0 ) {
                      if ( !( tvi.state & TVIS_EXPANDEDONCE ) ) 
                      { //node has children but has never been expanded. 
                        //TVN_EXPANDED will take over now.
                        SetCurrentDirectory ( text );
                        TreeView_Expand ( hTree, tvi.hItem, TVE_EXPAND ); // we are through here
                        return FALSE; //terminate the routine
                      }
                    }
                  }
                }
                nmtvi = TreeView_GetNextSibling ( hTree, tvi.hItem );
              } // just in case the handle is corrupted 
            } // if you have the child, do the loop 
          } // is there a start-in-directory
        } //you 've got the requested attributes 
        return FALSE;
      }
      break; // case TVN_ITEMEXPANDED

      case  NM_CLICK:
      { // populates ListView when node is selected by keyboard and then clicked by mouse. 
        HTREEITEM  nmtvi;
        NMHDR * lpnmh;
        TCHAR Text [ 256 ] = { 0 };
        TCHAR text [ 256 ] = { 0 };
        lpnmh = ( LPNMHDR ) lParam;
        HWND hwndFrom = ( lpnmh )->hwndFrom;
        TVHITTESTINFO  lpht = { 0 };
        POINT pt;
        GetCursorPos ( &pt ); // get the screen coordinates of the cursor
        ScreenToClient ( hwndFrom, &pt ); // convert them to client coordinates
        lpht.pt = pt; //put it in the hit test info structure
        TreeView_HitTest ( hwndFrom, &lpht ); // hit me!
        SetFocus ( hTree );
        HTREEITEM nmClickedtvi = lpht.hItem;
        nmtvi = TreeView_GetSelection ( hwndFrom );
        if ( nmtvi != nmClickedtvi )   // the clicked node hasn 't been 
        {
          return 0;
        }  // selected yet. don 't do anything else
        if ( lpht.flags && TVHT_ONITEM ) // TVHT_ONITEMBUTTON 
                                                 // you clicked on a selected treeview node. 
        { // The selection status is not going to change. You have to handle it. 
          memset ( &tvi, 0, sizeof ( tvi ) );
          tvi.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE;
          tvi.pszText = Text;
          tvi.cchTextMax = 255;
          tvi.hItem = nmtvi;
          if ( TreeView_GetItem ( hwndFrom, &tvi ) ) { // you got the item that was clicked 
            GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
            if ( tvi.iImage == 4 ) { // the item is not a folder, add it to the listview
              LVITEM item;
              memset ( &item, 0, sizeof ( item ) );
              item.mask = LVIF_TEXT;
              item.iItem = 0;
              item.iSubItem = 0;
              item.cchTextMax = 260;
              item.pszText = tvi.pszText;
              int ret = ListView_InsertItem ( hList, &item );
              item.iItem = ret;
              item.iSubItem = 2;
              item.pszText = nodeCD;
              ListView_SetItem ( hList, &item );
              int nRet = ret + 1;
              ListView_GetItemText ( hList, nRet, 0, text, 255 );
              while ( _wcsicmp ( text, tvi.pszText ) == 0 ) {
                ListView_GetItemText ( hList, nRet, 1, text, 255 );
                if ( _wcsicmp ( text, nodeCD ) == 0 ) {
                  ListView_DeleteItem ( hList, ret ); // delete one of them
                  ret = nRet;
                }
                nRet = nRet + 1;
                ListView_GetItemText ( hList, nRet, 0, text, 255 );
              }
              ListView_SetItemState ( hList, -1, 0, LVIS_DROPHILITED | 
                                      LVIS_SELECTED | LVIS_FOCUSED );
              ListView_SetItemState ( hList, ret, LVIS_DROPHILITED, LVIS_DROPHILITED );
              ListView_SetItemState ( hList, ret, LVIS_SELECTED, LVIS_SELECTED );
              ListView_SetItemState ( hList, ret, LVIS_FOCUSED, LVIS_FOCUSED );
              ListView_EnsureVisible ( hList, ret, TRUE );
              SetFocus ( hList );
            } // the item is not a folder, addit to the listview
            else if ( tvi.cChildren == 1 ) { // the item has children, 
                                             // get the first one and set-up loop  
              nmtvi = TreeView_GetChild ( hwndFrom, nmtvi );
              memset ( &tvi, 0, sizeof ( tvi ) );
              memset ( &Text, 0, sizeof ( Text ) );
              tvi.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE;
              tvi.pszText = Text;
              tvi.cchTextMax = 255;
              tvi.hItem = nmtvi;
              if ( TreeView_GetItem ( hwndFrom, &tvi ) ) 
              { // delete all the listview items and re build the list 
                ListView_DeleteAllItems ( hList );
                GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
                do { //  while (TreeView_GetItem(hwndFrom, &tvi))
                  LVITEM item;
                  memset ( &item, 0, sizeof ( item ) ); // zap the item
                  item.mask = LVIF_TEXT; // item is textual
                  item.iItem = 0; // item is sorted so start at first row
                  item.iSubItem = 0; //column number one
                  item.cchTextMax = 260;
                  item.pszText = tvi.pszText;
                  int ret = ListView_InsertItem ( hList, &item );
                  item.iItem = ret; // row number where irem was inserted
                  item.iSubItem = 2; // 3d column
                  item.pszText = nodeCD;
                  ListView_SetItem ( hList, &item ); // set the 2nd column sub item text
                  if ( ret < 1 )         ret *= -1;
                  nmtvi = tvi.hItem;
                  nmtvi = TreeView_GetNextSibling ( hwndFrom, nmtvi ); // get the 
                                                                // next child's handle
                  memset ( &tvi, 0, sizeof ( tvi ) );
                  memset ( &Text, 0, sizeof ( Text ) );
                  tvi.mask = TVIF_TEXT;
                  tvi.pszText = Text;
                  tvi.cchTextMax = 255;
                  tvi.hItem = nmtvi;
                }
                while ( TreeView_GetItem ( hwndFrom, &tvi ) ); // get the child's label or stop
              }  // delete all the listview items and re build the list 
            } // the item has children, get the first one and set-up loop  
          }  // you got the item that was clicked 
        } // you clicked on a treeview node
      }break; // case  NM_CLICK

      case TVN_KEYDOWN:
      {
        NMTVKEYDOWN * ptvkd;
        ptvkd = ( LPNMTVKEYDOWN ) lParam;
        if ( ( ptvkd )->wVKey == VK_TAB ) {
          SetFocus ( hList );
        }

        if ( ( ptvkd )->wVKey == VK_F5 ) {
          #pragma region search_current_folder_to_build_file_list
          GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
          SetFocus ( hList );
          return 0;
          #pragma endregion then send vk_f5 to listview
        }
        break;

      case NM_RETURN:
      { // populates ListView when node is selected by keyboard and 
        // then Keyboard ENTER Key is pressed.
        HTREEITEM  nmtvi = { 0 };
        TCHAR Text [ 256 ] = { 0 };
        TCHAR text [ 256 ] = { 0 };
        TCHAR tStr [ 256 ] = { 0 };
        TCHAR searchStr [ 256 ] = { 0 };
        TCHAR titleText [ 256 ] = { 0 };
        GetWindowText ( hMainWnd, titleText, 255 );
        if ( wcscmp ( titleText, L "   USE CURSOR KEYS to Navigate in the TREEVIEW, 
        select a folder path, then push ENTER to save path temporarily. " ) == 0 ) {
          if ( GetWindowTextLength ( hEdit ) ) {
            GetWindowText ( hEdit, nodeCD, 255 );
            SetEnvironmentVariable ( L "STARTINDIR ", nodeCD );
          }
          else {
            GetCurrentDirectory ( 255, nodeCD );
          }
          Edit_SetCueBannerTextFocused ( hEdit, L " ", TRUE );
          HWND hwndstatus = getThisWindowHandle ( hMainWnd, IDC_STATUS );
          SendMessage ( hwndstatus, SB_SETTEXT, 0, ( LPARAM ) nodeCD );
          return TRUE;
        }
        if ( wcscmp ( titleText, L "   USE CURSOR KEYS to Navigate in the TREEVIEW, 
        select a folder path, then push ENTER to search path. " ) == 0 ) {
          if ( GetWindowTextLength ( hEdit ) ) {
            GetWindowText ( hEdit, nodeCD, 255 );
            SetEnvironmentVariable ( L "STARTINDIR ", nodeCD );
          }
          else {
            GetCurrentDirectory ( 255, nodeCD );
          }

          Edit_SetCueBannerTextFocused ( hEdit, L " ", TRUE );
          HWND hwndStatus = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_STATUS );
          GetEnvironmentVariable ( L "SEARCHSTR ", searchStr, 255 );
          StringCchPrintf ( tStr, MAX_PATH, L "Searching for %s in   ", searchStr );
          SendMessage ( hwndStatus, SB_SETTEXT, 0, ( LPARAM ) tStr );
          SendMessage ( hwndStatus, SB_SETTEXT, 1 | SBT_POPOUT, ( LPARAM ) nodeCD );
          HWND hProgress = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_PROGRESS1 );
          ShowWindow ( hProgress, SW_NORMAL );
          SendMessage ( hProgress, PBM_SETMARQUEE, PBST_NORMAL, 0 );
          recursivefileSearch ( nodeCD, FALSE );
          SendMessage ( hProgress, PBM_SETMARQUEE, PBST_PAUSED - 3, 0 );
          ShowWindow ( hProgress, SW_HIDE );
          SendMessage ( hwndStatus, SB_SETTEXT, 0, ( LPARAM ) nodeCD );
          int iCount = ListView_GetItemCount ( hList );
          int result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
          StringCchPrintf ( tStr, MAX_PATH, L "Number of Files %d. ", iCount );
          SendMessage ( hwndStatus, SB_SETTEXT, 1 | SBT_POPOUT, ( LPARAM ) tStr );
          StringCchPrintf ( tStr, MAX_PATH, L "%d of %d Files. ", result + 1, iCount );
          SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) tStr );
          return TRUE;
        }
        if ( wcscmp ( titleText, L "   USE CURSOR KEYS to Navigate in the TREEVIEW 
        to select a folder path, then push ENTER. 
        The path will be saved permanently " ) == 0 ) {
          if ( GetWindowTextLength ( hEdit ) ) {
            GetWindowText ( hEdit, nodeCD, 255 );
            SetEnvironmentVariable ( L "STARTINDIR ", nodeCD );
          }
          else {
            GetCurrentDirectory ( 255, nodeCD );
          }
          StringCchPrintf ( Text, 255, L " /k Setx STARTINDIR \ "%s\ " ", nodeCD );
          SetEnvironmentVariable ( L "STARTINDIR ", nodeCD );
          persist_This ( Text );
          HWND hwndstatus = getThisWindowHandle ( hMainWnd, IDC_STATUS ); 
          SendMessage ( hwndstatus, SB_SETTEXT, 0, ( LPARAM ) nodeCD );
          return TRUE;
        }
        memset ( &tvi, 0, sizeof ( tvi ) );
        tvi.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE;
        tvi.pszText = Text;
        tvi.cchTextMax = 255;
        nmtvi = TreeView_GetSelection ( hTree );
        tvi.hItem = nmtvi;
        if ( TreeView_GetItem ( hTree, &tvi ) ) {
          GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
          if ( tvi.iImage == 4 ) { // not a folder
            LVITEM item;
            memset ( &item, 0, sizeof ( item ) );
            item.mask = LVIF_TEXT;
            item.iItem = 0; // we start at zero but list is sorted, 
                            // so it will find a place for it
            item.iSubItem = 0; // sub-item zero is first column
            item.cchTextMax = 260;
            item.pszText = tvi.pszText; //the file name
            int ret = 0;
            ret = ListView_InsertItem ( hList, &item ); // insert in sorted order
            item.iItem = ret; // ret is the index of the one we inserted
            item.iSubItem = 2; // sub-item two is third column
            item.pszText = nodeCD; // the  'PATH ' part of filename
            ListView_SetItem ( hList, &item ); // second column text
            int nRet = ret + 1;
            ListView_GetItemText ( hList, nRet, 0, text, 255 );
            while ( _wcsicmp ( text, tvi.pszText ) == 0 ) {
              ListView_GetItemText ( hList, nRet, 1, text, 255 );
              if ( _wcsicmp ( text, nodeCD ) == 0 ) {
                ListView_DeleteItem ( hList, ret ); // delete one of them
                ret = nRet;
              }
              nRet = nRet + 1;
              ListView_GetItemText ( hList, nRet, 0, text, 255 );
            }
            ListView_SetItemState ( hList, -1, 0, LVIS_DROPHILITED | 
                                    LVIS_SELECTED | LVIS_FOCUSED );
            ListView_SetItemState ( hList, ret, LVIS_DROPHILITED, LVIS_DROPHILITED );
            ListView_SetItemState ( hList, ret, LVIS_SELECTED, LVIS_SELECTED );
            ListView_SetItemState ( hList, ret, LVIS_FOCUSED, LVIS_FOCUSED );
            ListView_EnsureVisible ( hList, ret, TRUE );
            SetFocus ( hList );
          }
          else if ( tvi.cChildren == 1 ) {
            nmtvi = TreeView_GetChild ( hTree, nmtvi );
            memset ( &tvi, 0, sizeof ( tvi ) );
            memset ( &Text, 0, sizeof ( Text ) );
            tvi.mask = TVIF_TEXT | TVIF_CHILDREN | TVIF_IMAGE;
            tvi.pszText = Text;
            tvi.cchTextMax = 255;
            tvi.hItem = nmtvi;
            if ( TreeView_GetItem ( hTree, &tvi ) ) {
              ListView_DeleteAllItems ( hList );
              GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
              do {
                LVITEM item;
                memset ( &item, 0, sizeof ( item ) );
                item.mask = LVIF_TEXT;
                item.iItem = 0;
                item.iSubItem = 0;
                item.cchTextMax = 260;
                item.pszText = tvi.pszText;
                int ret = 0;
                ret = ListView_InsertItem ( hList, &item );
                item.iItem = ret;
                item.iSubItem = 2;
                item.pszText = nodeCD;
                ListView_SetItem ( hList, &item );
                if ( ret < 1 )         ret *= -1;
                nmtvi = tvi.hItem;
                nmtvi = TreeView_GetNextSibling ( hTree, nmtvi );
                memset ( &tvi, 0, sizeof ( tvi ) );
                memset ( &Text, 0, sizeof ( Text ) );
                tvi.mask = TVIF_TEXT;
                tvi.pszText = Text;
                tvi.cchTextMax = 255;
                tvi.hItem = nmtvi;
              }
              while ( TreeView_GetItem ( hTree, &tvi ) );
            }
          }
        } // if(TreeView_GetItem(hTree, &tvi))
      } break; // case NM_RETURN - Keyboard ENTER Key
      } // TVN_KEYDOWN
      } // switch (((LPNMHDR)lParam)->code)
    } break;// case IDC_TREE1
    } // switch (((LPNMHDR)lParam)->idFrom)
  }  break; //  case WM_NOTIFY:

  case WM_SIZE:
  {
    UINT width = GET_X_LPARAM ( lParam );
    UINT height = GET_Y_LPARAM ( lParam );
    MoveWindow ( hTree, 0, 0, width, height, TRUE );
    return 0;
  }break;
  }
  return ( INT_PTR ) FALSE;
}

SetTreeviewImagelist 函数:用于树

这些图像仅用于 `TreeView`,因为假设 `ListView` 中的大多数文件都是相同类型,因此在大多数情况下价值较低。如果用户选择了包含 5 或 6 个成员的扩展名集,这肯定不成立,但扩展名的图标对搜索过程没有帮助。

// FUNCTION: Creates a 32 by 32 imagelist containing 6 bitmaps of folder icons. 
// The images are; closed-folder, selected-closed-folder, opened-folder, 
// selected-opened-folder, document(not a folder) and selected-document. 
// No attempt is made to identify nature of document. 
bool inline SetTreeviewImagelist ( const HWND hTv ) {
  HIMAGELIST hImageList = ImageList_Create ( 16, 16, ILC_COLOR32, 6, 1 );
  HBITMAP hBitMap = LoadBitmap ( hInst, MAKEINTRESOURCE ( IDB_BITMAP1 ) );
  ImageList_Add ( hImageList, hBitMap, NULL );
  DeleteObject ( hBitMap );
  TreeView_SetImageList ( hTv, hImageList, 
        TVSIL_NORMAL );  // attach image lists to tree view common control
  return true;
}

getNodeFullPath:递归查找父项

此函数接受 2 个参数,一个指向 Tree Control 的句柄和一个指向当前 `HTREEITEH` 的句柄。获取该项,它检查第二个字符是否为冒号,仅在 `ROOT` 项中找到。如果满足此条件,则设置当前目录并返回 `TRUE`,停止递归,否则,它调用 `TreeView_GetParent`,获取该项的父节点,并使用父节点调用 `getNodeFullPath`。完成递归后,我们将在每次递归返回时将项文本与当前目录连接起来。实际上,我们颠倒了节点在堆栈中的顺序。

getNodeFullPath 函数

// FUNCTION: recursively get the current item 's parent until you find the 
// drive then return the tvi.pszText and collect it to build the full-path 
// of the current node. Call this routine when a node is selected or when 
// a button(+ or -) is clicked. This is necessary because clicking a button 
// does not automatically select a node, it only expands it.
BOOL getNodeFullPath ( HWND hTree, HTREEITEM  nmtvi ) {
  TVITEM tvi;
  TCHAR Text [ 256 ] = {};
  tvi.hItem = nmtvi;
  tvi.mask = TVIF_TEXT;
  tvi.cchTextMax = 255;
  tvi.pszText = Text;
  HWND hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
  HWND hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
  hTree = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_TREE1 );
  TCHAR nodeCD [ 250 ] = { 0 };
  if ( TreeView_GetItem ( hTree, &tvi ) ) {
    if ( tvi.pszText [ 1 ] == L ': ' ) {
      // you found the drive, the root of the folders
      StringCchPrintf ( nodeCD, 255, L "%s ", tvi.pszText ); // concat in text buffer
      SetCurrentDirectory ( nodeCD );
      SetWindowText ( hEdit, nodeCD );         // set the text in IDC_EDIT2
      return true; // stop recursing
    }
    else {
      nmtvi = TreeView_GetParent ( hTree, tvi.hItem );
      getNodeFullPath ( hTree, nmtvi ); // loop recursively 

    // when you get here you are no longer recursing,
    // you are just collecting the returned information
    // in the reverse order of the get parent calls. This
    // has the effect of a last-in - first-out stack,
    //  resulting in a  'reversal by recursion '.  
      if ( *nodeCD == 0 ) {
        GetCurrentDirectory ( 255, nodeCD );
      }
      if ( tvi.pszText ) {
        if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' ) {
          StringCchPrintf 
          ( nodeCD, 255, L "%s\\%s ", nodeCD, tvi.pszText ); // concat in text buffer
        }
        else {
          StringCchPrintf 
          ( nodeCD, 255, L "%s%s ", nodeCD, tvi.pszText ); // concat in text buffer
        }
        SetCurrentDirectory ( nodeCD );
        SetWindowText ( hEdit, nodeCD );
      }
    }
  }
  return true; // return current value of tvi.pszText
}

主窗口过程:WndProc

当我们进入 `WndProc` 时,我们在堆栈上分配所有需要的变量。然后我们在 `if` 语句中处理 `WM_CREATE`。结果是,`else` 块是不需要的,所以它可以放在消息开关中。第一个创建的对话框是容器 `hTopView`,放置了我认为不需要移动的控件的地方。这个容器窗口包含一个控件和其中的另一个对话框。控件是编辑控件,名为 `hEdit`。`Dialog` 是 `hFormView0`,它包含两个按钮、两个组合框和一个进度条。进度条在显示时将完全填充 `hFormView0`,从而阻止用户输入。

接下来创建的对话框是 `hBottom`。它将包含 `hFormView1`、`hFormview2`、`hFormView3 hMiddle`。`hFormView1` 包含 `TreeView`,`hFormView2` 是 `RESIZIBG Window` 并包含 `ListView`,`hFormView3` 包含 `RichEdit` 控件,最后 `hMiddle` 包含状态栏。如果所有这些窗口都成功创建,我们将为它们创建 `RECT` 并建立它们的初始大小并将它们移动到位并显示主窗口 `hWnd`。

现在,我们获取环境变量 `HOMEDRIVE` 并调用 `InitTreeViewItems` 函数。接下来,我们获取环境变量 `STARTINDIR`(如果存在),否则获取 `USERPROFILE` 来设置当前目录。我们获取 `SEARCHSTR`,这是上次使用的文件扩展名集,或者将 `searchStr` 设置为 '`*.cpp;*.c`' 并设置 `SEARCHSTR`。在调用 `recursivefileSearch` 函数之前,进度条会被打开并显示。返回后,进度条会隐藏,状态栏会更新,标题会设置,`ListView` 中的第一个文件会被选中并加载到 `RichEdit` Viewer Panel 中,焦点会设置到 `ListView`,并且 `WndProc` 返回零。

`WM_SIZE` 消息处理程序获取 `hBottom` 的窗口矩形,并将其移动到 `left=0, top = 90, right=width, bottom=feight`。这会导致 `hFormView`(`1,2,3`,以及 `hNiddle`)收到 `WM_SIZE` 消息。这样做是为了正确设置所有由 `hFormView2` 的 `RESIZING` 边框调整大小的窗口。`top=90` 是 `Menu` 和 `hTopView` 的组合高度。`hTopView` 中的控件或多或少是固定的,因此移动的次数不如 `hTree`、`hList` 和 `hRich` 多,因此它们只需要 `SIZE_RESTORED` 和 `SIZE_MAXIMIZED`。我还使用了一个小技巧,当 `wParam` 是 `SIZED_RESTORED` 时,我再次发送相同的消息,但将 `wParam` 改为 `SIZE_MAXIMIZED` 并添加了 `SIZE_MAXSHOW` 消息。这样做是为了消除一些只是 `SIZE_MAXIMIZED` 块副本的代码块。

在 `WM_COMMAND` 消息开关中,`ID_FILE_CD` 命令处理程序会将主窗口的标题文本设置为 '` USE CURSOR KEYS to Navigate in the TREEVIEW, select a folder path, then push ENTER to search path. '。然后它会清空 `hEdit` 并设置 CUE Banner 并将焦点设置到 `hTree`。用户可以通过不按 `ENTER` 来取消命令,但如果用户确实按了 `ENTER`,则将搜索选定的树节点,并将找到的文件添加到 `ListView`。

`ID_FILE_TEMP` 命令处理程序会将主窗口的标题文本设置为 '`USE CURSOR KEYS to Navigate in the TREEVIEW, select a folder path, then push ENTER to save path temporarily.`'。然后它会清空 `hEdit` 并设置 CUE Banner,将焦点设置到 `hTree`。用户可以通过不按 `ENTER` 来取消命令,但如果用户确实按了 `ENTER`,则选定的树节点将被保存到环境变量块中,当前目录将被设置,但该值不会写入注册表。

`ID_FILE_CHANGE` 命令处理程序会将主窗口的标题文本设置为 '`USE CURSOR KEYS to Navigate in the TREEVIEW to select a folder path, then push ENTER. The path will be saved permanently.`'。然后它将清空 `hEdit`,设置 CUE Banner 并将焦点设置到 `hTree`。用户可以通过不按 `ENTER` 来取消命令,但如果用户确实按了 `ENTER`,则选定的树节点将被保存到环境变量块中,当前目录将被设置,并且该值将被写入注册表。

文件菜单下的 `IDM_EXIT` 命令会在程序终止时销毁主窗口。

`ID_EDIT_CMD` 命令将 `hEdit` 中的窗口文本设置为 '`#&cmd&sol;f:on /k set prompt=&P&_&l&D&G&L&T&G && ver && echo Path Completion is turned ON. For directory use control + D. Control + Shift + F for Files. `' 然后它向 `hEdit` 发送一个 RETURN 键。`hEdit` 被子类化以获取 **RETURN** 键并处理窗口文本,从而启动一个 CMD 提示符。

`ID_VIEW_RESET` 和 `ID_VIEW_FO` 命令用于启动重置 `SHMessageBoxCheck` 消息框中的 `Checkbox` 的过程。`ID_VIEW_RESET` 命令将 `hEdit` 中的窗口文本设置为 '`#@WHOAMI /USER /FO CSV /NH | clip `'',`ID_VIEW_FO` 命令设置为 '`##WHOAMI /USER /NH |clip `''。`hEdit` 子类过程使用前两个字符来决定是通过询问“`Are You Sure?`”来优雅地重置 Check Box(对于“`#@`”)还是直接“`FOrce”重置(对于“`##”)。接下来,我们向 `hEedit` 发送一个 `RETURN` 键。

帮助菜单项显示一个下拉列表,其中包含以下命令

“关于...”命令由 `IDM_ABOUT` 处理。它显示通常的关于对话框。

“关于扩展名”命令由 `ID_HELP_EXTENSIONS` 处理。它显示一个对话框,解释文件扩展名以及如何设置它们。

“关于字符串”命令由 `IDD_HELP_STRING` 处理。它显示一个对话框,解释要搜索的字符串以及如何设置它们。

“关于路径”命令由 `IDD_HELP_PATH` 处理。它显示一个对话框,解释如何设置路径。

“关于 CheckBox”命令由 `IDD_HELP_CHECKBOX` 处理。它显示一个对话框,解释如何重置消息框复选框。

“关于 CMD.EXE”命令由 `IDD_HELP_CMD` 处理。它显示一个对话框,解释如何使用 *cmd.exe* 提示符。

`WM_INITMENU` 消息处理程序设置“编辑”菜单命令的默认菜单项。这使得双击即可激活默认项

`WM_SYSCOMMAND` 消息处理程序在用户按下 ALT 键时移除或恢复菜单。

`WM_ACTIVATE` 消息处理程序将焦点设置到 `ListView`。

帮助菜单命令不仅仅是“关于”

Click on the Menu and select ...

子菜单解释了“关于”如何在 TwoStageSearch 中执行操作

WndProc 函数

//
//  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  PURPOSE: Processes messages for the main window.
//
//  WM_CREATE   - process main window creation and create all dialogs. Retrieve state and 
//                perform initial file list build, load first file into RichEdit
//  WN_SIZE     - process sizing message
//  WM_COMMAND  - process the application menu
//  WM_SYSCOMMAND process SC_MENU to show or hide menu
//  WM_ACTIVATE - set initial focus to ListView
//  WM_PAINT    - Paint the main window
//  WM_DESTROY  - post a quit message and return
//
LRESULT CALLBACK WndProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
  int wmEvent;
  int mnh = 0;//MeNu Height
  TCHAR curntFname [ 256 ];
  TCHAR curntPath [ MAX_PATH ];
  TCHAR tStr [ MAX_PATH ];
  TCHAR extensionCue [ MAX_PATH ];
  TCHAR nodeCD [ MAX_PATH - 1 ] = { 0 };
  TCHAR searchStr [ MAX_PATH ] = { 0 };
  TCHAR startInDir [ MAX_PATH ] = { 0 };
  static RECT rcClip, rcOldClip;
  HWND hWndFocus = NULL, hEdit = NULL, hTree = NULL, hList = NULL, 
                         hRich = NULL, hFormView0 = NULL,
    hFormView1 = NULL, hFormView2 = NULL, hCombo1 = NULL, 
                       hCombo2 = NULL, hProgress = NULL,
    hFormView3 = NULL, hTopView = NULL, hBottomView = NULL, 
                       hwndStatus = NULL, hStatusView = NULL;


  if ( WM_CREATE == message ) {
    RECT rc = { 0, 0, 1380, 480 };
  
    hTopView = CreateDialog ( GetModuleHandle ( NULL ),
                     MAKEINTRESOURCE ( IDD_TOP ), hWnd, editBoxProc );
    if ( hTopView != NULL ) {
      hEdit = GetDlgItem ( hTopView, IDC_EDIT2 );
      ShowWindow ( hEdit, SW_SHOW );
      ShowWindow ( hTopView, SW_SHOW );
    }
    hFormView0 = CreateDialog ( GetModuleHandle ( NULL ),
                     MAKEINTRESOURCE ( IDD_FORMVIEW0 ), hWnd, comboBoxProc );
    if ( hFormView0 != NULL ) {
      hCombo1 = GetDlgItem ( hFormView0, IDC_COMBO1 );
      hCombo2 = GetDlgItem ( hFormView0, IDC_COMBO2 );
      hProgress = GetDlgItem ( hFormView0, IDC_PROGRESS1 );
      ShowWindow ( hCombo1, SW_SHOW );
      ShowWindow ( hCombo2, SW_SHOW );
      ShowWindow ( hProgress, SW_SHOW );
      ShowWindow ( hFormView0, SW_SHOW );
    }
    else return FALSE;

    hBottomView = CreateDialog ( GetModuleHandle ( NULL ),
                      MAKEINTRESOURCE ( IDD_BTTOM ), hWnd, bottomProc );
    if ( hBottomView != NULL ) {
      hStatusView = CreateDialog ( GetModuleHandle ( NULL ),
                      MAKEINTRESOURCE ( IDD_MIDDLE ), hBottomView, statusBarProc );
      hwndStatus = GetDlgItem ( hStatusView, IDC_STATUS );
      ShowWindow ( hwndStatus, SW_SHOW );
      ShowWindow ( hStatusView, SW_SHOW );
      ShowWindow ( hBottomView, SW_SHOW );
      if ( hwndStatus ) {
        SendMessage ( hwndStatus, SB_SETTEXT, 0, ( LPARAM ) L "Ready " );
        SendMessage ( hwndStatus, SB_SETTEXT, 1 | 
                      SBT_POPOUT, ( LPARAM ) L "Blah blah blah " );
        SendMessage ( hwndStatus, SB_SETTEXT, 2 | 
                      SBT_POPOUT, ( LPARAM ) L "Blah blah blah " );
        SendMessage ( hwndStatus, SB_SETTEXT, 3 | 
                      SBT_POPOUT, ( LPARAM ) L "|H:32|W:10| " );
        SendMessage ( hwndStatus, WM_SIZE, 0, 0 );
      }
      hFormView3 = CreateDialog ( GetModuleHandle ( NULL ),
                       MAKEINTRESOURCE ( IDD_FORMVIEW3 ), hBottomView, richEditProc );
      if ( hFormView3 != 0 ) {
        hRich = GetDlgItem ( hFormView3, IDC_EDIT1 );
        ShowWindow ( hRich, SW_SHOW );
        ShowWindow ( hFormView3, SW_SHOW );
      }
      hFormView2 = CreateDialog ( GetModuleHandle ( NULL ),
                       MAKEINTRESOURCE ( IDD_FORMVIEW2 ), hBottomView, listViewProc );
      if ( hFormView2 != 0 ) {
        hList = GetDlgItem ( hFormView2, IDC_LIST1 );
        ShowWindow ( hList, SW_SHOW );
        ShowWindow ( hFormView2, SW_SHOW );
      }
      hFormView1 = CreateDialog ( GetModuleHandle ( NULL ),
                       MAKEINTRESOURCE ( IDD_FORMVIEW1 ), hBottomView, treeViewProc );
      if ( hFormView1 != 0 ) {
        hTree = GetDlgItem ( hFormView1, IDC_TREE1 );
        ShowWindow ( hTree, SW_SHOW );
        ShowWindow ( hFormView1, SW_SHOW );
      }
      ShowWindow ( hBottomView, SW_SHOW );
    }

    RECT wrc = { 0 }, wrccl = { 0 }, src = { 0 }, srccl = { 0 }, crc = { 0 }, crccl = { 0 },
      brc = { 0 }, brccl = { 0 },  toprc = { 0 }, 
                   toprccl = { 0 },  rrc = { 0 }, rrccl = { 0 },
      erc = { 0 }, erccl = { 0 },  lrc = { 0 }, lrccl = { 0 },  trc = { 0 }, trccl = { 0 };
    GetWindowRect ( hWnd, &wrc );
    GetClientRect ( hWnd, &wrccl );
    GetWindowRect ( hTopView, &toprc );
    GetClientRect ( hTopView, &toprccl );
    GetWindowRect ( hBottomView, &brc );
    GetClientRect ( hBottomView, &brccl );
    GetWindowRect ( hFormView0, &erc );
    GetClientRect ( hFormView0, &erccl );
    GetWindowRect ( hFormView1, &trc );
    GetClientRect ( hFormView1, &trccl );
    GetWindowRect ( hFormView2, &lrc );
    GetClientRect ( hFormView2, &lrccl );
    GetWindowRect ( hFormView3, &rrc );
    GetClientRect ( hFormView3, &rrccl );
    GetWindowRect ( hStatusView, &src );
    GetClientRect ( hStatusView, &srccl );
    OffsetRect ( &toprc, -toprc.left, -toprc.top );
    OffsetRect ( &erc, -wrc.left, -wrc.top );
    OffsetRect ( &brc, -wrc.left, -wrc.top );
    MoveWindow ( hFormView0, 0, 0, wrc.right, 30, TRUE );
    MoveWindow ( hTopView, 0, 40, wrc.right, 50, TRUE );
    MoveWindow ( hEdit, 10, 10, wrc.right, 30, TRUE );
    MoveWindow ( hBottomView, 0, 90, brc.right-brc.left, wrc.bottom, TRUE );
    MoveWindow ( hFormView1, 0, 0, trc.right - trc.left, lrc.bottom - lrc.top+4, TRUE );
    MoveWindow ( hFormView2, trc.right - trc.left, 0, 
                 lrc.right - lrc.left, lrc.bottom - lrc.top, TRUE );
    GetWindowRect ( hFormView2, &lrc );
    OffsetRect ( &lrc, -wrc.left, -wrc.top );
    MoveWindow ( hFormView3, lrc.right-8, 0, 
                 wrccl.right - lrc.right+8, lrc.bottom - lrc.top, TRUE );
    MoveWindow ( hRich, 0, 0, rrccl.right+8, rrccl.bottom , TRUE );
    MoveWindow ( hList, 0, 0, lrc.right - lrc.left - 16, lrc.bottom - lrc.top - 16, TRUE );

    MoveWindow ( hStatusView, 0, wrccl.bottom - srccl.bottom - 85, 
                 wrccl.right, srccl.bottom, TRUE );
    MoveWindow ( hwndStatus, 0, 0, wrccl.right, srccl.bottom, FALSE );
    ShowWindow ( hWnd, SW_NORMAL );
    ShowWindow ( hwndStatus, SW_NORMAL );
    DWORD sidLen = GetEnvironmentVariable ( L "HOMEDRIVE ", startInDir, 255 );
    InitTreeViewItems ( hTree, startInDir );
    sidLen = GetEnvironmentVariable ( L "STARTINDIR ", startInDir, 255 );
    if ( 0 == sidLen ) {
      GetEnvironmentVariable ( L "USERPROFILE ", startInDir, 256 );
    }
    SetCurrentDirectory ( startInDir );
    GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
    sidLen = GetEnvironmentVariable ( L "SEARCHSTR ", searchStr, 255 );
    if ( 0 == sidLen ) {
      wsprintf ( searchStr, L "%s ", L "*.cpp;*.c " );
      SetEnvironmentVariable ( L "SEARCHSTR ", searchStr );
    }
    StringCchPrintf ( tStr, MAX_PATH, L "Searching for %s in   ", searchStr );
    SendMessage ( hwndStatus, SB_SETTEXT, 0, ( LPARAM ) tStr );
    SendMessage ( hwndStatus, SB_SETTEXT, 1 | SBT_POPOUT, ( LPARAM ) startInDir );
    SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, 
                                              ( LPARAM ) L "Number of Files......... " );
    SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, 
                                              ( LPARAM ) L "Line: 0 Column: 0......... " );
      MoveWindow ( hStatusView, 0, wrccl.bottom - srccl.bottom - 85, 
                                   wrccl.right, srccl.bottom, TRUE );
    MoveWindow ( hwndStatus, 0, 0, wrccl.right, srccl.bottom, FALSE );
    SendMessage ( hwndStatus, WM_SIZE, 0, 0 );
    ShowWindow ( hProgress, SW_NORMAL );
    SendMessage ( hProgress, PBM_SETMARQUEE, PBST_NORMAL, 0 );
    recursivefileSearch ( nodeCD, FALSE );
    SetFocus ( hList );
    SendMessage ( hProgress, PBM_SETMARQUEE, PBST_PAUSED - 3, 0 );
    ShowWindow ( hProgress, SW_HIDE );
    int iCount = ListView_GetItemCount ( hList );
    int result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
    StringCchPrintf ( tStr, MAX_PATH, L "Number of Files %d. ", iCount );
    SendMessage ( hwndStatus, SB_SETTEXT, 0, ( LPARAM ) startInDir );
    SendMessage ( hwndStatus, SB_SETTEXT, 1 | SBT_POPOUT, ( LPARAM ) tStr );
    StringCchPrintf ( tStr, MAX_PATH, L "%d of %d Files. ", result+1 , iCount );
    SendMessage ( hwndStatus, SB_SETTEXT, 2 | SBT_POPOUT, ( LPARAM ) tStr );
    SendMessage ( hwndStatus, SB_SETTEXT, 3 | SBT_POPOUT, 
                                              ( LPARAM ) L "Line: 0    Column: 0 " );
    SendMessage ( hwndStatus, WM_SIZE, 0, 0 );
    SetWindowText ( hWnd, L "Two Stage Search - Build File Set to SEARCH, 
    then SEARCH File Set for a strings! File List Initial 
    Build uses Path and Extensions used in previous invocation. " );
    SetWindowText ( hEdit, L "To SEARCH list, select a string in View panel. 
    Hold control key down and push  'F 'to begin search. 
    PF3 will find next occurrence. When last occurrence is found, 
    next file containing string is searched for. " );
    ListView_GetItemText ( hList, 0, 0, curntFname, MAX_PATH - 1 );
    ListView_GetItemText ( hList, 0, 2, curntPath, MAX_PATH - 1 );
    StringCchPrintf ( tStr, MAX_PATH, L "%s\\%s ", curntPath, curntFname );
    FillRichEditFromFile ( hRich, ( LPCTSTR ) &tStr );
    SetFocus ( hList );
    return 0;
  }
    switch (message)
    {

  case WM_SIZE:
  {
  UINT width = GET_X_LPARAM ( lParam );
  UINT height = GET_Y_LPARAM ( lParam );
    RECT erc = { 0 }, erccl = { 0 }, rrc = { 0 }, rrccl = { 0 }, 
                        lrc = { 0 }, lrccl = { 0 }, trc = { 0 }, 
      trccl = { 0 }, wrc = { 0 }, wrccl = { 0 }, brc = { 0 }, 
                        brccl = { 0 },  src = { 0 }, srccl = { 0 };
    HWND hMainWnd = FindWindow ( L"TWOSTAGESEARCH", 0 );
    hTopView = FindWindowEx ( hMainWnd, 0, L"#32770", 0 );
    hEdit = getThisWindowHandle ( hTopView, IDC_EDIT2 );
    hFormView0 = FindWindowEx ( hMainWnd, hTopView, L"#32770", 0 );
    hCombo1 = getThisWindowHandle ( hFormView0, IDC_COMBO1 );
    hCombo2 = getThisWindowHandle ( hFormView0, IDC_COMBO2 );
    hBottomView = FindWindowEx ( hMainWnd, hFormView0, L"#32770", 0 );
    hTree = getThisWindowHandle ( hBottomView, IDC_TREE1 );
    hStatusView = FindWindowEx ( hBottomView, 0, L"#32770", 0 );
    hwndStatus = FindWindowEx ( hStatusView, 0, L"MSCTLS_STATUSBAR32", 0 );
    hFormView3 = FindWindowEx ( hBottomView, hStatusView, L"#32770", 0 );
    hFormView2 = FindWindowEx ( hBottomView, hFormView3, L"#32770", 0 );
    hFormView1 = FindWindowEx ( hBottomView, hFormView2, L"#32770", 0 );
    hList = FindWindowEx ( hFormView2, 0, L"SYSLISTVIEW32", 0 );
    hTree = FindWindowEx ( hFormView1, 0, L"SYSTREEVIEW32", 0 );
    hRich = FindWindowEx ( hFormView3, 0, L"RICHEDIT50W", 0 );
    if ( 0 == hTree ) {  return 0;}
    if ( 0 == hList ) {  return 0;}
    if ( 0 == hEdit ) {  return 0;}
    if ( 0 == hRich ) {  return 0;}
    if ( 0 == hFormView0 ) {return 0;}
    if ( 0 == hFormView1 ) {return 0;}
    if ( 0 == hFormView2 ) {return 0;}
    if ( 0 == hFormView3 ) {return 0;}
    MoveWindow ( hBottomView, 0, 90, width, height, TRUE );
    GetClientRect ( hBottomView, &brccl );
    GetWindowRect ( hBottomView, &brc );
    GetClientRect ( hFormView3, &rrccl );
    GetWindowRect ( hFormView3, &rrc );
    GetClientRect ( hWnd, &wrccl );
    GetClientRect ( hWnd, &erccl );
    GetWindowRect ( hWnd, &erc );
    GetWindowRect ( hFormView0, &erc );
    GetClientRect ( hFormView1, &trccl );
    GetWindowRect ( hFormView1, &trc );
    GetWindowRect ( hFormView2, &lrc );
    GetWindowRect ( hWnd, &wrc );
    GetClientRect ( hFormView2, &lrccl );
    GetWindowRect ( hFormView3, &rrc );
    GetClientRect ( hRich, &rrccl );
    GetWindowRect ( hStatusView, &src );
    GetClientRect ( hStatusView, &srccl );
    OffsetRect ( &erc, -wrc.left, -wrc.top );
    OffsetRect ( &trc, -wrc.left, -wrc.top );
    OffsetRect ( &lrc, -wrc.left, -wrc.top );
    OffsetRect ( &rrc, -wrc.left, -wrc.top );
    OffsetRect ( &src, -wrc.left, -wrc.top );
    OffsetRect ( &brc, -wrc.left, -wrc.top );
    
    if ( wParam == SIZE_RESTORED ) {
      MoveWindow ( hTopView, 0, 40, wrc.right, 50, TRUE );
      MoveWindow ( hFormView0, 0, 0, wrc.right, 30, TRUE );
      MoveWindow ( hEdit, 10, 0, wrc.right, 30, TRUE );
      SendMessage ( hWnd, message, SIZE_MAXIMIZED, lParam );
    }
    if ( wParam == SIZE_MAXIMIZED || wParam == SIZE_MAXSHOW ) {
      MoveWindow ( hTopView, 0, 40, wrc.right, 50, TRUE );
      MoveWindow ( hFormView0, 0, 0, wrc.right, 30, TRUE );
      MoveWindow ( hEdit, 10, 0, wrc.right, 30, TRUE );
      MoveWindow ( hList, 0, 0, lrc.right - lrc.left - 16, lrc.bottom- lrc.top-16, TRUE );
      MoveWindow ( hFormView1, 0, 0, trc.right - trc.left, height-brc.top+12, TRUE );
      MoveWindow ( hFormView2, trc.right - trc.left - 8, 0, 
                     lrc.right - lrc.left, height - brc.top+16, TRUE );
      MoveWindow ( hTree, trccl.left, trccl.top, 
                     lrc.left - trc.left, height-brc.top+12, TRUE );
      MoveWindow ( hFormView3, lrc.right-8, 0, 
                     width - lrc.right+8, height - brc.top+16, TRUE );
      MoveWindow ( hRich,       0, 0, width - lrc.right+8, height-brc.top+16, TRUE );
      MoveWindow ( hStatusView, 0, lrc.bottom - lrc.top+8, 
                    brc.right-brc.left, srccl.bottom, TRUE );
      MoveWindow ( hwndStatus, 0, 0, wrccl.right, srccl.bottom, TRUE );
      if ( wParam == SIZE_MAXIMIZED ) {
        SendMessage ( hWnd, message, SIZE_MAXSHOW, lParam );
      }
    }
    return 0;
  } // case WM_SIZE:
  break;


  case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
      wmEvent = HIWORD ( wParam );
      // For menu items lparam is zero
      // Parse the menu selections:
            switch (wmId)
            {
      case ID_FILE_CD:
      {
        hEdit = getThisWindowHandle ( hWnd, IDC_EDIT2 );
        hTree = getThisWindowHandle ( hWnd, IDC_TREE1 );
        if ( hEdit && GetWindowTextLength ( hEdit ) < 255 ) {
          SetWindowText ( hWnd, L "   USE CURSOR KEYS to Navigate in the TREEVIEW, 
          select a folder path, then push ENTER to search path. " );
          SetWindowText ( hEdit, L " " );
          StringCchCopy ( extensionCue, 259,
                  L "Navigate in the TreeView using the CURSOR Keys,  " );
          StringCchCat ( extensionCue, 259,
                   L "Left will close an open folder but Right will open a folder.  " );
          StringCchCat ( extensionCue, 259,
                   L " Push  'ENTER ' Key to begin build. " );
          Edit_SetCueBannerTextFocused ( hEdit, extensionCue, TRUE );
          SetFocus ( hTree );
          return 0;
        }
      }
      break;

      case ID_FILE_TEMP:
      {
        hEdit = getThisWindowHandle ( hWnd, IDC_EDIT2 );
        hTree = getThisWindowHandle ( hWnd, IDC_TREE1 );
        if ( hEdit && GetWindowTextLength ( hEdit ) < 255 ) {
          SetWindowText ( hWnd, L "   USE CURSOR KEYS to Navigate in the TREEVIEW, 
          select a folder path, then push ENTER to save path temporarily. " );
          SetWindowText ( hEdit, L " " );
          StringCchCopy ( extensionCue, 259,
                  L "Navigate in the TreeView using the CURSOR Keys,  " );
          StringCchCat ( extensionCue, 259,
                   L "Left will close an open folder but Right will open a folder.  " );
          StringCchCat ( extensionCue, 259,
                   L " Push  'ENTER ' Key to select folder. " );
          Edit_SetCueBannerTextFocused ( hEdit, extensionCue, TRUE );
          SetFocus ( hTree );
          return 0;
        }
      }
      break;

      case ID_FILE_CHANGE:
      {
        hEdit = getThisWindowHandle ( hWnd, IDC_EDIT2 );
        hTree = getThisWindowHandle ( hWnd, IDC_TREE1 );
        if ( hEdit && GetWindowTextLength ( hEdit ) < 255 ) {
          SetWindowText ( hWnd, L "   USE CURSOR KEYS to Navigate in the TREEVIEW 
          to select a folder path, then push ENTER. The path will be saved permanently " );
          SetWindowText ( hEdit, L " " );
          StringCchCopy ( extensionCue, 259,
                  L "Navigate in the TreeView using the CURSOR Keys,  " );
          StringCchCat ( extensionCue, 259,
                   L "Left will close an open folder but Right will open a folder.  " );
          StringCchCat ( extensionCue, 259,
                   L " Push  'ENTER ' Key to select folder. " );
          Edit_SetCueBannerTextFocused ( hEdit, extensionCue, TRUE );
          SetFocus ( hTree );
          return 0;
        }
      }
      break;

      case ID_EDIT_CMD:
      {
        hEdit = getThisWindowHandle ( hWnd, IDC_EDIT2 );
        SetWindowText ( hEdit, L "#&cmd /f:on /k set prompt=&P&_&l&D&G&L&T&G && 
        ver && echo Path Completion is turned ON. For diewctory use control + 
        D. Control + Shift + F for Files. " );
        sendMeMyKey ( hEdit, VK_RETURN );
        return 0;
      }
      break;

      case ID_VIEW_RESET:
      case ID_VIEW_FO:
      {
        hEdit = getThisWindowHandle ( hWnd, IDC_EDIT2 );
        if ( wmId == ID_VIEW_RESET ) {
          SetWindowText ( hEdit, L "#@WHOAMI /USER /FO CSV /NH |clip " );
        }
        else  {
          SetWindowText ( hEdit, L "##WHOAMI /USER /NH |clip " );
        }
        // This command  "wmic USERACCOUNT get sid|findstr  "1001 "|clip "
        // or this one   "wmic USERACCOUNT get sid|find  "1001 "|clip " " will
        // produce  a string ending with  2 spaces and 2 crlf line endings
        // This could be used by setting  L '\0 ' six bytes before end of line
        // as is done with  '#@whoami ' to cut the suffix from the string.
        sendMeMyKey ( hEdit, VK_RETURN );
        return 0;
      }
      break;

      case IDM_ABOUT:
                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
                break;

      case ID_HELP_EXTENSIONS:
        DialogBox ( hInst, MAKEINTRESOURCE ( IDD_HELP_EXT ), hWnd, AboutExt );
        break;

      case IDD_HELP_STRING:
        DialogBox ( hInst, MAKEINTRESOURCE ( IDD_HELP_STRING ), hWnd, AboutString );
        break;

      case IDD_HELP_PATH:
        DialogBox ( hInst, MAKEINTRESOURCE ( IDD_HELP_PATH ), hWnd, AboutPath );
        break;

      case IDD_HELP_CHECKBOX:
        DialogBox ( hInst, MAKEINTRESOURCE ( IDD_HELP_CHECKBOX ), hWnd, AboutCheckBox );
        break;

      case IDD_HELP_CMD:
        DialogBox ( hInst, MAKEINTRESOURCE ( IDD_HELP_CMD ), hWnd, AboutCMD );
        break;

      case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;

  case WM_INITMENU:
  {
    HMENU hMenu = GetMenu ( hWnd ); // == the application menu
    HMENU hsubmenu = GetSubMenu ( hMenu, 1 ); // == the Edit item of the menu
    BOOL bsmdi = SetMenuDefaultItem ( hsubmenu, ID_EDIT_CMD, FALSE );
    DWORD dwerr = GetLastError ( );
    return 0;
  }
  break;


  case WM_SYSCOMMAND:
  {
    switch ( LOWORD ( wParam ) & 0xfff0 ) {
    case SC_KEYMENU:
      if ( ( !GetMenu ( hWnd ) ) ) {
        HMENU hMenu = LoadMenu ( hInst, MAKEINTRESOURCE ( IDC_TWOSTAGESEARCH ) );
        SetMenu ( hWnd, hMenu );
        mnh = GetSystemMetrics ( SM_CYMENU );//MeNu Height
        UpdateWindow ( hWnd );
        return 0;
      }
      else {
        SetMenu ( hWnd, NULL );
        mnh = 0;
        UpdateWindow ( hWnd );
        return 0;
      }
    default:
      return DefWindowProc ( hWnd, message, wParam, lParam );
    }
  }
  break;

  case WM_ACTIVATE:
  {
    if ( LOWORD ( wParam ) & WA_ACTIVE ) {
      hWndFocus = GetFocus ( );
      hList = getThisWindowHandle ( hWnd, IDC_LIST1 );
      if ( hWndFocus ) {
        SetFocus ( hWndFocus );
        return 0;
      }
      else {
        hWndFocus = hList;
        SetFocus ( hList );
        if ( hList && ListView_GetItemCount ( hList ) >= 1 ) {
          if ( ListView_GetNextItem ( hList, -1, LVNI_SELECTED ) == -1 ) {
            // select this one
            ListView_SetItemState ( hList, 0, LVIS_SELECTED, LVIS_SELECTED );
          }
        }
        return 0;
      }
    }
    else
      return -1;
  }break;

    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            BeginPaint(hWnd, &ps);
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
  return DefWindowProc ( hWnd, message, wParam, lParam );
}

InitTreeViewItems:为它们提供路径

此函数接收 2 个参数,一个指向 Tree Control 的句柄和 `HOMEDRIVE`。它将 `ROOT` 项添加到 Tree Control,然后获取逻辑驱动器字符串并调用 `AddItemToTree` 函数,将每个驱动器添加到 `Tree` 并为每个驱动器调用 `getDirectories` 函数以填充第一级,从而产生 `TreeView` 的按需填充行为并展开 `HOMEDRIVE`。这是应该包含开始于目录的项,展开它会导致 `TVN_ITEMEXPANDING` 消息被发送到 `TreeView` 控件,然后是 `TVN_ITEMEXPANDED` 消息。当控件返回时,`HOMEDRIVE` 已被填充并展开到开始于目录。接下来,它选择高亮显示的树节点,该节点应该是开始于目录。由该例程调用的另外两个函数也由 `TreeView` 重复调用。

InitTreeViewItems 函数

// FUNCTION: Populates treeview with logical drives and one level of sub-directories by calling 
// AddItemToTree and getDirectories for each drive. Begins process to find startindir
BOOL InitTreeViewItems ( HWND hwndTV, TCHAR * startPoint ) {
  // Define the ROOT of each drive 
  HTREEITEM nodeParent;
  HTREEITEM hti; // the root folder item to insert.
  TV_ITEM tvi = { 0 };
  // Defibe the temporary drive string 
  TCHAR szDTemp [ ] = TEXT (  " :\\ " ); // first position will contain drive letter.
  // Define string to hold Drive List.
  TCHAR szTemp [ 512 ];
  // add a root item to the tree 
  hti = ( HTREEITEM ) TVI_ROOT; //initialize root item
  if ( GetLogicalDriveStrings ( 512 - 1, szTemp ) ) { // szTemp contains list of drives 
    TCHAR* p = szTemp;   // point p at  'A ' in  'A:\\\0C:\\\0...\0\0 '
    TCHAR* d = szDTemp;  // point d at blank space in  ' :\\\0 '
    do {
      // loop through string of drives
      *d = *p; // copy 1 tchar from p to d 
      nodeParent = AddItemToTree 
      ( hwndTV, hti, szDTemp, 0, 1 ); // add a root item to the tree at level 2
      getDirectories ( nodeParent, szDTemp ); //  add it 's subdirectories at level 2
      if ( hti == NULL )              // you couldn 't add the item
        return FALSE;                 // go back and tell them it failed
      if ( NULL == nodeParent ) {
        return FALSE;                 // go back and tell them it failed
      }
      else if ( *d == *startPoint ) {
        TreeView_Expand ( hwndTV, nodeParent, TVE_EXPAND );
      }
      while ( *p++ ); // loop until p points at a  '\0 ', incrementing p afterwards. 
    }
    while ( *p );     // end of string when p points at second  '\0 ' of pair.
  }
  else
    return FALSE;     // go back and tell them you couldn 't get drive string.

  nodeParent = TreeView_GetSelection ( hwndTV );
  TreeView_Select ( hwndTV, nodeParent, TVGN_CARET );
  return TRUE;        // go back and tell them it 's okay.
}

AddItemToTree:填充分支

关于这个例程没什么好说的,毕竟它只是初始化 `TreeView_InsertItem` 宏的参数。但仔细看看它在做什么。tvins 结构的一个成员是 `hInsertAfter`。这个成员被设置为 `hPrev`。这意味着 `hPrev` 必须在调用之间保持其状态,因此我们将其设为 `static` 变量。如果它不是 `static`,`hPrev` 始终是 `TVI_FIRST`。这将导致 `Tree` 按**降序**排序,最后一项在前,第一项在后。

AddItemToTree 函数

//
// FUNCTION: Inserts an item into the tree as a child of hDir, after the 
// previously inserted item if it is a sibling. If not a sibling, previously 
// inserted item is ignored. 
HTREEITEM __inline AddItemToTree 
( HWND hwndTV, HTREEITEM hDir, LPTSTR lpszItem, int tviiImage, int tviiSelectedImage ) {
  TVITEM tvi;
  TVINSERTSTRUCT tvins;
  static   HTREEITEM hPrev = ( HTREEITEM ) TVI_FIRST;
  tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
  tvi.pszText = lpszItem; // Set the text of the item. 
  tvi.cchTextMax = 0; // set this to size of buffer when getting test label. Here it is ignored
  tvi.iImage = tviiImage;
  tvi.iSelectedImage = tviiSelectedImage;
  tvins.item = tvi;
  tvins.hInsertAfter = hPrev;
  tvins.hParent = hDir; // Set the parent item
  hPrev = TreeView_InsertItem ( hwndTV, &tvins ); // Add the item to the tree-view control. 
  return hPrev;
}

getDirectories:并将其挂在树上

此函数接受 2 个参数,一个 `HTREEITEM` 和一个 `LPTSTR`。它使用 `FindFirstFileExW` - `FindNextFile` 序列循环来获取 `HTREEITEM` `hDir` 的所有子项。它为每个项调用 `AddItemToTree`,设置参数以显示适当的图像,指示文件或文件夹。

getDirectories 函数

// FUNCTION: FindFirstFile in specified directory then repeatedly 
// FindNextFile until there aint no mo(aren 't any left to find).  
// Sets image index to show folder or not a folder, adds both to tree. 
void getDirectories ( HTREEITEM hDir, LPTSTR lpszItem ) {
  HANDLE hFind;
  WIN32_FIND_DATA win32fd;
  TCHAR szSearchPath [ _MAX_PATH ];
  LPTSTR szPath = lpszItem;
  HWND hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
  HWND hTree = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_TREE1 );

  if ( szPath [ wcslen ( szPath ) - 1 ] != L '\\ ' ) {
    wsprintf ( szSearchPath, L "%s\\* ", szPath ); // concat in text buffer
  }
  else {
    wsprintf ( szSearchPath, L "%s* ", szPath ); // concat in text buffer
  }

  if ( ( hFind = FindFirstFile ( szSearchPath, &win32fd ) ) == INVALID_HANDLE_VALUE ) return;
  do {
    if ( win32fd.cFileName [ 0 ] != L '. ' ) {
      if ( ( win32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 ) {
        AddItemToTree ( hTree, hDir, win32fd.cFileName, 0, 1 );
      }
      else {
        AddItemToTree ( hTree, hDir, win32fd.cFileName, 4, 5 );
      }
    }
  }
  while ( FindNextFile ( hFind, &win32fd ) != 0 );
  FindClose ( hFind );
}

editBoxProc 及其“分身”:editBoxSubClassProc

`editBoxProc` 是一个非常简单的函数。`WM_INITDIALOG` 消息处理程序创建控件并将 `hEdit` 的窗口 `Subclass` 设置为 `editBoxSubClassProc`。第三个参数 `IDC_EDIT2` 是控件的 id,但这里是 `uIdSubclass` 参数,它可以是任何 `UINT_PTR`,只要它能唯一地标识子类过程即可。第四个参数是 `dwRefData`,它在每次调用时都会传递给过程。`WM_INITDIALOG` 还建立了 `hFormView0(hDlg)` 的位置并将 `hEdit` 移入其中。`WM_COMMAND` 消息仅处理 `WM_SETFOCUS` 消息以将焦点设置在 `ListView` 上。`WM_SIZE` 在必要时调整控件的大小。

editBoxProc 函数

// Subclass the proc and handle vk_f3, vk_f6 and vk_f6. handle vk_return to 
// invoke system commands.
INT_PTR CALLBACK editBoxProc ( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) {
  UNREFERENCED_PARAMETER ( lParam );
  int wmId, wmEvent;
  static HWND hwndEdit2;
  HWND hEdit = NULL, hMainWnd = NULL,  hList = NULL;
  
  if ( WM_INITDIALOG != message ) {

    hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
    if ( 0 == hMainWnd ) {  return 0;}
    hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
    if ( 0 == hEdit ) {  return 0;}
  }
  
  switch ( message ) {

  case WM_INITDIALOG:
  {
    hEdit = FindWindowEx ( hDlg, 0, L "Edit ", 0 );
    if ( 0 == hEdit ) {

      RECT rc = { 0 }; // 20, 1380, 20

      GetClientRect ( hDlg, &rc );
      hEdit = CreateWindowExW ( WS_EX_CLIENTEDGE, // extended styles
                    L "EDIT ",          //control  'class ' name
                    L " ",              //control caption
                    //control style
                    WS_CHILD | WS_VISIBLE | 
                    ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL, // | ES_WANTRETURN,
                    10,                           //position: left
                    42,                           //position: top
                    rc.right,                     //width
                    25,                           //height
                    hDlg,                         //parent window handle
                    //control 's ID
                    ( HMENU ) ( IDC_EDIT2 ),
                    hInst,                        //application instance
                    0 );                          //user defined info
      //BOOL bTrue = 
      SetWindowSubclass ( hEdit, editBoxSubClassProc, ( UINT_PTR ) IDC_EDIT2, 0 );
      RECT eRc = { 0 };
      GetWindowRect ( hDlg, &eRc );
      MoveWindow ( hDlg, 0, 10, eRc.right, 30, TRUE );
      MoveWindow ( hEdit, 10, 0, eRc.right, 23, TRUE );
      return FALSE;
    }
  }

  case WM_COMMAND:
    wmId = LOWORD ( wParam );
    wmEvent = HIWORD ( wParam );

    switch ( wmId ) {
    case IDC_EDIT2:
      if ( HIWORD ( wParam ) == WM_SETFOCUS ) {
        hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 );
        if ( 0 == hList ) {
          return 0;
        }
        if ( ListView_GetItemCount ( hList ) >= 1 )
          if ( ListView_GetNextItem ( hList, -1, LVNI_SELECTED ) == -1 ) {
            SetFocus ( hList );
            // select first one
            ListView_SetItemState 
            ( hList, -1, 0, LVIS_DROPHILITED | LVIS_SELECTED | LVIS_FOCUSED );
            ListView_SetItemState ( hList, 0, LVIS_SELECTED, LVIS_SELECTED );
            return 0;
          }
      }
      break;
    default:
      break;
    }
    break;

  case WM_SIZE:
  {
    UINT width = GET_X_LPARAM ( lParam );
    UINT height = GET_Y_LPARAM ( lParam );
    if ( hEdit ) {
      MoveWindow ( hEdit, 0, 0, width, height, TRUE );
    }

    return 0;
  }break;
  }
  return ( INT_PTR ) FALSE;
}

另一方面,`editBoxSubClassProc` 稍微复杂一些。在 `uMsg switch` 中,`WM_GETDLGCODE` 处理程序监听 `wParam` 等于 `VK_F3`、`Vk_F5`、`VK_F6` 和 `VK_RETURN`。对于所有这些,它返回 `DLGC_WANTMESSAGE`,阻止控件 `hEdit` 执行这些键的默认处理。相反,我们将处理它们。对于功能键,我们发送 `VK_F3` 和 `VK_F6` 到 `Richedit`,`VK_F5` 发送到 `ListView`。

对于 `VK_RETURN`,我们从 `hEdit` 获取窗口文本。将前两个字符与“`#!”进行比较,当它们相等时,它们会被剥离,并使用其余文本调用 `_tsystem` 函数。用户看到的内容取决于他们输入的命令。例如,命令“time”,他们会看到带有当前时间的控制台窗口,并提示输入新时间,但如果命令是“`time /t”,它会执行得如此之快,以至于他们只会看到命令的返回代码在 hEdit 中。在这种情况下,用户可以在命令末尾添加 && pause 以保持控制台打开。

将前两个字符与“`#@”进行比较,当它们相等时,它们会被剥离,并使用其余文本调用 `_tsystem` 函数。文本应该是由 `Reset_CheckBox` 菜单选择写入的。下一步是等待一小段时间让命令完成,然后将结果粘贴到 `hEdit` 中并再次获取窗口文本。这是“`WMIC /USER /FO CSV /NH |clip”命令的输出。它包含以逗号分隔的 `username` 和 `SID`。我们在逗号处拆分 `string`。`SID` 用双引号括起来,所以要去掉第一个,我们就给指针加一。要去掉最后一个,我们用字符零覆盖它。我们使用 `SID` 来构造注册表项到 CheckBox Flag,并将其放入环境变量中。然后我们构造注册表查询来查找重置标志条目并删除它。Reg Delete 会询问您是否确定。查询和删除由“`#!”运行,它将返回代码写入 `hEdit` 窗口。用户看到的是带有确认提示的控制台窗口,以及 `hEdit` 中的命令返回代码。

强制重置 SHMessageBoxCheck 复选框

Uncheck the check box in the SHMessageBoxCheck message box

取消选中 SHMessageBoxCheck 消息框中的复选框

将前两个字符与“`##”进行比较,当它们相等时,它们会被剥离,并使用其余文本调用 `_tsystem` 函数。文本应该是由 `Force_Reset_CheckBox` 选择写入的。下一步是等待一小段时间让命令完成,然后将结果粘贴到 `hEdit` 中并再次获取窗口文本。这是“`WMIC /USER /NH |clip”命令的输出。它包含以空格分隔的 `username` 和 `SID`。我们在空格处拆分 `string`。我们使用 `SID` 来构造注册表项到 `CheckBox` Flag,并将其放入环境变量中。然后我们构造注册表查询来查找重置标志条目并删除它。Reg Delete 使用“`/;F”开关,它强制删除并且不询问您是否确定。查询和删除由 `_tsystem` 运行,以获取返回代码并通过 `SHMessageBoxCheck` 消息框告知用户成功或失败。

`ID_EDIT_CMD` 菜单选择使用 `hEdit` 窗口文本中的前缀“`#&”来启动命令控制台。它不使用 `_tsystem`,而是使用 `ShellExecute` 函数。cmd.exe 以 `/f` 开关启动并执行“`ver”内置函数来显示 Windows 版本,就像常规控制台一样。它设置提示符并显示消息“`Path Completion is on...”。包含此菜单项的原因是,如果用户尝试使用“`#!”启动控制台,Two Stage Search 将变得无响应,直到控制台退出。此控制台在提示符中显示日期和时间。一旦您学会了如何使用它,路径补全将非常有用。

editBoxSubClassProc 函数

// The EditBox was subclassed to intercept and relay vk_f3, vk_f5 and vk_f6 when the
// EditBox had keyboard focus. It also receives vk_return to invoke system commands.
LRESULT CALLBACK editBoxSubClassProc 
( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData ) {
  UNREFERENCED_PARAMETER ( uIdSubclass );
  UNREFERENCED_PARAMETER ( dwRefData );
  HWND hMainWnd = FindWindow ( L "TwoStageSearch ", 0 );
  HWND hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1 );
  HWND hEdit = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT2 );
  HWND hRich = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_EDIT1 );
  TCHAR searchStr [ MAX_PATH ] = { 0 };
  TCHAR nodeCD [ MAX_PATH - 1 ] = { 0 };
  TCHAR fsiaf [ 256 ] = { 0 };  // Find this String In A File

  switch ( uMsg ) {
  case WM_GETDLGCODE:

    if ( lParam ) {
      LPMSG lpmsg = ( LPMSG ) lParam;
      if ( lpmsg->message == WM_KEYDOWN )
        switch ( lpmsg->wParam ) {
        case VK_F3:
        case VK_F5:
        case VK_F6:
        case VK_RETURN:
          return DLGC_WANTMESSAGE;
          break;
        default:
          return DefSubclassProc ( hWnd, uMsg, wParam, lParam );
        }
      break;
    }
    break;

  case WM_KEYDOWN:
    switch ( wParam ) {
    case  VK_F3:
    {
      return sendMeMyKey ( hRich, VK_F3 );
    }
    break;

    case  VK_F5:
    {
      return sendMeMyKey (hList, VK_F5 );
    }
    break;

    case  VK_F6:
    {
      return sendMeMyKey ( hRich, VK_F6 );
    }
    break;

    case  VK_RETURN:
    {
      TCHAR titleText [ 256 ] = { 0 };
      TCHAR tStr [ 256 ] = { 0 };
      int retCode = 0;
      GetWindowText ( hEdit, titleText, 255 );
      if ( wcsncmp ( titleText, L "#! ", 2 ) == 0 ) {
        TCHAR * cmdLine = titleText; cmdLine += 2;
        TCHAR   rgky [ 256 ];
        retCode = _tsystem ( cmdLine );
        StringCchPrintf ( rgky, 255, L "Return Code was %d ", retCode );
        SetWindowText ( hEdit, rgky );
        return 0;
      }
      if ( wcsncmp ( titleText, L "#@ ", 2 ) == 0 ) {
        TCHAR * cmdLine = titleText; cmdLine += 2;
        TCHAR   rgky [ 256 ], *prgky;
        _tsystem ( cmdLine );
        Sleep ( 10 );
        SetWindowText ( hEdit, L " " );
        SendMessage ( hEdit, WM_PASTE, 0, 0 );
        GetWindowText ( hEdit, rgky, 255 );
        TCHAR * token = wcstok_s ( rgky, L ", ", &prgky );
        token = wcstok_s ( nullptr, L " ", &prgky );

        ++token; token [ wcslen ( token ) - 1 ] = L '\0 ';

        StringCchPrintf ( rgky, 255, L 
        "hku\\%s\\Software\\Microsoft\\Windows\\
                  CurrentVersion\\Explorer\\DontShowMeThisDialogAgain ", 
        token );
        SetEnvironmentVariable ( L "rgky ", rgky );
        StringCchCopy ( titleText, 255, L "#!reg query %rgky% /f 
        {51842606-D64C-4EDE-AF3F-4EE7AD3755A3} /e &&reg delete %rgky%  " );
        StringCchCat ( titleText, 255, L " 
        /v {51842606-D64C-4EDE-AF3F-4EE7AD3755A3}&&pause " );
        GetEnvironmentVariable ( L "rgky ", fsiaf, 255 );
        SetWindowText ( hEdit, titleText );
        sendMeMyKey ( hEdit, VK_RETURN );
        return 0;
      }
      if ( wcsncmp ( titleText, L "## ", 2 ) == 0 ) {
        TCHAR * cmdLine = titleText; cmdLine += 2;
        TCHAR   rgky [ 256 ], *prgky;
        _tsystem ( cmdLine );
        Sleep ( 10 );
        SetWindowText ( hEdit, L " " );
        SendMessage ( hEdit, WM_PASTE, 0, 0 );
        GetWindowText ( hEdit, rgky, 255 );
        TCHAR * token = wcstok_s ( rgky, L "  ", &prgky );
        token = wcstok_s ( nullptr, L " ", &prgky );
        StringCchPrintf ( rgky, 255, L "hku\\%s\\Software\\Microsoft\\
        Windows\\CurrentVersion\\Explorer\\DontShowMeThisDialogAgain ", token );
        SetEnvironmentVariable ( L "rgky ", rgky );
        StringCchCopy ( titleText, 255, L "reg query %rgky% /f 
        {51842606-D64C-4EDE-AF3F-4EE7AD3755A3} /e &&reg delete %rgky%  " );
        StringCchCat ( titleText, 255, L " /v {51842606-D64C-4EDE-AF3F-4EE7AD3755A3} /f " );
        retCode = _tsystem ( titleText );
        if ( retCode == 0 ) {
          SetWindowText ( hEdit, L "MessageBox CheckBox Successfully RESET " );
          SHMessageBoxCheck ( 0, L "MessageBox CheckBox 
          Successfully RESET ", L "MessageBox CheckBox Successfully RESET ",
                    MB_OK | MB_ICONINFORMATION | MB_APPLMODAL, IDOK, 
                    L "{51842606-D64C-4EDE-AF3F-4EE7AD3755A3} " );
        }
        else {
          SetWindowText ( hEdit, L "MessageBox CheckBox FAILED To RESET " );
          SHMessageBoxCheck ( 0, L "MessageBox CheckBox 
          FAILED To RESET (It 's not set!). ", 
          L "MessageBox CheckBox FAILED To RESET ",
                    MB_OK | MB_ICONINFORMATION | MB_APPLMODAL, 
                    IDOK, L "{51842606-D64C-4EDE-AF3F-4EE7AD3755A3} " );
        }
        return 0;
      }
      if ( wcsncmp ( titleText, L "#& ", 2 ) == 0 ) {
        TCHAR * cmdLine = titleText; cmdLine += 2;
        GetCurrentDirectory ( 255, nodeCD );
        ShellExecute ( NULL, L " ", 
        L "cmd.exe ", cmdLine, nodeCD, SW_SHOW );
        SetWindowText ( hEdit, L '\0 ' );
        return 0;
      }

      return TRUE;
    }
    break;
    }
  default:
    return DefSubclassProc ( hWnd, uMsg, wParam, lParam );
  }
  return 0;
}

保持状态:我现在在哪里?

SET 和 SETX 之间的关系

首次使用 Two Stage Search 时,文件系统中没有预先存在要搜索的位置或要查找的文件扩展名。`ListView` 可以留空,用户可以选择根路径和文件扩展名集来构建文件列表。但是,已经做出了任意选择来作为程序功能的示例。如果用户自己做出选择,那么下次使用 Two Stage Search 时,他们选择的值应该被保留。有许多方法可以保存这些值,允许它们被调用。这里选择的方法是使用“SETX ”函数。问题是,“`SETX”不是 Windows 函数,它是 *CMD.EXE* 的内置函数。它需要使用 `ShellExecuteEx`,并将 `SHELLEXECUTEINFO` 结构初始化为所需信息,`cmdToExeCute` 必须包含 `SETX` 动词、变量名及其值。如果值包含空格,则必须用双引号括起来。语法类似于“`SET”动词,但逗号被空格替换。'`SET' 动词或 `SetEnvironmentVariable` Windows 函数将信息写入程序正在使用的环境变量块。'`SRTX' 动词将信息写入注册表。这意味着直到程序重新启动后才能使用“`SET”或 `GetEnvironmentVariable` 检索新值。如果您想立即检索它,您必须使用 `GetEnvironmentVariable 保存它。通过同时使用“`SET”和“`SETX”,我们可以跨函数调用和跨程序调用来持久化程序的状态。使用 `ShellExecuteEx` 来获取 *CMD.EXE* 的返回代码,因为“`SETX ”如果参数正确,总是会成功,但这并不意味着值已保存。我们从命令获取输出以显示给用户,但我们也获取进程返回代码以报告命令可能失败。

persist_This 函数

// This function writes to the Registry using the Batch SETX
// command to save environment variables permanently, until 
// they are changed by the User. Also, the SHMessageBoxCheck
// function writes to the Registry to prevent future displays
// of the message box. Refer to Menu Command VIEW>Reset_CheckBox
// to restore display of message
DWORD WINAPI  persist_This ( TCHAR * tStr ) {
  TCHAR cmdToExeCute [ 512 ] = { 0 };
  // Below is a sample output from both a successful and
  // an unsuccessful function execution. Notice that 
  // SETX sets ERRORLEVEL to zero either way, or possibly
  // doen 't set it at all. All we can do is give the 
  // process enough time to finish before we close the 
  // handle and report any error code returned by ShellExecuteEx
  //
  // --- Successful Execution.Notice the path is enclosed in  quotes.
  //  SUCCESS: Specified value was saved.
  //  errorlevel is 0
  //  Command Line was  "C:\Windows\System32\cmd.exe "  
  //  /k Setx STARTINDIR  "C:\Users\All Users "
  //  errorlevel is %ERRORLEVEL%
  //  Command Line was %CMDCMDLINE%
  //
  // ---Un-Succeddful Execution. Notice the path is not enclosed in quotes.
  //  This is seen as a third default parameter by SETX
  //  ERROR: Invalid syntax. Default option is not allowed more than  '2 ' time(s).
  //  Type  "SETX /? " for usage.
  //  errorlevel is 0
  //  Command Line was  "C:\Windows\System32\cmd.exe "  
  //  /k Setx STARTINDIR C:\Users\All Users
  //  errorlevel is %ERRORLEVEL%
  //  Command Line was %CMDCMDLINE%
  //
  // Get a temporary path 
  TCHAR nodeCD [ MAX_PATH - 1 ] = { 0 };
  GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
  TCHAR tempFname [ MAX_PATH ];
  TCHAR tempPath [ MAX_PATH ];
  TCHAR tempStr [ 1024 ] = { 0 };// buffer for setx output
  CHAR setXresults [ 1024 ] = { 0 };// buffer for setx output
  TCHAR resultFileName [ MAX_PATH ];
  DWORD dwResult = GetTempPath ( MAX_PATH, tempPath );
  UINT uiResult = 0;
  // The length of the temp path(result) must be between 0 and MAX_PATH
  if ( dwResult < MAX_PATH || dwResult != 0 )
    // Get a temp file name based on temp path and  'search '
    uiResult = GetTempFileName ( tempPath, L "persist_This ", 0, tempFname );
  if ( uiResult == 0 ) {
    // if this user can 't get temp file name, 
    GetEnvironmentVariable ( L "USERPROFILE ", tempPath, 255 );
    // and create your own temp file name
    StringCchPrintf ( resultFileName, 255, L "%s\\result1.txt ", tempPath );
  }
  else {
    StringCchPrintf ( resultFileName, 255, L "%s ", tempFname );
  }
  #pragma region Line Continuation

  StringCchCopy ( tempStr, 511, tStr );
  StringCchCat ( tempStr, 511, L " >> %s &  " );
  StringCchCat ( tempStr, 511, L "echo errorlevel is %%ERRORLEVEL%% >> %s &  " );
  StringCchCat ( tempStr, 511, L "echo Command Line was:  " );
  StringCchCat ( tempStr, 511, L "%%CMDCMDLINE%% >> %s " );
  StringCchPrintf ( cmdToExeCute, 511, tempStr, tempFname, tempFname, tempFname );
  #pragma endregion for Command to Execute String

  SHELLEXECUTEINFO ShExecInfo = { 0 };
  ShExecInfo.cbSize = sizeof ( SHELLEXECUTEINFO );
  ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NOASYNC;
  ShExecInfo.hwnd = NULL;
  ShExecInfo.lpVerb = NULL;
  ShExecInfo.lpFile = L "cmd.exe ";
  ShExecInfo.lpParameters = cmdToExeCute;
  ShExecInfo.lpDirectory = nodeCD;
  ShExecInfo.nShow = SW_HIDE;
  ShExecInfo.hInstApp = NULL;
  ShellExecuteEx ( &ShExecInfo );
  DWORD dwCode = STILL_ACTIVE;
  BOOL gotReturn = FALSE;
  for ( size_t i = 0; i < 100 && dwCode == STILL_ACTIVE; ++i ) {
    if ( ShExecInfo.hProcess ) gotReturn = GetExitCodeProcess ( ShExecInfo.hProcess, &dwCode );
    MSG msg; while ( PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE ) ) DispatchMessage ( &msg );
    Sleep ( 10 );
  }  //  process is still active
  // if process is still active it will eventually return 0
  if ( STILL_ACTIVE == dwCode ) dwCode = 0;
  if ( 0 != dwCode ) {
    StringCchPrintf ( cmdToExeCute, 255, L "Return Code is %d. 
    File System ERROR.\nDATA was not be saved. Try it again! ", dwCode );
    MessageBox ( 0, cmdToExeCute, L "persist_This Return Code ",
           MB_OK | MB_ICONEXCLAMATION | MB_APPLMODAL );
  }
  CloseHandle ( ShExecInfo.hProcess );
  HANDLE resultFile = NULL;
  DWORD dwRead = 0;
  resultFile = CreateFile ( tempFname, GENERIC_READ, 0, NULL,
                OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  BOOL bSuccess = ReadFile ( resultFile, setXresults, MAX_PATH, &dwRead, NULL );
  if ( !bSuccess || dwRead == 0 ) {
    DWORD dwErr = GetLastError ( );
    StringCchPrintf ( cmdToExeCute, 255, L "Return Code is %d.
    \nThe data was not saved!\nMaybe you should do it again. ", dwErr );
    MessageBox ( 0, cmdToExeCute, L "persist_This Results File Read Failed! ",
           MB_OK | MB_ICONEXCLAMATION );
  }
  if ( dwRead != 0 ) {
    StringCchPrintf ( cmdToExeCute, 255, L "%S ", setXresults );
    TCHAR * pCmd = cmdToExeCute;
    for ( size_t i = 0; i < 4; i++ ) {
      pCmd = wcschr ( ( LPWSTR ) pCmd, L '\r ' );
      pCmd++;
    }
    *pCmd = L '\0 ';
    SHMessageBoxCheck ( 0,   cmdToExeCute, L "SET and 
    PERSIST this ENVIRONMENT VARIABLE Return Code ",
           MB_OK | MB_ICONINFORMATION | MB_APPLMODAL, IDOK, 
           L "{51842606-D64C-4EDE-AF3F-4EE7AD3755A3} " );
    if ( uiResult>0 ) {
      StringCchPrintf ( cmdToExeCute, 255, L "%s\\per*.tmp ", tempPath );
      DeleteFile ( cmdToExeCute );
    }

  }
  CloseHandle ( resultFile );
  return 0;
}

EnumChildWindows 和 EnumChildProc:保留窗口句柄的状态

窗口句柄由 Create Window 函数初始化,因此我们无法使用“`SETX”来持久化它们。句柄是窗口的一部分,所以我们只需要找到窗口来获取句柄。要查找主窗口,我们可以使用 `FindWindow`,它带有类名或窗口名称,内联调用。我们可以使用 `FindWindowEx` 来查找子窗口,但 `EnumChildProc` 是一个更简单的方法。但这确实需要编写一个函数来调用它。实际上有两个函数,但仍然是最简单的方法。序列中的第一个函数是 `getThisWindowHandle`,它接受父窗口句柄和该父子窗口的控件 ID,并返回该子窗口的句柄。它使用一个包含句柄和 uint 控件 ID 的 `struct` 变量。`getThisWindowHandle` 将句柄成员设置为 `NULL`,将 `uint` 成员设置为控件 ID。现在它调用 `EnumChildWindows`,参数是父句柄,`EnumChildProc` 的地址(强制转换为 `WNDENUMPROC`)以及包含控件 ID 的 `struct` 的地址。返回时,结构包含子窗口句柄,该句柄可以为 `NULL`。`EnumChildWindows` 是一个枚举函数。它的工作方式类似于“for (child in parents tree) do child processing”,为它找到的每个子句柄调用 `EnumChildProc`。`EnumChildProc` 调用 `GetDlgCtrlID` 并传入子窗口的句柄,并返回该控件的 ID。如果该 ID 与 `struct` 中的 ID 匹配,则将子窗口句柄复制到 `struct` 的窗口句柄中,并将 `b_isNotDone` 设置为 `false`,这会结束枚举。控制权传递给 `getThisWindowHandle`,它将子窗口句柄返回给调用者。这里是 `struct` 和两个函数。

getThisWindowHandle 和 EnumChildProc 函数

// Struct to Identify Window by CTRL-ID
typedef struct  ecwData {
  HWND hCWind;
  UINT iD;
} ecpWnd, lpecpWnd;
//  EnumChildProc is called repeatedly by EnumChildWindows function
// it returns FALSE or cannot find any more child windows of the HWND
// that EnumChildWindows gave it. In this version of EnumChildProc,
// we find the control id of the Child Window enumerations and compare
// it to our controlId. If it matches we set the HWND of our ECPWND
// structure to the hwndChild of the enumeration and set b_isNotDone
// to false. We return b_isNotDone to EnumChildWindows, which stops 
// calling EnumChildProc so that the bext statement is executed and
// our ECPWND may now contain the Child HWND or zero. Therefore we
// have to test thr HWND to satisfy the Structured Attributes Language
// specification because we don 't want SAL to be dis-satisfied.
BOOL WINAPI EnumChildProc ( HWND hwndChild, LPARAM  lPar ) {
  lpecpWnd * pecw = (lpecpWnd*)(LPARAM )lPar;
  BOOL b_isNotDone = TRUE;
  ULONG dlgId = GetDlgCtrlID (hwndChild );
  if ( dlgId == pecw->iD ) {
    pecw->hCWind = hwndChild;
    b_isNotDone = FALSE;// signal end of enumeration
  }
  return b_isNotDone;
}

// put an ECPWND structure on the stack and put it 's address
// in a pointer variable and call the EnumChildWindows function
// with the hWnd, the address of the EnumChildProc and controlId.
HWND WINAPI getThisWindowHandle ( HWND hWnd, UINT  controlId ) {
  ecpWnd ecw;
  ecw.hCWind = NULL;
  ecw.iD = controlId;
  lpecpWnd * pecw = &ecw;
  EnumChildWindows ( hWnd, ( WNDENUMPROC ) EnumChildProc, ( LPARAM ) pecw );
  return ecw.hCWind;
}

Sample getThisWindowHandle 函数调用

  HWND hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1);
  if (hLidt == 0) return 0;
  

连接查看器和列表:sendMeMyKey 函数

`richEditProc` 在 `WM_NOTIFY` 消息处理程序中的几个地方调用一个名为 `sendMeMyKey` 的函数,并且似乎停止了处理。实际上,它已经将球(执行)传给了另一名跑步者。`sendMeMyKey` 函数接受 2 个参数,一个窗口句柄和一个虚拟键码,返回一个 `BOOL`。它使用 `SendInput` 函数将输入事件数组插入到您提供给 `sendMeMyKey` 的窗口的输入流中。它不向窗口发送消息。它在具有焦点的窗口中合成事件。因此,它首先做的是将焦点设置到您发送的窗口,然后发送包含虚拟字符的按下和释放键的数组。如果输入作为 2 个单独的事件发送,其他程序有时会抢占焦点,导致问题,其中最严重的是程序死锁。它的工作方式几乎就像用户设置了焦点然后键入键一样,但这会受到 UIPI 阻止的影响,因此您只能向同等或更低完整性级别的应用程序发送输入。

sendMeMyKey 函数

// Set the focus to intended target abd send it a keydown and keyup sequence
BOOL __forceinline sendMeMyKey ( HWND hFocus, unsigned char pfKey ) {
  SetFocus ( hFocus );
  INPUT keyPr [ 2 ] = { 0 };
  keyPr [ 0 ].type = INPUT_KEYBOARD;
  keyPr [ 0 ].ki.dwFlags = 0; // KEYEVENTF_KEYDOWN?;
  keyPr [ 0 ].ki.wScan = 0;
  keyPr [ 0 ].ki.time = 0;
  keyPr [ 0 ].ki.dwExtraInfo = 0;
  keyPr [ 0 ].ki.wVk = pfKey;

  keyPr [ 1 ].type = INPUT_KEYBOARD;
  keyPr [ 1 ].ki.dwFlags = KEYEVENTF_KEYUP;
  keyPr [ 1 ].ki.wScan = 0;
  keyPr [ 1 ].ki.time = 0;
  keyPr [ 1 ].ki.dwExtraInfo = 0;
  keyPr [ 1 ].ki.wVk = pfKey;
  SendInput ( 2, keyPr, sizeof ( INPUT ) );
  return TRUE;
}

FillRichEditFromFile:如何将文件加载到 RichEdit

当收到按键或鼠标单击时,执行的最常见操作之一是将文件加载到 `RichEdit` 中。`FillRichEditFromFile` 函数从 `listViewProc` 的五个不同点和一个从 `WndProc` 调用。所有这些调用(除了来自 `WndProc` 的调用)都是在按键或鼠标单击的结果下发生的。因此,将其在此处讨论该函数似乎是合适的,在 `sendMeMyKey` 之后。它是 Microsoft 编写和版权的。它接收两个参数,`RICHEDIT` 实例的句柄以及要加载的文件的路径和名称,如果成功,则创建一个 `EDITSTREAM` 结构,其中包含 `EditStreamCallback` 函数的地址和指向先前创建的文件句柄的 `dword` 指针。它向 `RICHEDIT` 控件发送一个 `EM_STREAMIN` 消息,将 `wParam` 设置为 `SF_TEXT`,并将 `EDITSTREAM` 强制转换为 `LPARAM`。如果 `sendmessage` 返回非零且结构中的 `dwerror` 成员为零,则将 `fSuccess` 设置为 `TRUE` 并关闭文件句柄。`EditStreamCallback` 函数被调用多次以读取整个文件。Microsoft Visual Studio Online 文档文章“如何使用流”是这两个函数的源文档。如果您需要更多信息,可以在那里找到。

使用 Two Stage Search 的技巧

第一次执行程序时,它会检查环境变量中是否有两个变量:`STARTINDIR` 和 `SEARCHSTR`。如果它们不存在,则将它们初始化为与您的 `USERPROFILE` 相同的值(对于 `STARTINDIR`)和“*.cpp;*.c”(对于 `SEARCHSTR`)。此后,这些值可以随时更改,并且该值将被写入注册表,因此在后续执行程序时会找到并使用它们。

更改 STARTINDIR:文件列表的根文件夹

要更改开始于目录,请选择“**文件**”菜单命令,然后选择 Change_Directory|Temp_Change_CD|CD_AND_Build 之一。第一个项目 `Change_Directory` 将允许您更改目录并将更改保存到注册表中。第二个项目将允许您更改当前执行的目录,但不会将其保存到注册表中。第三个项目也将更改目录,但不会将其保存到注册表中。对于这些项目中的每一个,`TreeView` 都会获得键盘焦点。使用光标键移动到所需的文件夹不会干扰 `ListView` 中的条目。当 Tree Control 接收到 **ENTER** 键时,它会将当前 Tree 节点设置为当前目录。这允许将多个根的文件包含在文件列表中。可以通过鼠标单击 Tree 节点,但这会清除 `ListView` 并用节点子项填充它。

更改 SEARCHSTR:用于文件列表构建的文件扩展名集

要更改包含在文件列表中的文件类型,请单击应用程序窗口左侧的 `Combobox EditCtrl` 或该 `Combobox` 的下拉按钮。按 **ENTER** 或单击“开始”按钮后,`Combobox` 消息处理程序会将 `EditCtrl` 的文本与 `SEARCHSTR` 的当前值进行比较,如果不同,则将新值保存在 `Registry` 中。如果 `EditCtrl` 为空,则检索并使用最后已知的值。

文件扩展名集的规则

文件名的单个后缀或结尾部分。它可以是整个文件名,但不能是缺少结尾部分的前半部分。

一个星号,后跟一个句点和一个文件类型扩展名

前面规则的一个或多个,用分号分隔,即 `*.a;*.b;*.c;*.d;`...等等。

如何输入要搜索的字符串

有两种不同的方法可以输入要搜索的字符串。在 Viewer Panel 中选择一个 `string`,然后按住 Control 键按下“`f ”。您可以选择一个不带空格的字符 `string,只需单击任何字符即可。要选择带有空格的 `string,请单击一端并将高亮显示拖到另一端。

另一种方法是单击应用程序窗口右侧的 `Combobox EditCtrl` 并键入 `string。`string` 会添加到 `Combobox List` 中,允许回忆起先前输入的 `string。`string` 被保存在 `Registry` 中,但新的 `string` 会替换旧的。

如何查找要搜索的字符串的下一个或上一个出现

要查找输入的字符串的下一个出现,请按 PF3。如果搜索到达文件末尾,它将搜索下一个文件(或多个文件),直到找到匹配项。如果到达文件列表末尾,它将环绕并从头开始搜索。如果按住 Shift 键,则搜索方向是向后。PF6 始终向后搜索。PF5 将查找包含匹配项的下一个文件。向后搜索并到达文件开头时,将找到上一个文件的最后一个出现。

如何用另一个应用程序打开文件

要使用“**打开方式**”对话框,请右键单击 `ListView` 中的文件。要将文件位置在文件资源管理器中打开,请双击左键,或者如果文件被选中,您可以按 **ENTER**。

如何在 Two Stage Search 中运行批处理命令

要执行 *cmd.exe* 命令或批处理文件,请单击组合框下方的 `EditBox` 控件,然后输入 '`#!' 后跟要执行的命令。这将打开一个控制台窗口来执行该命令。如果您想看到命令的输出,请在命令后输入 '`&& pause'。这会导致系统等待用户输入。不要试图永久保持此窗口打开。而是选择“**编辑>CMD 控制台**”,这将为您提供一个打开的控制台窗口,而不会冻结 Two Stage Search。

如何摆脱输入字符串时显示的烦人的消息框

每当在注册表中保存值时,都会显示SHMessageBoxCheck。由于它仅供参考,因此可以通过选中对话框左下角的Checkbox来消除它。一旦消除了恼人的提示消息,您仍可能被偶尔显示的错误消息框所困扰。发生这种情况时,新值可能未保存,因此您应该考虑重新输入该值。

如何重新显示恼人的消息框

在主菜单中,选择“View>Reset_CheckBox | Force_Reset_CheckBox”。第一个选项Reset_CheckBox将显示一个控制台窗口以请求确认。如果您改变了主意,请输入“n”取消请求,或输入“y”重置它。第二个选项Force_Reset_CheckBox不请求确认,它直接执行。

帮助菜单

帮助菜单有 6 个选项,它们是:

  1. 关于...____________ 两阶段搜索
  2. 关于扩展__ 如何更改扩展
  3. 关于字符串______ 如何输入要搜索的字符串
  4. 关于路径 ________ 如何更改文件列表构建的路径
  5. 关于复选框___ 如何重置复选框
  6. 关于 CMD.EXE____ 如何调用CMD.EXE控制台

这 6 个帮助面板,只需点击按钮即可随时访问,应提供足够的信息,使两阶段搜索足够易于使用,无需单独的帮助文件。如果足够多的读者不同意并想要一个帮助文件,我很乐意提供一个。

关注点

当我最初决定发布“最不频繁字符偏移算法”时,我考虑将其用于使用 SSE2 或 AVX2 指令的程序,但当我在旧计算机上进行测试时,它不起作用。该计算机没有这些指令。所以我拿出了一款旧程序U2Charfunc,它使用了 SSE 指令,并在仅具有 SSE 指令的 Windows 8.1 计算机上表现与 Windows 10 计算机一样好。所以我要使用旧程序。

中间的窗口,即RESIZING ListView窗口,在我看来比使用主窗口的客户端背景来模拟分隔条是一个更简单的概念。尤其是如果您想要超过 2 个窗口,例如 3 个或 9 个。对于 3 个面板,您确实必须阻止顶部和底部边框被移动,但这可以通过光标裁剪轻松实现。对于 9 个面板,您可以使用中心面板的所有 4 个边框来控制其他 8 个面板。对于 3 到 9 个面板之间的面板,您至少需要使用 1 个额外的可调整大小边框,如果您想使其可调整大小的话。

历史

  • 2019 年 8 月 30 日:首次发布
© . All rights reserved.