Announcement

Collapse
No announcement yet.

DebugBreak und Delphi

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • #31
    Vielleicht will ja jemand noch ein kleines Delphi IDE Plugin coden. z.B. den Compileroptions-Dialog patchen und eine ComboBox reinsetzen in der man das Verhalten des Asserthandlers noch einstellen kann. Wäre ja echt toll.

    Gruß Hagen

    PS: rede ich nur mir mir selbst, oder scheint die Sonne draussen ? :

    Comment


    • #32
      <i>PS: rede ich nur mir mir selbst, oder scheint die Sonne draussen ? </i><br>
      Sorry, aber meine heimische Internetanbindung hat den Geist aufgegeben. Und das rechtzeitig vor einem langen WE. :-( / :-)<br>
      Soeben bin ich wieder ins Internet gekommen und sehe als erstes neues Posting Nummer 13 von 27! Offenbar habe ich deine Neugier/Spieltrieb/... geweckt. :-)
      Ich werd mir das Ganze baldmöglichst mal reinziehen.<br>
      Ciao, Uli.
      <p>PS: Die Idee mit dem Plugin klingt gut. Bin grad eh aufm ToolsApi-Trip

      Comment


      • #33
        Hi Ulrich,

        habe den Source gerade unter D6-D7 getestet, wie zu erwarten funktionierte er da nicht Aber, mit einigen kleineren Änderungen läuft er nun auch unter D6-D7.<br>
        Falls du ihn haben willst dann mail mir an [email protected], ich habe nämlich keine Lust hier wieder 4 Einzelpostings zu posten nur weil die maximale Messagelänge so gering gehalten wird.<br>

        Übrigens, wenn der Code an der Assert() stoppt, kann nur mit F8 weitergesteppt werden. Bei F7,Strg+F7 springt er in den Code der über-übergeordneten Funktion ( <br>
        Mir persönlich reicht aber F8.
        <br>
        Zusätzlich habe ich am neuen Source noch eiige Vereinfachungen im Interface vorgenommen.<br>
        Wie gesagt, schick mir ne mail.

        Gruß Hagen

        PS: bezugnehmend auf eine meiner letzten Diskussionen hier im Forum. Der Grund warum der D5 Source nicht auf D6-D7 sofort funktionierte waren Änderungen in der RTL um diese Kylix kompatibel zu machen.<br>
        &#10

        Comment


        • #34
          Hallo Hagen,<br>
          Mail kommt -- danke im Voraus. Ich benutze zwar D5, aber ich will ja nichts verpassen. :-)
          <br>Ciao, Uli

          Comment


          • #35
            Hi Hagen!<br>
            So, jetzt hab ich alles mal aus den diversen Postings zusammenklabustert
            und ausprobiert -- funktioniert, aber keine Ahnung wie.
            <b>Grob</b> versteh ich ja noch, was du machst (am Stack rumbasteln,
            Returnadressen verbuchseln, ...) und in der Assemblerversion von ChoiceAssertErrorHandler
            glaube ich die Überbleibsel meines schnuckeligen Pascalcodes zu erkennen --
            dank einiger Konstanten als Orientierunhgspunkte. Aber einigermaßen im Detail...
            <p>
            Jetzt hab ich mir gedacht: kein Thema, debuggst du halt mal durch, dann
            wird's schon heller, aber nix da -- Debuggen is' nich'.
            Sobald ich auf CALL AssertErrorProc in _Assert stehe und F7 drücke,
            fängt's an, lustig zu werden. Ich komme zum Beispiel einfach nicht <b>in</b>
            die Implementation von ChoiceAssertErrorHandler hinein, sondern nur in
            z.B. das Format, das du in Debug aufrufst. In Debug selber wiederum komme
            ich auch nicht. Usw.ufs. Ich probiere später noch weiter, jetzt ist erstmal
            Feierabend. :-)
            <br>Ciao, Uli

            Comment


            • #36
              Hi Uli

              Als erstes musst du unbedingt die Compilerswitches hinter dem "Unit Asserts" ausklammern, ersetze einfach das "$" durch einen Punkt, oder setze ihn einfach davor. Dann wird alles mit einem Build und Debuginfos neu compliliert.<br>
              Im CPU Windows dann mit F7 debuggen.<br>

              Am Stack selber fummle ich sehr wenig rum. Und Recht haste, die Logik deines PASCAL Codes habe ich in die Assemblerroutine mit übernommen. Die ersten zeile mit dem CMP's sind deine CASE Abfrage. Der Compiler würde ähnlchen Code generieren. Alles bis auf den abDebug-Part ist eigentlich so wie in deinem PASCAL Source identisch.<br>

              Im abDebug Part, werden nun als erstes die parameter vom Stack geholt und der Stack so abgeräumt das ein RET NICHT in die übergeordnete _Assert() procedure zurückspringt sondern an die Adresse nach dem Assert() im Source.<br>

              Wir haben auf dem Stack, topdown:<br>
              PUSH AssertAddresse + 4<br>
              PUSH ReturnAddresse zu _Assert<br>
              PUSH ReturnAddresse zu Assert im Source = AssertAddress +4<br>

              Uns interssiert nur die AssertAdresse, deshalb wird alle andere vom Stack entfernt.<br>

              Bevor ich hier aber weiterposte, schicke ich dir erstmal die neueste Version, da sie wesentliche Änderungen enthält.<br>

              Gruß Hage

              Comment


              • #37
                Hier mochmal die überarbeitete Version. Sie benutzt nun einen anderen Trick und unterstützt auch die Keys F7,F8,F9 korrekt.<br>
                Lauffähig unter D5,D6,D7.<br>

                <pre>

                <code><font size=2 face="Courier New"><b>unit </b>Asserts;
                <i>{$A+,B-,C-,D-,E-,F-,G+,H+,I-,J+,K-,L-,M-,N+,O+,P+,Q-,R-,S-,T-,U+,V+,W-,X+,Y-,Z1}
                <br>
                </i><b>interface
                <br>
                type
                </b>TAssertType = (atDelphi, atIgnore, atHalt, atDebug, atAsk, atSavedOption);
                <br>
                <b>var
                </b>AssertType: TAssertType = atSavedOption;
                <br>
                <b>implementation
                <br>
                </b><i>// get complier
                {$DEFINE VER_???}
                <br>
                {$IFDEF VER140} // D6
                {$DEFINE VER_D6H}
                {$UNDEF VER_???}
                {$ENDIF}
                {$IFDEF VER150} // D7
                {$DEFINE VER_D6H}
                {$UNDEF VER_???}
                {$ENDIF}
                <br>
                {$IFDEF VER90} // D2
                {$UNDEF VER_???}
                {$ENDIF}
                {$IFDEF VER100} // D3
                {$UNDEF VER_???}
                {$ENDIF}
                {$IFDEF VER120} // D4
                {$UNDEF VER_???}
                {$ENDIF}
                {$IFDEF VER130} // D5
                {$UNDEF VER_???}
                {$ENDIF}
                <br>
                {$IFDEF VER_???}
                {$Error please verify code for compatibility}
                {$ENDIF}
                <br>
                <br>
                </i><b>uses
                </b>Windows, SysConst, SysUtils, Registry, TypInfo;
                <br>
                <b>var
                </b>OldAssertErrorProc: <b>procedure</b>(<b>const Message</b>, Filename: <b>string</b>; LineNumber: Integer; ErrorAddr: Pointer);
                <br>
                <b>function </b>HasDebugger: Boolean;
                <b>asm
                </b>MOV EAX,FS:[018h]
                MOV EAX,[EAX + 030h]
                MOV AL,[EAX + 002h]
                <b>end</b>;
                <br>
                <b>function </b>GetUserChoice(<b>const Message</b>, Filename: <b>String</b>; LineNumber: Integer; ErrorAddr: Pointer): TAssertType;
                <b>resourcestring
                </b>sAssertAsk = '%s'#13#10'(%s, Line %d, Address $%p)'#13#10#13#10 +
                'Wollen Sie die Anwendung beenden (&quot;Abbrechen&quot, ' +
                'debuggen (&quot;Wiederholen&quot oder die Assertion ignorieren?';
                <b>var
                </b>S: <b>String</b>;
                <b>begin
                </b>S := Format(sAssertAsk, [<b>Message</b>, Filename, LineNumber, ErrorAddr]);
                <b>case </b>MessageBox(GetForegroundWindow, PChar(S), PChar(sAssertionFailed),
                MB_ABORTRETRYIGNORE <b>or </b>MB_ICONERROR) <b>of
                </b>IDABORT: Result := atHalt;
                IDRETRY: <b>if </b>HasDebugger <b>then </b>Result := atDebug
                <b>else </b>Result := atDelphi;
                <b>else
                </b>Result := atIgnore;
                <b>end</b>;
                <b>end</b>;
                <br>
                <b>function </b>GetSavedChoice: TAssertType;
                <b>begin
                with </b>TRegIniFile.Create('SOFTWARE\Borland') <b>do
                try
                </b>Result := TAssertType(ReadInteger('Assertion', 'Type', 0));
                <b>if not </b>(Result <b>in </b>[atDelphi..atAsk]) <b>then </b>Result := atDelphi;
                <b>finally
                </b>Free;
                <b>end</b>;
                <b>end</b>;
                </font>
                </code></pre&gt

                Comment


                • #38
                  <pre>

                  <code><font size=2 face="Courier New"><b>function </b>Patch(Addr: PChar): Boolean;
                  <i>// patch at Addr to INT 3; MOV EDX,[EDX]
                  // if not possible the return FALSE, otherwise TRUE
                  </i><b>type
                  </b>PBreakpoint = ^TBreakpoint;
                  TBreakpoint = <b>packed record
                  </b>INT3: Byte;
                  MOV_EDX_EDX: Word;
                  <b>end</b>;
                  <br>
                  <b>var
                  </b>Protect: DWord;
                  <b>begin
                  </b>Result := VirtualProtect(Addr, SizeOf(TBreakpoint), PAGE_READWRITE, @Protect);
                  <b>if </b>Result <b>then
                  try
                  </b>PBreakpoint(Addr).INT3 := $CC;
                  PBreakpoint(Addr).MOV_EDX_EDX := $128B; <i>// MOV EDX,[EDX] where EDX is 0
                  </i><b>finally
                  </b>VirtualProtect(Addr, SizeOf(TBreakpoint), Protect, <b>nil</b>);
                  FlushInstructionCache(GetCurrentProcess, Addr, SizeOf(TBreakpoint));
                  <b>end</b>;
                  <b>end</b>;
                  <br>
                  <b>function </b>AssertAddr: PChar;
                  <i>// get address of _Assert in System.pas
                  </i><b>asm
                  </b>MOV EAX,OFFSET System.@Assert
                  <b>end</b>;
                  <br>
                  <b>procedure </b>Unpatch(Addr: PChar);
                  <i>// patch at Addr CALL System._Assert, we restore so above patch
                  </i><b>type
                  </b>PCall = ^TCall;
                  TCall = <b>packed record
                  </b>OpCode: Byte;
                  Offset: Integer;
                  <b>end</b>;
                  <br>
                  <b>var
                  </b>Protect: DWord;
                  <b>begin
                  if </b>VirtualProtect(Addr, SizeOf(TCall), PAGE_READWRITE, @Protect) <b>then
                  try
                  </b>PCall(Addr).OpCode := $E8;
                  PCall(Addr).Offset := AssertAddr - Addr - SizeOf(TCall);
                  <b>finally
                  </b>VirtualProtect(Addr, SizeOf(TCall), Protect, <b>nil</b>);
                  FlushInstructionCache(GetCurrentProcess, Addr, SizeOf(TCall));
                  <b>end</b>;
                  <b>end</b>;
                  <br>
                  <b>procedure </b>Debug(<b>const Message</b>, Filename: <b>string</b>; LineNumber: Integer; ErrorAddr: Pointer);
                  <b>resourcestring
                  </b>sAssertDebug = 'Assert %s: %s at line %d on address $%p';
                  <b>var
                  </b>S: <b>String</b>;
                  <b>begin
                  </b>S := Format(sAssertDebug, [<b>Message</b>, FileName, LineNumber, ErrorAddr]);
                  OutputDebugString(PChar(S));
                  <b>end</b>;
                  </font>
                  </code></pre&gt

                  Comment


                  • #39
                    <pre>

                    <code><font size=2 face="Courier New"><b>procedure </b>ChoiceAssertErrorHandler(<b>const Message</b>, Filename: <b>string</b>; LineNumber: Integer; ErrorAddr: Pointer);
                    <b>type
                    </b>PContext = ^TContext;
                    TContext = <b>packed record
                    </b>ContextFlags: DWord;
                    <i>// Debug
                    </i>RegDr0,RegDr1,RegDr2,RegDr3,RegDr6,RegDr7: DWord;
                    <i>// Floating Point
                    </i>ControlWord,StatusWord,TagWord,
                    ErrorOffset,ErrorSelector,
                    DataOffset,DataSelector: DWord;
                    Registers: <b>array</b>[0..79] <b>of </b>Byte;
                    Cr0NpxState: DWord;
                    <i>// Segments
                    </i>SegGS,SegFS,SegES,SegDS: DWord;
                    <i>// Integers
                    </i>RegEDI,RegESI,RegEBX,RegEDX,RegECX,RegEAX: DWord;
                    <i>// Control
                    </i>RegEBP,RegEIP,SegCS,EFlags,RegESP,SegSS: DWord;
                    <b>end</b>;
                    <br>
                    <br>
                    <b>const
                    </b>CONTEXT_i386 = $00010000;
                    CONTEXT_i486 = $00010000;
                    CONTEXT_CONTROL = CONTEXT_i386 <b>or </b>$00000001;
                    CONTEXT_INTEGER = CONTEXT_i386 <b>or </b>$00000002;
                    CONTEXT_SEGMENTS = CONTEXT_i386 <b>or </b>$00000004;
                    CONTEXT_FLOATING_POINT = CONTEXT_i386 <b>or </b>$00000008;
                    CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 <b>or </b>$00000010;
                    <br>
                    CONTEXT_NORMAL = CONTEXT_CONTROL <b>or </b>CONTEXT_INTEGER <b>or </b>CONTEXT_SEGMENTS;
                    CONTEXT_ALL = CONTEXT_CONTROL <b>or </b>CONTEXT_INTEGER <b>or </b>CONTEXT_SEGMENTS <b>or
                    </b>CONTEXT_FLOATING_POINT <b>or </b>CONTEXT_DEBUG_REGISTERS;
                    <br>
                    <b>type
                    </b>PExceptionRecord = ^TExceptionRecord;
                    TExceptionRecord = <b>packed record
                    </b>ExceptionCode: DWord;
                    ExceptionFlags: DWord;
                    NextExceptionRecord: PExceptionRecord;
                    ExceptionAddress: Pointer;
                    NumberParameters: Integer;
                    <b>case </b>Integer <b>of
                    </b>0: (ExceptAddr: Pointer; ExceptObject: Pointer);
                    1: (ExceptionInformation: <b>array </b>[0..14] <b>of </b>DWord);
                    <b>end</b>;
                    <br>
                    PExceptionFrame = ^TExceptionFrame;
                    TExceptionFrame = <b>packed record
                    </b>Next: PExceptionFrame;
                    Desc: Pointer;
                    RegEBP: DWord;
                    <b>end</b>;
                    <br>
                    PExceptionParams = ^TExceptionParams;
                    TExceptionParams = <b>packed record
                    </b>Caller: Pointer;
                    Exception: PExceptionRecord;
                    Frame: PExceptionFrame;
                    Context: PContext;
                    Dispatcher: PContext;
                    <b>end</b>;
                    <br>
                    </font>
                    </code></pre&gt

                    Comment


                    • #40
                      <pre>

                      <code><font size=2 face="Courier New"><b>asm
                      </b>PUSH EAX <i>// save params
                      </i>PUSH EDX
                      PUSH ECX
                      <br>
                      PUSH ErrorAddr <i>// some Debugoutput
                      </i>CALL Debug <i>// visible at eventlog
                      <br>
                      </i>MOV AL,AssertType <i>// set AL to default
                      </i>CMP AL,atSavedOption
                      JNZ @@0
                      <br>
                      CALL GetSavedChoice
                      <br>
                      @@0: CMP AL,atAsk
                      JNZ @@1 <i>// AssertiType &lt;&gt; atAsk ??
                      <br>
                      </i>MOV ECX,[ESP + 0]
                      MOV EDX,[ESP + 4]
                      MOV EAX,[ESP + 8] <i>// Message
                      </i>PUSH ErrorAddr <i>// NO, ask user
                      </i>CALL GetUserChoice
                      <br>
                      @@1: POP ECX
                      POP EDX
                      <br>
                      CMP AL,atHalt
                      JNZ @@2 <i>// AssertType &lt;&gt; atHalt ??
                      </i>MOV EAX,666 <i>// NO, then FatalExist(666)
                      </i>CALL FatalExit
                      JMP @@9 <i>// jmp to restore stack, but normaly never called
                      <br>
                      </i>@@2: CMP AL,atDelphi <i>// AssertType == atDelphi ??
                      </i>JZ @@8
                      <br>
                      @@3: CMP AL,atDebug <i>// AssertType &lt;&gt; atDebug ??
                      </i>JNZ @@9
                      <br>
                      MOV EAX,ErrorAddr <i>// patch in our INT3, MOV EDX,[EDX]
                      </i>SUB EAX,3
                      CALL Patch
                      TEST AL,AL <i>// successfully patched ??
                      </i>JZ @@8 <i>// NO, jump to @@8
                      </i>MOV EBP,[ESP + 4] <i>// first restore EBP
                      </i>ADD ESP,16 <i>// remove some stuff
                      {$IFDEF VER_D6H} // difference D6,D7 because of Kylix PIC changes
                      </i>POP EBX <i>// we cleanup here the stack from our caller, eg. _assert()
                      {$ENDIF} // because we don't want to return to it
                      <br>
                      </i>MOV EAX,[ESP] <i>// leave ErrorAddr on Stack as EBP of ExceptFrame
                      </i>XOR EDX,EDX
                      SUB EAX,3 <i>// address of our INT 3
                      <br>
                      </i>PUSH OFFSET @@4 <i>// install SEH
                      </i>PUSH DWord Ptr FS:[EDX]
                      MOV DWord Ptr FS:[EDX],ESP
                      <br>
                      JMP EAX <i>// jump to patch
                      <br>
                      { here we have in our SEH = try except block follow executions
                      <br>
                      try -&gt; above install SEH
                      JMP EAX
                      @EAX: INT 3 -&gt; raise Exception if NO debuggee present
                      MOV EDX,[EDX] -&gt; raise Exception anycase, because EDX=ZERO
                      except -&gt; @@4: SEH Handler
                      end; -&gt; RET SEH
                      <br>
                      }
                      <br>
                      </i>@@4: MOV EDX,[ESP].TExceptionParams.Frame <i>// our SEH Handler
                      </i>MOV EAX,[EDX].TExceptionFrame.Next <i>// remove SEH here
                      </i>MOV FS:[0],EAX
                      MOV EAX,[EDX].TExceptionFrame.RegEBP <i>// ErrorAddr
                      </i>MOV EDX,[ESP].TExceptionParams.Context <i>// Context
                      </i>ADD [EDX].TContext.RegESP,12 <i>// cleanup stack 3 DWords of SEH
                      </i>MOV [EDX].TContext.RegEIP,EAX <i>// new EIP right after call _Assert()
                      </i>SUB EAX,5
                      MOV EDX,[EAX] <i>// hm?, important access to codesegment,
                      // otherwise Debugger don't work with F8/F7
                      </i>CALL Unpatch <i>// remove INT3; MOV EDX,[EDX];
                      </i>SUB EAX,EAX
                      RET
                      </font>
                      </code></pre>
                      &#10

                      Comment


                      • #41
                        <pre>

                        <code><font size=2 face="Courier New"><br>
                        DB 'copyright 2003 by Hagen Reddmann, ' <i>//
                        </i>DB 'idea and basics by Ulrich Gerhardt'
                        <br>
                        @@8: MOV EAX,[ESP] <i>// restore message
                        </i>PUSH ErrorAddr <i>// delphi's assert handling
                        </i>CALL OldAssertErrorProc
                        <br>
                        @@9: POP EAX
                        <br>
                        <i>{ How it work
                        We assign our ChoiceAssertErrorHandler as Assertion Handler.
                        If an assertion fails this procedure is called. We check here AssertType and if
                        set we ask the user trough a dialog what should happend.
                        If AssertType is atDebug the user want to stop the Debuger at the Assert() location
                        in the sources.
                        To get this working we patch the code on this Assert() call with INT 3 and MOV EDX,[EDX] and
                        after successfull patching we jump to this location. The INT 3 let the debugger stop.
                        If we trace or run (eg. keys F7,F8,F9) then the next instruction, MOV EDX,[EDX] is executed.
                        BUT, we have setup EDX to ZERO, this means MOV EDX,[EDX] raise an access violation.
                        Now, in this procedure here we have setup our own SEH=Structured Exception Handler to
                        grap all exceptions. Means, after execution of the INT 3 and next execution of MOV EDX,[EDX=0]
                        our SEH Handler is called. Above at label @@4. Then we uninstall our SEH, remove our
                        patched INT 3, MOV EDX,[EDX] and setup the new EIP=Instruction Pointer, where the OS
                        continue our Program, to the code right after our CALL to Assert(). Then all
                        code, stack and important registeres are such as before calling Assert().
                        With the exception that we have stopped the debuggee on the Assert() location.
                        But what's happend if we start our program without a Debuggee ??
                        Then the INT 3 would raise an Breakpoint Exception visible to our installed
                        Exceptionhandler. Means instead of an Exception raised from MOV EDX,[EDX] we get
                        an Exception raised by our INT 3. New our SEH is called on Label @@4, such as with
                        MOV EDX,[EDX=0]. In any case our SEH uninstalls it self, unpatch our INT 3 and
                        let continue our program right after the failed assertion.
                        <br>
                        One important consideration is the fact that we have to take care with our stack.
                        First we must cleanup the stack in such a way that the call from System._Assert to
                        our handler is removed. Means if we returned without our patch then we don't return
                        to System._Assert(), instead we return to the Assert() of the sourcecode.
                        <br>
                        After the jump to our INT 3 the stack and all important registers, except EAX,EDX,ECX
                        should be in state as before the call to Assert(). That's the case here.
                        This means we can fully work with Debugwatches.
                        }
                        </i><b>end</b>;
                        <br>
                        <br>
                        <b>initialization
                        </b>OldAssertErrorProc := AssertErrorProc;
                        AssertErrorProc := @ChoiceAssertErrorHandler;
                        <b>finalization
                        </b>AssertErrorProc := @OldAssertErrorProc;
                        <b>end</b>.
                        </font>
                        </code></pre&gt

                        Comment


                        • #42
                          Nachfolgende Unit als Komponente ins User Package installieren. Unter Menu\Debugger Optionen\Allgemein kann man über die Delphi IDE das Assert-Verhalten einstellen.<br>

                          <pre>

                          <code><font size=2 face="Courier New"><b>unit </b>AssertsIDE;
                          <br>
                          <b>interface
                          <br>
                          implementation
                          <br>
                          uses </b>Windows, Messages, Classes, Controls, StdCtrls, SysUtils, Registry, Forms;
                          <br>
                          <i>// get complier
                          {$DEFINE VER_???}
                          <br>
                          {$IFDEF VER140} // D6
                          {$DEFINE VER_D6H}
                          {$UNDEF VER_???}
                          {$ENDIF}
                          {$IFDEF VER150} // D7
                          {$DEFINE VER_D6H}
                          {$UNDEF VER_???}
                          {$ENDIF}
                          <br>
                          {$IFDEF VER90} // D2
                          {$UNDEF VER_???}
                          {$ENDIF}
                          {$IFDEF VER100} // D3
                          {$UNDEF VER_???}
                          {$ENDIF}
                          {$IFDEF VER120} // D4
                          {$UNDEF VER_???}
                          {$ENDIF}
                          {$IFDEF VER130} // D5
                          {$UNDEF VER_???}
                          {$ENDIF}
                          <br>
                          <br>
                          </i><b>type
                          </b>TAssertion = <b>class
                          class procedure </b>OnClick(Sender: TObject);
                          <b>class function </b>GetType: Integer;
                          <b>class procedure </b>SetType(Value: Integer);
                          <b>end</b>;
                          <br>
                          <b>class procedure </b>TAssertion.OnClick(Sender: TObject);
                          <b>begin
                          </b>SetType((Sender <b>as </b>TComboBox).ItemIndex);
                          <b>end</b>;
                          <br>
                          <b>class function </b>TAssertion.GetType: Integer;
                          <b>begin
                          with </b>TRegIniFile.Create('SOFTWARE\Borland') <b>do
                          try
                          </b>Result := ReadInteger('Assertion', 'Type', 0);
                          <b>finally
                          </b>Free;
                          <b>end</b>;
                          <b>end</b>;
                          <br>
                          <b>class procedure </b>TAssertion.SetType(Value: Integer);
                          <b>begin
                          with </b>TRegIniFile.Create('SOFTWARE\Borland') <b>do
                          try
                          </b>WriteInteger('Assertion', 'Type', Value);
                          <b>finally
                          </b>Free;
                          <b>end</b>;
                          <b>end</b>;
                          <br>
                          <b>var
                          </b>Hook: hHook = 0;
                          <br>
                          <b>function </b>MyHook(Code,wParam,lParam: Integer): Integer; <b>stdcall</b>;
                          <b>var
                          </b>P,C: TWinControl;
                          B: TComboBox;
                          <b>begin
                          if </b>lParam &lt;&gt; 0 <b>then
                          with </b>PCWPStruct(lParam)^ <b>do
                          if message </b>= wm_Create <b>then
                          begin
                          </b>P := FindControl(hWnd);
                          <b>if </b>(P &lt;&gt; <b>nil</b>) <b>and </b>(P.ClassName = 'TDebuggerOptionsPage') <b>then
                          begin
                          </b>C := GetParentForm(P);
                          <b>if </b>(C &lt;&gt; <b>nil</b>) <b>and </b>(C.ClassName = 'TDebuggerOptDialog') <b>then
                          begin
                          </b>TComponent(B) := P.FindComponent('AssertionComboBox');
                          <b>if </b>B = <b>nil then
                          begin
                          </b>B := TComboBox.Create(P);
                          B.Parent := P;
                          <i>{$IFDEF VER_D6H}
                          </i>B.Left := 8;
                          B.Width := P.ClientWidth - 16;
                          B.Top := P.ClientHeight - 8;
                          <i>{$ELSE}
                          </i>B.Left := 4;
                          B.Width := P.ClientWidth - 8;
                          B.Top := P.ClientHeight - 25;
                          <i>{$ENDIF}
                          </i>B.Style := csDropDownList;
                          B.Items.Add('Assertionhandling by Delphi, raise EAssertError Exception');
                          B.Items.Add('ignore Assertion and execute code');
                          B.Items.Add('Halt on Assertion and stop Application');
                          B.Items.Add('Debug on Assertion and goto Sourcecode');
                          B.Items.Add('Ask User on Assertion');
                          B.ItemIndex := TAssertion.GetType;
                          B.OnClick := TAssertion.OnClick;
                          <b>end</b>;
                          <b>end</b>;
                          <b>end</b>;
                          <b>end</b>;
                          <b>if </b>Code &lt; 0 <b>then </b>Result := 0
                          <b>else </b>Result := CallNextHookEx(Hook, Code, wParam, lParam);
                          <b>end</b>;
                          <br>
                          <b>initialization
                          </b>Hook := SetWindowsHookEx(WH_CALLWNDPROC, MyHook, 0, GetCurrentThreadID);
                          <b>finalization
                          </b>UnhookWindowsHookEx(Hook);
                          <b>end</b>.
                          </font>
                          </code></pre&gt

                          Comment


                          • #43
                            obiger Trick ist schmutzig aber effektiv

                            Comment


                            • #44
                              Mails sind angekommen. Da hab ich wieder ein Päckchen durchzuarbeiten. Wie ich sehe, hat sich mein ToolsAPI-Part erledigt, dank deiner Hookerei. Vielleicht bastel ich da trotzdem noch ein wenig, weil mir Hooks suspekt sind. :-) Aber <b>in</b> den Debuggeroptionsdialog krieg ich die Einstellmöglichkeit vermutlich nicht, soweit ein schneller Blick in die ToolsAPI.pas das hergibt.
                              <br>Uli.
                              <p>PS: Wenn man das $D- aus'm Quellcode nimmt, geht das Debuggen um Längen besser. :-)
                              <br>PPS: Seh ich das richtig, dass du mit den hartcodierten Compileroptionen stabile Verhältnisse für die Asm-Teile schaffen willst

                              Comment


                              • #45
                                Korrekt, es ist sogar notwendig da man ja zur Entwicklungszeit nicht will das man diesen Code per F7/F8 erreicht. Wozu sollte ich jedesmal diesen assembler part debuggern wollen ??<br>

                                Klar, wenn man zur Entwicklungszweit diesen Source entwickeln will, also so wie jetzt, dann muß man diese Switches ALLE deaktivieren. Besser so, da man dann auch die zusätzlichen Überprüfungen wieder aktiviert.<br>

                                About ToolsAPI:<br>
                                Ich weiß mein Weg ist schmutzig aber echt stabil und nach allen Regeln der Kunst. Es kann also nichts schiefgehen.<br>
                                Wie du schon festgestellt hast unterstützt das ToolsAPI keine Möglichkeit ähnliches ohne diese Tricks zu erreichen. Desweiteren verspreche ich dir das eine adäquate ToolsAPI Lösung 5 mal mehr Sourcecode benötigt. Da sieht man mal wie eine unglückliche Schnittstelle den Aufwand erhöht statt zu reduzieren.<br>
                                Natürlich wäre eine saubere Lösung in jedem Fall vorzuiehen, aber noch mehr Zeit will ich eigentlich nicht in das Projekt investieren.<br>

                                Interssant wäre nun noch zu wissen ob der Source auch unter Win9x Systemen funktioniert.<br>

                                Gruß Hagen

                                PS: besser ist es Du beschäftigst dich mehr mit dem Patchtrick und dem SEH. Dies verstanden zu haben eröffnet einem enorme neue Möglichkeiten.
                                Z.b. eigene try except Implementationen die NICHT vom Debugger gesehen werden, sind manchmal sinnvoll. Es gibt nämlich keine Wege per Programcode den Debuggerprozess zu beeinflussen. Unnötige Exception können also damit für die Zukunft beseitigt werden OHNE das man auf das Exceptionhandling verzichten muß. Oder so wie im Code gezeigt ist es möglich ein Exceptionhandling aufzubauen das über proceduren hinweg funktioniert. Es muß also NICHT immer hart und logisch strukturiert sein.
                                &#10

                                Comment

                                Working...
                                X