Announcement

Collapse
No announcement yet.

Hooks for TextOut, ExtTextOut & DrawText

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

  • Hooks for TextOut, ExtTextOut & DrawText

    Hi folks! I'm new here and new to PowerBASIC as well. Currently, I'm trying to create a DLL that can be used to capture text from a third-party program. Unfortunately, the program does not use standard Windows controls and so, attempting to use WM_GETTEXT only yields empty strings. After searching around for a solution, I learnt that setting up Hooks to monitor the DrawText, TextOut and ExtTextOut APIs is the best way to go.

    I did managed to find related example code in these forums, although the example shows only how to hook for TextOut. See link below:

    http://www.powerbasic.com/support/pb...ad.php?t=21331

    I modified the same code and replaced it with DrawText and ExtTextOut. Tested the DLL with the exe included (code for exe is in the example above). After I compile and run the exe, I open PowerBASIC's Help file just to test it out. Immediately, I get a msgbox with the string "Help &Topics" which comes from DrawText. The funny thing is that I can't find that string anywhere on the frontpage of the Help file itself! After that, i get another msgbox which is blank, this time from ExtTextOut. Immediately after these two msgboxs, the Help file generates a Windows error and has to be closed. I've included my modified code below. If anyone here can please take a look and let me know where I'm going wrong, I'd really appreciate it. Thanks!

    Code:
    #COMPILE DLL "CaptureText.Dll"
       #REGISTER NONE
       #DIM ALL
       #INCLUDE "Win32Api.Inc"
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    FUNCTION SetMyDrawText(Op AS LONG) AS LONG
          STATIC hProc AS LONG, tProc AS STRING
          LOCAL hLib AS LONG
          IF hProc = 0 THEN
             hLib = GetModuleHandle("user32.dll")
             hProc = GetProcAddress(hLib, "DrawTextA")
             tProc = PEEK$(hProc, 7)
             IF ISFALSE(VirtualProtect (BYVAL hProc, 7, BYVAL %PAGE_EXECUTE_READWRITE, BYREF hLib)) THEN _
                MSGBOX "Error in VirtualProtect": hProc = 0: EXIT FUNCTION
             ' Under 9x VirtualProtect doesn't work in the shared virtual address space (from 0x80000000 through 0xBFFFFFFF).
             ' For example, for system dlls.
          END IF
          IF Op = 1 THEN POKE$ hProc, CHR$(&HB8) + MKL$(CODEPTR(MyDrawText)) + CHR$(&HFF, &HE0)
          IF Op = 2 THEN POKE$ hProc, tProc
          FlushInstructionCache GetCurrentProcess, BYVAL hProc, BYVAL 7
       END FUNCTION
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    FUNCTION MyDrawText (BYVAL hdc AS LONG, BYVAL lpString AS DWORD, BYVAL nCount AS LONG, BYVAL lpRect AS RECT, BYVAL uFormat AS DWORD) AS LONG
          LOCAL MyString AS STRING
          IF lpString THEN MyString = PEEK$(lpString, nCount)
          MSGBOX MyString, %MB_OK, "Captured by DrawText"
          'LSET MyString = "Ha-ha"
          SetMyDrawText 2
          DrawText hdc, BYVAL STRPTR(MyString), LEN(MyString), lpRect, uFormat
          SetMyDrawText 1
       END FUNCTION
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    FUNCTION SetMyExtTextOut(Op AS LONG) AS LONG
          STATIC hProc AS LONG, tProc AS STRING
          LOCAL hLib AS LONG
          IF hProc = 0 THEN
             hLib = GetModuleHandle("gdi32.dll")
             hProc = GetProcAddress(hLib, "ExtTextOutA")     'DrawTextA
             tProc = PEEK$(hProc, 7)
             IF ISFALSE(VirtualProtect (BYVAL hProc, 7, BYVAL %PAGE_EXECUTE_READWRITE, BYREF hLib)) THEN _
                MSGBOX "Error in VirtualProtect": hProc = 0: EXIT FUNCTION
             ' Under 9x VirtualProtect doesn't work in the shared virtual address space (from 0x80000000 through 0xBFFFFFFF).
             ' For example, for system dlls.
          END IF
          IF Op = 1 THEN POKE$ hProc, CHR$(&HB8) + MKL$(CODEPTR(MyExtTextOut)) + CHR$(&HFF, &HE0)
          IF Op = 2 THEN POKE$ hProc, tProc
          FlushInstructionCache GetCurrentProcess, BYVAL hProc, BYVAL 7
       END FUNCTION
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    FUNCTION MyExtTextOut (BYVAL hdc AS DWORD, BYVAL x AS LONG, BYVAL y AS LONG, BYVAL dwOptions AS DWORD, BYVAL lpRect AS RECT, BYVAL lpString AS DWORD, BYVAL nCount AS DWORD, lpDx AS LONG) AS LONG
          LOCAL MyString AS STRING
          IF lpString THEN MyString = PEEK$(lpString, nCount)
          MSGBOX MyString, %MB_OK, "Captured by ExtTextOut"
          'LSET MyString = "Ha-ha"
          SetMyExtTextOut 2
          ExtTextOut hdc, x, y, dwOptions, lpRect, BYVAL STRPTR(MyString), LEN(MyString), lpDx
          SetMyExtTextOut 1
       END FUNCTION
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    $UniqueName = "My Hook"
    
       GLOBAL hInstDLL AS LONG, MainDll AS LONG
    
       FUNCTION LIBMAIN(BYVAL hInstance AS LONG, BYVAL fwdReason AS LONG, _
          BYVAL lpvReserved AS LONG) EXPORT AS LONG
          SELECT CASE fwdReason
             CASE %DLL_PROCESS_ATTACH: hInstDLL = hInstance: LIBMAIN = 1: SetMyDrawText 1: SetMyExtTextOut 1
             CASE %DLL_PROCESS_DETACH: LIBMAIN = 1: SetMyDrawText 2: SetMyExtTextOut 2
          END SELECT
       END FUNCTION
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    FUNCTION HookProc(BYVAL nCode AS LONG, BYVAL wParam AS LONG, BYVAL lParam AS LONG) EXPORT AS LONG
          STATIC hHook AS LONG, hDlg AS LONG
          hDlg = FindWindow("", $UniqueName)
          IF hHook = 0 THEN IF hDlg THEN hHook = GetProp(hDlg, BYVAL 1)
          IF hHook THEN FUNCTION = CallNextHookEx(BYVAL hHook, BYVAL nCode, BYVAL wParam, BYVAL lParam)
          IF ISFALSE(MainDll) AND (ISFALSE(hDlg) OR ISFALSE(hHook)) THEN FreeLibrary hInstDll
       END FUNCTION
    
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       FUNCTION SetHook ALIAS "SetHook" (hWnd AS LONG) EXPORT AS LONG
          LOCAL hHook AS LONG
          hHook = SetWindowsHookEx (%WH_CBT, CODEPTR(HookProc), BYVAL hInstDLL, BYVAL 0)
          SetProp hWnd, BYVAL 1, BYVAL hHook
          MainDll = 1
       END FUNCTION

  • #2
    Your difficulties are God's way of warning you that you are stealing someone else's copyright content.
    Michael Mattias
    Tal Systems Inc. (retired)
    Racine WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      I am not sure this is causing the issues, but one thing that stands out to me is the fact that your replacement functions are not returning the result code for the actual API call. If the programs that are calling these functions are looking for the API return code to know if it worked (like a good programmer should) they will not get the response back they expect which can cause problems.

      I would change your code to return the real result code from the API call.

      For example instead of:
      Code:
      DrawText hdc, BYVAL STRPTR(MyString), LEN(MyString), lpRect, uFormat
      I would use:
      Code:
      Function = DrawText(hdc, BYVAL STRPTR(MyString), LEN(MyString), lpRect, uFormat)
      Although it will need to be tested to see if the return code actual makes it back to the caller or not.
      "I haven't lost my mind... its backed up on tape... I think??" :D

      Comment


      • #4
        The other caution I would suggest is to not use message boxes since they will pause the function. And that can cause serious issues when you hook in to system functions because you can hose things up real fast by pausing message queues even briefly. Instead of msgbox, I would write the results to a file to be reviewed later. (just make sure you do the proper error checking in case the file gets locked etc since if your function dies while it is controlling basic system functions like DrawText, you could easily crash the system.)
        "I haven't lost my mind... its backed up on tape... I think??" :D

        Comment


        • #5
          @ Michael Mattias : I'm really surprised you'd jump to a conclusion like that, especially for a person with your keen intelligence. My exe is an auto-trader that needs to read market signals from a third party program (platform) because it doesn't have a convenient API to interface with... neither does the third-party program write its signals to a file, which would've made my job a whole lot easier. I don't see any copyright issue in my case. I understand you may come across quite a few people who may not have the noblest of intentions when asking about hooks, but please make room for the possibility that there might be people who have legitimate uses for it too...

          @ William Burns : Thank you so much for the insight you've provided. I'll change the code according to your suggestions and try it out. And you are so right about the msgboxs! (my bad!)

          Comment


          • #6
            It may in fact be a permitted use of the product and not a violation of copyright.

            Then again, it may just constitute an unlicensed use. As you point out...

            " My exe is an auto-trader that needs to read market signals from a third party program (platform)
            because it doesn't have a convenient API...
            Licensed products are licensed for specific uses only. There may be no API because the publisher does not want to provide one because he does not want to share the knowledge and techinques used in his product.

            The disclaimer: as someone who earns a living developing and licensing copyright materials, I am highly sensitive about this subject.
            Michael Mattias
            Tal Systems Inc. (retired)
            Racine WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              Your difficulties are God's way of warning you that you are stealing someone else's copyright content.
              I never gave him problems

              Ok enough of the god jokes.

              The simple fact of the statement
              " My exe is an auto-trader that needs to read market signals from a third party program (platform)
              indicates to me either
              1. The 3rd party developer did not have an api so they did as you are attempting (Aka: WARNING....you may be treading illegal or at best case a hack on a hack that someone else did)
              2. The 3rd part developer did not provide an api because they did not want you to be doing what you are attempting (or wanted to sell you an api or other reason)


              or the same as above but with the original programmer.

              There may be an api to be bought to do things directly, or they just do not want you to do ideas such as this.

              The other side could also VERY WELL be that the programmers in question never thought about the option of providing and API because the original app does everything anyone would ever want to do? (Aka, sales tells them to make it, and they make it. There was no thought to potential sales of developers or other possibilities that could come from it)

              I can see MCM's side of things of protecting what you wrote, but on the other side I can see in Helen Slater's comment (Legend of Billie Jean - 1985) "What's Fair is FAIR!!!")

              Lawyers and such have way too much power, but who is to say what is fair???

              Anyways the comment of
              The disclaimer: as someone who earns a living developing and licensing copyright materials, I am highly sensitive about this subject.
              could be read as you developed the code and its yours? (give or take permission as needed) ORRRRRrrr (parts of code could be not mine and I used as permitted, but here is what you are permitted to)

              How far does it go??? am I breaking rules for using a "FOR LOOP"????
              (and yes I have been asked to sign confidentiality on a "FOR LOOP" that it could never be used again without the customers permission)

              :shhh:
              (I am hunting wabbbits, just dont tell them I am using some one elses trigger to fire the thing)
              Engineer's Motto: If it aint broke take it apart and fix it

              "If at 1st you don't succeed... call it version 1.0"

              "Half of Programming is coding"....."The other 90% is DEBUGGING"

              "Document my code????" .... "WHYYY??? do you think they call it CODE? "

              Comment


              • #8
                Ok, so I may be really sensitive about this particular topic.

                I have one application, my 837 API, which includes a screen which shows the source and destination relationship between the input data and the output table/column for about 2400 discrete fields. Creating this "map" was about 60% of the work I put into the product, so darned if I am going to give that away.

                I have had requests for this map and I tell every one of them, "license the developer version of the API ($3500) with the source code option (an additional $3500) and you can create it yourself, but that information is NOT available for those who license only the runtime ($500 or $600 depending on options). "

                And to keep this information confidential when I send out a demo version, I use TextOut() to draw all elements of that screen.. so nobody can 'tap in' and get that info in magnetic format during their evalution period and then decide NOT to purchase.


                MCM
                Michael Mattias
                Tal Systems Inc. (retired)
                Racine WI USA
                [email protected]
                http://www.talsystems.com

                Comment

                Working...
                X