Announcement

Collapse
No announcement yet.

Declare Issue

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

  • Declare Issue

    In the compression benchmark I posted last week, I tried to load two different DLLs so I could compare versions of the library. I assumed using the declares below would allow me to call both versions from the same module. It ended up not working, and the second one took precedence. Have I misunderstood how Declares work? I assumed the 'alias' was specific to the 'lib' (dll). Or, are the public entrypoint names global?

    Code:
    Declare Function ZSTD_VersionNumber_010405 Lib "libzstd_010405.dll" cdecl Alias "ZSTD_versionNumber" () As Long
    Declare Function ZSTD_VersionNumber_010304 Lib "libzstd_010304.dll" cdecl Alias "ZSTD_versionNumber" () As Long
    In the above example, no matter which 'function' I call, I receive the version number of the last definition. Flip the definitions and I receive the version number of the latter one.


  • #2
    ALIAS is GLOBAL.

    But what you can do is put a separate DECLARE statement inline in the DLL code instead of in an #INCLUDE file shared by both. the DLLs will now individually be compiled with the desired library in the import table.

    PowerBASIC keys on the ALIAS name. The ability to refer to the same function (internal or extermal) by multiple names is a fearture... but it comes at the cost of requiring a unique ALIAS per module. (You are right it need not be so limited... but you will have to speak to the publisher about changing the compiler).

    ALTERNATELY...

    You can eschew the "hard" DECLARE and load and call the function/DLL of your fondest wishes dynamically. I think PB WIN 10 even has a statement to make that easier (I never used it).

    Yes I HAVE done the "Separate DECLARE right in the code module" thing when I had conflicts.

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

    Comment


    • #3
      Thanks Michael!

      Comment


      • #4
        Originally posted by Michael Mattias View Post
        ALIAS is GLOBAL
        ...
        The ability to refer to the same function (internal or extermal) by multiple names is a fearture... but it comes at the cost of requiring a unique ALIAS per module..
        Hmm, that's a trap for young players when using multiple DLLs and something to be aware of if you are writing your own DLLs which may be used together.

        I've never encountered that and wasn't aware of it.

        It is not clear from PB Help, in fact it seems to state the opposite (although the example following is two calls to the same function in the same DLL):
        "Although a ProcName must be unique, you may use the same AliasName in multiple declarations."

        Comment


        • #5
          Verified OP error.
          Expected operation with LoadLibrary. It did. Try code below.
          PBWin 10's (and PBCC 6's) new IMPORT statement should also work, but got to go for cardiac exercise.

          1st sample DLL '
          Code:
          #compile dll "libzstd_010405.dll"
          #dim all
          
          #include once "Win32API.inc"
          
          global ghInstance as dword
          
          function ZSTD_versionNumber alias "ZSTD_versionNumber" export as long
            function = 10405
          end function
          '-------------------------------------------------------------------------------
          ' Main DLL entry point called by Windows...
          '
          function libmain (byval hInstance   as long, _
                            byval fwdReason   as long, _
                            byval lpvReserved as long) as long
          
              select case fwdReason
          
              case %DLL_PROCESS_ATTACH
                  'Indicates that the DLL is being loaded by another process (a DLL
                  'or EXE is loading the DLL).  DLLs can use this opportunity to
                  'initialize any instance or global data, such as arrays.
          
                  ghInstance = hInstance
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!  This will prevent the EXE from running.
          
              case %DLL_PROCESS_DETACH
                  'Indicates that the DLL is being unloaded or detached from the
                  'calling application.  DLLs can take this opportunity to clean
                  'up all resources for all threads attached and known to the DLL.
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!
          
              case %DLL_THREAD_ATTACH
                  'Indicates that the DLL is being loaded by a new thread in the
                  'calling application.  DLLs can use this opportunity to
                  'initialize any thread local storage (TLS).
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!
          
              case %DLL_THREAD_DETACH
                  'Indicates that the thread is exiting cleanly.  If the DLL has
                  'allocated any thread local storage, it should be released.
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!
          
              end select
          
          end function '
          2nd sample DLL
          '
          Code:
          #compile dll "libzstd_010304.dll"
          #dim all
          
          #include once "Win32API.inc"
          
          global ghInstance as dword
          
          function ZSTD_versionNumber alias "ZSTD_versionNumber" export as long
            function = 10304
          end function
          
          
          '-------------------------------------------------------------------------------
          ' Main DLL entry point called by Windows...
          '
          function libmain (byval hInstance   as long, _
                            byval fwdReason   as long, _
                            byval lpvReserved as long) as long
          
              select case fwdReason
          
              case %DLL_PROCESS_ATTACH
                  'Indicates that the DLL is being loaded by another process (a DLL
                  'or EXE is loading the DLL).  DLLs can use this opportunity to
                  'initialize any instance or global data, such as arrays.
          
                  ghInstance = hInstance
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!  This will prevent the EXE from running.
          
              case %DLL_PROCESS_DETACH
                  'Indicates that the DLL is being unloaded or detached from the
                  'calling application.  DLLs can take this opportunity to clean
                  'up all resources for all threads attached and known to the DLL.
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!
          
              case %DLL_THREAD_ATTACH
                  'Indicates that the DLL is being loaded by a new thread in the
                  'calling application.  DLLs can use this opportunity to
                  'initialize any thread local storage (TLS).
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!
          
              case %DLL_THREAD_DETACH
                  'Indicates that the thread is exiting cleanly.  If the DLL has
                  'allocated any thread local storage, it should be released.
          
                  function = 1   'success!
          
                  'FUNCTION = 0   'failure!
          
              end select
          
          end function '
          Demo exe
          '
          Code:
          #compile exe 'testAlias.bas
          #dim all
          #include "WinBase.inc"
          '
          declare function VersionNumber() as long
          function pbmain () as long
            local hLib1, hLib2, pLib1Func, pLib2Func as dword
            local Result1, Result2 as long
            '
            hLib1 = LoadLibrary("libzstd_010405.dll")
            hLib2 = LoadLibrary("libzstd_010304.dll")
            pLib1Func = GetProcAddress(hLib1, "ZSTD_versionNumber") 'note - ALIAS names
            pLib2Func = GetProcAddress(hLib2, "ZSTD_versionNumber") 'are the same.
            call dword pLib1Func using VersionNumber to Result1
            call dword pLib2Func using VersionNumber to Result2
          
            ? str$(Result1) + " " + str$(Result2) 'the 2 different numbers here!
            FreeLibrary(hLib1)
            FreeLibrary(hLib2)
          end function '
          Cheers,
          Dale

          Comment


          • #6
            Dale, Not sure what you meant by "Verified OP error"

            Do you mean that the OP made an error or that he was correct in his supposition "I assumed the 'alias' was specific to the 'lib' (dll). Or, are the public entrypoint names global?"

            Your example does not use DECLARE..... ALIAS..... so doesn't answer this question.

            Contrary to the name of your exe ("testAlias") , it doesn't test ALIAS.

            The problem apparently lies with the value of ALIAS when the same function name used with multiple DECLARE statements pointing to different DLLs, not with the actual exported function names being the same.
            i.e. You have demonstrated a "workaround" for the OP's problem , it demonstrates nothing concerning the "Declare Issue"

            Comment


            • #7
              Originally posted by Dale Yarker View Post
              PBWin 10's (and PBCC 6's) new IMPORT statement should also work, but got to go for cardiac exercise.
              Yes, it does:
              '
              Code:
              #COMPILE EXE 'testImport.bas
              #DIM ALL
              #INCLUDE "WinBase.inc"
              '
              DECLARE FUNCTION VersionNumber() AS LONG
              FUNCTION PBMAIN () AS LONG
                LOCAL hLib1, hLib2, pLib1Func, pLib2Func AS DWORD
                LOCAL Result1, Result2 AS LONG
              
                hLib1 = LoadLibrary("libzstd_010405.dll")
                hLib2 = LoadLibrary("libzstd_010304.dll")
                pLib1Func = GetProcAddress(hLib1, "ZSTD_versionNumber")
                pLib2Func = GetProcAddress(hLib2, "ZSTD_versionNumber")
              
                IF pLib1Func THEN CALL DWORD pLib1Func USING VersionNumber TO Result1
                IF pLib2Func THEN CALL DWORD pLib2Func USING VersionNumber TO Result2
                ? STR$(Result1) + " " + STR$(Result2) 'the 2 different numbers here!
                FreeLibrary(hLib1)
                FreeLibrary(hLib2)
              
                IMPORT ADDR "ZSTD_versionNumber","libzstd_010405.dll" TO pLib1Func,hLib1
                IMPORT ADDR "ZSTD_versionNumber","libzstd_010304.dll" TO pLib2Func,hLib2
              
                IF pLib1Func THEN CALL DWORD pLib1Func USING VersionNumber TO Result1
                IF pLib2Func THEN CALL DWORD pLib2Func USING VersionNumber TO Result2
                ? STR$(Result1) + " " + STR$(Result2) 'the 2 different numbers here!
                IMPORT CLOSE hLib1
                IMPORT CLOSE hLib2
              
              END FUNCTION '
              '
              Last edited by Stuart McLachlan; 30 Jun 2020, 06:49 AM.

              Comment


              • #8
                The problem stated by OP was repeatable here.

                I also would have assumed the alias was specific to the lib in the declare.

                It is the alias name given to the functions in the DLL. Not being defined as alias in GetProcAddress maybe why it did not fail.

                It did test that identically aliased functions in two different DLLs could be individually called within one exe.

                Yes, a workaround; unless everyone wants to complain about DECLARE some more.

                -----------------------------------------------------------------
                Personally I hope Adam fixes DECLARE to work as we expected. Second choice is reword Help.

                ((added - I haven't used implicit loading in a long time. I don't like unexplained failures ))

                ((much later - the coding I do for here all starts with "test" in the file name. If I like it I rename with "demo", or a project name. When directory gets too full I delete *test*.* ))

                Cheers,
                Last edited by Dale Yarker; 30 Jun 2020, 04:57 AM.
                Dale

                Comment


                • #9
                  Hmm, that's a trap for young players when using multiple DLLs and something to be aware of if you are writing your own DLLs which may be used together.

                  I've never encountered that and wasn't aware of it.
                  Patience, Grasshopper. In time you will learn.
                  Michael Mattias
                  Tal Systems Inc. (retired)
                  Racine WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    Let's ask the real questiojn here, re ..

                    Code:
                    Declare Function ZSTD_VersionNumber_010405 Lib "libzstd_010405.dll" cdecl Alias "ZSTD_versionNumber" () As Long
                    Declare Function ZSTD_VersionNumber_010304 Lib "libzstd_010304.dll" cdecl Alias "ZSTD_versionNumber" () As Long
                    If this is really a function to get the version number, why is the version number not simply stored in the DLL's resource?

                    Code:
                    // CGSSCC20R.RC
                    // Resource for Cloyes Gear & Products Co 'cgarchive' Program
                    // =====
                    // 01.04.12 MCM 1.0.0 Original.
                    
                    #include "resource.h"
                    
                    #define COPYRIGHT_SYMBOL "\251"
                    #define REGISTERED_SYMBOL "\256"
                    #define TRADEMARK_SYMBOL "\231"
                    
                    #include "resource.h"
                    #define VERSION_MAJOR 1
                    #define VERSION_MINOR 0
                    #define VERSION_BUILD 0
                    #define VERSION_LITERAL " 1.0.0\0"
                    #define VERSION_DATE "January 4 2012\0"
                    #define VERSION_DESC "Command Line File Archiver Utility\0"
                    #define VERSION_COPYRIGHT "\251 2012 Cloyes Gear and Products Co Paris AR USA\0"
                    #define VERSION_TRADEMARK "None\0"
                    #define VERSION_COMPILER "PowerBASIC Inc. Console Compiler 6.0.3\0"
                    #define VERSION_COMPANY "Cloyes Gear and Products Company Paris AR USA\0"
                    #define VERSION_AUTHOR "Michael Mattias Tal SYstems Inc Racine WI USA\0"
                    
                    // STANDARD VERSION RESOURCE
                    VS_VERSION_INFO VERSIONINFO
                    FILEVERSION VERSION_MAJOR, VERSION_MINOR, 0, VERSION_BUILD
                    PRODUCTVERSION VERSION_MAJOR, VERSION_MINOR, 0, VERSION_BUILD
                    FILEOS VOS_WINDOWS32
                    FILETYPE VFT_APP
                    //* VFT_DLL FOR DLLs
                    BEGIN
                    BLOCK "StringFileInfo"
                    BEGIN
                    BLOCK "040904E4"
                    BEGIN
                    VALUE "CompanyName", VERSION_COMPANY
                    VALUE "FileDescription", VERSION_DESC
                    VALUE "FileVersion", VERSION_LITERAL
                    VALUE "InternalName", "cgarchive\0"
                    VALUE "OriginalFilename", "cgarchive\0"
                    VALUE "LegalCopyright", VERSION_COPYRIGHT
                    VALUE "LegalTrademarks", VERSION_TRADEMARK
                    VALUE "ProductName", "none\0"
                    VALUE "ProductVersion", VERSION_LITERAL
                    VALUE "Comments", "none\0"
                    VALUE "Author", VERSION_AUTHOR
                    VALUE "VersionDate", VERSION_DATE
                    VALUE "Compiler", VERSION_COMPILER
                    END
                    END
                    BLOCK "VarFileInfo"
                    BEGIN
                    VALUE "Translation", 0x409, 0x4E4 /// parms 2, 3 must match line in BLOCK above
                    END
                    END
                    
                    // END OF FILE
                    Enquiring Minds Want to Know!
                    Michael Mattias
                    Tal Systems Inc. (retired)
                    Racine WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      Originally posted by Michael Mattias View Post
                      Let's ask the real questiojn here, re ..

                      Code:
                      Declare Function ZSTD_VersionNumber_010405 Lib "libzstd_010405.dll" cdecl Alias "ZSTD_versionNumber" () As Long
                      Declare Function ZSTD_VersionNumber_010304 Lib "libzstd_010304.dll" cdecl Alias "ZSTD_versionNumber" () As Long
                      If this is really a function to get the version number, why is the version number not simply stored in the DLL's resource?
                      If it's wasn't, you'd have to ask the developers of ZStandard, not here

                      Actually it is, but, a simple function call is a lot simpler than working with GetFileVersionInfo(). for a DLL that you are using.

                      And as for your example, the #RESOURCE VERSIONINFO built into PB makes it a lot simpler nowadays.

                      Comment


                      • #12
                        If it's wasn't, you'd have to ask the developers of ZStandard.
                        True enough. Many publishers don't include a version number in their executable files.

                        Call up "resource hacker" or equal (or simply Windows' File Explorer "properties") on, say, "pbwin.exe" (any version) or on "pbedit.exe" prior to version 9.0

                        Tthat's why you see the references to the compiler (version string resources) in my sample resource in Post #10. For all the different executables I have created for clients I wanted a quick way to tell what compiler I used to create it so I could use the same one to maintain it. Sure, I have that info "in text" in the program history comments section of the source code, but just hitting the file with Explorer and asking for properties is a lot quicker.

                        With how easy PB has made it now to get a version number into the executable (#RESOURCE VERSIOINIFO statements, PB Win10+ or CC6+ ) I really suggest everyone get into the habit of storing a resource number in your executable files (EXEs AND DLLs)

                        MCM


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

                        Comment


                        • #13
                          I hoped the LoadLibrary trick would work, but had not had time to try it yet. Thanks for all the commentary everyone!

                          Code:
                            hLib1 = LoadLibrary("libzstd_010405.dll")
                            hLib2 = LoadLibrary("libzstd_010304.dll")
                            pLib1Func = GetProcAddress(hLib1, "ZSTD_versionNumber") 'note - ALIAS names
                            pLib2Func = GetProcAddress(hLib2, "ZSTD_versionNumber") 'are the same.
                            call dword pLib1Func using VersionNumber to Result1 call dword pLib2Func using VersionNumber to Result2

                          Comment

                          Working...
                          X