Solved

Searching through a big file.

Posted on 2008-10-27
36
287 Views
Last Modified: 2012-05-05
Hi

I was wondering what can be the most efficient way to search through the contents of a large binary data file (eg. 10GB) to find a specified string ?
0
Comment
Question by:tadzio_blah
  • 13
  • 7
  • 6
  • +3
36 Comments
 
LVL 37

Expert Comment

by:Geert Gruwez
ID: 22819372
any specifics about the binary file ?
0
 

Author Comment

by:tadzio_blah
ID: 22819402
nope... none... some random data - just need to find the specified string in it.
0
 
LVL 37

Expert Comment

by:Geert Gruwez
ID: 22819658
start to search from several locations with threads
keep in mind that the io will be the bottleneck
0
 
LVL 37

Expert Comment

by:Geert Gruwez
ID: 22819677
0
 
LVL 5

Expert Comment

by:wd123
ID: 22820431
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22821963
Try the code below:

John

function MemPos(const Filename, TextToFind: string; iFrom: cardinal = 0;

         CaseSensitive: boolean = False): int64;

var

  buffer : string;

  len : integer;

  MS : TMemoryStream;

  charsA, charsB : set of char;

  p, pEnd : PChar;

begin

  result := -1;

  if (TextToFind <> '') and FileExists(Filename) then

   begin

    len := Length(TextToFind);

    SetLength(buffer, len);

    if CaseSensitive then

     begin

      charsA := [TextToFind[1]];

      charsB := [TextToFind[len]];

    end

    else

     begin

      charsA := [ AnsiLowerCase(TextToFind)[1],   AnsiUpperCase(TextToFind)[1] ];

      charsB := [ AnsiLowerCase(TextToFind)[len], AnsiUpperCase(TextToFind)[len]];

    end;

    MS := TMemoryStream.Create;

    try

      MS.LoadFromFile(Filename);

      if (len <= MS.Size) and (iFrom <= (MS.Size - len) + 1) then

       begin

        p := MS.Memory;

        pEnd := p;

        Inc(p, iFrom);

        Inc(pEnd, (MS.Size - len) + 1);

        while (p <= pEnd) and (result < 0)

        do

         begin

          if (p^ in charsA) and ((p +len -1)^ in charsB) then

           begin

            MoveMemory(@buffer[1], p, len);

            if (CaseSensitive and (AnsiCompareStr(TextToFind, buffer) = 0)) or

                 (AnsiCompareText(TextToFind, buffer) = 0) then

                   result := p -MS.Memory;

           end;

          Inc(p);

         end;

       end;

    finally

      MS.Free;

    end;

  end;

end;
 
 

// Usage
 

if MemPos(Path + File.Name, TextToFind.Text, 0 , CaseSense.Checked) > 0 then

 begin

   .... do whatever

 end;

Open in new window

0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22821988
PS..

I have tried this on CD ISO files 650 MB and a few DVD ISOs > 1GB.

For really large files, it will take some time! Not certain how to optimize it further.

John
0
 

Author Comment

by:tadzio_blah
ID: 22822858
Let's say that it can't load more than 100mb of that file at one time (because i don't think that loading). The code you provided only works when file size < system memory size.
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22822906
Did you try it?

Windows will cache memory to the hard disk. Your virtual memory settings will control the amount.

John
0
 

Author Comment

by:tadzio_blah
ID: 22823005
yes, i've tried it, that's way i wrote the previous comment. i'm getting an out of memory exception.
Snap1.jpg
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22823090
Hmmm... your PC should have cached it in virtual memory... I think. Check your settings there and increase as necessary.

And, sorry I didn't get out of your comment that you tried it as by writing, "Let's say that it can't load more than 100mb...' sounds hypothetical. The misleading part was "let's say".

I do have a lot of RAM on my development PCs so that could very well be a problem for you as it is an in memory function.

Sorry this did not work out for you. I tried but everyone has different needs and machines!

John
0
 

Author Comment

by:tadzio_blah
ID: 22823231
I have 4GB of mem on my dev laptop.
Did you trie to load a file >6GB with that function ?
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22823240
Well, the way I see it is that your going to have read the file in blocks. At the 10gb size using a memory stream that size is not a good idea. Your going to have to read the files in blocks(16-128Kb)'s at a time and search through each of those blocks.

I'll give it a whirl but the catch here is that the data might be split between two blocks.
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22823243
I do have a question, is the data your searching for normal strings? I.E. 'This is a string'#0?
0
 
LVL 37

Expert Comment

by:Geert Gruwez
ID: 22823245
i won't load more than 2 gig or maybe 4 gig (windows boundary)

you need to use a TFileStream and start reading from a certain point

i'll try an example
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22823255
Nope... my largest was a DVD ISO image. 1.2 gig + or -.

John
0
 
LVL 37

Expert Comment

by:Geert Gruwez
ID: 22823262
oops, looks there is more than 1 expert with that idea
0
 

Author Comment

by:tadzio_blah
ID: 22823274
and my vmem size is to min 6gb and max 20gb... so that shouldn't be a problem
btw i'm using D2007 - maybe that's the problem (maybe they've chenged something in the memstream that it has to fit the available physical mem).
0
Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

 

Author Comment

by:tadzio_blah
ID: 22823302
i'm searching for a standard delphi string in those files
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22823663
This is my go, probably have some errors as I didn't get to test it.
function StrChr(pStr: PChar; ch: char; Length: DWORD): PChar;

var

  EndPtr : Pointer;

begin

  EndPtr := pStr + Length;

  Result := nil;

  While (Addr(pStr) <> EndPtr) Do

    begin

    If (pStr^ = ch) Then

      begin

      Result := pStr;

      Exit;

    end;

    Inc(pStr);

  end;

end;
 

function GetTextOffset(const FileName: PChar; TextToFind: PChar; TextLength: DWORD; BlockSize: DWORD = $4000): Int64;

var

  FileHandle : DWORD;

  Buffer : PChar;

  Data : PChar;

  BytesRead : DWORD;

  BytesToRead : DWORD;

  CharStart,

  CharEnd : Char;

  DoLoop : Bool;

  Offset : Int64;

  Original : Pointer;

  DoInit : Boolean;

begin

  //Initialize result. -1 = String not found

  Result := -1;

  //Open the file read only

  FileHandle := CreateFile(FileName,GENERIC_READ,0,nil,OPEN_EXISTING,0,0);

  //If we could not open the file exit

  If FileHandle = INVALID_HANDLE_VALUE Then Exit;

  Try

    //Allocate memory the size specified

    Buffer := AllocMem(BlockSize);

    //We couldn't get the memory, exit

    If @Buffer = nil Then Exit;

    Try

      //Mark the original buffer address

      Original := @Buffer;

      //Initialize the variable that will exit our loop

      DoLoop := True;

      //Initialize the DoInit variable to false, we'll use this after

      //  the first loop

      DoInit := False;

      //Save this for later

      BytesToRead := BlockSize;

      Repeat

        //initialize the constants back to normal if we didn't find a partial

        If DoInit Then

          begin

          Buffer := Original;

          BytesToRead := BlockSize;

        end;

        //zero the memory of the buffer every reiteration

        ZeroMemory(Buffer,BytesToRead);

        //read the data from the file

        ReadFile(FileHandle,Buffer,BytesToRead,BytesRead,nil);

        //if we didn't read enough we hit the end of the file

        //  so we are done with the loop

        If BytesRead < BytesToRead Then DoLoop := False;

        //We refilled the buffer, let's set it back

        If Not(DoInit) Then

          begin

          Buffer := Original;

        end;

        //increment our current position(easier than calling SetFilePointer)

        Inc(Offset,BytesRead);

        //Search the buffer for the first char in TextToFind

        Buffer := StrChr(Buffer,TextToFind^,BytesRead);

        //the first char isn't there, so restart the loop

        If @Buffer = nil Then Continue;

        //we hit the end of the buffer, not enough room for the string anyway

        If (Buffer + TextLength) >= Original Then

          begin

          //Put what we found, which could be a partial of the TextToFind

          //  in the beginning of the buffer

          Move(Buffer,Original,BlockSize - (Original - Buffer));

          Buffer := Original;

          Inc(Buffer,(BlockSize - (Original - Buffer)));

          Dec(BytesToRead,BlockSize - (Original - Buffer));

          DoInit := False;

          Continue;

        end;

        //Check to see if the last char and string length is matching

        If Buffer[TextLength - 1] = TextToFind[TextLength - 1] Then

          begin

          //Match the actual string

          If StrComp(Buffer,TextToFind) = 0 Then

            begin

            //It matches. Bring the offset back to where Buffer is

            Result := Offset - (Buffer - Original);

            //Exit, we found what we're looking for

            Exit;

          end;

        end;

        DoInit := True;

      Until (Not(DoLoop));

    Finally

      //Deallocate the memory, we're done with it

      FreeMem(Buffer,BlockSize);

    end;

  Finally

    //Close the handle to the file so windows will like us

    CloseHandle(FileHandle);

  end;

end;

Open in new window

0
 
LVL 26

Accepted Solution

by:
Russell Libby earned 300 total points
ID: 22823775

Definitely a case where you can't attempt to load the whole thing in memory (not even using mem mapped files; you could only map portions). The  issue is the memory address space; which is 2GB for 32 bit processes. 3GB if process is set with LARGE ADDRESS AWARE. This means you have to chunck the file in order to do the searching. There are a few optimizations that can be done, such as opening the file for sequential scan, etc. But the biggest bottleneck will be the disk IO. Also, keep in mind that when working with blocks / chuncks, you have to account for the possibility (albeit low) of the search string falling on the "crack"; meaning it falls between two processed blocks. To account for this, you need to use a sliding window where you always slide the last [Search Length - 1] bytes from the end of the current block to the head of the next block to process.

Regards,
Russell

Example provided below:

// Array of Int64 values indicating where the match was found
type
  TFoundMatch       =  Array of Int64;

// Buffer read size: keep in multiples of 1 MB
const
  BLOCK_SIZE        =  1048576;

function FileFindString(FileName: String; Search: String; IgnoreCase: Boolean; out Found: TFoundMatch): Boolean;
var  lpShiftTable:  Array [#0..#255] of LongWord;
     lpCompTable:   Array [#0..#255] of LongWord;
     hOpen:         THandle;
     dwIndex:       LongWord;
     dwRead:        LongWord;
     dwBlock:       LongWord;
     dwSearch:      LongWord;
     dwHold:        Int64;
     lpBuffer:      PChar;
     bMatch:        Boolean;
     cChar:         Char;
begin

  // Clear outbound parameter
  SetLength(Found, 0);

  // Resource protection
  try
     // Get search string length
     dwSearch:=Length(Search);
     // Search string must be specified
     if (dwSearch > 0) then
     begin
        // Attempt to open the file with sequential scan flag
        hOpen:=CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
        // Check open results
        if (hOpen <> INVALID_HANDLE_VALUE) then
        begin
           // Resource protection
           try
              // Need to work with tail char
              cChar:=Search[dwSearch];
              // Initialize comparison table
              FillChar(lpCompTable, SizeOf(lpCompTable), 255);
              // Set last char
              lpCompTable[cChar]:=0;
              // Handle case insensitive matching
              if IgnoreCase then
              begin
                 case cChar of
                    'A'..'Z' :  lpCompTable[Char(Ord(cChar) + 32)]:=0;
                    'a'..'z' :  lpCompTable[UpCase(cChar)]:=0;
                 end;
              end;
              // Initialize the boyer-moore shift table
              for cChar:=#0 to #255 do lpShiftTable[cChar]:=dwSearch;
              // Update shift table
              for dwIndex:=1 to Pred(dwSearch) do
              begin
                 // Set shift for char
                 lpShiftTable[Search[dwIndex]]:=dwSearch - dwIndex;
                 // Set shift for other case of char
                 if IgnoreCase then
                 begin
                    case Search[dwIndex] of
                       'A'..'Z' :  lpShiftTable[Char(Byte(Search[dwIndex]) + 32)]:=dwSearch - dwIndex;
                       'a'..'z' :  lpShiftTable[Char(Byte(Search[dwIndex]) - 32)]:=dwSearch - dwIndex;
                    end;
                 end;
              end;
              // Allocate memory for file chunk and sliding window
              lpBuffer:=AllocMem(BLOCK_SIZE + Succ(dwSearch));
              // Resource protection
              try
                 // Decrement the search length
                 Dec(dwSearch);
                 // Seed the first block
                 if ReadFile(hOpen, lpBuffer^, BLOCK_SIZE, dwRead, nil) then
                 begin
                    // Set hold marker
                    dwHold:=0;
                    // While data to process
                    while (dwRead > 0) do
                    begin
                       // Set block chunck index
                       dwBlock:=dwSearch;
                       // Process chunk (must be as large as search string)
                       while (dwBlock < dwRead) do
                       begin
                          // Compare last byte of search string with text pointer
                          dwIndex:=lpCompTable[lpBuffer[dwBlock]];
                          // Check for match
                          if (dwIndex = 0) then
                          begin
                             // String compare
                             if IgnoreCase then
                                // Case insensitive (last byte already matches)
                                bMatch:=(StrLIComp(Pointer(Search), @lpBuffer[dwBlock - dwSearch], dwSearch) = 0)
                             else
                                // Case sensitive (last byte already matches)
                                bMatch:=(StrLComp(Pointer(Search), @lpBuffer[dwBlock - dwSearch], dwSearch) = 0);
                             // Check match
                             if bMatch then
                             begin
                                // Get current size
                                dwIndex:=Length(Found);
                                // Allocate space on array
                                SetLength(Found, Succ(dwIndex));
                                // Set entry
                                Found[dwIndex]:=dwHold + (dwBlock - dwSearch);
                                // Update block position by full length of search string
                                Inc(dwBlock, Succ(dwSearch));
                                // Continue
                                Continue;
                             end;
                          end;
                          // Update block processing index by shift index
                          Inc(dwBlock, lpShiftTable[lpBuffer[dwBlock]]);
                       end;
                       // If the last block read < block size, then no data left to process
                       if (dwRead < BLOCK_SIZE) then
                          // Done processing
                          break
                       else
                       begin
                          // Set hold marker
                          Inc(dwHold, dwRead - dwSearch);
                          // Need to account for sliding window (slide bytes from end of buffer to head)
                          Move(lpBuffer[dwRead - dwSearch], lpBuffer[0], dwSearch);
                          // Read next block (break on failure)
                          if not(ReadFile(hOpen, lpBuffer[dwSearch], BLOCK_SIZE, dwRead, nil)) then break;
                          // Update read count by the sliding window size
                          Inc(dwRead, dwSearch);
                       end;
                    end;
                 end;
              finally
                 // Free buffer
                 FreeMem(lpBuffer);
              end;
           finally
              // Close the file handle
              CloseHandle(hOpen);
           end;
        end;
     end;
  finally
     // Was the search string found?
     result:=(Length(Found) > 0);
  end;

end;


--- example usage ---
// If string was found, open the file, seek to each result and verify the data at the location
var  Found:         TFoundMatch;
     liPos:         LARGE_INTEGER;
     dwIndex:       Integer;
     hOpen:         THandle;
     dwRead:        DWORD;
     lpBuffer:      Array [0..7] of Char;
begin

  if FileFindString('C:\Backup\VSNet\2.0 SDK\setup.exe', 'install', True, Found) then
  begin
     hOpen:=CreateFile('C:\Backup\VSNet\2.0 SDK\setup.exe', GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);
     if (hOpen <> INVALID_HANDLE_VALUE) then
     begin
        try
           for dwIndex:=0 to Pred(Length(Found)) do
           begin
              lpBuffer[7]:=#0;
              Int64(liPos):=Found[dwIndex];
              SetFilePointer(hOpen, liPos.LowPart, @liPos.HighPart, FILE_BEGIN);
              ReadFile(hOpen, lpBuffer[0], 7, dwRead, nil);
              MessageBox(0, PChar(@lpBuffer), nil, MB_OK);
           end;
        finally
           CloseHandle(hOpen);
        end;
     end;
  end;

end;




0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22826671
Russell,

Nice function! Works well. I am anxious to do some speed tests on  < 1 gig files comparing the two functions provided.

One question.....

// Buffer read size: keep in multiples of 1 MB
const
  BLOCK_SIZE        =  1048576;

Shouldn't block_size = 1024000?

That is actually 1 meg...

Just wondering.

John
0
 
LVL 18

Expert Comment

by:Johnjces
ID: 22826731
Russell,

Never mind the stupid brain dead question.  duh.... 2 - 20 power = 1,048,576.

Sorry.

You got my points!

John
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22826878
John, no problem ;-)

Hopefully you should find the two pretty close (performance wise) on smaller files. Let me know how it goes.

Russell
0
 

Author Comment

by:tadzio_blah
ID: 22827490
ThievingSix:
your code doesn't work. i'm getting access violation on the line:

        If (Buffer + TextLength) >= Original Then
          begin
          //Put what we found, which could be a partial of the TextToFind
          //  in the beginning of the buffer
>>>          Move(Buffer,Original,BlockSize - (Original - Buffer));
          Buffer := Original;
          Inc(Buffer,(BlockSize - (Original - Buffer)));
          Dec(BytesToRead,BlockSize - (Original - Buffer));
          DoInit := False;
          Continue;
        end;

can you fix that please ? i'd like to do a timetest on both provided functions.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22827675

Btw, if you plan on timing the two functions (which is a good idea), then you need to modify my routine to "even" things out, as I return ALL matches, not just the first one.


                            // Check match
                             if bMatch then
                             begin
                                // Get current size
                                dwIndex:=Length(Found);
                                // Allocate space on array
                                SetLength(Found, Succ(dwIndex));
                                // Set entry
                                Found[dwIndex]:=dwHold + (dwBlock - dwSearch);
                                //  Done
                                exit;
                             end;
----
Russell
0
 

Author Comment

by:tadzio_blah
ID: 22828084
Don't worry ;) I've done it allready.
Just need the working version of ThievingSix's function.
0
 
LVL 13

Expert Comment

by:ThievingSix
ID: 22828370
Let's see. Add a variable I, type Integer. Then replace the Move with the following:
For I := 0 To (BlockSize - (Original - Buffer)) Do

  begin

  PByteArray(Original)[I] := Buffer[I];

end;

Open in new window

0
 

Author Comment

by:tadzio_blah
ID: 22829521
error in the code. should be:

For i := 0 To (BlockSize - (Original - Buffer)) Do
  begin
  PByteArray(Original)[i] := byte(Buffer[i]);
end;

, but even now i'm getting access violation on: PByteArray(Original)[i] := byte(Buffer[i]); line.
0
 
LVL 37

Expert Comment

by:Geert Gruwez
ID: 22829646
off topic:
>>Russell
nice to have your site back
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22831654
Geert,
Thanks, didn't know anyone still used my site.

ThievingSix,
You might want to take another pass through your code. There are a number of pointer errors (still can't get it to run my system). Eg:

//Mark the original buffer address
Original := @Buffer; // should be Original := Buffer;

Also, the ReadFile is failing to read anything due to the missing ^ . Change to:
ReadFile(FileHandle, Buffer^, BytesToRead, BytesRead, nil);

----

Regards,
Russell
0
 

Author Comment

by:tadzio_blah
ID: 22833384
0
 

Author Comment

by:tadzio_blah
ID: 22836340
I've got tired of waiting, and used Russell's code in my app. Thanks.
0
 
LVL 26

Expert Comment

by:Russell Libby
ID: 22836794
I had no problem with you waiting, regardless if you already started using my routine.

I will share this final note though...

In timing tests where I placed the match string at the end of a 1 GB file, 13500 ms of the routine was spent loading the file (avg speed of 60~70 MB / sec, which was consistent with the avg throughput of my SATA 300 drives), and 500ms was spent performing the actual search though the 1 GB of data, for a combined total of 14 seconds. In the end you would find that most of the search routines are fairly comparable; (like I said earlier) the disk IO is the biggest bottleneck.

Regards,
Russell
0
 

Author Comment

by:tadzio_blah
ID: 22847577
your function works fine. i've made some changes to it to better fit my needs.
it returns only one position, it always searches case insensitive, and you can give a starting position of the search (for use with incremental searching).

function szukaj(FileName, Search: string; StartPos : int64) : int64;

const

  BLOCK_SIZE = 10485760;
 

var

  lpShiftTable:  Array [#0..#255] of LongWord;

  lpCompTable:   Array [#0..#255] of LongWord;

  hOpen:         THandle;

  dwIndex:       LongWord;

  dwRead:        LongWord;

  dwBlock:       LongWord;

  dwSearch:      LongWord;

  dwHold:        Int64;

  lpBuffer:      PChar;

  bMatch:        Boolean;

  cChar:         Char;

  SP:            LARGE_INTEGER;
 

begin

  Result := -1;

  dwSearch:=Length(Search);

  if (dwSearch > 0) then

    begin

      hOpen:=CreateFile(PChar(FileName), GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, 0);

      if (hOpen <> INVALID_HANDLE_VALUE) then

        begin

          try

            if (StartPos > 0) then

              begin

                SP := LARGE_INTEGER(StartPos);

                SetFilePointer(hOpen, Longint(SP.LowPart), @SP.HighPart, FILE_BEGIN);

                pBar.Progress := StartPos;

                Application.ProcessMessages;

              end;

            cChar:=Search[dwSearch];

            FillChar(lpCompTable, SizeOf(lpCompTable), 255);

            lpCompTable[cChar]:=0;

            case cChar of

              'A'..'Z' :  lpCompTable[Char(Ord(cChar) + 32)]:=0;

              'a'..'z' :  lpCompTable[UpCase(cChar)]:=0;

            end;

            for cChar:=#0 to #255 do

              lpShiftTable[cChar]:=dwSearch;

            for dwIndex:=1 to Pred(dwSearch) do

              begin

                lpShiftTable[Search[dwIndex]]:=dwSearch - dwIndex;

                case Search[dwIndex] of

                  'A'..'Z' :  lpShiftTable[Char(Byte(Search[dwIndex]) + 32)]:=dwSearch - dwIndex;

                  'a'..'z' :  lpShiftTable[Char(Byte(Search[dwIndex]) - 32)]:=dwSearch - dwIndex;

                end;

              end;

            lpBuffer:=AllocMem(BLOCK_SIZE + Succ(dwSearch));

            try

              dwSearch := dwSearch - 1;

              if ReadFile(hOpen, lpBuffer^, BLOCK_SIZE, dwRead, nil) then

                begin

                  dwHold:=StartPos;

                  pBar.Progress := pBar.Progress + dwRead;

                  Application.ProcessMessages;

                  while (dwRead > 0) do

                    begin

                      dwBlock:=dwSearch;

                      while (dwBlock < dwRead) do

                        begin

                          dwIndex:=lpCompTable[lpBuffer[dwBlock]];

                          if (dwIndex = 0) then

                            begin

                              bMatch:=(StrLIComp(Pointer(Search), @lpBuffer[dwBlock - dwSearch], dwSearch) = 0);

                              if bMatch then

                                begin

                                  Result:=dwHold + dwBlock;

                                  Exit;

                                end;

                            end;

                          dwBlock := dwBlock + lpShiftTable[lpBuffer[dwBlock]];

                        end;

                        if (dwRead < BLOCK_SIZE) then

                          Break

                        else

                          begin

                            dwHold := dwHold + (dwRead - dwSearch);

                            Move(lpBuffer[dwRead - dwSearch], lpBuffer[0], dwSearch);

                            if not(ReadFile(hOpen, lpBuffer[dwSearch], BLOCK_SIZE, dwRead, nil)) then

                              Break

                            else

                              pBar.Progress := pBar.Progress + dwRead;

                            Application.ProcessMessages;

                            dwRead := dwRead + dwSearch;

                          end;

                    end;

                end;

            finally

              FreeMem(lpBuffer);

            end;

          finally

            CloseHandle(hOpen);

          end;

        end;

    end;

end;

Open in new window

0
 

Author Comment

by:tadzio_blah
ID: 22847584
oh... and the position returned is just behind the search string.
0

Featured Post

Is Your Active Directory as Secure as You Think?

More than 75% of all records are compromised because of the loss or theft of a privileged credential. Experts have been exploring Active Directory infrastructure to identify key threats and establish best practices for keeping data safe. Attend this month’s webinar to learn more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Delphi 10 Seattle Dataset Actions 5 79
System restore point 4 91
calling Tcolordialog in dll make the form disappear 8 103
delphi prevent click fast 2 192
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
This is used to tweak the memory usage for your computer, it is used for servers more so than workstations but just be careful editing registry settings as it may cause irreversible results. I hold no responsibility for anything you do to the regist…
Internet Business Fax to Email Made Easy - With  eFax Corporate (http://www.enterprise.efax.com), you'll receive a dedicated online fax number, which is used the same way as a typical analog fax number. You'll receive secure faxes in your email, f…

863 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now