Results 1 to 27 of 27

Thread: Redirect where Debug.Print goes.

  1. #1

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,716

    Redirect where Debug.Print goes.

    It'd be nice if I could figure out how to redirect where Debug.Print goes.

    I've got a little persistent DebugPrint program I often use. It's easy to use, as I just remove the dot from my Debug.Print statements. However, it'd be even more cool if I could just redirect everything.

    At first, I created an auto-instantiating class named "Debug". That worked just fine.

    However, there are still two problems:

    1) This class still doesn't override the internal Debug object.

    2) "Print" is a reserved word and can't be used as a procedure name.

    So drat. Any ideas?
    Last edited by Elroy; Oct 31st, 2022 at 02:16 PM.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  2. #2
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    I was actually just looking at it this weekend. I still want a debug.clear (I use the persistent debug window as well thank you

    I was look at vb5 because I could get the debug symbols for vba5.dll to load, it should be very similar in vb6

    the pcode for debug.print triggers opcode FF11 _lblEX_Debug2

    Code:
    0FBD898C > A1 D49EBE0F      MOV EAX,DWORD PTR DS:[FBE9ED4]  -> 001516D4  0FA66720  VBA5.DWIN::`vftable'
    0FBD8991   50               PUSH EAX
    0FBD8992   33C0             XOR EAX,EAX
    0FBD8994   8A06             MOV AL,BYTE PTR DS:[ESI]
    0FBD8996   46               INC ESI
    0FBD8997   FF2485 909BBD0F  JMP DWORD PTR DS:[EAX*4+FBD9B90]
    
    vtable
    $ ==>    >0FB562F5  VBA5.DWIN::QueryInterface
    $+4      >0FB54BC5  VBA5.DWIN::AddRef
    $+8      >0FB54BCD  VBA5.DWIN::Release
    $+C      >0FB56173  VBA5.DWIN::PrintText
    $+10     >0FB5622E  VBA5.DWIN::SetColumn
    $+14     >0FB562DE  VBA5.DWIN::GetColumn
    $+18     >0FB5654E  VBA5.RECORD::LinesInserted
    $+1C     >0FB56572  VBA5.RECORD::LinesDeleted
    $+20     >0FB54BC1  VBA5.RECORD::LinesChanged
    $+24     >0FB54BC4  VBA5.RECORD::TxtmgrChange
    $+28     >0FB54BA6  VBA5.LNBUFZ::`scalar deleting destructor'
    $+2C     >0FB40A34  VBA5.LNBUF::EnsureSize
    $+30     >0FB409B7  VBA5.LNBUF::Clear
    $+34     >0FB4098B  VBA5.LNBUF::Free
    $+38     >0FB40E71  VBA5.LNBUFZ::GetXszDelim
    $+3C     >0FB40AB8  VBA5.LNBUF::ReallocBuffer
    $+40     >0FB40F7E  VBA5.LNBUFZ::IsEqual
    $+44     >00000000
    After the debug object is loaded it goes on to to VBA5.DWIN::QueryInterface VBA5.IID_IVbaPrint
    then calls __vbaPrintObj which eventually leads to

    .text:0FB56173 ; int __stdcall DWIN::PrintText(DWIN *__hidden this, LPCWCH lpWideCharStr)

    I havent traced it further yet but my first stab would be to hook that

    symbols for 5/6: http://sandsprite.com/vb-reversing/vb_symbols.zip

    Btw if anyone has working symbols for vba6.dll I’d love a copy. (Or if you can find a way to load those in the zip)
    Last edited by dz32; Oct 31st, 2022 at 04:56 PM.

  3. #3
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    4,483

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by dz32 View Post
    I was look at vb5 because I could get the debug symbols for vba5.dll to load
    That's clever! Do you a PDB or a DBG for the debug symbols of VBA5.DLL?

    Edit: Oops, you linked it already. I'll snatch it before admin take it down :-))

    cheers,
    </wqw>

  4. #4
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    I got super lucky finding those files lol. I think I first stumbled across the ms knowledge base articles

    Q188588: VB5SP3DS Contains Visual Basic SP3 Debugging Symbols
    Q176547: Vb5sp2ds Contains Visual Basic SP2 Debugging

    Then I had to try to find the actual downloads from the 90s. Eventually an ancient mirror of the ms ftp server from that era was found.

    To open the .dbg debug symbols in IDA you must use the MSDIA parser I use a batch file:

    [path]\ida.exe -Opdb:msdia %1

    Olly seems happy with most of them. in at least one case, only my 2004 copy of IDA 4.8 would properly parse the dbg file so I included that idb as well

    I still haven’t found anything that can handle the vba6.dbg symbols maybe I will have to try to find a tool to parse/export the data

    Ms used to be generous with thier symbols. A bunch more info available in the zips parent directory page.

    One other note vba print obj had a call to deserialize in it. It looks like debug.print might be able to do more fancy stuff than I imagined like deserialize some complex types like if you pass it a class object? Might have to implement an extra interface though. Or how get/put handle udts? Dunno need to examine it more could be nothing 🤷
    Last edited by dz32; Nov 1st, 2022 at 06:39 AM.

  5. #5

  6. #6

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by dz32 View Post
    I was look at vb5 because I could get the debug symbols for vba5.dll to load, it should be very similar in vb6
    Hello.

    The Trick has just told me that he managed to get Debug Symbols for VBAx.DLL (aka EB part in EB/Ruby terminology). Our further conversation led me to this topic and your discovery.

    This is cruical exploration in context of VB reverse-engineering, I must say.

    As you know, I have spent some years trying to reverse-engineer VB6 as a whole product. Diving into msvbvm guts was relatively easy work, while digging into VBAx.DLL was really challenging, since you need to guess purpose of every function, every variable, and there is no hint except of your own interpretation of what's going to happen at this particular code.

    So, I was wondered to match my own guessed function/variable names with "official" ones to check how accurate was my assumptions regarding nature of things that functions do and tasks they are supposed to solve.

    It's now less than a hour passed since I opened your DBG file, but I've already matched dozens of entities with my own reverse-engineering results. I think the next few days or weeks will be very exciting.

    So, as I mentioned before, I discovered/reverse-engineered/described like 2000 or more functions/classes/variables from VBA6.DLL.

    For obvious reasons they were given temporary names (guessed names) which are different to authentic ones (which are now available from VBA5.DBG). However, for many of these functions I have very descriptive articles describing each argument, the behaviour of function and C languange pseudo-code illustrating how original source code might look like.

    I will be definitely usefull if all information I collected during these year can be merged information that can be extracted from this DBG.

    Probably I should make public portal and open all my reverse-engineering documentations, so that you can take function/method from this DBG and read what does it do and what arguments does it take from my articles?

    One major problem is that names within my work do not match official or authentic Microsoft names (available in DBG). They need to be matched manually, at least until I/we make a tool that can match names by analyzing/traversing CFG/EFG (code flow graph/execution flow graph) of two binaries (VBA6.DLL and VBA6.DLL).

    Here's some observations I've already made (even though I got VBA5.DBG less than a hour ago):
    1. Official names of functions and variables seem to be more brief than names assigned by me. However, they are less obvious and often confusing.
    2. They didn't follow one consistent naming scheme. Some functions do have prefixes indicating their relation to a particular component of EB engine, and some don't. Some classes are named using CCamelCaseClass scheme, some are use UPPERCASE names with lots of abbriations.
    3. For instance, there is class which I called CSmallHeap. It turned out that it was originally called BLKMGR32 (bulk memory allocations manager).
    4. There is a lack of consistency in how they call a single thing or conception in different places.

    Let me make small introduction into how VB IDE represents BASIC code under the hood. VB IDE is an example of very clever technique where parsing/analyzing and compilation of source code starts at the moment of inserting it into IDE window. VB IDE does not store source code as big plain text, nor it stores it as an array of lines or rows. When you finishing editing a line of code by pressing Enter of moving caret to another line using mouse or keyboard arrows, this line is parsed, analyzed and converted to more compact binary format of representation. Code isn't represented as text, lines of text or parts of lines of text internally. Additional internal structures and entries are created exactly when you type a new line with declaration of new Sub or variable. In other words, at any given moment of time VB IDE already knows all its subs, functions and other entities defined in code, contrary to traditional compilers which start to parse/analyze code only when compilation/run pass is invoked.

    I use the following terminology.
    1. Line as a text. The line of VBcode exist in form of pure text only during line editing process. As soon as you finish line by pressing Enter or moving caret, a line will undergo few transformations. The first one is parsing and building PCR tree from sequence of characters.

    2. PCR tree. PCR stands for Parsed Code Representation. PCR tree consists of nodes, a node may represent a whole statement and its subnodes may represent some details of statements, literals, name references and so on. For every syntactically correct line of VB code PCR tree can be built and is actually built during parsing process. Syntax error encountered during parsing abort process of PCR tree construction and leave PCR tree unfinished. In any case, PCR tree is short-living form of VB-code source line representation.

    3. No matter whether line was successfully parsed or there was an error (Syntax error), yet another form of representation is used for long-term storage of VB code lines. I called this BSCR. BSCR stands for Binary Source Code Representation. Before I discovered BSCR, I though PCR is actually used for long-term source code storage. Although PCR is "binary" too, there are major differences between PCR and BSCR. PCR is short-living and BSCR is for long-term storage. PCR tree is made of nodes, each node is a structure containing 32-bit pointers to child PCR nodes (if any). PCR tree therefore can be rearranged or inserted into another PCR tree (so it becomes subtree) just be setting 1 pointer. In contrast to PCR, BSCR is a linear sequence of 16-bit words (I call them BSCR entities). There is no pointers in BSCR representations. The relationship between entities in BSCR is encoded by the order in which BSCR entities lie in memory rather than by pointers to child elements. Therefore BSCR data can be easily moved in memory to a new location. While PCR trees can only represent syntactically correct lines of VB codes, BSCR supports representing invalud code. While comments and labels are not part of PCR trees, they are always part of BSCR data for code lines.

    4. When source code must be rendered on a screen, saved into file on disk or temporarely represented as a text for performing search (Ctrl+F), the reverse transformation takes place. I call this TSCR. TSCR stands for Text(ual) Source Code Reconstruction. Sometimes I call initial form in VB-code representation as TSCR too (in this case TSCR is likely to be Textual Source Code Representation). TSCR does more than reconstructing text (a sequence of characters that can be rendered on a screen) which matches original line of source code, it also generates syntax-highlighting map and provides calling side with a so-called capturing feature: having a logical reference to some sub-expression within the line, you can determine which range of characters it spans (used for mouse-hovering tips, yellow highlighting of current statement during step-by-step execution and so on).


    With all names that I assigned (by guessing) during my work, I use a prefix (Parsed/Pcr/Bscr/Tscr) to determine which mechanism this function relates to. The authentic names do not follow this rule, and sometimes the same thing is called differently which brings confusion (at least, in my opinion).

    The thing that I call PCR is turned out to be called PT (parsing tree, obviously?).
    The next stage which I call BSCR and BSCR entities is sometimes referred to as Opcodes, and sometimes as Pcode. This annoys be, because term 'PCode' is already used for byte code of VB virtual machine, and BSCR is definitely is not the same as P-code. Opcodes isn't very suitable name too, because it creates confustion with opcodes of P-code or opcodes of native code x86 (yet again, BSCR entities are neither P-code opcodes nor x86 machine code opcodes).
    All functions involved into reconstructing text representation for BSCR representations turned out to have "Ulist" prefix in its names. What is U in Ulist? Who knows...


    Here is example of matching my names against authentic names:

    class CBscrLines


    • Class CSmallHeap turned out to be BLKMGR32
    • Class CVbaProject turned out to be GEN_PROJECT
    • Class CBscrLines turned out to be OPCODE_MGR
    • mynmParserParseLine turned out to be static method parser::parseOneLine
    • mynmParserCommitPcrNode turned out to be ptCreateOperNode
    • mynmTscrReconstructLine turned out to be ListOneLine
    • mynmTscrProcessLineBscr turned out to be ListOpcodes
    • tblBscrToTextHandlers array turned out to be g_fnListRules
    • mynmTscrEmitParticles turned out to be ListRule
    • CBucketTree class turned out to be RECLIST
    • CBucketTree::TraverseTreeOfElements turned out to be RECLIST::FTraverse
    • mynmTscrEmitKeyword turned out to be SingleWord
    • mynmTscrEmitTwoKeywords turned out to be DoubleWord
    • mynmTscrStartNewStatement turned out to be ResetGlobals
    • mynmTscrHandleDefXxxStatement turned out to be UListDeftype
    • mynmTscrHandleForEachStatement turned out to be UListForEach
    • mynmCbLengthOfBscrVl turned out to be CbVariableSizeOpcode
    • mynmXstackCleanUp turned out to be parser::parseRsrcRelease
    • CMSNR class (module-specific name-related, represents module scope actually) turned out to be TYPE_DATA class (very misleading name...).
    • mynmBscrWriteCodeLine is actually parser::generatePcode.
    • mynmParserParseOptionStatement is actually called parser::parseOptionStmt


    and so on. Hundreds or thousands of such entities.

    I understand, however, that for you both my name and original Microsoft's name don't make much sense.

    Anyway, I am amazed by this DBG file. Even though original names looks unfamiliar to me and misleading in some case, this is a great source of information about those functions and classes that haven't been toched by my reversing process at all.

  7. #7
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    I have been tracking down this type of information and exploring what I can while creating a vb6 disassembler and pcode debugger:

    http://sandsprite.com/vbdec/

    There is also a link there for an archive of vb6 reserving resources as well as a rebuild the old vb-decompiler.theautomaters.com
    message board. The top level site also has a series of my papers on vb6 reversing.

    Quote Originally Posted by firehacker View Post
    Probably I should make public portal and open all my reverse-engineering documentations, so that you can take function/method from this DBG and read what does it do and what arguments does it take from my articles?
    Thats great, I am excited to hear what you uncover. I would love to read through any/all of the work that you would be willing to share. I have read through some of the articles on vbstreets and you have really done amazing work. (I aim to read everything on vbstreets in time!)

    I still spend my holidays and weekends exploring this stuff mostly just because I love vb6. This years vacation was spent discovering how to extract full function prototypes from vb6 IDispatch implementation: http://sandsprite.com/papers.php?expand=0&show=34

    I have mostly been exploring the runtime and exe file format still. I had not even really even looked at the ide symbols yet. Its awesome that you are already a master with them though. Your work did get me thinking about looking at the IDE as the next piece of the puzzle.

    Sysenter-eip article on Vb compiler leaks info from host ( http://sandsprite.com/vb-reversing/VBParser/ ) had a copy of vba6.dbg which I could not load. This is what gave me the idea to start searching for vba5 symbols.

    So much of this information can be easily lost to the ages. I do think it is important to publish as much as we can. The more people who understand it the better I think. VB6 is beautiful on the inside and out.
    Last edited by dz32; Nov 1st, 2022 at 04:12 PM.

  8. #8
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by dz32 View Post
    One other note vba print obj had a call to deserialize in it. It looks like debug.print might be able to do more fancy stuff than I imagined like deserialize some complex types like if you pass it a class object? Might have to implement an extra interface though. Or how get/put handle udts? Dunno need to examine it more could be nothing 🤷
    One little tidbit I never knew about debug.print

    So you can pass debug.print a class as an argument. If that class has a default function, printObject will call its default method and display its output. kinda neat if you wanted to incorporate a complex dump method as default to get easy output
    Last edited by dz32; Nov 7th, 2022 at 07:46 PM.

  9. #9
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    4,483

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by dz32 View Post
    One little tidbit I never knew about debug.print

    So you can pass debug.print a class as an argument. If that class has a default function, printObject will call its default method and display its output. kinda neat if you wanted to incorporate a complex dump method as default to get easy output
    This *had* to be implemented otherwise something like Debug.Print MyRecordset!ID would have not been able to work as expected and would have left a lot of people scratching their heads :-))

    cheers,
    </wqw>

  10. #10
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    isnt that the magic of the bang operator though? rs("id") is already a base value type not an object?

    the following works

    Code:
    'class 1 
    'this is the default method of the class
    Function def(x) As String
        def = x
    End Function
    
    ------------
    'form1
    Private Sub Form_Load()
        Dim c As New Class1
        MsgBox c!test
    End Sub

  11. #11
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Location
    Sofia, Bulgaria
    Posts
    4,483

    Re: Redirect where Debug.Print goes.

    rs!ID is rs("ID") is ADODB.Field instance so rs!ID.Value is valid syntax to access property Value of the ADODB.Field instance.

    cheers,
    </wqw>

  12. #12
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    aghh gotcha, makes sense thanks

  13. #13
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,606

    Lightbulb Re: Redirect where Debug.Print goes.

    Quote Originally Posted by Elroy View Post
    It'd be nice if I could figure out how to redirect where Debug.Print goes.

    I've got a little persistent DebugPrint program I often use. It's easy to use, as I just remove the dot from my Debug.Print statements. However, it'd be even more cool if I could just redirect everything.

    At first, I created an auto-instantiating class named "Debug". That worked just fine.

    However, there are still two problems:

    1) This class still doesn't override the internal Debug object.

    2) "Print" is a reserved word and can't be used as a procedure name.

    So drat. Any ideas?
    The problem is not Print, the problem is Debug.

    You cannot normally use 'Debug' as the name of a procedure because it is a reserved keyword.
    You could name a class Debug, but that does not serve for the purpose.
    You'll need to edit the VBA6.DLL and change where 'Debug' is written in the reserved keywords list with something else, for example DxBxg.
    I have done it, if you want the DLL, I can send it to you privately.

    About the Print method, it is not a problem.

  14. #14

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,716

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by Eduardo- View Post
    The problem is not Print, the problem is Debug.

    You cannot normally use 'Debug' as the name of a procedure because it is a reserved keyword.
    You could name a class Debug, but that does not serve for the purpose.
    You'll need to edit the VBA6.DLL and change where 'Debug' is written in the reserved keywords list with something else, for example DxBxg.
    I have done it, if you want the DLL, I can send it to you privately.

    About the Print method, it is not a problem.
    Hi Eduardo,

    Hmm, did you just open the VBA6.DLL in a hex editor and search for "Debug"? If that's all it is, I can certainly do that.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  15. #15
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,606

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by Elroy View Post
    Hi Eduardo,

    Hmm, did you just open the VBA6.DLL in a hex editor and search for "Debug"? If that's all it is, I can certainly do that.
    I used another one that had already the 'Tab' keyword changed by Shaggrat.

    Then, I compared that DLL with the original one using WinMerge, and could see in what section the keywords were.
    I found the 'Debug' keyword there, changed it to Dxbxg (using WinMerge), and tested that it worked.

  16. #16
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    Or do a search and destroy in memory so it’s only active when you want it

  17. #17
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,606

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by dz32 View Post
    Or do a search and destroy in memory so it’s only active when you want it
    I have thought about that, but it seemed more complicated to do at first sight.
    If you have code to do it, it would be nice to test it.

    Edit:
    It would be good because it would be possible to post a Debug object replacement without having to edit the DLL, but... I'm thinking that if you start the IDE with full compilation it won't even run, because it would see the Debug keyword used before you can change the keyword list...

  18. #18
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    I opened up vb6 and attached ollydbg native debugger then opened up the memory view window and did a search for the string. I can’t remember which one did the trick it might have been in heap memory by the time vb6 was loaded. Eventually there was one where debug keyword was no longer blue and intellisense didn’t work in it anymore. Then I crested a module named debug but I could not implement a print method so gave up. Your technique sounds to solve that.

    Once you find the right instance to replace manually then you can devise a way to find it programmatically. Trick or firehacker might know where already. This might be the harder way to go about it honestly I just poked at it briefly to see if it might be a quick win and did get results. The hooking thing is smoother but requires more fancy stuff.

  19. #19
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,606

    Re: Redirect where Debug.Print goes.

    As I said: I don't think it would be possible because the program wouldn't have the chance to run, since the compiler will detect the procedure named 'Debug' and would not even run one line of the program.

  20. #20
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,347

    Re: Redirect where Debug.Print goes.

    Code:
    ' //
    ' // Debug redirect
    ' // by The trick
    ' //
    
    Option Explicit
    
    Private Enum PTR
        [_]
    End Enum
    
    Private Declare Function GetModuleHandle Lib "kernel32" _
                             Alias "GetModuleHandleW" ( _
                             ByVal lpModuleName As PTR) As PTR
    Private Declare Sub GetMem4 Lib "msvbvm60" ( _
                        ByRef pAddr As Any, _
                        ByRef pDst As Any)
    Private Declare Sub GetMemPtr Lib "msvbvm60" _
                        Alias "GetMem4" ( _
                        ByRef pAddr As Any, _
                        ByRef pDst As Any)
    Private Declare Sub GetMem8 Lib "msvbvm60" ( _
                        ByRef pAddr As Any, _
                        ByRef pDst As Any)
    Private Declare Sub PutMemPtr Lib "msvbvm60" _
                        Alias "PutMem4" ( _
                        ByRef pDst As Any, _
                        ByVal pVal As PTR)
    
    Private Function ReplaceDebugObject( _
                     ByVal pObj As PTR) As PTR
        Static s_pCurObject As PTR
        Dim hVBA        As PTR
        Dim pNTHdr      As PTR
        Dim pStart      As PTR
        Dim pEnd        As PTR
        Dim cSign       As Currency
        Dim lLength     As Long
        Dim lOldProtect As Long
        
        If s_pCurObject = 0 Then
        
            hVBA = GetModuleHandle(StrPtr("vba6"))
            If hVBA = 0 Then Exit Function
        
            GetMem4 ByVal hVBA + &H3C, pNTHdr
            pNTHdr = pNTHdr + hVBA
            
            GetMem4 ByVal pNTHdr + &H12C, pStart
            pStart = pStart + hVBA
            
            GetMem4 ByVal pNTHdr + &H128, lLength
            pEnd = pStart + lLength - 8
            
            Do While pStart <= pEnd
                
                GetMem8 ByVal pStart, cSign
                
                If cSign = 511398171365990.4051@ Then
                
                    GetMemPtr ByVal pStart + &H11, pStart
                    GetMemPtr ByVal pStart + &H44, pStart
                    GetMemPtr ByVal pStart + &H1, s_pCurObject
                    Exit Do
                    
                End If
                
                pStart = pStart + 1
                
            Loop
    
        End If
        
        If s_pCurObject = 0 Then
            Err.Raise 51
        End If
        
        GetMemPtr ByVal s_pCurObject, ReplaceDebugObject
        PutMemPtr ByVal s_pCurObject, pObj
        
    End Function
    
    Private Sub Form_Load()
        Dim pOriginal   As PTR
        
        Me.AutoRedraw = True
        
        pOriginal = ReplaceDebugObject(ObjPtr(Me))
        
        Debug.Print "test"
        Debug.Print "Hello", "world", Spc(10); "1234"; Tab(3); "vb6"
    
        ReplaceDebugObject pOriginal
        
    End Sub

  21. #21
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,606

    Re: Redirect where Debug.Print goes.

    Just I want to note that what The trick have published is not what I said it was not possible, but something else. FWIW.

    BTW: Very good The Trick!

  22. #22
    PowerPoster Arnoutdv's Avatar
    Join Date
    Oct 2013
    Posts
    5,493

    Re: Redirect where Debug.Print goes.

    How does this work when you compile a project to an executable?
    Or better said, what is the effect on the compiled executable?

  23. #23

  24. #24
    Fanatic Member
    Join Date
    Jun 2015
    Posts
    848

    Re: Redirect where Debug.Print goes.

    tadah magic

    awesome

    works perfect with Eduardos IVBPrint interface too
    Last edited by dz32; Nov 16th, 2022 at 01:14 PM.

  25. #25

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,716

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by The trick View Post
    Code:
    ' //
    ' // Debug redirect
    ' // by The trick
    ' //
    
    Option Explicit
    
    Private Enum PTR
        [_]
    End Enum
    
    Private Declare Function GetModuleHandle Lib "kernel32" _
                             Alias "GetModuleHandleW" ( _
                             ByVal lpModuleName As PTR) As PTR
    Private Declare Sub GetMem4 Lib "msvbvm60" ( _
                        ByRef pAddr As Any, _
                        ByRef pDst As Any)
    Private Declare Sub GetMemPtr Lib "msvbvm60" _
                        Alias "GetMem4" ( _
                        ByRef pAddr As Any, _
                        ByRef pDst As Any)
    Private Declare Sub GetMem8 Lib "msvbvm60" ( _
                        ByRef pAddr As Any, _
                        ByRef pDst As Any)
    Private Declare Sub PutMemPtr Lib "msvbvm60" _
                        Alias "PutMem4" ( _
                        ByRef pDst As Any, _
                        ByVal pVal As PTR)
    
    Private Function ReplaceDebugObject( _
                     ByVal pObj As PTR) As PTR
        Static s_pCurObject As PTR
        Dim hVBA        As PTR
        Dim pNTHdr      As PTR
        Dim pStart      As PTR
        Dim pEnd        As PTR
        Dim cSign       As Currency
        Dim lLength     As Long
        Dim lOldProtect As Long
        
        If s_pCurObject = 0 Then
        
            hVBA = GetModuleHandle(StrPtr("vba6"))
            If hVBA = 0 Then Exit Function
        
            GetMem4 ByVal hVBA + &H3C, pNTHdr
            pNTHdr = pNTHdr + hVBA
            
            GetMem4 ByVal pNTHdr + &H12C, pStart
            pStart = pStart + hVBA
            
            GetMem4 ByVal pNTHdr + &H128, lLength
            pEnd = pStart + lLength - 8
            
            Do While pStart <= pEnd
                
                GetMem8 ByVal pStart, cSign
                
                If cSign = 511398171365990.4051@ Then
                
                    GetMemPtr ByVal pStart + &H11, pStart
                    GetMemPtr ByVal pStart + &H44, pStart
                    GetMemPtr ByVal pStart + &H1, s_pCurObject
                    Exit Do
                    
                End If
                
                pStart = pStart + 1
                
            Loop
    
        End If
        
        If s_pCurObject = 0 Then
            Err.Raise 51
        End If
        
        GetMemPtr ByVal s_pCurObject, ReplaceDebugObject
        PutMemPtr ByVal s_pCurObject, pObj
        
    End Function
    
    Private Sub Form_Load()
        Dim pOriginal   As PTR
        
        Me.AutoRedraw = True
        
        pOriginal = ReplaceDebugObject(ObjPtr(Me))
        
        Debug.Print "test"
        Debug.Print "Hello", "world", Spc(10); "1234"; Tab(3); "vb6"
    
        ReplaceDebugObject pOriginal
        
    End Sub
    This is amazing. I've seen The Trick do that memory searching of a library before. And, if I wasn't out camping, I was hoping to use his work and do that myself with the VB6.DLL library. But he beat me to it. Also, he probably got it done in a 10th the time it would have taken me.

    I'm exhausted right now from setting up a campsite, but this looks absolutely wonderful ... CodeBank worthy.

    Thank You!!!
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

  26. #26
    PowerPoster
    Join Date
    Feb 2017
    Posts
    4,606

    Re: Redirect where Debug.Print goes.

    Quote Originally Posted by Elroy View Post
    CodeBank worthy.
    I was thinking in an add-in that offers to save the text sent to the Debug.Print to a file.
    My idea is:

    Output to the immediate window as usual but at the same time save the text in a file.
    Save it in different files each run, like:

    ProjectFileName_Debug_1.txt
    ProjectFileName_Debug_2.txt
    ProjectFileName_Debug_3.txt

    Being the max number of files configurable. Once the max number is reached, delete the older one.
    ProjectFileName would be the file name of the *.vbp file. If the project is not already saved, then it does not write to a file.

    At the start of each file, put the date and time (Now).

    But... I don't think I'm going to do it right now anyway.
    I share my thoughts in case someone else is interested.

  27. #27

    Thread Starter
    PowerPoster Elroy's Avatar
    Join Date
    Jun 2014
    Location
    Near Nashville TN
    Posts
    8,716

    Re: Redirect where Debug.Print goes.

    Here's actually my idea/usage. I've got this little PersistentDebugPrint program so that I can survive IDE crashes as well as get much more in the output window before it scrolls off the top. The program has a feature where it puts a BAS stub into the clipboard that can be quickly inserted into any program I'm developing.

    However, currently, to use it, you call DebugPrint (notice, no period). So, I search-and-replace "Debug.Print" with "DebugPrint" to use it. But, it'd be super nice if I could just have a call named something like "DoDebugRedirect" that I could comment in-or-out to use the PersistentDebugPrint, and not have to replace all my Debug.Print statements.

    So, I'll be looking at working it all into a call to a BAS Sub DoDebugRedirect(bTrueFalse As Boolean) type call.
    Any software I post in these forums written by me is provided “AS IS” without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. Please understand that I’ve been programming since the mid-1970s and still have some of that code. My contemporary VB6 project is approaching 1,000 modules. In addition, I have a “VB6 random code folder” that is overflowing. I’ve been at this long enough to truly not know with absolute certainty from whence every single line of my code has come, with much of it coming from programmers under my employ who signed intellectual property transfers. I have not deliberately attempted to remove any licenses and/or attributions from any software. If someone finds that I have inadvertently done so, I sincerely apologize, and, upon notice and reasonable proof, will re-attach those licenses and/or attributions. To all, peace and happiness.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width