The program I'm writing now had some strange behaviour, probably caused by InString. Under certain conditions (I don't know which exactly), this function does not preserve edi or crashes. My program parses e-mail headers, here's an example:

teststring	db	09,'boundary="----=_nextpart_000_000e_01c0e459.387ef300".171.135]:138',0
tFieldFrom	db	"from:",0
(the 09 is a tab char of course) then:

invoke	InString,1, ADDR teststring, ADDR tFieldFrom
edi changes when this line is executed. Is there a bug in this function? Thomas
Posted on 2001-05-24 09:12:00 by Thomas
Hi Thomas. This post does not constitute a solution by itself. Just wanted you to know that i thought i was the only loony thinking that instring was buggy. I had some trouble before and had to 'patch' instring. The patch is by no means a healthy thing for i tweaked the function to fit my needs. Maybe Hutch should review this function? Byes
Posted on 2001-05-24 09:56:00 by latigo
Likewise, While i was writing my Object Creator tool, i tried using InString to add valid variable names to the PROTO list (while making the function proc's). It was crashing left-right-&-center. I eventually traced it to the same file. But i didnt make any efforts to debug the problem either, i just wrote a work-around... NaN
Posted on 2001-05-24 17:44:00 by NaN
Guys, Sorry about any problems in InString but I have tested it to death and cannot reproduce the problem at all. The way its coded preserves and restores EBX, ESI & EDI in the normal manner so I just don't know what is happening when it fails. I have been working on search algorithms on and off for a while and when I get enough time, I am going to restructure the algorithm with a different type of search procedure anyway. I posted a brute force algorithm recently that is both a lot faster and does not appear to have any problems but it requires that you get the length of the zero terminated string yourself, Agner Fog's StrLen algo is plenty fast enough to do that. Regards, hutch@pbq.com.au
Posted on 2001-05-24 20:04:00 by hutch--
I've had this trouble too. Never saw anything wrong in the code, but still it crashed. My workaround was to surround the Instring invoke with pushad and popad. That worked.
Posted on 2001-05-24 22:44:00 by Ernie
Shlwapi.dll has a StrStr function for this.
Posted on 2001-05-25 00:06:00 by gfalen
Thomas, I need a favour from you, if you still have the code that caused the problem with InString, would you test the following for me, place, push eax push ecx push edx ; call InString pop edx pop ecx pop eax and let me know if this prevents the problem ? Ernie used PUSHAD and POPAD but what I am after is preserving the three normal registers rahter than all of them. Regards, hutch@pbq.com.au
Posted on 2001-05-26 22:39:00 by hutch--
Sorry Hutch, the problem isnt that :( I took some time (~1 1/2 hrs) and decided to figure it out (that is some wierd code, but anyways). First I will show you the problem:


  szList   db "12A45A7A4",0   ; String to search
  szSList  db "A",0           ; Sub String

 Correct 1 based answers-> 3, 6, 8

My Test Code:
     push ebx
     mov ebx, 0
          DPrintValH esp, "ESP"
     @@:
     cmp ebx, 10
     je @F
     push ebx

     DPrint " -= New Trial =- "
     DPrintValD ebx, "Start Index"
     invoke InString, ebx, addr szList, addr szSList
     DPrintValD eax, "Found Index" 

     pop ebx
     inc ebx
     jmp @B
     @@:
          DPrintValH esp, "ESP"
     pop ebx

The Results:
ESP:  0063FE14H
 -= New Trial =- 
Start Index:  0
Found Index:  -2       ; good!
 -= New Trial =- 
Start Index:  1
Found Index:  3        ; somehow good
 -= New Trial =- 
Start Index:  2
Found Index:  4        ; bad from here on...
 -= New Trial =- 
Start Index:  3
Found Index:  5
 -= New Trial =- 
Start Index:  4
Found Index:  9
 -= New Trial =- 
Start Index:  5
Found Index:  10
 -= New Trial =- 
Start Index:  6
Found Index:  11
 -= New Trial =- 
Start Index:  7
Found Index:  14
 -= New Trial =- 
Start Index:  8
Found Index:  -2
 -= New Trial =- 
Start Index:  9
Found Index:  -2
ESP:  0063FE14H

I traced out a few problems in the code.  I will point them out below:

; #########################################################################

    .386
    .model flat, stdcall  ; 32 bit memory model
    option casemap :none  ; case sensitive

    StrLen PROTO :DWORD

    .code

; #########################################################################

InString proc StartPos:DWORD, lpszString:DWORD, lpszSubStr:DWORD

  ; ------------------------------------------------------------------
  ; InString searches for a substring in a larger string and if it is
  ; found, it returns its position in eax. 
  ;
  ; It uses a one (1) based character index (1st character is 1,
  ; 2nd is 2 etc...) for both the "StartPos" parameter and the returned
  ; character position.
  ;
  ; Return Values.
  ; If the function succeeds, it returns the 1 based index of the start
  ; of the substring.
  ;  0 = no match found
  ; -1 = substring same length or longer than main string
  ; -2 = "StartPos" parameter out of range (less than 1 or longer than
  ; main string)
  ; ------------------------------------------------------------------

    LOCAL lnStrng :DWORD
    LOCAL lnSubSt :DWORD
    LOCAL reg1    :DWORD
    LOCAL reg2    :DWORD
    LOCAL Byte1   :BYTE

    push esi
    push edi
    push ebx

    .if StartPos < 1
      mov eax, -2               ; set eax -2
      jmp Get_Outa_Here         ; exit if less than 1
    .endif

    dec StartPos                ; correct to 0 based index

    invoke StrLen,lpszString    ; main string length
    mov lnStrng, eax
    push eax

    invoke StrLen,lpszSubStr    ; sub string length
    mov lnSubSt, eax

    pop eax
    sub eax, lnSubSt            ; subtract substr len from main string


   ; ######################## THIS SHOULDNT BE HERE #############
    dec eax                     ; for following comparison
   ; ######################## SIMPLY REMOVE IT ##################


   ; ######## THE COMPARISON SHOULD BE  if (StartPos > eax)
    .if StartPos >= eax      

      mov eax, -2               ; set eax -2
      jmp Get_Outa_Here         ; exit if greater than lnStrng
    .endif

    .if eax >= lnStrng
      mov eax, -1               ; set eax -1
      jmp Get_Outa_Here         ; exit if not shorter
    .endif

    mov esi, lpszSubStr         ; get 1st byte in substring
    lodsb
    mov bl, al                  ; put comparison byte in bl

  ; -------------------------------------------------------
  ; set maximum count as main string length minus substring
  ; -------------------------------------------------------
    mov ecx, lnStrng
    sub ecx, lnSubSt

  ;
Posted on 2001-05-27 02:13:00 by NaN
Hutch: I tried that before and it did solve some problems but even then there were unexpected results, and even some crashes inside InString. But I think NaN solved the problem. Thanks everyone, Thomas
Posted on 2001-05-27 07:53:00 by Thomas
Nan, Thanks for doing the work on the InString algo, it needs a full rewrite as I have enough search algos going at the moment and they are a lot faster than the one in InString. I have just finished Installing win95b by telephone and I am genuinely brain dead so I will have a look at the code later. Thanks again for your effort. hutch@pbq.com.au
Posted on 2001-05-27 10:26:00 by hutch--
No problem... I was thinking the same actually about rewriting the code, i found it very convoluted to follow through with. As for the 'lodsb' type commands, as i understand them anyways, they are supposed to be out-of-date in terms of performance... not too positive here, but i never use them when i code. NaN
Posted on 2001-05-27 10:46:00 by NaN
Here is a test piece I whipped up to test the example that Thomas posted. The pattern "from:" does not occur in the main string so the correct return value from InString is 0 and EDI does not change over 1000 iterations. You will note that I use a mem variable for the counter in the test loop, not a register. This behaviour is normal and what the procedure was designed to do so I don't know what is happening.

          .data
            teststring db 09,'boundary="----=_nextpart_000_000e_01c0e459.387ef300".171.135]:138',0
            tFieldFrom db "from:",0
          .code

            mov var, 1000

          @@:
            invoke InString,1, ADDR teststring, ADDR tFieldFrom
            UseTitleBar hWnd, eax, 1
            UseStatusBar hStatus, edi, 1
            dec var
            jnz @B

            ShowReturn hWnd, eax
Regards, hutch@pbq.com.au
Posted on 2001-05-27 11:01:00 by hutch--
Hutch, if you noticed in my lengthy post above, showing the origional set of errors, when a value of '1' is used as a starting position it "somehow" comes up with the correct answer, but if you change that starting position, it then begins to generate wrong ansers (due to the code near the bottom where i outlined it is unneeded). Repeating the same search 1000 times with out changing a variable will not indicate any problem from the first. I had prooved to myself that the code was ok in terms of EDI,ESI, and EBX (which is why i said its not in there..). There is a "Pseudo bug" for the condition your testing for however. I use Pseudo because it actually doesn't cause the stack pointer to become out of sink (kinda of a surprise really). The code pushes a value on the stack, on to the stack again! And if the first char of the substring is found it get poped off cleanly.. but if the first character is not found (your test condition), the code simply jumps to the exit where it pop's ebx, esi, and edi. Forgetting that ther is still a push of the lpszString! You would think this would cause complete havok, but it doesnt! (this was my surprise) I can only assume that MASM has enought 'intellegence' to realize this and ignore the push/pop and optomise better what was intended (ie] mov eax, lpszString) Anywho, just recapping on my above post... NaN
Posted on 2001-05-27 12:44:00 by NaN
I have just recoded the InString function from scratch and it is testing up OK so far, finds text at the beginning, midle and end of a source text and manages recursive calls with no problems.

; ########################################################################

Istring proc startpos:DWORD,lpSource:DWORD,lpPattern:DWORD

    LOCAL sLen:DWORD
    LOCAL pLen:DWORD

    push ebx
    push esi
    push edi

    invoke lnstr,lpSource
    mov sLen, eax           ; source length
    invoke lnstr,lpPattern
    mov pLen, eax           ; pattern length

    cmp startpos, 1
    jge @F
    mov eax, -2
    jmp isOut               ; exit if startpos not 1 or greater
  @@:

    dec startpos            ; correct from 1 to 0 based index

    cmp  eax, sLen
    jl @F
    mov eax, -1
    jmp isOut               ; exit if pattern longer than source
  @@:

    sub sLen, eax           ; don't read past string end
    inc sLen
    mov ebx, sLen

    cmp ebx, startpos
    jg @F
    mov eax, -2
    jmp isOut               ; exit if startpos is past end
  @@:

    mov esi, lpSource
    mov edi, lpPattern
    mov al,            ; get 1st char in pattern
    xor ecx, ecx

    add ecx, startpos       ; add starting offset

    jmp Loop_Start

  ; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

  Pre_Loop:
    pop ecx                 ; restore ECX
    inc ecx                 ; start on next byte

  Loop_Start:
    cmp al, 
    je Pre_Sub
    inc ecx
    cmp ecx, ebx
    jne Loop_Start
    jmp No_Match

  Pre_Sub:
    push ecx                ; preserve ECX
    mov edx, pLen
    mov edi, lpPattern

  Sub_Loop:
    mov ah, 
    cmp ah, 
    jne Pre_Loop            ; jump back on mismatch
    inc ecx
    inc edi
    dec edx
    jnz Sub_Loop            ; fall through if match

    pop ecx                 ; restore ECX
    mov eax, ecx            ; match
    inc eax
    jmp isOut
    
  ; @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

  No_Match:
    mov eax, 0

  isOut:

    pop edi
    pop esi
    pop ebx

    ret

Istring endp

; ########################################################################
This is one of the test pieces using the new function, it counts identical words in a source text.

          .data
            tString db "test test test test test test test test test test",0
            pString1 db "test",0
          .code

          mov cnt, 1
          mov var, 0

        @@:
          invoke Istring,cnt,ADDR tString,ADDR pString1
          inc eax
          mov cnt, eax
          inc var
          cmp eax, 0
          jg @B

          dec var

          ShowReturn hWnd, var
I would appreciate if anyone has the time to test this new version so ity can be placed in the MASM32 library. Regards, hutch@pbq.com.au This message was edited by hutch--, on 5/29/2001 4:07:00 AM
Posted on 2001-05-29 04:05:00 by hutch--