The String found by U2Charfunc, shown in its full context in the Viewer Panel
Introduction: The Explorer Imperative Two Stage Search using the Least Frequent Character Offset
What Is the Least Frequent Character Offset and Why Should I Care?
The Least Frequent Character Offset is the position of LFC in a pattern you want to find. Any character in the pattern could be used but by using the LFC, it will be found much less often. With SSE SIMD Instructions, the compare can examine sixteen bytes at a time. The first character of the pattern must occur at the correct prefixed offset to the LFC or they will both disappear when the SIMD registers are 'ANDED' together. This means the SSE intrinsics can scan a file in 16 byte increments, finding only those instances of the LFC that are at the proper offset from a character that matches the first character of the pattern being searched for. When the LFC and the 'other ' character are found, then a string
compare is executed to confirm or deny that a match has been found.
This program, U2Charfunc
, was written to run on a machine that had only the SSE Instructions or possibly only the headers for SSE. I am not sure at this point, and I no longer have that machine but I know that someone does and they can still use this program. SSE2 has some additional instructions that make it a little faster, notably the _mm_and_si128
which allows anding the 16 bytes as bytes whereas SSE has only the _mm_and_ps
which ands them as 32 bit floating point, so you can't use it to 'and' 2 16 byte SSE registers. In other words, you have to store them in an intermediate DWORD
and use a '&
' to 'and
' the intermediate results. This makes a tighter loop and a faster search possible with SSE2, but it only works if you have the SSE2 Instruction Set.
The LFC Algorithm uses a Control String and a short preprocessing routine to determine the LFC in the pattern you supply. The Control String contains the characters of the alphabet arranged in the order of their frequency, most frequent to least frequent in 'C ' and 'C++' on my computer. They were counted by U2Charfunc
. It will count characters, pairs of characters and strings, tested to a length of 64 characters. The preprocessing routine tries to find the last character in the LFCARRAY Control string
in the search pattern. It scans backwards until a character is found. This is the Least Frequent Character. It is the OFFSET of this character in the search pattern that is the LFC Offset used in the scanning loop. Once the LFC and its Offset are found, the file is opened and read. There are separate routines for a single character, two character and more than two character patterns. The single character routine uses one SSE register and pops the count of occurrences. The two character routine uses two registers but it also pops the count. Neither of these routines requires a compare to verify the match.
In the three or more character routine, the (MEMORY
) register is aligned on a 16 byte boundary and loaded aligned from the current file position, nWts
. These 16 bytes are compared to a 16 bytes containing copies of the first character (of the pattern) to match, called fCtm
, using _mm_cmpeq_epi8
and the resulting mask is moved to first character of match, fCom
. Now memory is loaded from nWts
plus the LFC Offset. This is compared to the 16 copies of the Least Frequent Character and the mask is moved to sCom
, the second character to match. Now fCom
and sCom
are anded, causing any 1 bits that aren't matched to disappear. If the anded results are zero, nWts
is increased by 16 and the window To scan, wTs
, is loaded from nWts.
When the results of the AND operation are not zero, we use _BitScanForward
to find the least significant bit that is set. This is the match Index or mIdx, which is added to nWts
to get the address of a possible match. Here, we use a _memicmp
function to do the compare and possibly increment a count. If you only wanted to know whether a file contained a string
and didn't care about the number of times it contained it, you could just return a flag to indicate that it did or at the end of the program return a flag to indicate that it didn't.
Here Is the Code for U2Charfunc as a Standalone Program. The 'main() ' Function Is Commented Out.
The u2Charfunc Function
#include <intrin.h>
#include <stdio.h>
#define MAXLINE 1024 * 4
#define MAXPATH 260
#pragma warning( disable: 4706)
int u2Charfunc ( CHAR * chBuf, char * pattern, int patlen ) {
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 );
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 ) {
char * nWts = line + 16;
__m128i wTs;
__m128i fCtm = _mm_set1_epi8 ( *pattern );
__m128i tempMask;
unsigned fCom = 0;;
unsigned aO = 15 & ( UINT_PTR ) nWts;
nWts -= aO;
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;
__m128i wTs;
__m128i fCtm = _mm_set1_epi8 ( scanChar );
__m128i sCtm = _mm_set1_epi8 ( scanChar2 );
unsigned fCom = 0;
unsigned sCom = 0;
unsigned sM = 0;
unsigned aO = 15 & ( uintptr_t ) nWts;
nWts -= aO;
wTs = _mm_load_si128 ( ( __m128i const * ) nWts );
while ( 1 ) {
if ( ( fCom = _mm_movemask_epi8 ( _mm_cmpeq_epi8 ( fCtm, wTs ) ) != 0 ) ) {
wTs = _mm_loadu_si128 ( ( __m128i const * )( nWts + maskOffset ) );
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 ) {
char * nWts = line + 16;
unsigned long mIdx = 0ul;
__m128i wTs;
__m128i fCtm = _mm_set1_epi8 ( scanChar );
__m128i sCtm = _mm_set1_epi8 ( scanChar2 );
__m128i tempMask;
unsigned fCom = 0;;
unsigned sCom = 0;
unsigned sM = 0;
unsigned aO = 15 & ( uintptr_t ) nWts;
nWts -= aO;
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;
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;
}
This program, U2Charfunc
, is the reason for this article. But I realize that only those developers that have a pre-existing interest in string
matching techniques will find this of interest. Therefore, I have included a short windows program to demonstrate a possible use of this program.
Background
Why another find utility? Simple. I don't like the ones I have available. I have tried many different products and I can 't find the one I need. There may be times when I need one of them and their capabilities but primarily I want to find a keyword and the context in which it is used. Most of the time, I want to see how I used it in my own code or maybe how someone else used it. I need this when I read the Visual Studio Documentation, which says can't find requested topic, or sometimes when it can find requested topic. I need it to not take my focus away from what I am trying to do. I don't want to take 30 minutes to figure out how to use it. I have tried to provide these features while trying to not let the program grow to large.
But This Is a Batch Program! What's It Got to Do With Windows?
This can only be a Batch program if you uncomment the 'main()
' function. Otherwise, it is a function in a Windows program. U2Charfunc
is not called by 'main()
' in a Windows program, instead it is called by the findStringInAllFiles()
function. This function takes no arguments and returns a BOOL
. The pattern to find is taken from the Environment Variable 'FSIAF
', which is the acronym for 'Find string in all files
'. The File Name and Path are retrieved from the currently selected item in the List View. The pattern length is determined by _snprintf_s
as the number of characters copied from fsiaf
to pattern. It gets the first path, which is the current item in ListView
. Then gets the item count and loops through the ListView
item count times, getting the next path, setting the window text for the main window and Edit Box and calling u2Charfunc(path, pattern, patlen)
. If the return value is greater than zero, the 'OCCURS
' Column in the ListView
is updated with the count
. Then it gets the next Item's path and peeks at the message queue so that the user won't cause the program to become un-responsive by attempting to multi-task. Then we loop until all of the items have been searched. When the loop finishes, the current item in the ListView
is the same as it was in the beginning. We send an F5 key to the ListView
, causing it to search the 'Occurs
' Column for the first file after the current one to contain an occurrence.
findStringInAllFiles() Function
BOOL findStringInAllFiles ( ) {
# pragma region Initialize_Storage
TCHAR searchCap [ 256 ];
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 };
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 ) );
ListView_GetItemText ( hList, i, 0, curntFname, MAX_PATH - 1 );
ListView_GetItemText ( hList, i, 2, curntPath, MAX_PATH - 1 );
memset ( &chBuf, 0, sizeof ( chBuf ) );
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 );
}
}
}
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;
}
Selecting or Entering the extensions for the File List.
Putting the File Name and Path into the ListView: recursivefileSearch() Function
Most developers have a primary language in which they code most of their work. This suggests they might want a 'default' type of file. We don't know what that is in advance, so the initial 'default ' is set to "*.cpp;*.c". This will result in the ListView
being filled with files having the extensions 'cpp
' and 'c
', with the root folder being the %USERPROFILE%
Environment Variable. Both of these 'defaults' can be changed and the new value will be stored in the registry using the setx
function. The set of extensions is selected or entered using the combo box on the left. The user can click on the dropdown row he wants or the EditCtrl
and type in their choice, then push ENTER or click the START Button on the left.
In the screen print above, the combo box dropdown shows a long string of extensions preceded by an asterisk and a period and separated by semicolons. The semicolon is necessary when more than one extension is used. The asterisk must be used unless you are searching for a specific title or suffix, such 'the Wind' might find 'Gone With the Wind' but not find 'The Wind blows Cold' because it looks at the ending of the argument. These three cases are handled by three different routines which are recursive. Since the cases can be differentiated by the presence of a semicolon and asterisk, we use a non recursive function to dispatch them. This makes the logic much simpler for the individual case but does require more code. Here is the code for the non recursive 'recursiveFileSearch
' function.
Choosing the Right Recursive File Search
The recursivefileSearch Function
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;
}
Looking for a Matching Suffix: recursivefileSearch1
Now, the criteria has been established to build the File List for the ListView
. We are just entering the function but we don't know how many times we have already been here. Since it could have been a while, we peek at the message queue to make sure we remain responsive. This is in a loop so we don't need to loop de loop. We make sure the path has a backslash on the end and add an '*
' to the ending. We create a handle to a find with FindFirstFileExW
. This fills a structure with information about the initial findings. If this is a file, we compare the suffix of the filename with our argument. If it matches, we put it in the ListView
. If it's a dot or dot dot, we ignore it. When it's a directory, we add the directory to the end of the path and put the path in the Title of the main window and the Edit Box. Then we call recursivefileSearch1
. We do this until FindNextFile
returns a zero instead of new information from the handle to the hFind
. Here's the code for the recursivefileSearch1
function.
The recursivefileSearch1 Function
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 '\\ ' ) {
wsprintf ( recursiveSearchPath, L "%s\\* ", name );
}
else {
wsprintf ( recursiveSearchPath, L "%s* ", name );
}
hFind = FindFirstFileExW ( recursiveSearchPath, FindExInfoBasic,
&ffd, FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH );
do {
if ( hFind == INVALID_HANDLE_VALUE )
return -1;
int fnlen = ( int ) wcslen ( ffd.cFileName );
int extlen = ( int ) wcslen ( searchStr );
if ( ( wcscmp ( ffd.cFileName, L ". " ) ) == 0
|| ( wcscmp ( ffd.cFileName, L ".. " ) ) == 0 ) {
;
}
else if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN && !hidden ) {
;
}
else if ( !( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) {
if ( wcsncmp ( ffd.cFileName + fnlen - extlen, searchStr, extlen ) == 0 )
{
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 ) {
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 );
}
}
while ( FindNextFile ( hFind, &ffd ) != 0 );
dwError = GetLastError ( );
if ( dwError != ERROR_NO_MORE_FILES )
if ( dwError != ERROR_ACCESS_DENIED ) {
SetWindowText ( hEdit, L "Error in Find File Routine ! " );
}
FindClose ( hFind );
return dwError;
}
Progress Bar covering combo boxes as recursiveFileSearch fills ListView
Listing Multiple Extensions:recursivefileSearch2()
The recursivefileSearch2()
function is very similar to recursivefileSearch1
. But there is one major difference. recursivefileSearch1
compares the argument with the end of the file name whereas recursivefileSearch2
uses the PathMatchSpec
function to determine if the filename extension is in the arguments set of extensions. If it is, it is added to the File List. Since everything else is the same, the code block is not shown here.
Splitting the Recursion:recursivefileSearch3
When there is only one extension and there is an asterisk in the argument, we can tell the FindFirstFileExW
function we only want to see files with this extension and no directories, by using the FindExSearchNameMatchflag
in the FINDEX_SEARCH_OPS
field. Then we loop through the results, putting them all in the File List, until FindNextFile
returns zero. Then we call FindFirstFileExW
again, this time using FindExSearchLimitToDirectories
as in the FINDEX_SEARCH_OPS
parameter to recurse through the directories. Since the logic is quite different, I will present it in its entirety. And here it is...
The recursivefileSearch3 Function
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 '\\ ' ) {
wsprintf ( recursiveSearchPath, L "%s\\*%s ", name, searchStr );
}
else {
wsprintf ( recursiveSearchPath, L "%s* ", name );
}
hFind = FindFirstFileExW ( recursiveSearchPath, FindExInfoBasic, &ffd,
FindExSearchNameMatch, NULL, FIND_FIRST_EX_LARGE_FETCH );
if ( hFind != INVALID_HANDLE_VALUE ) {
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 ) {
SetWindowText ( hEdit, L "Error in Find File Routine ! " );
}
FindClose ( hFind );
}
if ( name [ wcslen ( name ) - 1 ] != L '\\ ' ) {
wsprintf ( recursiveSearchPath, L "%s\\* ", name );
}
else {
wsprintf ( recursiveSearchPath, L "%s* ", name );
}
hFind = FindFirstFileExW ( recursiveSearchPath, FindExInfoBasic, &ffd,
FindExSearchLimitToDirectories, NULL, FIND_FIRST_EX_LARGE_FETCH );
do {
if ( hFind == INVALID_HANDLE_VALUE )
return -1;
if ( ( wcscmp ( ffd.cFileName, L ". " ) ) == 0
|| ( wcscmp ( ffd.cFileName, L ".. " ) ) == 0 ) {
;
}
else if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN && !hidden ) {
;
}
else if ( ffd.dwFileAttributes & FILE_ATTRIBUTE_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 );
}
}
while ( FindNextFile ( hFind, &ffd ) != 0 );
dwError = GetLastError ( );
if ( dwError != ERROR_NO_MORE_FILES ) {
if ( dwError != ERROR_ACCESS_DENIED ) {
SetWindowText ( hEdit, L "Error in Find File Routine ! " );
}
}
FindClose ( hFind );
return dwError;
}
Selecting or Entering a String to Search For: ComboBox2
When you change the Directory, Extension or string to search for, this message box is displayed.
When you see this message box, there is a checkbox
on the lower left corner of the message box. If the checkbox
is checked, the message box will no longer be displayed. The method to recover the message box will be discussed later. Currently, all three of these message boxes use the same GUID to control the state of the Checkbox
. The message box is purely informational, since the function used (SETX
) returns the same result even if it fails. There is additional error checking to inform the user if the data was not saved. The most likely time for this function to fail is the very first attempt of the day and will only matter if it is the last attempt. since only the last value set is the one retrieved.
When you change the Directory, Extension or string to search for, and the data is not saved, this message box is displayed.
Selecting the combobox EditCtrl on the RIGHT and Pushing ENTER will Retrieve the Last Value SAVED. Clicking the DROPDOWN Triangle Will Show the Values Entered Today
With the selected string in the EditCtrl, pushing ENTER has the same effect as clicking on the Start Search Button.
Both ComboBox
es are subclassed in the same comboBoxSubClassProc
function. It's a very simple function. The first thing it does is get the window handles for the main window and IDC_BTN1
and IDC_BTN2
. When uMsg
is WM_KEYDOWN
and wParam
is VK_RETURN
, it is known that it is coming from the child of a combobox
, so it gets the parent combobox
, which it uses to GetDlgCtrlID
to differentiate between the two ComboBox
es and sends a BM_CLICK
message to the corresponding Button
. This clicks the button and initiates the requested function. It also processes WM_KEYUP
and WM_CHAR
for WM_KEYUP
by returning zero. Everything is returned to the DefSubclassProc
function.
Handling the Enter Key: comboBoxSubClassProc
The comboBoxSubClassProc Function
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;
}
Handling the Input and Life-Cycle of Data: The Comboboxproc Function
The String found by U2Charfunc, shown in its full context in the Viewer Panel
The Extension in ComboBox1, the 'string to search for ' and the File It was Found in
The comboBoxProc
function processes the user input for the TwoStageSearch
. Before the 'message
' switch, it determines if the message is not WM_INITDIALOG
so it can retrieve the handles to all of the windows that WM_INITDIALOG
has created. The reason this is done is a constant cannot be used twice in case
statements. By using WM_INITDIALOG
in an 'if
' construct, it can still be used in a switch
case.
The handle for ComboBox1
, hCombo1
is used to prevent the initialization of some items until all items are ready for it. After the hCombo1
handle has been initialized in WndProc
, everything else can be done. We get the Combobox
Info and SetWindowSubclass
for the proc. This is the new process for sub-classing a control. As was shown previously, we are only processing the Return Key to generate a Mouse Click of a Button. The first Combo Box will generate a mouse click for the 'Start File List Build' Button. Next, we fill the Combo Box drop down List with some possible choices for the Set of Extensions for the build and initialize the Cue Banner for the Combo Box. For ComboBox2
, we do the same things except for the drop down List which we leave empty. Next, we give the controls an initial size and move them into the initial position. The Progress Bar is positioned to cover the Comboxes but is hidden.
In the WM_COMMAND
message we process the wmId
switch, the first case is IDC_BUTTON1
. Here, we respond to the BM_CLICK
message by getting the text of the Combobox EditCtrl
. If it's non- blank, it is added to the drop down List, otherwise, the extension set used last time is retrieved and placed in the EditCtrl
and zero is returned. In other words, if the EditCtrl
is empty and the CUE
is displayed, when the user clicks on the EditCtrl
or clicks the button on the left, the previous value is placed in the EditCtrl
. If there is a value in the EditCtrl
, it is compared to the previous extension set and if they are different, the new value is saved in the Registry by calling the persist_This
function. Either way, the Progress Bar is activated to cover the ComboBox
es and prevent user multi-tasking, the status bar is updated and the recursivefileSearch
is called to Build the File List. When it returns, the status bar is updated, the Progress Bar is hidden and the current file is selected.
Also in the wmId
switch, the next case is IDC_COMBO1
. The only event we respond to is CBN_EDITUPDATE
. We get the text of the EditCtrl
and check the first character, if it's a question mark, we blank out the field and set the CUE
Banner. The processing for IDC_COMBO0
is the same as for IDC_COMBO1
. IDC_BUTTON2
handles BM_CLICK
in the same way as IDC_BUTTON1
but calls the findStringInAllFiles
function instead of the recursivefileSearch
function.
The WM_SIE
message gets the position and size for all of the controls in the hFormView
container and moves them to their proper positions. The only problem child(window) was ComboBox2
. If I used the size from the GetWindowRect
function, it would sometimes show the combobox
button, and sometimes it wouldn't show it. In full screen, it always showed it but I don't think it would be reasonable to ask a user to go full screen when they wanted to click the drop down triangle. To make sure the drop down button was always displayed, I chose to make it a static value unless it was full screen, in which case I used the GetWindowRect
values.
The comboBoxProc Function
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 ( );
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 ( );
}
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 ( );
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 " " );
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 " " );
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 ( );
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;
}
Using the Viewer Panel: richEditProc
In the WM_INITDIALOG
message handler, the RichEdit
Control is in a window, hFormView3
which is used for sizing of the RichEdit
Control. The window hFormView3
is in a window, hBottom
, that is used for sizing hFormView1
, hFormView2
, hForView3
and hMiddle
. To prevent RichEdit
from being created in hBottom
but not in hFormview3
, we get the handles for the main window and hDlg
. Because hDlg
can be hBottom
or hFormView3, we differentiate between them by getting the parent of hDlg
. If that is the main window, we return 0
. We then make sure there is no RichEdit
before we load the "MSFTEDIT
" DLL which contains RICHEDIT50W
, which is used to create the RichEdit
Control. Next, we send RichEdit
an EM_SETEVENTMASK
telling it we want key events, mouse events and requestresize
. Then the zoom ratio is set so that the user can zoom the text font with the mouse wheel and ctrl key.
In the WM_THEMECHANGED
message handler, we need to correct a very bad(for me) color combination when the theme is changed during program execution. The other controls change their colors to an acceptable combination, but RichEdit50w
does not. We send it an EM_SETBKGNDCOLOR
with the wParam
set to one, telling it to set the color to the system color. With the wParam
set to 1
, it ignores the color you send it but you still have to send it a color. I don't know if this happens with other versions of RichEdit
or with other controls.
In the WM_NOTIFY
message handler, for the pMsgFilter
member msg equal to WM_KEYUP
, we get the position of the CARET
or if there is a selection, the first character of the selection. This is translated into the Line Number by the EM_EXLINEFROMCHAR
and EM_LINEINDEX
messages and the Column
by subtracting the beginning of the line from the CARET
position. Then the status bar is updated with the Line number and Column.
The member msg WM_LBUTTONDOWN
handler performs the same processing as WM_KEYUP
, that is getting the Line Number and Column for the status bar, But with WM_LBUTTONDOWN
, we also get the mouse position, convert it from Client coordinates to screen coordinates and save it in a static Point
variable named 'rpt
'. If the left button was clicked on a word, this Point may be used to select the word programmatically.
This is handled when pMsgFilter
member msg is WM_KEYDOWN
and wParam
is 'f
' and the control key is down. Again, we use EM_GETSEL
. This time, we compare the startsel
and endsel
to determine if we have a selection. If they are, we don't so we send a mouse double click to the Point
'rpt
'. When RichEdit
receives a mouse double click, it selects the word under the mouse. Then we send an 'f
' to RichEdit
which causes the same block of code to be executed because the control key is still down. This time there is a selection, so the 'else
' block is executed. Now an EM_GETSELTEXT
message is sent to RichEdit
and the text is copied to 'fsiaf
' and the Environment Variable is set. The value is also put into ComboBox2
. The word in RichEdit
is then highlighted.
The message handler for pMsgFilter
member msg
equals WM_KEYDOWN
and wParam
is VK_F3(F3)
and the static
variable keyshifted is set, we set the table entry keyst[VK_SHIFT]
to 0x80
, Set the Keyboard State, copying keyst
to the system table. This is done to prevent RichEdit
from capitalizing the word we are searching for.
The message handler for pMsgFilter
member msg
equals WM_KEYDOWN
and wParam
is VK_F3(F3)
and there is nothing in 'fsiaf
' the user may have left double clicked and pushed F3. Here, we get the selection and copy it into 'fsiaf
'. Now we determine if the SHIFT Key is down and if it is, we get the keyboard state table and put it in keyst
. Setting the keyst
entry for VK_SHIFT
to zero, we set the keyboard state table to keyst
and set the static
variable keyshifted to true
. Then we find the previous occurrence of the word and highlight it. If the SHIFT Key is not down, we simply find the next occurrence and highlight it. If nothing is found, then a VK_F5
is sent to the ListView
requesting the next file containing a match be found. VK_F17
is sent if the SHIFT Key is down. If the find
function was successful, we place the CARET in the middle of the screen. This requires we get the first character of the line containing the selection, chline
, the first visible line, fvline
. Scroll down a page to get the page size, scroll backup a page, divide page size by 2 and add it to fvline
, subtract that from chline
to find the number of lines to scroll. You also have the information to update the status bar. Piece of cake, piece of candy!
The message handler for pMsgFilter
member msg
equals WM_KEYDOWN
and wParam
is VK_F6(F6)
is provided as an alternative to the shift F3 processing. It always finds previous and when it fails, it send a VK_F17
to the ListView
to find the previous file containing a match. When it succeeds, the CARET
is moved to the middle of the screen and the status bar is updated.
The message handler for pMsgFilter
member msg equals WM_KEYDOWN
and wParam
is VK_F5(F5)
will simply send the key to ListView
.
The message handler for when pMsgFilter
member msg
is EN_REQUESTRESIZE
, if it gives me a size request, I'll give it the room.
The richEditProc Function
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;
hLib = LoadLibrary ( L "Msftedit.dll " );
hRich = CreateWindowExW ( WS_EX_CLIENTEDGE,
L "RICHEDIT50W ",
L " ",
WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_MULTILINE |
ES_NOHIDESEL | ES_WANTRETURN,
rc.left,
rc.top,
rc.right,
rc.bottom,
hDlg,
( HMENU ) ( IDC_EDIT1 ),
hInst,
0 );
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:
{
SendMessage ( hRich, EM_SETBKGNDCOLOR, 1, 0x008888ff );
return ( HRESULT ) FALSE;
}
case WM_NOTIFY:
{
wmId = LOWORD ( wParam );
wmEvent = HIWORD ( wParam );
TCHAR fsiaf [ 256 ] = { 0 };
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;
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;
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;
SetKeyboardState ( keyst );
}
#pragma endregion so release shift key
}
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 {
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;
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;
}
Finding the Next or Previous File with a Match: The listViewProc Function
In the message switch, the WM_INITDIALOG
message is received for hBottom
and hFormView2
. If it's hBottom
, we just get out. If it's hFormView2
, a font is created for a larger text size. Then the ListView
Control is created and the Column Headers are put in. Having created the ListView
, a WM_SETFONT
message is sent to it to set the font we created earlier. Now for a word about hFormView2
. Looking in the Resource View, you can see that it has a RESIZING Border. This is used instead of Emulating a Splitter Bar. The RESIZING
Border can give you a 3 by 3 grid if you want it but we want a 1 by 3 row from that grid. In order to do this, the Top and Bottom Borders of hFormView2
must be dis-abled. The Cursor
is clipped to achieve this. The sizing of all of the controls in hBottom
is controlled by hFormView2
and is performed in the WM_SIZING
message handler. There is an issue with using the RESIZING
Border instead of a splitter Bar. The issue is the size of the RECTS
received by the size control messages. I know how to fix the issue properly but for now, I just use hard coded values to give them the proper sizes.
The WM_COMMAND
message is used to process the WM_SETFOCUS
message. When an item is selected, it is highlighted here if the ListView
receives focus but nothing is selected, the first item in ListView
is selected and highlighted.
In the WM_NOTIFY
message handler, we get the value of fsiaf
from the environment block. We also get all of the window handles we might need. In the switch
for the LPNMHDR lParam
member code, we handle the code for NM_CLICK
by getting the path of the selected file and loading it into RichEdit
and update the status bar unless it is larger than 64 megabytes, in which case we use the Open with Dialog to open the file in another application.
The code for NM_DBLCLK
is handled by opening the folder or file in File Explorer with the item selected.
The code for NM_RCLICK
is handled by invoking the Open With Dialog. It does not display a context menu.
The code for LVN_KEYDOWN
is handled by creating a pointer to a NMLVKEYDOWN
structure and calling it pnkd
and initializing it to the lParam
cast to a LPNMLVKEYDOWN
'. The pnkd
member wVKey
will contain the virtual key code that was typed.
The wVKey
code for VK_DOWN
is handled by moving down one and loading that file into RichEdit
unless it is larger 64 megabytes.
The wVKey
code for VK_TAB
is handled by setting the focus on the RichEdit
Control, simulating a tab key.
The wVKey
code for VK_UP
is handled by moving up one and loading that file into RichEdit
unless it is larger than 64 megabytes.
The wVKey
code for VK_F5
and VK_F17*SHIFT F5)
is handled by getting the text for the current item's second Column, the 'Number of Occurrences' to determine if it contains fsiaf
. If it does, it calls U2Charfunc
to make sure and highlights the file name in ListView
, then places the highlighted line in the middle of the visible list, loads the file into RichEdit
and the RichEdit
find function is used to place the RichEdit CARET
on the match and scroll it to the middle of the screen.
When a next or previous file cannot be found in the ListView
Occurs Column (determined by counting the number of misses until it equals or exceeds the number of rows in the List View), if the number of rows is greater, it displays a message box informing that the next step could take a while. Then it calls findStringInAllFiles
search the File List itself to populate the Occurs Column with the counts. Next, it finds the next file containing a match and loads it into RichEdit
, positioning the highlights in the middle of the screen. Note this will only happen when the user has entered the string
to search for in the RichEdit
Viewer Panel.
When the number of hits exceeds the number of rows in the ListView
, a message box is displayed to inform the user that NO FILES containing the Requested STRING
were found, and suggesting that they check the spelling.
The wVKey
code for VK_F3
and VK_F6
is handled by sending the keys to the RichEdit
proc.
The wVKey
code for VK_F4
is handled by displaying the Properties for the file.
NM_RETURN
is a WM_NOTIFY
message. We process it the same as a NM_DBLCLK
with the file location being opened in File Explorer.
NM_SETFOCUS
is a WM_NOTIFY
message. We process it the same as a WM_SETFOCUS
. If there is anything in the ListView
, we make sure it's highlighted.
For the message WM_NCLBUTTONUP
, we cancel the ClipCursor
by setting it to NULL
so that the cursor can move freely.
For the WM_SIZING
message, we get the drag rectangle from the lParam
. We also get all of the window handles, window rectangles and client rectangles, and offset the window rectangles to make their positions relative to the main window. Then we determine which border is being moved. If it's not the left or right, then we clip the cursor to prevent vertical movement. The same formula works for the top and bottom. The cursor is only allowed to move from the left border to the right border in either direction. For the right border, we clip to prevent the user from covering the RichEdit
Viewer Panel with the ListView
. Then we move hFormView3
, the container for the RichEdit
Control, to its new position and fill it with RichEdit
. If it is the left border, we clip the cursor to prevent the ListView
from covering the entire treeview
and move the treeview
to its new size and position. For both the left and right border, we move the ListView Control
into hFormView2
.
The listViewProc 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 ];
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;
lvCol.pszText = ( LPWSTR ) L "Name ";
lvCol.cx = 0x96;
ListView_InsertColumn ( hList, 0, &lvCol );
lvCol.pszText = ( LPWSTR ) L "Number of Occurrences ";
lvCol.cx = 0x96;
ListView_InsertColumn ( hList, 1, &lvCol );
lvCol.pszText = ( LPWSTR ) L "Path ";
lvCol.cx = 0x64;
ListView_InsertColumn ( hList, 2, &lvCol );
lvCol.pszText = ( LPWSTR ) L "File Size ";
lvCol.cx = 0x072;
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 ( hList && ListView_GetItemCount ( hList ) >= 1 ) {
int selectedItem = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
if ( selectedItem == -1 ) {
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 };
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
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_DBLCLK:
{
#pragma region user_left_double_clicked_an_item
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_RCLICK:
{
#pragma region mouse_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 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 );
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);
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 );
nVirtKey = GetAsyncKeyState ( VK_CONTROL );
if ( wcscmp ( fsiaf, L " " ) != 0 ) {
do {
if ( nVirtKeySh & 0x8000|| ( pnkd )->wVKey == VK_F17) {
#pragma region shift_key_down
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
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
ListView_GetItemText ( hList, result, 1, numbOfOccurs, 255 );
numbOfOccurs [ 255 ] = 0;
if ( wcsstr ( (LPWSTR)numbOfOccurs, (LPWSTR)fsiaf ) != 0 ) {
ListView_GetItemText ( hList, result, 0, curntFname, MAX_PATH - 1 );
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 );
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 );
result = ListView_GetNextItem ( hList, -1, LVNI_SELECTED );
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 );
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 ) ) {
PeekMessage ( &msg, NULL, 0, 0, PM_REMOVE );
DispatchMessage ( &msg );
}
nVirtKey = GetAsyncKeyState ( VK_CONTROL );
if ( nVirtKey & -1 ) {
nVirtKey = GetAsyncKeyState ( 0x43 );
if ( nVirtKey & -1 )
return 0;
}
}
#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;
}
}
}
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;
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;
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
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;
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_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 );
return 1;
}
}
}
break;
}
}break;
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 ) {
ListView_SetItemState ( hList, 0, LVIS_SELECTED, LVIS_SELECTED );
}
}
return 0;
}
}
else
return -1;
}
break;
default:
break;
}
return 0;
}
How to Change the ROOT Folder for the File List: The Menu and the Tree View
The Windows program that the user sees resembles the Windows File Explorer superficially. It has a TreeView
on the left, used to navigate the file system and select a current directory path, a ListView
in the middle and a file viewer on the right side. But it's not intended for file management. Its only purpose is to find a string
in a file and display it in the context in which it is used. It finds forward with PF3 and finds backwards when PF3 is used with shift. It will also find backwards with PF6. When finding backwards, if no more occurrences can be found, it will find the first occurrence in the next containing a match or the last occurrence in a previous file with a match. You can immediately go to the next file with PF5 or the preceding file with PF5 and shift. There is other functionality in the program but it is there to support the find
functions.
The TwoStageSearch
Menu is very sparse, and that is intentional. In the screen shot below, you see the menu with Change Directory selected. The text in the Title Bar of the main window says 'Two Stage Search - Build ... '. In the slightly smaller screen shot on the right, we see the Title Text now says 'Use CURSOR KEYS to Navigate in the ... '. In the TreeView
, the user moves the selection from 'ASUS
' To 'All Users
'. The Edit Box contains 'c:\Users\All Users'. The status bar still says 'c:\Users\asus'. The last step is to push 'ENTER ' to accept the change. The user could just click on the folder of their choice, but doing this deletes the contents of the ListView
and populates it with the children of the selected folder, if any. To get files from more than one Root
, you must use the Menu
.
The significance of the Title Text is it is used by the TreeView
Proc to detect the request to change directories. When the focus is on the TreeView
and the user pushes 'ENTER', the NM_RETURN
handler gets the Title Text. It is compared to several different control string
s. If the control string
matches the Title Text, the corresponding request is executed. The Change Directory request will change the root folder and save it in the Registry. The second request, Temp_Change_CD
will temporarily change it and the third request, CD_AND_Build
will change it and build the file list.
Changing the Start In Directory: Click on the Menu
To Change the ROOT Folder or Start In Directory, click on the Menu and select Change_Directory
The TreeView Proc also Handles the Other Messages Sent to the TreeView Control
In the WM_INITDIALOG
message handler, we make sure that we're not in hBottom
itself, but are in hFormView1
. Then we create a font, create the treectrl
and send it the WM_SETFONT
message. Then we call SetTreeviewImagelist
and get the search root, startInDir
from the Environment Variable to set the current Directory. We set the TreeView
extended styles to double buffered in order to cut down on flickering. Then we show the main window.
The message handler for WM_NOTIFY
contains the switch for the (LP)NMHDR lParam
code for TVN_SELCHANGING
, which gets the NODE
's full path, back to the drive and ensures that it is visible.
The NMHDR lParam
code for TVN_SELCHANGED
checks if the action is TVC_UNKNOWN
and startInDir
is not blank, it populates and expands the tree nodes until startInDir
is located. If the action is TVC_BYKEYBOARD
, the current item is selected and highlighted. If the action is TVC_BYMOUSE
and current item is a folder, the contents of the folder replaces the ListView
contents. If it is not a folder, the item is added to the ListView
.
The NMHDR lParam
code for TVN_ITEMEXPANDING
populates the child nodes with files and folders, always and only one level deeper. This creates the populate on demand behavior of the TreeView
.
The NMHDR lParam
code for TVN_ITEMEXPANDED
will, if startInDir
is not blank, populate and expand the tree nodes until startInDir
is located. This will show the Root Folder for the File List Build as the selected TreeView
Item.
The NMHDR lParam
code for NM_CLICK
populates ListView
when node is already selected by keyboard and then clicked by mouse.
The NMHDR lParam
code for 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 Main Window to determine whether to save CD temporarily, permanently or just to use it to perform file list build, also gets environment variable for extension set.
The NMHDR lParam
code for TVN_KEYDOWN
will handle VK_TAB
and VK_F5
by setting the focus to ListView
.
The WM_SIZE
message handler will 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.
The treeViewProc Function
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 );
SetCurrentDirectory ( nodeCD );
GetCurrentDirectory ( 255, startInDir );
}
else {
StringCchPrintf ( nodeCD, 255, L "%s ", startInDir );
}
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:
{
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:
{
NMTREEVIEW * pnmtv;
pnmtv = ( LPNMTREEVIEW ) lParam;
tvi = ( ( pnmtv )->itemNew );
getNodeFullPath ( hTree, tvi.hItem );
TreeView_EnsureVisible ( hTree, tvi.hItem );
return FALSE;
} break;
case TVN_SELCHANGED:
{
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 ) {
GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
if ( _wcsnicmp ( startInDir, nodeCD, wcslen ( nodeCD ) ) == 0 ) {
if ( TreeView_GetItem ( hTree, &tvi ) ) {
if ( tvi.cChildren > 0 ) {
if ( tvi.state & TVIS_EXPANDEDONCE ) {
nmtvi = TreeView_GetChild ( hTree, nmtvi );
while ( 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 ) ) {
if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' ) {
wsprintf ( text, L "%s\\%s ", nodeCD, tvi.pszText );
}
else {
wsprintf ( text, L "%s%s ", nodeCD, tvi.pszText );
}
result = ( int ) wcslen ( text );
if ( sidLen == result && ( _wcsicmp
( startInDir, text ) == 0 ) ) {
SetCurrentDirectory ( nodeCD );
memset ( startInDir, 0, sizeof ( startInDir ) );
TreeView_Select ( hTree, nmtvi, TVGN_CARET );
TreeView_Select ( hTree, nmtvi, TVGN_DROPHILITE );
TreeView_Select ( hTree, nmtvi, TVGN_FIRSTVISIBLE );
SetFocus ( hTree );
return 0;
}
else {
wsprintf ( text, L "%s\\ ", text );
result = ( int ) wcslen ( text );
if ( _wcsnicmp ( startInDir,
text, result ) == 0 ) {
if ( tvi.cChildren > 0 ) {
if ( !( tvi.state & TVIS_EXPANDEDONCE ) )
{
TreeView_Expand ( hTree, tvi.hItem,
TVE_EXPAND );
return 0;
}
else {
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 {
return 0;
}
}
else {
nmtvi = TreeView_GetNextSibling ( hTree, nmtvi );
}
}
}
}
}
}
}
}
else {
return 0;
}
return 0;
}
}
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 ) {
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 ) {
ListView_GetItemText ( hList, nRet, 1, text, 255 );
if ( _wcsicmp ( text, nodeCD ) == 0 ) {
ListView_DeleteItem ( hList, ret );
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 = 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_ITEMEXPANDING:
{
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 ) {
TreeView_SelectItem ( hTree, tvi.hItem );
}
if ( ( pnmtv )->action & TVE_COLLAPSE ) {
tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvi.iImage = 0;
tvi.iSelectedImage = 1;
TreeView_SetItem ( hTree, &tvi );
return FALSE;
}
tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvi.iImage = 2;
tvi.iSelectedImage = 3;
TreeView_SetItem ( hTree, &tvi );
if ( tvi.state & TVIS_EXPANDEDONCE )
return FALSE;
tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
tvi.pszText = Text;
tvi.cchTextMax = 255;
if ( TreeView_GetItem ( hTree, &tvi ) ) {
if ( tvi.pszText [ 1 ] == L ': ' ) {
wsprintf ( nodeCD, L "%s ", Text );
SetCurrentDirectory ( nodeCD );
nmtvi = TreeView_GetChild ( hTree, tvi.hItem );
}
else {
memset ( &nodeCD, 0, sizeof ( nodeCD ) );
nmtvi = TreeView_GetChild ( hTree, tvi.hItem );
}
while ( nmtvi ) {
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 ) ) {
DWORD dwLen = GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
if ( dwLen == 0 || dwLen > MAX_PATH - 2 )
return 0;
if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' )
{
wsprintf ( nodeCD, L "%s\\ ", nodeCD );
}
wsprintf ( nodeCD, L "%s%s ", nodeCD, tvi.pszText );
getDirectories ( tvi.hItem, nodeCD );
nmtvi = TreeView_GetNextSibling ( hTree, tvi.hItem );
}
}
}
return FALSE;
} break;
case TVN_ITEMEXPANDED:
{
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;
tvi = ( ( pnmtv )->itemNew );
tvi.mask = TVIF_TEXT | TVIF_CHILDREN;
tvi.pszText = Text;
tvi.cchTextMax = 255;
if ( TreeView_GetItem ( hTree, &tvi ) ) {
if ( *startInDir ) {
memset ( &nodeCD, 0, sizeof ( nodeCD ) );
nmtvi = TreeView_GetChild ( hTree, tvi.hItem );
while ( nmtvi ) {
tvi.hItem = nmtvi;
DWORD dwLen = GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
if ( dwLen == 0 || dwLen > MAX_PATH - 2 )
return 0;
if ( TreeView_GetItem ( hTree, &tvi ) ) {
if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' )
{
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 ) )
{
SetCurrentDirectory ( text );
TreeView_Expand ( hTree, tvi.hItem, TVE_EXPAND );
return FALSE;
}
}
}
}
nmtvi = TreeView_GetNextSibling ( hTree, tvi.hItem );
}
}
}
}
return FALSE;
}
break;
case NM_CLICK:
{
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 );
ScreenToClient ( hwndFrom, &pt );
lpht.pt = pt;
TreeView_HitTest ( hwndFrom, &lpht );
SetFocus ( hTree );
HTREEITEM nmClickedtvi = lpht.hItem;
nmtvi = TreeView_GetSelection ( hwndFrom );
if ( nmtvi != nmClickedtvi )
{
return 0;
}
if ( lpht.flags && TVHT_ONITEM )
{
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 ) ) {
GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
if ( tvi.iImage == 4 ) {
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 );
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 ( 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 ) )
{
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 ( hwndFrom, 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 ( hwndFrom, &tvi ) );
}
}
}
}
}break;
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:
{
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 ) {
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 );
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 );
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 ) );
}
}
}
} break;
}
}
} break;
}
} break;
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 Function: For the Tree
These images are just for the TreeView
because it is assumed that most of the files in the ListView
will be of the same type and thus of a lower value in most cases. This certainly would not be true if the user selected a set of extensions with 5 or 6 members but the icon for the extension would add nothing to the search process.
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 );
return true;
}
getNodeFullPath: Find the Parents Recursively
This function accepts 2 parameters, a handle to the Tree Control and the handle to the current HTREEITEH
. Getting the item, it checks to see if the second character is a colon, found on in the ROOT
Item. If this condition is met, it sets the current directory and returns TRUE
, stopping the recursion, otherwise, it calls TreeView_GetParent
, getting the item 's parent node and calling getNodeFullPath
with the parent. Having completed the recursion, we concatenate the items text to the current directory with each return from the recursion. In effect, we reversed the order of the nodes as they were on the stack.
The getNodeFullPath Function
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 ': ' ) {
StringCchPrintf ( nodeCD, 255, L "%s ", tvi.pszText );
SetCurrentDirectory ( nodeCD );
SetWindowText ( hEdit, nodeCD );
return true;
}
else {
nmtvi = TreeView_GetParent ( hTree, tvi.hItem );
getNodeFullPath ( hTree, nmtvi );
if ( *nodeCD == 0 ) {
GetCurrentDirectory ( 255, nodeCD );
}
if ( tvi.pszText ) {
if ( nodeCD [ wcslen ( nodeCD ) - 1 ] != L '\\ ' ) {
StringCchPrintf
( nodeCD, 255, L "%s\\%s ", nodeCD, tvi.pszText );
}
else {
StringCchPrintf
( nodeCD, 255, L "%s%s ", nodeCD, tvi.pszText );
}
SetCurrentDirectory ( nodeCD );
SetWindowText ( hEdit, nodeCD );
}
}
}
return true;
}
The Main Window Procedure: WndProc
When we enter the WndProc
, we allocate all of the variables we need on the stack. Then we handle WM_CREATE
in an if
statement. As it turned out, the else
block was not needed, so it could have been in the message switch. The first dialog we create is the container hTopView
, the place where the controls that I thought would not have to be moved around were placed. This container window has one control and another dialog in it. The control is an Edit control, named hEdit
. The Dialog
is hFormView0
which contains two buttons, two Combo Boxes and a Progress Bar. The Progress Bar will fill hFormView0
completely when shown, preventing user input.
The next dialog to be created is hBottom
. It will contain hFormView1
, hFormview2
, hFormView3 hMiddle
. hFormView1
contains the TreeView
, hFormView2
is the RESIZIBG Window
and contains the ListView
, hFormView3
contains the RichEdit
Control and finally, hMiddle
contains the status bar. If all of these windows were created successfully, we create RECT
s for them and establish their initial sizes and move them into position and show the main window, hWnd
.
Now, we get the Environment Variable HOMEDRIVE
and call the InitTreeViewItems
function. Next, we get the Environment Variable STARTINDIR
if it exists, otherwise we get USERPROFILE
to set the current directory. We get SEARCHSTR
, which is the set of file extensions last used, or set searchStr
to '*.cpp;*.c' and set SEARCHSTR
. The Progress Bar is turned on and displayed before we call the recursivefileSearch
function. On return, the Progress Bar is hidden, the status bar is updated, the Caption is set, the first file in the ListView
is selected and loaded into the RichEdit
Viewer Panel, focus is set to the ListView
and WndProc
returns zero.
The WM_SIZE
message handler gets the window rectangle for hBottom
and moves it to left=0, top = 90, right=width, bottom=feight
. This causes hFormView
(1,2,3
,and hNiddle
) to be sent WM_SIZE
messages. This is done so that all of the windows that are re-sized by the RESIZING
Border of hFormView2
can be set properly. The top=90
is the combined height of the Menu
and hTopView
. The Controls in hTopView
are more or less stationary and therefore don't get moved as much as the hTree
, hList
and hRich
, so they only need to be SIZE_RESTORED
and SIZE_MAXIMIZED
. I also used a little trickery when wParam
was SIZED_RESTORED
, I sent the same message again but changed wParam
to SIZE_MAXIMIZED
and added a SIZE_MAXSHOW
message. This was done to eliminate some blocks of code that were just copies of the SIZE_MAXIMIZED
block.
In the WM_COMMAND
message switch, the ID_FILE_CD
command handler will set the Title Text for the main window to ' USE CURSOR KEYS
to Navigate
in the TREEVIEW
, select a folder path, then push ENTER
to search path. '. Then it will blank out hEdit
and set the CUE Banner and set focus to hTree
. The user can cancel the command by not pushing ENTER
, but if the user does push ENTER
, then the selected tree node will be searched and the files found will be added to the ListView
.
The ID_FILE_TEMP
command handler will set the Title Text for the main window to ' USE CURSOR KEYS
to Navigate in the TREEVIEW
, select a folder path, then push ENTER to save path temporarily.' Then it will blank out hEdit
and set the CUE Banner, set the focus to hTree
. The user can cancel the command by not pushing ENTER
, but if the user does push ENTER
, then the selected tree node will be saved to the Environment Block and the Current Directory will be set but the value will not be written to the Registry.
The ID_FILE_CHANGE
command handler will set the Title Text for the main window to 'USE CURSOR KEYS
to Navigate in the TREEVIEW
to select a folder path, then push ENTER
. The path will be saved permanently.' Then it will blank out hEdit
, set the CUE Banner and set the focus to hTree
. The user can cancel the command by not pushing ENTER
, but if the user does push ENTER
, then the selected tree node will be saved to the Environment Block and the Current Directory will be set and the value will be written to the Registry.
The IDM_EXIT
command under the File Menu destroys the main window at program termination.
The ID_EDIT_CMD
command sets the Window TEXT in hEdit
to '#&cmd/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. 'Then it sends a RETURN Key to hEdit
. hEdit
is subclassed to get the RETURN Key and process the Window Text., resulting in a CMD Prompt being started.
The ID_VIEW_RESET
and ID_VIEW_FO
commands are used to start the process to reset the Checkbox
in the SHMessageBoxCheck
message box. The command sets the Window Text in hEdit
to '#@WHOAMI /USER /FO CSV /NH | clip
' for ID_VIEW_RESET
and '##WHOAMI /USER /NH |clip
' for ID_VIEW_FO
. The hEdit
subclass proc uses the first two characters to determine whether to reset the Check Box nicely by asking 'Are You Sure?
' for '#@
' or just 'FOrce
' it for '##
'. Next we send a RETURN
Key to hEedit
.
The Help Menu Item displays a dropdown with the following commands:
The About.. command handled by IDM_ABOUT
. It displays the usual About Dialog.
The About Extensions command handled by ID_HELP_EXTENSIONS
. It displays a Dialog explaining the file extensions and how to set them.
The About Strings command handled by IDD_HELP_STRING
. It displays a Dialog explaining the string to search for and how to set them.
The About Path command handled by IDD_HELP_PATH
. It displays a Dialog explaining how to set the Path.
The About CheckBox command handled by IDD_HELP_CHECKBOX
. It displays a Dialog explaining how to reset the message box check box.
The About CMD.EXE command handled by IDD_HELP_CMD
. It displays a Dialog explaining how to USE the cmd.exe prompt.
The WM_INITMENU
message handler sets the default menu item for the Edit Menu command. This enables a double click to activate the default
The WM_SYSCOMMAND
message handler removes or restores the Menu when user pushes the ALT Key.
The WM_ACTIVATE
message handler sets the focus to the ListView
.
The Help Menu Command is About more than 'About '
The Sub Menus explain 'about ' how to do things in TwoStageSearch
The WndProc Function
LRESULT CALLBACK WndProc ( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) {
int wmEvent;
int mnh = 0;
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;
}
break;
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
wmEvent = HIWORD ( wParam );
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 " );
}
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 );
HMENU hsubmenu = GetSubMenu ( hMenu, 1 );
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 );
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 ) {
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: Give them a Path to Follow
This function receives 2 parameters, the handle to the Tree Control and the HOMEDRIVE
. It adds the ROOT
Item to the Tree Control, then gets the logical drive string and calls the AddItemToTree
function adding each of the drives to the Tree
and calling the getDirectories
function for each drive to populate the first level, producing the populate on demand behavior of the TreeView
and expanding the HOMEDRIVE
. This is the one that should contain the start in directory, expanding it results in a TVN_ITEMEXPANDING
message bring sent to the TreeView
Control, followed by a TVN_ITEMEXPANDED
message. When control returns, the HOMEDRIVE
has been populated and expanded to the start in directory. Next, it selects the highlighted tree node, which should be the start in directory. The two additional functions called by this routine are also called by the TreeView
repeatedly.
The InitTreeViewItems Function
BOOL InitTreeViewItems ( HWND hwndTV, TCHAR * startPoint ) {
HTREEITEM nodeParent;
HTREEITEM hti;
TV_ITEM tvi = { 0 };
TCHAR szDTemp [ ] = TEXT ( " :\\ " );
TCHAR szTemp [ 512 ];
hti = ( HTREEITEM ) TVI_ROOT;
if ( GetLogicalDriveStrings ( 512 - 1, szTemp ) ) {
TCHAR* p = szTemp;
TCHAR* d = szDTemp;
do {
*d = *p;
nodeParent = AddItemToTree
( hwndTV, hti, szDTemp, 0, 1 );
getDirectories ( nodeParent, szDTemp );
if ( hti == NULL )
return FALSE;
if ( NULL == nodeParent ) {
return FALSE;
}
else if ( *d == *startPoint ) {
TreeView_Expand ( hwndTV, nodeParent, TVE_EXPAND );
}
while ( *p++ );
}
while ( *p );
}
else
return FALSE;
nodeParent = TreeView_GetSelection ( hwndTV );
TreeView_Select ( hwndTV, nodeParent, TVGN_CARET );
return TRUE;
}
AddItemToTree : Populate the Branches
There isn't too much to say about this routine, after all, it is just initializing the parameters to the TreeView_InsertItem
macro. But look closely at what it is doing. One of the members of the tvins structure used by TreeView_InsertItem
is hInsertAfter
. This is being set to hPrev
. This means hPrev
must retain its state between calls, so we make it a static
variable. If it weren't static
, hPrev
would always be TVI_FIRST
. This would cause the Tree
to be sorted Descending
with the last item first and the first item last.
The AddItemToTree Function
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;
tvi.cchTextMax = 0;
tvi.iImage = tviiImage;
tvi.iSelectedImage = tviiSelectedImage;
tvins.item = tvi;
tvins.hInsertAfter = hPrev;
tvins.hParent = hDir;
hPrev = TreeView_InsertItem ( hwndTV, &tvins );
return hPrev;
}
getDirectories: and hang 'm in the Tree
This function accepts 2 parameters, an HTREEITEM
and an LPTSTR
. It uses the FindFirstFileExW
- FindNextFile
sequence loop to get all of the child items of the HTREEITEM
, hDir
. It calls AddItemToTree
for each item, setting the parameters to show the appropriate images, indicating a file or folder.
The getDirectories Function
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 );
}
else {
wsprintf ( szSearchPath, L "%s* ", szPath );
}
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 );
}
The editBoxProc and its Alter-ego: The editBoxSubClassProc
The editBoxProc
is a very simple function. THE WM_INITDIALOG
message handler creates the control and sets the Window Subclass
for hEdit
to editBoxSubClassProc
. The third parameter, IDC_EDIT2
is the control's id but here it is the uIdSubclass
parameter and it could be any UINT_PTR
as long as it uniquely identifies the subclass procedure to the system. The fourth parameter is the dwRefData
and is passed to the procedure with each invocation. WM_INITDIALOG
also establishes the position of hFormView0(hDlg)
and moves hEdit
into it. The WM_COMMAND
message processes only the WM_SETFOCUS
message to set the focus on the ListView
. WM_SIZE
resizes the control when necessary.
The editBoxProc Function
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 };
GetClientRect ( hDlg, &rc );
hEdit = CreateWindowExW ( WS_EX_CLIENTEDGE,
L "EDIT ",
L " ",
WS_CHILD | WS_VISIBLE |
ES_AUTOHSCROLL | ES_AUTOVSCROLL | ES_NOHIDESEL,
10,
42,
rc.right,
25,
hDlg,
( HMENU ) ( IDC_EDIT2 ),
hInst,
0 );
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 );
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;
}
The editBoxSubClassProc
, on the other hand, is a bit more complicated. In the uMsg switch
, the WM_GETDLGCODE
handler listens for wParam
equal to VK_F3
, Vk_F5
, VK_F6
and VK_RETURN
. For all of these, it returns DLGC_WANTMESSAGE
, preventing the control, hEdit
, from performing the default processing for these keys. Instead, we will process them. For the function keys, we send VK_F3
and VK_F6
to Richedit
, VK_F5
is sent to ListView
.
For VK_RETURN
, we get the window text from hEdit
. Comparing the first two characters with '#!
', when they are equal, they are stripped off and the _tsystem
function is called with the rest of the text. What the user sees depends on the command they entered. The command 'time' they would see the console window with the current time and a prompt to enter the new time, but if the command was 'time /t
', it would execute so quickly they would see nothing but the command's return code in hEdit. In such situations, the user could add && pause to the end of the command to keep the console open.
Comparing the first two characters with '#@
', when they are equal, they are stripped off and the _tsystem
function is called with the rest of the text. The text should have been put there by the Reset_CheckBox
Menu selection. The next step is to wait for a short time for the command to complete, then paste the results into hEdit
and get the window text again. This is the output of the 'WMIC /USER /FO CSV /NH |clip
' command. It has the username
and SID
separated by a comma. We split the string
at the comma. The SID
is enclosed in double quotes, so to get rid of the first, we add one to the pointer. To get rid of the last, we overlay it with a character zero. We use the SID
to construct the Registry Key to the CheckBox Flag and put it in the Environment Variable. Then we construct the Registry Query to find the reset flag entry and delete it. The Reg Delete asks if you are sure. The query and delete were ran by '#!
' which writes the return code to the hEdit
window. What the user sees is the console window with the confirmation prompt and command's return code in hEdit
.
Forcing the Reset of the SHMessageBoxCheck Checkbox
Uncheck the check box in the SHMessageBoxCheck message box
Comparing the first two characters with '##
', when they are equal, they are stripped off and the _tsystem
function is called with the rest of the text. The text should have been put there by the Force_Reset_CheckBox
selection. The next step is to wait for a short time for the command to complete, then paste the results into hEdit
and get the window text again. This is the output of the 'WMIC /USER /NH |clip
' command. It has the username
and SID
separated by a space. We split the string
at the space. We use the SID
to construct the Registry Key to the CheckBox
Flag and put it in the Environment Variable. Then we construct the Registry Query to find the reset flag entry and delete it. The Reg Delete uses the '/;F
' switch, which forces the delete and does not ask if you are sure. The query and delete were run by _tsystem
to get the return code and inform the user of success or failure using the SHMessageBoxCheck
message box.
The ID_EDIT_CMD
Menu selection uses the prefix '#&
' in the window text of hEdit
to start a command console. Instead of _tsystem
, it uses the ShellExecute
function. The cmd.exe is started with the /f switch
set on and executes the 'ver
' built-in function to display the version of windows, just like a regular console. It sets the prompt and prints the message 'Path Completion is on...
'. The reason this Menu Entry was included is if the user tried to start a console using '#!
' Two Stage Search would become unresponsive until the console was exited. This console shows the Date and Time in the prompt. The path completion is very useful once you learn how to use it.
The editBoxSubClassProc Function
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 };
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 &® 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 &® 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;
}
Retaining State: Now WHERE Was I?
The Relationship between SET and SETX
The first time Two Stage Search is used, there is no pre-existing place in the file system to search or file extension to seek out. The ListView
could be left blank and the user could choose the root path and set of file extensions to build the File List with. But arbitrary choices have been made to act as an example of what the program does. If the user makes their own choices, the values they selected should be honored the next time they want to use the Two Stage Search. There are many ways to save these values allowing them to be recalled. The method chosen here is to use the 'SETX ' function. The problem is, 'SETX
' is not a Windows function, it is a built-in CMD.EXE function. It is a batch command. It requires the use of ShellExecuteEx
with the SHELLEXECUTEINFO
structure initialized with the required information, the cmdToExeCute
must contain the SETX
verb, the variable name and its value. The value must be enclosed in double quotes if it contains white space. The syntax resembles the 'SET
' verb but the comma is replaced by a space. The 'SET
' verb, or the SetEnvironmentVariable
Windows function writes the information to the Environment Block being used by the program. The 'SRTX
' verb writes the information to the Registry. This means the new value cannot be retrieved with 'SET
' or GetEnvironmentVariable
until the program is re-started. If you want to retrieve it immediately, you have to save it with GetEnvironmentVariable
. Using 'SET
' and 'SETX
' together, we can persist the state of the program across function calls and across program invocations. ShellExecuteEx
is used to get the return code from CMD.EXE because 'SETX
' always succeeds if the parameters are correct, but this doesn't mean the value was saved. We get the output from the command to show to the user but we also get the process return code to report the possible failure of the command.
The persist_This function
DWORD WINAPI persist_This ( TCHAR * tStr ) {
TCHAR cmdToExeCute [ 512 ] = { 0 };
TCHAR nodeCD [ MAX_PATH - 1 ] = { 0 };
GetCurrentDirectory ( MAX_PATH - 1, nodeCD );
TCHAR tempFname [ MAX_PATH ];
TCHAR tempPath [ MAX_PATH ];
TCHAR tempStr [ 1024 ] = { 0 };
CHAR setXresults [ 1024 ] = { 0 };
TCHAR resultFileName [ MAX_PATH ];
DWORD dwResult = GetTempPath ( MAX_PATH, tempPath );
UINT uiResult = 0;
if ( dwResult < MAX_PATH || dwResult != 0 )
uiResult = GetTempFileName ( tempPath, L "persist_This ", 0, tempFname );
if ( uiResult == 0 ) {
GetEnvironmentVariable ( L "USERPROFILE ", tempPath, 255 );
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 );
}
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 and EnumChildProc: Retaining the STATE of the Window Handles
The Window handles are initialized by the Create Window functions, so we can 't persist them with 'SETX
'. The handles are a part of the Window, so all we have to do is find the Window to get the handle. To find the main window, we can use FindWindow
with the Class Name or Window Name, called inline. We could FindWindowEx
to find a child window but the EnumChildProc
is a simpler method to use. But you do have to write a function to call it. There are actually 2 functions but it is still the easiest to use. The first function in the sequence is getThisWindowHandle
, which accepts the parent window handle and the control Id of the child window of that parent and returns the handle of that child window. It uses a struct
variable containing a handle and a uint control id. getThisWindowHandle
sets the handle member to NULL
and the uint
member to the control id. Now it calls EnumChildWindows
with the parent handle, the address of EnumChildProc
, cast to a WNDENUMPROC
and the address of the struct
containing the control id. On return, the structure contains the child window handle which can be NULL
. EnumChildWindows
is an enumerator function. It works like a 'for (child in parents tree) do child processing', calling EnumChildProc
for every child handle it finds. EnumChildProc
calls GetDlgCtrlID
with the handle of the child window and returns the id of that control. If that id matches the id in the struct
, the child window handle is copied into the struct
's window handle and b_isNotDone
is set to false
, which ends enumeration. Control passes to getThisWindowHandle
, which returns the child window handle to the caller. Here are the struct
and the two functions.
The getThisWindowHandle and EnumChildProc Functions
typedef struct ecwData {
HWND hCWind;
UINT iD;
} ecpWnd, lpecpWnd;
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;
}
return b_isNotDone;
}
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 Function Call
HWND hList = getThisWindowHandle ( hMainWnd, ( ULONG ) IDC_LIST1);
if (hLidt == 0) return 0;
Connecting the Viewer to the List: The sendMeMyKey Function
The richEditProc
calls a function named sendMeMyKey
from several in the WM_NOTIFY
message handler and apparently stops processing. In reality, it has passed the ball(of execution) to another runner. The sendMeMyKey
Function takes 2 parameters, a window handle and a virtual key code, returning a BOOL
. It uses the SendInput
function to insert an array of input events into the input stream of the window whose handle you supplied to sendMeMyKey
. It doesn't send message to a window. It synthesizes the event in the window that has focus. So, the first thing it does is set focus to the window you sent and then send the array consisting of a key down and key up of the virtual character. If the input is sent as 2 single events, other programs were sometimes grabbing focus, causing problems, the most serious of which was a program dead-lock. The way this works is almost like the user set the focus and then typing the keys, but this is subject to UIPI blocking so you can only send input to applications of equal or lower integrity level.
The sendMeMyKey Function
BOOL __forceinline sendMeMyKey ( HWND hFocus, unsigned char pfKey ) {
SetFocus ( hFocus );
INPUT keyPr [ 2 ] = { 0 };
keyPr [ 0 ].type = INPUT_KEYBOARD;
keyPr [ 0 ].ki.dwFlags = 0;
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: How to Load a File into RichEdit
When a key press or mouse click is received, one of the most common actions executed is to load a file into RichEdit
. The FillRichEditFromFile
function is called from five different points in the listViewProc
and once from the WndProc
. All of these calls, except for the one from WndProc
, occur as the result of a keypress or mouse click. So it seemed appropriate to discuss the function here, after sendMeMyKey
. It is code written by and copyrighted by Microsoft. It receives two parameters, The handle to a RICHEDIT
Instance and the path and name of the file to load and if successful, creates an EDITSTREAM
structure with the address of the EditStreamCallback
function and the dword
pointer to the file handle created earlier. It sends an EM_STREAMIN
message to the RICHEDIT
Control with the wParam
set to SF_TEXT
and the EDITSTREAM
cast to an LPARAM
. if the sendmessage returns non-zero and the structure 's dwerror
member is zero, it sets fSuccess
to TRUE
and closes the file handle. The EditStreamCallback
function is called multiple times to read the entire file. The Microsoft Visual Studio Online Documentation Article "How to Use Streams " is the source document of these two functions. If you need more information, you can find it there.
Tips on Using Two Stage Search
When the program is executed for the first time, it checks the Environment Block for two variables, The STARTINDIR
and the SEARCHSTR
. If they are not present, they are initialized to the same value as your USERPROFILE
for STARTINDIR
and "*.cpp;*.c" for SEARCHSTR
. These values can be changed at any time thereafter and the value will be put into the Registry and therefore, they will be found and used in subsequent executions of the program.
Changing the STARTINDIR: The ROOT Folder of the File List
To change the start in directory, select the 'File ' menu command and choose one of Change_Directory|Temp_Change_CD|CD_AND_Build. The first item, Change_Directory
will allow you to change the directory and save the change to the Registry. The second item will allow you to change the directory for the current execution but will not save the change to the Registry. The third item will also change the directory but not save it to the Registry. For each of these items, the TreeView
is given keyboard focus. Using the cursor keys to move to the desired folder does not disturb the entries in the ListView
. When the Tree Control receives an ENTER Key, it set the current Tree Node as the current directory. This allows files from several roots to be included in the File List. It is possible to click on the Tree Node with the mouse but this will clear the ListView
and populate it with the child items of the node.
Changing the SEARCHSTR: The File Extension Set Used in the File List Build
To change the types of files included in the File List, click on the Combobox EditCtrl
on the left side of the application window or the dropdown button for that Combobox
. When ENTER is pushed or the Start Button is clicked the Combobox
message handler compares the EditCtrl
text with the current value of SEARCHSTR
and if they differ the new value is saved in the Registry
. If the EditCtrl
is empty, the last known value is retrieved and used.
The Rules for the File Extension Set
A single suffix or ending part of a File Name. It can be the entire File Name but not the beginning part with the ending part missing.
An asterisk followed by a period and a file type extension
One or more of the previous rule, separated by semicolons, i.e.,*.a;*.b;*.c;*.d;
...etc.
How To ENTER the String To Search For
There are two different ways to enter the String To Search For. Select a string
in the Viewer Panel and push 'f
' while holding down the control key. You can select a string
of characters with no white space in the string
by clicking on any of the characters. To select a string
with spaces, click on one end and drag the highlight to the other end.
The other method is to click on the Combobox EditCtrl
on the right side of the application window and typing in the string
. The string
is added to the Combobox List
allowing a previously entered string
to be recalled. The string
is saved in the Registry
but a new string
replaces the old one.
How to Find the Next or Previous Occurrence of the String to Search For
To find the next occurrence of the string you have entered push PF3. If the search reaches the end of file it searches the next file(s) until a match is found. If it reaches the end of the file list it wraps around and searches from the beginning. If the Shift is held down, the search direction is backwards. PF6 will always search backwards. PF5 will find the next file containing a match. When searching backwards and the search reaches the beginning of the file, the last occurrence of the previous file will be found.
How to Open the File in Another App
To use the 'OPEN WITH ' Dialog, Right Click on the file in the ListView
. To open the file location in File Explorer, Double left click or if the file is selected you can push ENTER.
How to Run a Batch Command in Two Stage Search
To execute a cmd.exe command or batch file, click on the EditBox
Control under the ComboBox
es and enter '#!
' followed by the command to execute. That will open a Console Window to execute the command. If you want to see the output of the command enter '&& pause
' after the command. This causes the system to wait for user input. Do not attempt to keep this window open permanently. Instead, select "Edit>CMD Console" This will give you an open Console Window without freezing Two Stage Search.
How to Get Rid of the Annoying Message Box That Is Shown When You Enter a String
The SHMessageBoxCheck
is displayed whenever a value is saved in the Registry. Since it is purely informational, you can get rid of it by CHECKING the Checkbox
in the lower left corner of the dialog. After you get rid of the annoying message, you may still be annoyed by an occasional message box displaying an error message. When this happens, the new value may not have been saved, therefore you should consider re-entering the value.
How to Re-Display the Annoying Message Box
On the Main Menu, select "View>Reset_CheckBox | Force_Reset_CheckBox". The first choice, Reset_CheckBox
, will display a console window to ask for confirmation. If you have changed your mind, type an 'n
' to cancel the request or a 'y
' to reset it. The second choice, Force_Reset_CheckBox
does not ask for confirmation, it just does it.
The Help Menu
The Help Menu has 6 choices, they are :
- About...____________ Two Stage Search
- About Extensions__ How to Change Extensions
- About Strings______ How to enter String to search for
- About Path ________ How to Change the Path for File List build
- About CheckBox___ How to reset Checkbox
- About CMD.EXE____ How to invoke a CMD.EXE Console
These 6 help panels, always available at the click of a button, should provide enough information to make Two Stage Search easy enough to use that a separate Help File will not be necessary. If enough readers disagree and want a Help file, I will be happy to provide one.
Points of Interest
When I first decided to publish the Least Frequent Character Offset Algorithm, I considered using it in a program that used SSE2 or AVX2 Instructions, but when I tested it on an older computer, it didn't work. The computer didn't have the instructions. So I pulled out an older program, U2Charfunc
, that used SSE Instructions and discovered that it performed just as well on a Windows 8.1 machine, which only had SSE Instructions, as it did on a Windows 10 machine. So I am going with the older program.
The window in the middle, the RESIZING ListView
window, seemed to me to be a much simpler concept than using the client background of the main window to simulate a splitter bar. Especially if you want more than 2 windows, say 3 or 9. For 3 panels, you do have to keep the top and bottom borders from being moved but that is simple with cursor clipping. With 9 panels, you can use all 4 borders of the center panel to control the other 8 panels. For the panels between 3 and 9, you have to use at least 1 more resizing border if you want to make them resizable.
History
- 30th August, 2019: Initial release