Announcement

Collapse
No announcement yet.

CallWindowProc (or how do you LoadLibrary and call function?)

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

  • CallWindowProc (or how do you LoadLibrary and call function?)

    I know I must have asked this before, and either did not understand, or did not get a usable answer (and danged if I can find a link to verify that it was not just a fleeting thought )

    Anyways, maybe not the best forum (but probably the BEST since I know the reputations of many of you out there)

    I am revisiting code of my past, and planning for the future (mostly fixing "mistakes" I made then that now I think my coding "Style" would best taking what I had and apply what I know now )

    In the realm of me making a DLL (created in PB) and distribute to users of multiple languages (VB, C, Labview, .Net, Java, OOK, Matlab, LabWindows...and etc) I am looking to compile once, distribute many times sort of thing, so I do not really care the language, just that my code works.

    (Bonus Points for those of you that know what language "Ook" is )

    Anyways, PB allows you to LoadLibrary and GetProcAddress and Call DWORD to allow you to Load the Dll, then get the address of the function you wish to call, and then a way to call the function. (in this way you can KNOW that all parts exist, and not crash out because you had a missing peice of the puzzle)

    Now my question is "How the HECK do you do it in another language????" (Most popular would be in VB for us "VB-Refugee" types)

    API? Compiler? some trick that I do not know of??? (I am looking for generics here, so probably API is the common link)

    Right now my only idea is "CallWindowProc" but I think that only calls the window process (or callback) so I would need to add code there to allow the user to call the function I need????

    Is there a way to get the function and just call it if I know what the format of the function is????

    aka lets say "AddOne(NumberToAdd) AS LONG" and I load the library and get the function then how do I call "AddOne(NumberToAdd) AS LONG" without resorting to what PB makes easy for me?

    Reason I ask is documentation in other languages show loadlibrary, and if lucky if GetProcess....but Danged if they show you how to call if (Only PB shows a possible way, but even there the examples I see are PB built functions and not a generic (maybe API way) so I thought I would ask if anyone can solve my complete confusion

    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? "

  • #2
    CallWindowProc is NOT a 'generic calling function" . Do not use as such.

    For other languages to call functions in a DLL you distribute, just give em good doc for your function: ALIAS name, call convention, description of each parameter (and if by reference or by value) and description of the return data type ; eg for PB LONG, use "32-bit signed integer."

    With proper documentation on the part of the DLL publisher, the programmer can always find the right way to call exported procedures using his language of choice. (That's what YOU do with the WinAPI doc, right?)

    BTW, you should read into the above, "Don't use any PB-proprietary data types either as params or return type!"

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

    Comment


    • #3
      Assuming you have good reason to be avoiding load-time linking, then, well, different languages either offer a direct way of calling a function through a pointer, (CALL DWORD with PowBas) or they do not! With VB.NET you would typically use a delegate function to do it. With VB 6, for example, I remember having to set up a dummy class and altering the VTable directly in order to have the relevant function called etc. This seemed to be the standard way of doing it at the time.

      The point is that you are not going to find a generic way of doing this, not even with CallWindowProc_(). Some inline ASM would probably work, but then not all languages offer this facility.

      So, for each of the languages you wish to support, you need to check first that the facility exists for calling functions through pointers etc. I think most languages these days are going to offer some way of doing this.

      One alternative comes to mind. Assuming the languages you are targetting allow for load-time linking (early binding) you could create a wrapper dll in Powerbasic which would be linked in at load time (through Declare statements etc.) This wrapper can then export a function which uses LoadLibrary() etc. to load up whichever dll(s) are required and the remaining wrapper functions can then be used to call functions in the new dll through pointers using CALL DWORD etc.

      Sounds a bit convoluted!
      http://www.arctic-reports.com/
      http://www.nxsoftware.com

      Comment


      • #4
        Well I have to admit, you guys are close (maybe not close enough to light this dim-bulb, but close enough to give a glimmer)

        In VB, as soon as I declare a function, it is basically "mapped" into my VB app (and if it does not exist, then I get a problem when I run the program)

        I suspect the same problem cause in PB, if I declare a function that does not exist (or incorrect parameters) then I get a problem when I compile it.

        One of the things that "Struck home" of sorts is MCM with
        just give em good doc for your function: ALIAS name, call convention, description of each parameter (and if by reference or by value) and description of the return data type ; eg for PB LONG, use "32-bit signed integer."
        All well and good, and I try to do that, but I guess my lil niche of the world I have come to think of as "Read the RTFM for crying out loud!!!!" when posed with a question that may be on page 1 of my documentation, but may be the 3rd line down.....GAHHHHHHH

        I digress....


        Anyways, I think my generic question was kinda like the docs I read (for the most part, sure the compilers may be different, but there MUST be a Windows API behind the scenes or the compiler would not work) that you could call and pass the correct parameters and complete the "LoadLibrary...GetWindowProc....Call Function, and pass parameters)
        which to me is simple, but the call function and pass parameters is lacking (SERIOUSLY LACKING) in all languages but PB intrinsic calls

        Stephen has the same idea I thought of (although I thought "Discovering the function and call rather than declaring the function and call was called "Late Binding" but anyways)
        you could create a wrapper dll in Powerbasic which would be linked in at load time (through Declare statements etc.) This wrapper can then export a function which uses LoadLibrary() etc. to load up whichever dll(s) are required and the remaining wrapper functions can then be used to call functions in the new dll through pointers using CALL DWORD etc.
        I thought of doing the same, but then I am stuck with declaring functions in a wrapper in each language (to handle thier differences) and assume I have a function in my dll to pass it on.

        in most cases its a mute point (like assuming a function exists in Kernel32.dll, but for the instances that it does not exist...then what???)
        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


        • #5
          Windows advanced 101:

          CallWindowProc

          This API call is most commonly used when superclassing an existing window class.

          What is superclassing ?

          It is similiar to subclassing, but instead of changing the address to the window procedure for a class after the fact, superclassing is where you poll windows for the address of an existing class, create a new window class and then call the original window class window procedure.

          To get the info about an existing window class you use:

          GetClassInfoEx

          This will give you the address to the window procedure for the existing class. For example let's say you want to create your own hybris button class. You could call GetClassInfoEx to get the address to the window procedure for the Button class (which is actually in the operating system itself) and then store the address.

          Now create a new window class (RegisterClassEx) and call it "MYBUTTONCLASS". When you create your own window class, you write the window procedure and that is passed when you register the class.

          Now in your classes window procedure, instead of calling DefWindowProc at the end of it for all unprocessed messages where you want the default processing, you instead call CallWindowProc using the address you got back from GetClassInfoEx. This will make the BUTTON class do all of the default processing so your new window class is the same as the BUTTON class. Now of course a superclass if of no use, unless you do some extra processing of messages to make the class different than the original.

          Now this is a simple overview of superclassing. There are some more details such as making sure the class uses the same definitions as the original class (ie. same amount of extra window bytes or possible more for custom storage). One needs to understand how classes work and the meaning of all the members of the structure passed in RegisterClassEx.

          LoadLibrary

          This function can load any library, which is basically a DLL. The file extension though need not be .DLL

          For example you can compile DLL code to a DLL, then change the file extension aftewards to what ever you want and then still be able to load it using LoadLibrary.

          For example, in my Visual Designer I support plugins. Basically a plugin is a DLL with code that can be called by the Designer, but I use the file extension .EZP to differentiate plugins from normal DLLs.

          Now when a DLL is loaded via LoadLibrary you can call routines in the DLL and also access any resources in it (ie. bitmaps).

          One example of using a DLL just for resources, in EZGUI I store a default pattern library and a default image library in two separate DLL's. This way the EZGUI runtime DLL need not have a bunch of images or patterns (bitamps to be made into brushes) to support these resources. Many users won't use them so the runtime DLL does not waste extra KB's just to store images. If the user wants to use them, the runtime DLL loads the image DLL and loads the resources from them, using LoadLibrary (to load the DLL).

          If you plan on calling subs/functions in a DLL loaded using LoadLibrary you just can't just use the API includes and call the functions. That would explicitly load the DLL with your app so LoadLibrary is not needed.

          Then why use LoadLibrary and how do you make calls to it ?

          One benefit is if you need to call an API which may not exist on all operating systems. Let's say your app will run on Windows 95 to Vista. If the app is run on Vista you want to add extra features found in the operating system. If you use an include (explicitly links your app to the DLL) for an API only in Vista , the app won't run on Windows 95.

          The solution ?

          Load the DLL (if the app does not already explicitly load it) using LoadLibrary. Using the module handle returned, you can now call:

          GetProcAddress

          to get the address of any exported sub/function in the DLL.

          Once you have loaded the DLL and then have the address to a sub/function in it, you can now call it using:

          CALL DWORD (PB command)

          If a DLL is already loaded explicitly by your app (ie. user32.dll) then there is no need to load it using loadlibrary. You can get the module handle using:

          GetModuleHandle

          With the module handle you can now call GetProcAddress to get the address of a function which may only exists on a later operating system. If the function is not found, then your app must assume the operating system does not support the function so you must handle this in your app by disabling the feature this function is required for. If you get the address then you can call it using CALL DWORD.
          Chris Boss
          Computer Workshop
          Developer of "EZGUI"
          http://cwsof.com
          http://twitter.com/EZGUIProGuy

          Comment


          • #6
            Cliff, there is no avoiding the fact that each language you wish to target will require it's own 'wrapper'. Even if this contains little more than lists of constants and UDTs etc. There is no api function that can complete the function calls for you - only the underlying compiler can complete the code for that because only the compiler can accept the function prototype and act accordingly. Sure there are tricks and hacks (inline ASM for example), but none that the api will assist you with.

            Your best bet is to use load-time linking if possible unless you are creating some kind of plug-in system in which case run-time linking via LoadLibrary() etc. is of course the way to go.

            As Michael outlined, however, in cases where either method of linking with your dll is appropriate, then let the end-developer decide which method to use? All you need really provide is a list of exports and their prototypes in order for the developer to then decide on the best way of linking with your dll. Personally, if advocating load-time linking, then I provide separate wrappers for each target language with all the code and directives necessary to link with the library. E.g. with PowerBasic I would provide a wrapper containing all the appropriate DECLARE statements etc.

            There is another option of course, namely; convert your dll into an in-process COM server using PowerBasic's new COM facilities. You would then just provide a list of supported interfaces and their IIDs etc.
            http://www.arctic-reports.com/
            http://www.nxsoftware.com

            Comment


            • #7
              There is another option of course, namely; convert your dll into an in-process COM server using PowerBasic's new COM facilities. You would then just provide a list of supported interfaces and their IIDs etc.
              Um, that's EXACTLY the same thing as documenting call convention, parameters and return types: It's expressing the facilities you are providing in language-agnostic terms, allowing the user to take advantage of those facilities via syntax appropriate to his language product of choice!

              That's the beauty of both the dynamic link library and the Component Object Model interface: they are totally, one hundred percent using-language-product agnostic.

              BTW, unless some procedure in your DLL does something totally funky, the decision to use "load time linking" vs. "call-time linking" is not yours to make, it is the user's.

              And further expanding upon the "standard interface" topic.....
              convert your dll into an in-process COM server .....just provide a list of supported interfaces and their IIDs etc
              That's a TYPELIB.

              MCM
              Last edited by Michael Mattias; 4 Sep 2009, 07:22 AM.
              Michael Mattias
              Tal Systems (retired)
              Port Washington WI USA
              [email protected]
              http://www.talsystems.com

              Comment


              • #8
                Indeed, but using a COM server does remove the problem, in the case of late-binding, of calling functions through function pointers having first used GetProcAddress() etc. That is all I was getting at, thinking of VB 6 etc.

                Of course, even with type-libs you are then relying upon your target language having some kind of object browser able to create the necessary wrapper files etc. (VB aside of course). Either that or take the iDispatch route which simply returns us, or the user, to the choice of early or late binding! Personally, I just prefer a pre-made header file with all the info in that I need; interfaces, IID's whatever. No messing!

                But, as you say, unless the dll's are being used and switched around at run-time etc. it really should be left to the end-developer how he/she links with the dll. No argument there!
                http://www.arctic-reports.com/
                http://www.nxsoftware.com

                Comment


                • #9
                  Also FWIW...
                  .. when posed with a question that may be on page 1 of my documentation, but may be the 3rd line down...
                  Welcome to the world of package software development.
                  Michael Mattias
                  Tal Systems (retired)
                  Port Washington WI USA
                  [email protected]
                  http://www.talsystems.com

                  Comment


                  • #10
                    I think I may have not explained myself correctly. (Maybe its my docs )

                    What I was really after, was once you know the DLL exists, the Function exists, and ready to call the function. How do you do it in other languages (or even PB) without the "CALL DWORD Using" that PB provides???
                    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


                    • #11
                      What I was really after, was once you know the DLL exists, the Function exists, and ready to call the function. How do you do it in other languages (or even PB) without the "CALL DWORD Using" that PB provides???
                      COBOL:
                      Code:
                        CALL PROCEDURE-POINTER-DATA-ITEM 
                                        USING    BY REFERENCE | BY VALUE    data-item-1  [SIZE size] 
                                                    BY REFERENCE | BY VALUE    data-item-2  [SIZE size] 
                                                    ....
                                                    BY REFERENCE | BY VALUE    data-item-n  [SIZE size] 
                           [ GIVING | RETURNING     return-value-data-item]
                      ASM : CALL

                      PowerBASIC/Windows : CALL DWORD
                      MS-DOS BASIC : CALL ABSOLUTE
                      FORTRAN : I forgot, have not used in 42 years.


                      Generically - say, if you want to search for it - this is a "call by address" operation.

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

                      Comment

                      Working...
                      X