Announcement

Collapse

New Sub-Forum

In an effort to help make sure there are appropriate categories for topics of discussion that are happening, there is now a sub-forum for databases and database programming under Special Interest groups. Please direct questions, etc., about this topic to that sub-forum moving forward. Thank you.
See more
See less

Runtime DLL loading via macro. Will it work.

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

  • Runtime DLL loading via macro. Will it work.

    I have a need to load at runtime specific custom dll's by customer. I used some examples from the forum to get the idea and came up with the following. It works but I'm concerned if there are problems with this method.
    Declare and Macro code
    Code:
    DECLARE FUNCTION GET_CHN_PRD(STRING) AS STRING
    MACRO CALLDLL1(prm1,prm2,prm3,prm4,prm5)
      'This variable passes a single parameter
      'Other variations would just have prm6,prm7 etc.
      'prm1 root name of dll
      'prm2 function name in dll
      'prm3 return value. can be -1,0,any type of number or string
      'prm4 Number of DLL call in program. If more than 1 dll is called then it may be up to 10 (arbitrary limit)
      'prm5 Variable 1 passed BYREF
      DIM hLibrary???(10)
      DIM pProc???(10)
      'The DLL would be in PBSRC if running at out office and
      'in the program directory at the customer site.
      IF NOT FNSP(CUST_DIR$) THEN
        DLLPATH$ = "J:\PBSRC\TNET\" + CUST_DIR$ + "\"
      ELSE
        DLLPATH$ = ""
      END IF
      DLLNAME$ = prm1
      DLLNAME$ = DLLPATH$ + DLLNAME$ + ".DLL"
      hLibrary???(prm4) = LoadLibrary(BYCOPY DLLNAME$)
      IF hLibrary???(prm4) THEN
        pProc???(prm4) = GetProcAddress(hLibrary???(prm4),prm2)
        IF pProc???(prm4) THEN
          CALL DWORD pProc???(prm4) USING GET_CHN_PRD(prm5) TO prm3
          CALL FreeLibrary(hLibrary???(prm4))
        END IF
      ELSE
        MSGBOX "Required DLL " + DLLNAME$ + " is not present. Aborting program." ,%MB_OK OR %MB_ICONWARNING,"File not found"
        GOTO EJOB
      END IF
    END MACRO
    PBMAIN code snippet
    Code:
          IF CTRU THEN 'where CTRU is the customer code
            CALLDLL1("LBCHNPRD","GET_CHN_PRD",CHN_PRD$,1,BYCOPY F1.ACTNR$) 
          END IF
    LBCHNPRD is the root of the DLL name
    GET_CHN_PRD is the name of the function to call
    CHN_PRD$ is the returned value EXPORTED AS STRING
    1 is the number of the DLL being called to keep the pointers unique to the DLL being called.
    BYCOPY F1.ACTNR$ is the single parameter I'm passing to the DLL.

    I'm considering using OPTIONAL and ANY to make the MACRO more generic.

    Any comments and/or warnings and/or suggestions are welcome.

    Bob Mechler

  • #2
    Just noticed I'll probably need to use a macro parameter on the GET_CHN_PRD portion in CALL DWORD.

    I had problems when passing params with quotes and without quotes in the macro expansion. Any thoughts there also.

    Bob Mechler

    Comment


    • #3
      Your FreeLibrary is in the wrong place
      Code:
      IF hLibrary???(prm4) THEN
          pProc???(prm4) = GetProcAddress(hLibrary???(prm4),prm2)
          IF pProc???(prm4) THEN
            CALL DWORD pProc???(prm4) USING GET_CHN_PRD(prm5) TO prm3
            CALL FreeLibrary(hLibrary???(prm4))
          END IF
        ' FreeLibrary belongs here so it's called anytime LoadLibrary succeeded
           ' and not just when the named proc is found in the loaded library.
      Re MACRO/parameter/quotes usage: why bother with a MACRO? Just make it a procedure.
      Michael Mattias
      Tal Systems Inc. (retired)
      Racine WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        You're right of course, FreeLibrary should be in the scope of a successful LoadLibrary.

        DLL's that are common to all customers, I DECLARE at the beginning of the program. Conditional DLL'S that depend on the modules purchased and/or unique functionality per customer could be numerous and not everyone would have them.

        Anytime I see code repeating itself in different functions I tend to think MACRO to generalize it. Implicit run-time linking looked to me like something that could be generalized. That way I could place it once in an include file with all other MACRO's and in our programs a single DECLARE at the top of the program and a single line of code in PBMAIN would be able to call any DLL we write in the furture, handling in a custom directory at the office and in the customer program directory in the field.

        I chose a macro because I couldn't figure out how to make a function that could be used to load any dll, get a return value and then unload. The CALL DWORD pProc??? USING requires the name. Also I wanted to delay, in some cases, freeing the libary in the event the call to the DLL was in a loop. Then I would free the libary when ending the program. To free the library later, I would have to use a global array to keep track of the handles of the DLL'S that had been loaded.

        How could a function that needs to be able to load at runtime any DLL and call any function within that DLL be written?

        Bob Mechler

        Comment


        • #5
          How could a function that needs to be able to load at runtime any DLL and call any function within that DLL be written?
          Code:
          DECLARE FUNCTION ProtoType_One (sz AS ASCIIZ) AS LONG 
          DECLARE FUNCTION ProtoType_Two (sz AS ASCIIZ, l AS LONG) AS LONG
          
          FUNCTION CallMyFunction (szLib AS ASCIIZ, szProc AS ASCIIZ, OPTIONAL szparam1 AS ASCIIZ, szParam2 AS ASCIIZ.... ) AS LONG
          
            hLib = LoadLibrary (szLib)
            dwProc = getProcAddress (szProc)
          
            SELECT CASE szProc
              CASE "FunctionOne" 
                 CALL DWORD DwProc USING Prototyope_One (szParam1)  TO iRet
              CASE "FunctionTwo"
                 CALL DWORD dwProc USING Prototype_two (szParam1, VAL(szParam2))  TO iRet
          
              .....
            FreeLibrary  hLib 
            FUNCTION= iRet
          MCM
          Michael Mattias
          Tal Systems Inc. (retired)
          Racine WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            I am not good at macros but htink you should split the problem into two sections, one which just controls what dll's are loaded and used, the other which gets the data from them. Below is some sample code, note it only allows you to load a dll once and also can return results of any type.
            Code:
            DECLARE FUNCTION GetInfoFromDLL(Reslt AS ANY) AS LONG
            
            FUNCTION OpenCloseCustomDll(DllName AS STRING, DllPath AS STRING, Action AS LONG) AS DWORD
                ' Action = 0 to open, -1 to close
                DIM STATIC DllAdd(0) AS DWORD
                DIM STATIC DllNme(0) AS STRING
                LOCAL x AS LONG
                IF Action THEN
                    FOR x = 0 TO UBOUND(DllNme)
                        IF DllPath + DllName = DllNme(x) AND DllAdd(x) <> &hFFFFFFFF THEN
                            Freelibrary DllNme(x)
                            DllAdd(x) = &hFFFFFFFF
                            EXIT FOR
                            FUNCTION = 0
                        END IF
                    NEXT
                ELSE
                    FOR x = 0 TO UBOUND(DllNme)
                        IF DllPath + DllName = DllNme(x) THEN
                            IF DllAdd(x) <> &hFFFFFFFF THEN
                                FUNCTION = DllAdd(x)    'already open
                            ELSE
                                FUNCTION = 0    'tried before dll does not exist
                            END IF
                            EXIT FOR
                        END IF
                    NEXT
                    IF x > UBOUND(Dllnme)
                        REDIM PRESERVE DllNme(x)
                        REDIM PRESERVE DllAdd(x)
                        DllAdd(x) = LoadLibray(DllPath + DllName)
                        FUNCTION = DllAdd(x)
                        IF DllAdd(x) = 0 THEN DllAdd(x) = &HFFFFFFFF
                    END IF
                END IF
            END FUNCTION
            
            ' in your code first check the state of the dll and its existance
            ThisDllAdd = OpenCloseCustomDll(NameOfDll, OfficePath, 0)
            IF ThisDllAdd THEN
                ProcAdd = GetProcAddress(ThisDllAdd, ProcName)
                IF ProcAdd THEN
                    CALL DWORD ProcAdd USING GetInfoFromDll(VariableOfTypeYouExpect) TO Retn
                    'retn can give you an error code
                    
            'at some convenient time you can unload all the dlls that may be there just change the action to -1
            John

            Comment


            • #7
              Good clear examples. Thanks.

              I'm gonna forget the macro approach. I see now pretty much how I can do it from the examples.

              Thanks,

              Bob Mechler

              Comment


              • #8
                This code fits my needs. Thanks to suggestions from Michael and others.

                Code:
                'This code goes into an include file in a custom directory for the customer
                DECLARE FUNCTION GET_CHN_PRD(STRING) AS STRING
                MACRO CTRU_PATH(prm1)
                  IF NOT FNSP(CUST_DIR$) THEN
                    DLLPATH$ = "J:\PBSRC\TNET\" + CUST_DIR$ + "\"
                  ELSE
                    DLLPATH$ = ""
                  END IF
                  DLLNAME$ = prm1
                  DLLNAME$ = DLLPATH$ + DLLNAME$
                END MACRO
                FUNCTION CallDll (szLib AS STRING, szProc AS STRING,Param1 AS STRING, _
                 OPT Param2 AS STRING,OPT Param3 AS STRING,OPT Param4 AS STRING,OPT Param5 AS STRING) AS STRING
                  hLib??? = LoadLibrary(BYCOPY szLib)
                  IF hLib??? THEN
                    dwProc??? = getProcAddress (hLib???,BYCOPY szProc)
                    SELECT CASE szProc
                      CASE "GET_CHN_PRD"
                         CALL DWORD dwProc??? USING GET_CHN_PRD(Param1) TO lRet$
                    END SELECT
                  ELSE
                    MSGBOX "Required DLL " + szLib + " is not present. " ,%MB_OK OR %MB_ICONWARNING,"File not found"
                  END IF
                  IF hLib??? THEN
                    FreeLibrary  hLib???
                  END IF
                  FUNCTION = lRet$
                END FUNCTION
                
                
                'This code is in PBMAIN' CTRU is true if the correct customer is using the prgm
                
                IF CTRU THEN
                   CTRU_PATH("LBCHNPRD.DLL") 'returns local development or customer exe path
                   CHN_PRD$ = CALLDLL(DLLNAME$,"GET_CHN_PRD",BYCOPY F1.ACTNR$)
                   'Can pass up to 5 parameters see definition above
                   ...continues here
                END IF

                Comment

                Working...
                X