Announcement

Collapse
No announcement yet.

Runtime detection of missing DLL

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

  • Runtime detection of missing DLL

    I need advice for what to put in my powerbasic source code so that the compiled program can run if the source code references a DLL that does not exist on the system,

    My program has a few features that depends on a DLL being present, but most of the program does not rely on the DLL. I believed I had put sufficient protective logic into the source code: the main block in the code uses ISFILE to check if the DLL exists; if it does not, then the code does not attempt to invoke the Function that's in the DLL and instead displays a message box to the user.

    But I discovered that program fails before ever reaching my protective behavior. There is a runtime error as soon as the program starts (on a system missing the DLL) without the program even reaching my instruction to check for the DLL's presence.

    That is, I'm not getting the error message box that I coded. Instead, I'm getting a generic runtime error message box from powerbasic that it is "unable to locate component" with the DLL name.

    I guess that's happening because I declare the function and the DLL at the top of my code before even entering the main block (e.g.,
    DECLARE FUNCTION FunctionName LIB "DLLName.dll" ....

    This error outcome is unacceptable for my purpose, because it means that my entire program--including the features not dependent on that DLL--cannot be run if the DLL is missing.

    What can I do to declare the function in a way that my program will run regardless of the presence of the DLL?


  • #2
    If you have PBWin 10 or PBCC 6 use IMPORT ADDR to load the DLL.

    LoadLibrary() and GetProcAddress() for any version to load the DLL.

    Leave LIB part out of function DECLARE. (also do not use the IMPORT word in the DECLARE)

    Use CALL DWORD ... USING to call the function or sub that is in the DLL.

    If the handle or address from this method is returned as 0, the program can do something else, like a messagebox.

    Cheers,
    Dale

    Comment


    • #3
      LoadLibrary / GetProcAddress is the way. Lots of examples throughout the forums.

      https://www.google.com/search?q=GetP...client=gws-wiz

      <b>George W. Bleck</b>
      <img src='http://www.blecktech.com/myemail.gif'>

      Comment


      • #4
        Dale has suggested IMPORT ADDR. (assuming you are using PBWIN 10)

        If you read the PB Help file entry for IMPORT. and CALL DWORD, it is explained quite well.

        Comment


        • #5
          My previous (and George's) post answers your question about catching a failed load.

          You didn't show much code, but non use of ALIAS may be causing the failure. The default for the alias is upper case. If name in the DLL is a different mix of case or spelling, the load will fail. The alias in your code and the DLL code must match exactly.

          If you also wrote the DLL, then the FUNCTION or SUB line in the DLL code must include the word EXPORT.

          Cheers,
          Dale

          Comment


          • #6
            Originally posted by S Stamp View Post
            I need advice for what to put in my powerbasic source code so that the compiled program can run if the source code references a DLL that does not exist on the system,
            ...
            This error outcome is unacceptable for my purpose, because it means that my entire program--including the features not dependent on that DLL--cannot be run if the DLL is missing.

            What can I do to declare the function in a way that my program will run regardless of the presence of the DLL?
            SImple demo of Explicit Loading and test for existence of DLL and/or function in it:

            '
            Code:
            #COMPILE EXE
                DECLARE FUNCTION UserName (lpBuffer AS WSTRINGZ, nSize AS DWORD)  AS LONG
            
                FUNCTION PBMAIN() AS LONG
                    LOCAL dwProcAddr,dwProcAddr2,dwSize AS DWORD
                    LOCAL buff AS WSTRINGZ * 64
                    dwSize = SIZEOF(buff)
            
                   IMPORT ADDR "GetUserNameW","AdvApi32.dll" TO dwProcAddr
                    IF dwProcAddr <> 0 THEN
                        CALL DWORD dwProcAddr USING UserName(buff, dwSize)
                        ? "Explicit UserName = " & buff
                    ELSE
                        ? "Can't find DLL"
                    END IF
            
                    IMPORT ADDR "GetUserNameW","MIssingFIle.dll" TO dwProcAddr2
                    IF dwProcAddr2 <> 0 THEN
                        CALL DWORD dwProcAddr2 USING UserName(buff, dwSize)
                        ? "Explicit UserName = " & buff
                    ELSE
                        ? "Can't find DLL"
                    END IF
            
                END FUNCTION
            '

            Comment


            • #7
              Thanks guys. I see how your suggestions would be the optimal and elegant way of dealing with this. Let me add a wrinkle, to see if you can suggest any other workarounds. (By the way, my program is a utility that I wrote for myself. I'm the only user (although I use it on multiple PCs, some of which do not have the DLL), so it's acceptable if I have to solve this problem with an amateurish approach.)

              The function/DLL that may be potentially missing is not actually called in the source code that I wrote. Instead, my source code has INCLUDEs to powerbasic-provided *.INC files, and those files are using the LIB "DLL_name.DLL" method to declare the functions. It would be a time-prohibitive effort to rewrite the affected INC files.

              1) I realize that I can use #IF to control whether certain source code gets included or excluded at compile time. Is there some analogous function that could do that at runtime?

              2) one idea I'm considering is to take my functionality that depends on the DLL and isolate it into it's own EXE. Then I could have run time logic in my main EXE to check for the DLL existence and not invoke the new EXE if the DLL is missing. EXE_1 would need to convey some information to EXE_2--more data than would fit in a command line. I guess I would need EXE_1 to write the values to an INI for EXE_2 to read. My biggest reluctance with this approach is that there are some common routines currently embedded in EXE_1 that EXE_2 would also need access to, so I'd either have to duplicate that code in both programs (which would create double work for future code maintenance) or I'll have to restructure that existing code to pull out those components and put them into a DLL or INC so that both EXEs can access it).

              Any thoughts?

              Thanks

              Comment


              • #8
                Originally posted by S Stamp View Post
                Thanks guys. I see how your suggestions would be the optimal and elegant way of dealing with this. Let me add a wrinkle, to see if you can suggest any other workarounds. (By the way, my program is a utility that I wrote for myself. I'm the only user (although I use it on multiple PCs, some of which do not have the DLL), so it's acceptable if I have to solve this problem with an amateurish approach.)

                The function/DLL that may be potentially missing is not actually called in the source code that I wrote. Instead, my source code has INCLUDEs to powerbasic-provided *.INC files, and those files are using the LIB "DLL_name.DLL" method to declare the functions. It would be a time-prohibitive effort to rewrite the affected INC files.

                The *.INC FIles contain IMPLICIT Declares (i.e. the DLL is named), so PB will look always look for them at startup if they are used. So don't use the function names AS DECLARED in the .inc files.

                1) I realize that I can use #IF to control whether certain source code gets included or excluded at compile time. Is there some analogous function that could do that at runtime?

                No, #IF is a COMPILE TIME directive which tell s the compiler what to do when building your application.

                2) one idea I'm considering is to take my functionality that depends on the DLL and isolate it into it's own EXE. Then I could have run time logic in my main EXE to check for the DLL existence and not invoke the new EXE if the DLL is missing. EXE_1 would need to convey some information to EXE_2--more data than would fit in a command line. I guess I would need EXE_1 to write the values to an INI for EXE_2 to read. My biggest reluctance with this approach is that there are some common routines currently embedded in EXE_1 that EXE_2 would also need access to, so I'd either have to duplicate that code in both programs (which would create double work for future code maintenance) or I'll have to restructure that existing code to pull out those components and put them into a DLL or INC so that both EXEs can access it).

                Any thoughts?

                Thanks
                If you don't use the functions names as declared in the include files, they won't be included in your application.
                Just declare the functions explicitly with a slightly different function name and then call that with IMPORT ADDR... CALL DWORD... ( e.g. "UserName" in post #6)


                Comment


                • #9
                  DO NOT change the PB include files!

                  Just make a DECLARE for function with a letter added to name. This is for the USING part of CALL DWORD.

                  Explicit loading is how "run time" version of #IF is done for loading DLLs.

                  Sorry, but your "2)" looks like an hour of risky (or long way around) coding, to avoid 15 to 30 minutes to change from implicit to explicit. (IMO) The code that uses the return from the DLL function is unchanged.

                  If the DECLARE was the only use of the include file, then delete the #INCLUDE. (and changing name in new DECLARE is not needed.)

                  Cheers,
                  Dale

                  Comment


                  • #10
                    (edit - Whoa!! What the . . . )
                    How can an implicit load from a PB provided include fail????confused????

                    A really old include? Having only FuncName, while current includes have FuncNameA, FuncNameW and a #IF for FuncName (state of %UNICODE as the test); and recent Windows DLLs only have FuncNameA and FuncNameW.

                    So -
                    1. Your PB version, and are those the include files being used?
                    2. Your Windows version?
                    3. Specific Windows DLL function(s) ((APIs)) being called?

                    Cheers,
                    Dale

                    Comment


                    • #11
                      > How can an implicit load from a PB provided include fail????

                      I'm compiling with
                      PowerBASIC 10 For Windows

                      The INC files that my source code references are:
                      #INCLUDE "bcrypt.inc"
                      #INCLUDE "wincrypt.inc"
                      (note that these INCs in turn include other INC's, and so on.)

                      The two missing DLL's that I identified are:
                      bcrypt.dll
                      ntdll.dll
                      There are probably more. The run time error only tells me the first one it encounters, and I hadn't gone beyond identifying the first 2.

                      All the necessary DLLs are present on my Windows 10 machine. The problem is that I also have an old PC running Windows XP that I sometimes use. XP does not have these DLLs.

                      If you're wondering why would somebody even keep around an XP machine, the reason is that I have some old third-party applications that don't have Windows 10 compatible versions. So I switch back to XP when I need to use these. My program (the topic of this thread) runs fine on XP... or at least it did until today when I added some new functionality with the intent that that functionality would be available only on Windows 10.

                      Comment


                      • #12
                        "bcrypt.inc" is Vista, or later; so recommend explicit loading

                        Cheers,
                        Dale

                        Comment


                        • #13
                          Thanks to everyone who posted. I learned a lot from your answers.

                          Ultimately, I figured out that both the approaches proposed were more effort than necessary for solving my problem. The graceful approach that you recommended (to do explicit loading via IMPORT ADDR) was onerous due to the number of nested functions that would need to be identified and re-created. The Rube Goldberg approach that I pitched in post #7 has obvious disadvantages.

                          Then I realized that I didn't need to make my program so dynamic and automatically responsive to the particular Windows environment. My program was already divided into modules, where the new functionality that calls the Win10 DLLs was in its own DLL that I wrote. Instead of trying to make that single DLL dynamic at run time and respond appropriately depending on whether the necessary Win10 DLLs were present, I just compiled 2 versions of that DLL. The real, fully functional DLL goes on my Win10 machine (the DLL depends on the Win 10 DLLs). My alternate DLL has the same file name and the same function name and parameters, but it's trivial source code that does only 1 thing--return a graceful error message if the function is called--and has no dependency on other DLLs); I put this DLL on the WinXP machine. In this way, my main program can continue to run on XP without encountering the unable to locate component error.

                          Comment

                          Working...
                          X