Announcement

Collapse
No announcement yet.

DLL accesses controls of it's invoking EXE?

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

  • DLL accesses controls of it's invoking EXE?

    Normally, we add a control to a .bas file and get the handle of a combobox for instance. Then we can handle the control like an object, for example with Control Add ComboBox, hDlg, id& ....
    But what is when a DLL function wants to use the content of a Combo which is placed inside the code of the running EXE?
    I never needed or tried it until now, it's just a thought in this moment.
    So, when I have the handle of the combo inside the running exe. Can I use the combo with PB like it were implemented inside the DLL to avoid using the WinApi?, such like: COMBOBOX SELECT hDlg, id&, pos& - where hDlg could be the dialog handle in the EXE, id& the ID of the combo.
    Please notice that - in my case - the DLL is one of many invoked by event or by function directly or indirectly via another DLL from the main EXE, but all of them are using the same mem space.
    Norbert Doerre

  • #2
    The use of DDT commands (eg "COMBOBOX SELECT") is restricted to dialogs created in the same code module.

    However, the handle obtained by CONTROL HANDLE may be passed to any function in the process regardless of its code module ("in a DLL") and the Windows' API functions may be used against that handle.

    Eg
    Code:
    #COMPILE EXE 
    
       ... DIALOG NEW....
    
    CALLBACK FUNCTION X ....
    
           CONTROL HANDLE CBHNDL, %ID_COMBOBOX TO hCtrl
           iSel = ZERO-based index of desired selection
           CALL FunctionInDLL (hCtrl, iSel) 
    
    ...
    #COMPILE DLL
    
    FUNCTION FunctionInDLL (hCtrl As LONG, iSel AS LONG) As LONG
    
        SendMessage  hCtrl, %CB_SETCURSEL, iSel, %NULL  ' set current selection of combobox
    ...
    MCM
    Michael Mattias
    Tal Systems Inc. (retired)
    Racine WI USA
    [email protected]
    http://www.talsystems.com

    Comment


    • #3
      Michael,
      that's the way i use to do it for a long time with other programming languages, too. Here an example:

      'Code in DLL, pls don't bother german comments:
      Code:
      Function DefWindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
      	Select Case wParam 'getting command events from EXE
              '
              ' 
       		Case 1187 '<Schraffurlayer verwenden> wurde im Menü umgeschaltet
      			iChk = SwitchCheck(GetMenuItemCheck(1187, "Schraffurlayer verwenden"))
      			SetmenuItemCheck(1187, "Schraffurlayer verwenden", iChk)
      			If iChk > 0 Then 'nur aufrufen, wenn HatchLayer eingerichtet werden soll
      				Macro("TBH") 'invokes a modeless dialog in the EXE
      				Dialog DoEvents
      				lRet = FillHatchLayerComboAndSetCheck(iError) 'fills combo in EXE dialog
      			End If
      
              End select
      Functions like 'SetmenuItemCheck' or 'FillHatchLayerComboAndSetCheck' handle in detail the access of the EXE controls. If anybody is interested in how to do it I'd also send the code, which has a considerable length, however.

      But I'd rather use PB native commands to do all this.

      There occurs sometimes a situation that I have to use controls of the EXE the contents of which are currently to be used by the DLL or vice versa, just to avoid double definitions and double appearence of controls with the same sense on screen and also to avoid programming bugs.
      Norbert Doerre

      Comment


      • #4
        How you do it is up to you. I'm just trying to clarify the restrictions on the use of DDT-specific syntax.

        I suppose I should offer a disclaimer: I don't use DDT syntax all that often myself. I learned Windows' programming "SDK Style" and I've pretty much stayed with that.
        Michael Mattias
        Tal Systems Inc. (retired)
        Racine WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Michael,

          at least PB8 seems to do it ALL!
          By the way, how do You format the code for publishing?

          Here an example, comments are in German. DDT code marked with '!!!!'.
          Code:
          '-------------------------------------------------------------------------------------------------------
          ' ALERT FUNCTION UseHchLay (für den Zugriff mit Buchstabenbefehl <HL>, über CMDEXT.DEF und über's Menü)
          '-------------------------------------------------------------------------------------------------------
          Sub UseHchLay Alias "UseHchLay"(ByVal wParam As Long, ByVal lParam As Long) Export
          	'Verwendung des SchraffurLayers umschalten <HL>
          	Local iChk As Long
          	Local iError As Integer
          	Local cboWnd As Long
          	Local hSettingsDlg As Long
          	Local hSysTab As Long
          	Local hTab As Long
          	Local hChk As Long
          	Local lRes As Long
          	Local lTabIndex As Long
          	Local hParent As Long
          	Local szClassName As Asciiz * 128
          	Local hCtrlWithFocus As Long
          	iChk = SwitchCheck(GetMenuItemCheck(1187, "Schraffurlayer verwenden"))
          	SetmenuItemCheck(1187, "Schraffurlayer verwenden", iChk)
          	If iChk > 0 Then 'nur aufrufen, wenn HatchLayer eingerichtet werden soll
          		macro("TBH") 'Settings-Dialog aufrufen
          !!!!		Dialog DoEvents 
                hSettingsDlg = FindWindow ("#32770", "MechaniCAD Einrichtung") 'Handle des Einrichtungsdialogs
                If hSettingsDlg Then
                	hSysTab = FindWindowEx(hSettingsDlg, 0, "SysTabControl32", "") 'Handle der SysTab-Reiterschaltflächen
                   If hSysTab Then
          				lTabIndex = 4 'HchFillTab hat Index 4 von (0...9)
                   	lRes = SendMessage(hSysTab, %TCM_SETCURFOCUS, lTabIndex, 0) 'HchFillTab öffnen
          		   	'Control Send hSettingsDlg , 0, %TCM_GETCURSEL, 0, 0 To lRes 'Testen, welches Tab geöffnet ist.
          		   End If
          			cboWnd = FillHatchLayerComboAndSetCheck(iError)
                	SetFocusForegroundWindow(hSettingsDlg)
          		End If
          !!!!      Dialog Show Modeless hSettingsDlg, Call SysTabProc() 'für "MechaniCAD Einrichtung" zusätzliches CallBack erstellen
          	End If
          End Sub
          
          CallBack Function SysTabProc() 'Testing a CallBack function
          	Select Case CbMsg
          '		Case %WM_INITDIALOG
          			debug CbMsg
          	End Select
          End Function
          
          Sub SetFocusForegroundWindow(hDialog As Long) 'Correct the focus of Dialog
          	'Die folgende Procedure setzt zuerst den Eingabefokus auf die 
          	'Anwendung und ruft danach 'SetForegroundWindow' auf.
          	'INPUT:	Handle des Fensters, das in den Vordergrund soll 
          	Local Thread1 As Dword
          	Local Thread2 As Dword
          	Thread1 = GetCurrentThreadId()
          	Thread2 = GetWindowThreadProcessId(GetForegroundWindow(), 0)
          	AttachThreadInput(Thread1, Thread2, %True)
          	Try
          		SetForegroundWindow(hDialog)
          	Catch
          		AttachThreadInput(Thread1, Thread2, %False)
          	End Try
          End Sub
          Norbert Doerre

          Comment


          • #6
            Code:
            Try
            	SetForegroundWindow(hDialog)
            Catch
            	AttachThreadInput(Thread1, Thread2, %False)
            End Try
            TRY..CATCH...END TRY only works with PowerBASIC intrinsic verbs and functions.

            The CATCH will never be executed, since only PB intrinsics can fail the TRY.
            Michael Mattias
            Tal Systems Inc. (retired)
            Racine WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              "The CATCH will never be executed..."

              Where can I read this, except in Your comment, Michael?
              The compiler seems at least to 'catch' it all.
              Last edited by norbert doerre; 29 Mar 2008, 04:49 PM.
              Norbert Doerre

              Comment


              • #8
                Well, you could read it in the help file under TRY...
                emarks Statements in the TRY section are executed normally. The first time a run-time error occurs, control is transferred to the CATCH section. If no run-time errors are generated in the TRY section, the CATCH section is skipped entirely.
                SetForegroundWindow(), not being a PowerBASIC instrinsic, cannot generate a run-time error, ergo there is never anything to CATCH.
                Michael Mattias
                Tal Systems Inc. (retired)
                Racine WI USA
                [email protected]
                http://www.talsystems.com

                Comment


                • #9
                  MCM I think you meant more of
                  Remarks
                  Statements in the TRY section are executed normally. The first time a run-time error occurs, control is transferred to the CATCH section. If no run-time errors are generated in the TRY section, the CATCH section is skipped entirely.

                  Then, regardless of error status, the FINALLY section is executed, if it is present. Error trapping and control transfer are disabled in the CATCH and FINALLY sections, so you would normally use conventional " ERR =…" tests to check the success of error-prone operations in those sections.

                  However, TRY structures can be nested to any level, so it may be desirable to use another TRY block within these clauses.
                  And if you did, then more to the point of a post you made to me somewhere in the forums, was along the lines of not using ERR to catch an error made by a SDK (WinApi) call vs not using GetLastError to catch an error made by a DDT (PB Intrinsic) call????

                  If I got that right, then in this case "TRY/CATCH/FINALLY" is only as valid as "ON ERROR" and is only valid with errors that the PB Compiler (or runtime with PB values) is valid....???

                  (Keep special notes of the restrictions when reading my comparison of OnError and Try/Catch/Finally) though.
                  ON ERROR GOTO is invalid within a TRY structure, but may be used within the same Sub/Function.
                  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


                  • #10
                    If I got that right, then in this case "TRY/CATCH/FINALLY" is only as valid as "ON ERROR" and is only valid with errors that the PB Compiler (or runtime with PB values) is valid....???
                    Give that young man one of the big stuffed teddy bears for his little lady friend!
                    Michael Mattias
                    Tal Systems Inc. (retired)
                    Racine WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      Michael,
                      yes, the compiler simply ignores the 'catch' sequence. So, this on first sight Delphi-like code is nearly unusable under common circumstances. But there are some other solutions starting with if...end if. Do You know an alternative way to generate a 'legal' error just depending from the result of a SDK function? For instance, if 1 is returned, generate programatically a recognizable error code so that 'catch' would regognize it?
                      Norbert Doerre

                      Comment


                      • #12
                        Do You know an alternative way to generate a 'legal' error just depending from the result of a SDK function? For instance, if 1 is returned, generate programatically a recognizable error code so that 'catch' would regognize it?
                        Sure. Contact my office for rates and availability... well, OK, vacation has mellowed me out so I'll make it a freebie.

                        Step One: You MUST query the return of the API call. Eg in your example....
                        Code:
                           SetForegroundWindow(hDialog)
                        ... is unacceptable. You must use the form:

                        Code:
                           ireturn = SetForegroundWindow(hDialog)
                        Step Two: compare the value returned (ireturn above) with the SDK documentation for the subject call. about 99% of WinApi calls return at least a success/fail code. For SetForegroundWindow we note:
                        Return Value

                        If the window was brought to the foreground, the return value is nonzero.

                        If the window was not brought to the foreground, the return value is zero.
                        Unfortunately, Step Three does not apply here: most WinApi calls when they fail allow you to call GetLastError() to get an error code.

                        But back to your case.... if SetForegroundWindow fails, you perform Step Four by using the PB 'ERROR' statement to raise a runtime error. This will 'trigger' any active ON ERROR GOTO or TRY.. CATCH.

                        I'm not sure you can use "ERROR nnn" within TRY/CATCH so you might have to restructure your code, but this is how you check for success/failure of a WinAPI function call and raise a PB runtime error 'on demand.'


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

                        Comment


                        • #13
                          This could be a useful and necessary add to the PB8 manual!
                          Norbert Doerre

                          Comment


                          • #14
                            not sure if you meant this, but

                            I'm not sure you can use "ERROR nnn" within TRY/CATCH so you might have to restructure your code, but this is how you check for success/failure of a WinAPI function call and raise a PB runtime error 'on demand.'
                            did you mean the ERR function? or On Error? (from the docs shows)
                            ON ERROR GOTO is invalid within a TRY structure, but may be used within the same Sub/Function.
                            This kinda relates to a debate of styles that me and family/friends have (yep we are all programmers, and the women get sooo ticked when we start "Going Geek", debating on the quote unquote "Best" way to handle things) *LOL*

                            back to Norbert's original question
                            So, when I have the handle of the combo inside the running exe. Can I use the combo with PB like it were implemented inside the DLL to avoid using the WinApi?, such like: COMBOBOX SELECT hDlg, id&, pos& - where hDlg could be the dialog handle in the EXE, id& the ID of the combo.
                            the answer is "semi-tricky" because of the conflicts... in many cases...does not matter because both PB and WinApi points of view handle the problem.

                            I can not totally answer because I do not work for PB and do not know its inner designs as to if a program compiles directly to binary? or assembly? or WinApi? or some midst?, but whatever it is...is EXCEPTIONAL

                            Norbert, if you are asking what I think you are asking, then the answer is simple...its like math.
                            "As long as the units are the same, the answer is the same" as soon as you translate, and forget a unit, or round (forget the end result)

                            kinda like multiplying mm, cm, inches, pounds, newtons (added the last one for pure confusion)...you are good if translating into one base unit (willing to forgive rounding and conversion errors) but WAYYYY off, if wanting a precise number if you round on the 2nd and 10th calculation without taking full decimal points.
                            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


                            • #15
                              Cliff, i tried to explain one (formal) difficulty with an lParam value in the thread "SysTabControl and click event", but w/o success.
                              But I found the solution, and it is running fine!
                              Norbert Doerre

                              Comment


                              • #16
                                >But I found the solution,

                                Well, that makes two opinions. Please read my comments there.

                                You can "do something" when "user clicks on a tab control" by...

                                1. Testing for WM_NOTIFY messages
                                2. Within WM_NOTIFIY, casting lparam to an NMHDR PTR
                                3. Compare NMHDR.idfrom to the tab control ID
                                4. If true, compare NMHDR.code to NM_CLICK

                                Or, in Code...
                                Code:
                                FUNCTION wndProc (BYVAL hWnd AS LONG, BYVAL uMSG AS LONG, _
                                                             BYVAL wPAram AS LONG, BYVAL lParam AS LONG) AS LONG
                                
                                  LOCAL pNMHDR AS NMHDR PTR 
                                
                                   SELECT CASE uMSG
                                      CASE %WM_NOTIFY
                                        pNMHDR = lparam
                                        IF @pNMHDR.idFrom = %ID_TABCTRL THEN
                                           IF @pNMHDR.Code = %NM_CLICK THEN 
                                                   User Clicked on the tab control
                                           ELSEIF @pNMHDR.code = %TCN_SELECHANGE
                                                   User's click or keystroke resulted in a change of selected tab
                                           .....
                                Michael Mattias
                                Tal Systems Inc. (retired)
                                Racine WI USA
                                [email protected]
                                http://www.talsystems.com

                                Comment


                                • #17
                                  Just coming back tired into my office...
                                  Michael, your code seems to be pretty sophisticated; i'll try it tomorrow morning. But if my own 'code' with simply using the mentioned constant??? given in lParam will lead to the same result with other programming languages, then I would rather prefer the 'short' mode to avoid possible bugs. I'll write more to this subject soon.
                                  Norbert Doerre

                                  Comment


                                  • #18
                                    I will bet you 10,000 (USD or EUR, I don't care which) comparing lparam to 130812 or whatever it was will not work on other systems, nor on your own system under different run conditions.

                                    In your code, lparam is meaningless because you don't test the message first.
                                    Michael Mattias
                                    Tal Systems Inc. (retired)
                                    Racine WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment


                                    • #19
                                      I have to partially agree with MCM in this case, but more in layman's terms.

                                      I believe the point he was trying to drive home was not just getting "something" happened to your control, but "WHAT" happened to your control.

                                      Usually you do not see it, but if you only test for the control, and put a msgbox in that part of code, you will see MANY messages for that control (anything from a mouse move, to item selected to god knows what), and I believe what MCM is trying to drive home is testing for what the message is, rather than just your control got a message.

                                      That said Norbert, I too have that shortcoming of in the case of catching lparam, and wparam that I have no clue as to deciphering the lobyte hibyte parts of each to get a usable format (with the exception of snippets, that actually show the case I am after) in this case from MCM's example
                                      SELECT CASE uMSG
                                      CASE %WM_NOTIFY
                                      pNMHDR = lparam
                                      IF @pNMHDR.idFrom = %ID_TABCTRL THEN
                                      IF @pNMHDR.Code = %NM_CLICK THEN
                                      User Clicked on the tab control
                                      ELSEIF @pNMHDR.code = %TCN_SELECHANGE
                                      In this case I read it as:
                                      Msg sent = Notify...(not sure if all controls notify (forgetting the noparentnotify), but if this is true then its a massive clue)
                                      pNMHDR = lparam (ok so from docs if I read this particular function should tell me that this is true)

                                      From there, to me at least then it depends on the api used as to what part of lparam means anything, or what part of wparam means anything.

                                      Is there a generic format to what lparam or wparam mean? or is it all Dependant on the message that was sent?????

                                      (Sorry if I stepped on your question a bit, but this has always bugged me)
                                      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


                                      • #20
                                        Cliff,
                                        i do real time debugging implemented inside the dll. I never use msg boxes.
                                        Code 78 described by Michael is - until now - never thrown out on my PCs here in the office. I always get code 80/81 in uMsg which i forgot to copy/paste in the code extraction. My code fragments are manually copied/pasted because I cannot use the PB line debugger to get a logical sequence. It is hard not to forget one line of more than currently 9000 lines of code.
                                        By the way, code 80 signals HERE that tabsys selchange has just ended and 81 signals tab selchange is about to start. The lParam value is always constant, too.
                                        That's what i can find out with my proper tools, and the program runs on all PCs in my office. But I'm nevertheless very thankful for Michael's code hint and will test it today to write a report about it tonight. I'm curious if i'll manage to get code 78 with it, which is perhaps 'swollowed' with my current kind of processing. I'll honestly test it.
                                        Norbert Doerre

                                        Comment

                                        Working...
                                        X