How to dynamically load DLL's, check if your app is manifested properly, track back Access Violations in your app and a brief foray into the windows dll using the clarion debugger disassembler window

Edit Changed title from
GetProcAddress & Alexey’s Assembler code to pass additional parameters, & Larry Sand’s Cmag and Paul Attryde’s post on calling a DLL at runtime, ie not statically linked
To
How to dynamically load DLL’s, check if your app is manifested properly, track back Access Violations in your app and a brief foray into the windows dll using the clarion debugger disassembler window.

Original post:

So I’ve got a class, but also trying to get this to work in a simple prj file, the loadlibrary & getprocaddress windows apis’s returning the address of some api’s. Thats all working fine, but now I’m looking for a way to call the api and not have it statically linked.

I’ve tracked down these posts
Clarion, GetProcAddress() (computer-programming-forum.com)
Clarion, A DLL Question: Using LoadLibrary and GetProcAddress? (computer-programming-forum.com)
in particular the assembler code

MODULE('SRVCALL_.A')
  CallProc(_ADDRESS,_ADDRESS,_ADDRESS),_ADDRESS,PROC
END
SRVCALL_.A
==========
(* SRVCALL_.A
      Extra code for SRVCALL
*)
          module srvcall_
(* ============================================================================
   CallProc
     Indirect function call
   Created: 25.01.1996 14:39:06
   Revised: 08.02.1996 17:34:02
============================================================================ *)
segment SRVCALL_TEXT (CODE,48H)
  public CALLPROC:
(*%T _WIN32*)
        push   ebx
        push   ebx
        push   eax
        call   ecx
        pop    eax
        ret    0
(*%E *)
(*%F _WIN32*)
        push   bp
        mov    bp,sp
        push   dx
        push   cx
        push   dx
        push   cx
        push   bx
        push   ax
        call   dword [bp][6]
        pop    ax
        pop    dx
        db     0C9H
        ret    far 4
(*%E *)
          end

Also read through these two posts on TS assembler
Topspeed assembler - ide / docs - ClarionHub
Unfinished text with some information about the TopSpeed assembler - ide / docs - ClarionHub
and am at the stage where the compiler is throwing the error: (note the addition of the “.clw”)

(srvcall_.a.clw 1,8) Make error: Expected Program.

I’ve removed the first comment

(* SRVCALL_.A
          Extra code for SRVCALL
    *)

so line 1 reads: module srvcall_
and my module declaration reads

module(‘srvcall_.a’)

so am I missing a pragma or compiler directive to stop it from appending a .clw after the .a in the module declaration?

I’m obviously doing something wrong. Likewise I’ve also looked at Larry Sand’s Clarionmag 3 part Loading Dll’s at Runtime cmag-2001-05.pdf and not been able to get that to work, using TYPE to sort of function overload.
And also Paul Attrydes post on his website for doing a DIY CALL instead of using the built in CALL which doesnt handle parameters.

So any idea’s what I’m doing wrong and which is the best method to use? I’m assuming the compiler still allows these things considering when these posts and articles were written.

TIA

Edit.

I should add, I have PaulA 's loader.zip examples both working ( Dynamically loading DLLs at runtime (attryde.com)), but I’ve not looked at that in depth yet to see how I can modify it to accept parameters, and I wanted to ideally do a side by side comparison in the debugger examining memory to see how they differed with Alexey’s asm example.

I’ve not been able to get Larry’s example on dynamically loading dlls at runtime https://clarionmag.jira.com/wiki/download/attachments/399455/cmag-2001-05.zip?api=v2 but it looks similar to PaulA’s using the api prototype as a Type definition and then using the api type definition as a datatype like a group in the procedure parameter.
I’ve done 18hrs in front of the computer so I’m a bit squared eyed and brain dead atm.

TIA

Got this working and figured out how to pass parameters back and forth.

I dont have a github account so its posted here, with some examples on how to use BitShifts, how to spot if your app is manifested properly, a brief explanation on Unions seen in windows api data structures, and obviously how to dynamically load windows api’s using GetProcAddress and pass parameters to them

Use PaulA prj file from bottom of this webpage Dynamically loading DLLs at runtime (attryde.com) but change the source files loader1/2.clw and pipeline.clw to the one’s shown below.

Loader4.clw

! Example application showing how to load a DLL and call a procedure at runtime
! using the Windows API and Clarion 

  Program
  Map
    Module('Windows API')
      LoadLibrary(Long),Long,Raw,Pascal,Name('LoadLibraryA')
      FreeLibrary(ULong),Bool,Raw,Pascal
      GetProcAddress(Long,Long),Long,Raw,Pascal
      GetLastError(),Ulong,Raw,Pascal
    End
    Module('Dynamic') !Was PaulA's loadpipe module
      CallAddr(LONG),ulong,PROC,Name('CallAddr')                ! PaulA example
      IS_GetVersion(LONG),ulong,PROC,Name('IS_GetVersion')      ! Renamed example of PaulA's example

      !This works
      IS_GetSystemDirectoryALong(long,long,ulong),ulong,Proc,Name('IS_GetSystemDirectoryALong')  ! name

      !This doesnt work
      IS_GetSystemDirectoryACstring(long,*cstring,ulong),ulong,Proc,Name('IS_GetSystemDirectoryACstring')  !cant pass cstrings, need to pass an address like shown below.
    End
  End

GlobalVariables                 Group,Pre(Glo)
DLLName                           CString(255)
DllNamePtr                        ULong
DLLInstance                       Ulong
ProcName                          CString(255)
ProcNamePtr                       Ulong
ProcPtr                           Long
WinError                          ulong
                                End

Loc:RVulong                     ulong
Loc:SystemDirectory             Cstring(1000)
Loc:SystemDirectoryAddress      long
Loc:Length                      uLong(1000)

GetVersion                      Group,Pre(GV)
BuildNumber                         long
Major                               long
Minor                               long
                                End
loc:Short                       short
loc:ProcPtr                     long
  Code
    Glo:DLLName = 'C:\WINDOWS\system32\Kernel32.DLL'
    Glo:DllNamePtr = Address(Glo:DLLName)
    Glo:DLLInstance = LoadLibrary( Glo:DllNamePtr )
    If Glo:DLLInstance = 0 Then
      Glo:WinError = GetLastError()
      Message('Unable to find:' & Glo:DLLName &' Windows Errorcode:' & Glo:WinError)
    Else
      Glo:ProcName = 'GetVersion'
      Glo:ProcNamePtr = Address(Glo:ProcName)
      Glo:ProcPtr = GetProcAddress(Glo:DLLInstance,Glo:ProcNamePtr)
    End
    If Glo:ProcPtr = 0
      Message('Unable to find entry point in DLL')
    Else
        !This calls API's which return values
        Loc:RVulong     = CallAddr(Glo:ProcPtr)         !This calls CallAddr in Dynamic.clw and uses the procedure prototype declared in the Dynamic module above.
        Loc:RVulong     = IS_GetVersion(Glo:ProcPtr)    !This calls IS_GetVersion in Dynamic.clw but uses the procedure prototype declared in the Dynamic module above.
                                                        !If you change the return value in the Dynamic module above to a byte you'll only get a byte back

            !
            !This section shows how to extract bits out of a variable, but can also be used to extract info out of some windows datastructures with Unions.
            !Windows datastructures which show a Union, sometimes called a Dummy Union is basically take the element with the most amount of bytes and use that
            !in your clarion group, then make all the other elements a "[Long/cstring/whatever datatype],Over(the element with the greatest width)" FYI.
            !
            !The Window api GetVersion is no longer recommended for use because programmers have trouble getting the info out of the return value
            !so MS recommend using GetVersionEx for an easy life. See their webpage detailing this API.
            !The MS website states the summarised below.
            !low order word (clarion short) contains version number of the OS
            !low order byte of this low order word specifies the major version number eg 6 = XP
            !high order byte of this low order word specifies the minor version number eg 0 or 1 for XP.
            !High Order <--               -->Low Order
            !          3         2         1
            !987654 3210987654321098765432109876543210                              4   3  2 1
            !       |---  Type   ---||--- Ver     ---|                              32,24,16,8
        IF Loc:RVulong                !bShift converts to a long so need to move the length of a long even if the variable is a byte.
            GV:BuildNumber          = bShift(Loc:RVulong,-16)             !Move right 16bits lose Version. + = left then - = right

            loc:Short = Loc:RVulong  !is the same as (bShift(bShift(Loc:RVulong,16),-16)            !Same as moving 16bits left to lose the high order word (clarion short)
                                                                                                    !and then return it back to its original position having lost some bytes.

            GV:Major                = bShift(bShift(loc:Short,24),-24)    !Low order byte        + = left then - = right
            GV:Minor                = bShift(loc:Short,-8)                !High order byte

            GV:Major                = bShift(bShift(bShift(bShift(Loc:RVulong,16),-16),24),-24)
            GV:Minor                = bShift(bShift(bShift(Loc:RVulong,16),-16),-8)
            Message(    'BuildNumber:' & GV:BuildNumber &'<13,10>' &|
                        'OS Major:' & GV:Major &'<13,10>' &|
                        'OS Minor:' & GV:Minor )
        End

        !This calls an API which has a param passed to it by address which is filled by the api ie (*cstring pParam)
        !This demonstrates how to handle parameters with api's loaded using GetProcAddress.
        !You can not use a *cstring parameter because the clarion runtime handles the * address differently to a long or ulong.
        !Look in the Debugger, dissembly window to see the changes in assembler.
        !
        Glo:ProcName                = 'GetSystemDirectoryA'
        Glo:ProcNamePtr             = Address(Glo:ProcName)
        Glo:ProcPtr                 = GetProcAddress(Glo:DLLInstance,Glo:ProcNamePtr)
        Loc:ProcPtr                 = GetProcAddress(Glo:DLLInstance,Glo:ProcNamePtr)
        Loc:SystemDirectoryAddress  = Address(Loc:SystemDirectory) !mov ecx,Loc:SystemDirectory
        Message('Long Before Loc:SystemDirectory:' & Loc:SystemDirectory)
        Loc:RVulong                 = IS_GetSystemDirectoryALong(Glo:ProcPtr,Loc:SystemDirectoryAddress,Loc:Length)
        Message('Long After Loc:SystemDirectory:' & Loc:SystemDirectory)

        Loc:SystemDirectory = ''
        Message('Cstring Before Loc:SystemDirectory:' & Loc:SystemDirectory)
        Loc:RVulong                 = IS_GetSystemDirectoryACstring(Loc:ProcPtr,Loc:SystemDirectory,Loc:Length)
        Message('Cstring After Loc:SystemDirectory:' & Loc:SystemDirectory)

        x# = FreeLibrary(Glo:DLLInstance)
    End

Dynamic.clw

 Member()
  ! This file is made to have a "chunk" allowing to be able to "fake" Clarion inability to call a proc by address
  ! Each "real" external prototype has to be a TYPE, then Callprocedure receive the type as first parameter
  MAP
    ExternalProcedure(),BYTE,TYPE

    GetVersion(),ulong,Pascal,Type      !Type used as a parameter data type
    CallAddr(*GetVersion),ulong,PROC,NAME('CallAddr')
    IS_GetVersion(*GetVersion),ulong,PROC,NAME('IS_GetVersion')

    !This works
    GetSystemDirectoryALong(long,ulong),ulong,Pascal,Type           !The name is not important, the parameters are as its just a Type, the address of the api is already known
                                                                    !which is why the name is not important.
    IS_GetSystemDirectoryALong(*GetSystemDirectoryALong, long, ulong),ulong,Proc,NAME('IS_GetSystemDirectoryALong') !This api calls the Long version of the Typed api parameter
                                                                    !The parameters you would normally use in your statically linked api declared in a module('win32') are
                                                                    !added after the *GetSystemDirectoryALong because these are added inside the parenthesis of the Return line.


    !This doesnt work and below goes someway to help you see why. You might also find this helpful for examinine Access Violations using Clarion.
    !
    !So the *cstring version generates an access violation, which on my machine is 7c912b08, a windows DLL. If you run this on a newer OS or a 64bit machine,
    !you will almost certainly see different addresses. Likewise when running this on a Win10 machine with ASLR switched on, you might see some differences in how the debugger operates.
    !
    !To investigate, in the debugger set a breakpoint on the line "RETURN pApiAddress(pCstringSystemFolder,pPathlength)"
    !When you land on this line, expand the Dissembly window. I suspect this is the standalone dissembler that is referred to in the newsgroups and websites, now merged into the debugger.
    !It should be showing a hilighted line of assembler code:
    !push dword [ebp] ["PPATHLENGTH"]
    !Right mouse click and choose Locate Offset and type in the address shown in the Access Violation message. On my machine its "7c912b08".
    !This will highlight a line, depending on the OS it could show "mov [ecx][0H],bl" which is XPx32 or "mov [edx][ebx],al" which is Win10x64.
    !Set a breakpoint on this line. Then click G to go.
    !You should end up on this line and stop. In the stack trace window (bottom right), expand the Thread node and look at the register "EAX", "EBX", etc etc
    !Now this is where it gets interesting. Highlight the loc:ProcPtr in the top right window, right mouse click and choose examine memory.
    !In the 4 bytes before the address, on XP you will see 05 01 00 00 which on my machine is XP SP2, in Win10x64, you will see 06 02 00 00.
    !This tells you that the app is not manifested (which we know) as its a test app, but this is how you can see what version the api is using, which could be useful to know
    !if any manifest is not correct and an api is not working properly for you.
    !The address 8 bytes after the address stored loc:ProcPtr is the address which will be filled by the line you have breakpointed on.
    !In both XP and Win10 its filled with 43. You can see this if you repeat the above steps but first breakpoint on the long version first.
    !The line that throws the Access Violation is in the ntdll.dll file, with the api called RTLUnicodeToMultiByte.

    !Anyway thats where its going wrong. Why its going wrong I dont know, I've not tracked back through the assembler to find out. This might be because of the different
    !ways the Clarion runtime handles passing parameters, or it may be a Windows OS thing.
    !
    !When using the clarion disassembler, you will be stepping into windows dll's like ntdll.dl, so depending on how much you investigate the disassembler, you will also see
    !output going to a console window, like in the Linux world.

    
    GetSystemDirectoryACstring(*cstring,ulong),ulong,Pascal,Type    !The name is not important, the parameters are as its just a Type, the address of the api is already known
    IS_GetSystemDirectoryACstring(*GetSystemDirectoryACstring, *cstring, ulong),ulong,Proc,NAME('IS_GetSystemDirectoryACstring') !This api calls the Long version of the Typed api parameter


  END

CallAddr Function(Ptr)
  CODE
  Message('CallAddr')
  RETURN Ptr()



IS_GetVersion Function(pApiAddress)
  CODE
  Message('IS_GetVersion')
  RETURN pApiAddress()

IS_GetSystemDirectoryALong Function(pApiAddress, pLongSystemFolder,pPathlength)
  CODE
  Message('Long IS_GetSystemDirectoryA')
  RETURN pApiAddress(pLongSystemFolder,pPathlength)

IS_GetSystemDirectoryACstring Function(pApiAddress, pCstringSystemFolder,pPathlength)
  CODE
  Message('Cstring IS_GetSystemDirectoryA')
  RETURN pApiAddress(pCstringSystemFolder,pPathlength)

HTH whoever in the future.

I see you have already sorted this, so, well done!

I use a class contributed into ClarionMag way back by my Clarion hero = Jim Kane, he too used some assembler to make the calls.

His class supports standard Win32 and C calling convention so it’s great for use with third party .dlls. Out of the box it supports up to 10 integer parameters.

Using it requires …

a) adding call.a to project and setting its build action = compile
b) including the class into your application … INCLUDE(calldllcl.inc),ONCE

I tweaked Jim’s original files only very slightly and have attached them for completeness. (I don’t have his original post to hand).

With it, your sample becomes something this like …

myDLL       &callDLLclType
szDirectory CSTRING(256)
dwResult    LONG

   Code
      !-- Create a myDLL instance ..
      myDLL &= NEW(callDLLclType)

      !-- Load the required .dll .. remove the true param when working
      if myDLL.Init('kernel32.dll',true) = 0
      
         !-- I like to set a negative error state for the class (you may decide differently
         myDLL.SetProcFailCode(-2)
            
         !-- Call the required procedure ..
         dwResult = myDLL.call('GetSystemDirectoryA',Address(szDirectory),SIZE(szDirectory))
         
         MESSAGE('dwResult = ' & dwResult & |
            '|szDirectory = ' & szDirectory)
      .
      
      DISPOSE(myDLL)

Hope that helps.

Julian.

calldllcl.zip (6.0 KB)

I read somewhere, that windows will accept upto 32 parameters. I dont know if this was online or in the windows 11 SDK in ISO format I downloaded and installed the other day.
edit. Its in WowNT16.h, search for CallProc32W and says

This routine can pass a variable number of arguments, upto 32, to the target 32bit routine. These arguments are given to CallPRoc32W following the 3 required parameters…

Anyway I’ll have a look at this, thanks. I did find Jim had a COM thing on the icetips website, edit its a util to dump COM tlibs https://www.icetips.com/par2downloads/readtlib.zip and have noticed he’s done some of the more complicated articles in coding.

Anyway this shows a 100% clarion language solution ie not c or asm exists for calling dll’s on the fly, and now I just need to get this ported into a class.

I’m wondering if I can use evaluate() to create a generic dll loader, because evaluate() will do some fancy stuff… :grinning:

One of my tweaks to Jim’s class was to add an 11 parameter procedure call, it was quite easy, just following and extending the existing prototypes.
If you needed more, it would not be difficult to do likewise, for 12, 13, and so on.
I was not aware of the 32 parameter limit.

Well I think this shows there is a template waiting to be written that can let people dynamically call a DLL from inside an app. Its one of those basic templates that imo should have been in the IDE from day one, but I think its absence demonstrates the difference in thinking between the mainly hand coders who wrote the IDE and the users of Clarion. I’ve heard from conversations over time, the development centre was a highly charged testosterone rich environment in which to work in! :laughing:

Hi Richard, in the case of Larry Sand’s LoadLibrary, if you started a new hand coded project to test it, probably you missed the conditional compiler symbols in the project settings: ABCLinkMode=>1;ABCDllMode=>0

It doesn’t need the Type definition nor the Group in the procedure parameter, probably you infered that from the example in that link, but that is another wrapper class not needed to call loadlibrary (DiskInfo.clw vs being the call directly on FreDskSp.clw). In fact the technique is easy to implement as you keep the function prototypes the same as with statically linked, with just another function name to prevent name clashing and don’t using the NAME attribute.

I made an example modifying another previous simple example here:

That example had the function GetDiskFreeSpaceExA statically linked and the one attached here incorporates the extra code for loadlibrary like the one used on Larry’s example, and by the way, what are the chances, was about GetDiskFreeSpaceExA, although on his example he didn’t make the paremeters omittable or with default value. As a side note, the function ULIntToDec in DiskInfo.clw also confirms the conversion needed the high value to be multiplied by 2^32 and not by 2^32-1, but as stated i64toDecimal should be optimal, probably it not existed in 2001. A last note the Kernel LoadLibrary object could be without & / NEW. LlcFreeLibrary and DISPOSE omitted.

Attached modified TestOmitParam.clw (needs LoadLib.inc and LoadLib.clw) from ClarionMag’s link…TestOmitParam.clw (2,3 KB)

Not sure about Larry’s (which I’ve also used), but I used Jim Kane’s class to load a DLL that did not export proc names. It was by ordinal only. (QuickCrypt.DLL - no longer sold or supported, last I saw.)

So I’ve got the example I posted above working in a class now, so no need to have an assembler module to handle the calls.
I wanted to keep it all Clarion code because its what most people are familiar with, but the lack of docs or ambiguity in the docs meant I wasnt sure it could be done in Clarion so this is why I started looking at the asm and c solutions. As it happens it can all be done in Clarion code.

Looking at the assembler in Julians example, his (Jim Kane’s) didnt have any module reference in teh asm unlike Alexey’s which had the module servcall_ as its first line of code and this was giving me a fatal exception once I realised the module(‘filename.a’) was adding a .clw and looking for a .clw file.

I admit I havent tried it without Type, so I’ll give that a go.

I’ll check it out. I’m probably going to write a class to load all my api calls which would have been declared in a map module, into the class instead, because I’ve already got another class which will load icon resources from a file, beit a separate dll, icon file, my own exe or the windows OEM icons, and that class already keeps track of icons and whats loaded or unloaded using a queue in the class for the life of the app. So its sort of a logical progression to do something similar with the loading of dll’s and their api’s.

Thanks! I’ll have a nose. :wink:

FWIW, there is a PROP:ImageInstance which could help for that, there is sample code and advices from Lee White on comp.lang.clarion on the thread “Assign an icon from a string ?” started by Julian on 2022-04-01.

Oh well I’ve written the class now, but I also wanted to pull in icons and other files from other sources, {Prop:ImageInstance} only pulls in from the file its called from.

This is the inc file for my SystemTray Icon class, I’ve got a bit more flexibility than {Prop:ImageInstance}

!ABCIncludeFile

 OMIT('_EndOfInclude_',_SysTrayIcon_)
_SysTrayIcon_ EQUATE(1)

    MAP
    End

    Include('SysTray.inc'),Once

    !Include('AppWideEquates.inc')
    Include('SysTray_Icon_Equates.inc')
    Include('SysTray_Icon_Queues.inc')
    Include('SysTray_Icon_Groups.inc')

SysTrayIcon     Class,Type,Module('SysTray_Icon.clw'),Link('SysTray_Icon.clw',_ABCLinkMode_),DLL(_ABCDllMode_)
Construct                   Procedure
Destruct                    Procedure

WinErrorCode                ulong

!Construct
!Get Systray Icon Size
SysTrayIconWidthCX          long,Private
SysTrayIconHeightCY         long,Protected
GetSysTrayIconSize          Procedure

!Construct
!Initialise the Icon Queue
LoadedIconQ               &LoadedIconQT
IconQCount                  long
InitialiseIconQueue         Procedure   !Initialise NEW Icon Queue

!Destruct
!Unload any icons, clear Q and dispose of Q
DisposeIconQueue            Procedure   !Dispose Icon Queue

!Destruct
!Loop through Q and call UnloadIcon for each entry
UnloadAllIcons              Procedure

!Destruct
!Unload the icon
UnloadIcon                  Procedure(<long pQiconNumber>) !If pQiconNumber position Queue Else use current Q fields to unload icon (free library, destroy icon)

LoadIconFromFile            Procedure(String    pImageFilename,                                 ulong pWidthCX =0, ulong pHeightCY =0)
LoadIconFromFile            Procedure(*cstring  pImageFilename,                                 ulong pWidthCX =0, ulong pHeightCY =0)
LoadIconOrdinalFromEXE      Procedure(ulong     pIconOrdinal,       string pModuleFilename,     ulong pWidthCX =0, ulong pHeightCY =0)
LoadIconOrdinalFromEXE      Procedure(ulong     pOrdinal,           *cstring pModuleFilename,   ulong pWidthCX =0, ulong pHeightCY =0)
LoadIconFromEXE             Procedure(String    pIconName,          string pModuleFilename,     ulong pWidthCX =0, ulong pHeightCY =0)
LoadIconFromEXE             Procedure(*cstring  pIconName,          *cstring pModuleFilename,   ulong pWidthCX =0, ulong pHeightCY =0)
LoadOEMIcon                 Procedure(long      pOEM_OIC_ICON,                                  ulong pWidthCX =0, ulong pHeightCY =0)

AddIconToQueue              Procedure   !Loads image (LoadImageA), gets handle then adds everything to the queue

Uuid                        like(guid)
GetUuid                     Procedure(),string !groups are treated as strings Guid is a Type group
GetGuid                     Procedure(),string !groups are treated as strings

                            End

_EndOfInclude_

So you do need to have the TYPE attribute, otherwise the compiler throws an error.

Below is a version written as a class to show how to dynamically load api’s on the fly from inside a class, so I dont need to have any of the additional c or asm modulesnow, its all pure Clarion code.

Now a template could write this class out and then have it called from an app quite easily.

Oh well, I guess its time to write a class to handle COM objects and do a template for it, but after I’ve finished some other apps first. Maybe in time for Xmas?

LoaderClass.inc

!ABCIncludeFile

 OMIT('_EndOfInclude_',_LoaderClass_)
_LoaderClass_ EQUATE(1)

    MAP
        Module('LoaderClass.clw')
        !Prototypes declared here are coded in the clw and are not part of the class.
        End

    End
    !Include('AnyParentClass.inc'),Once !Any Parent Class

!    Include('LoaderClass_Groups.inc')
!    Include('LoaderClass_Equates.inc')
!    Include('LaoderClass_Queues.inc')

Eq:MaxSize     Equate(1000)

LoaderClass         Class,Type,Module('LoaderClass.clw'),Link('LoaderClass.clw',_ABCLinkMode_),DLL(_ABCDllMode_)
Construct                   Procedure
Destruct                    Procedure
WinErrorCode                ulong

LibraryName                 Cstring(Eq:MaxSize)
LibraryNameHandle           long
ApiName                     Cstring(Eq:MaxSize)
ApiProcAddress              long
GetApiAddress               Procedure

SystemDirectory             Cstring(Eq:MaxSize)
SystemDirectoryAddress      Long
GetSystemDirectoryA         Procedure

                    End

_EndOfInclude_

LoaderClass.clw

    Member

    Include('LoaderClass.inc'),Once

    MAP !Need this to access the builtins.clw
        Module('Win32')
    IS_FreeLibrary(long),bool,Pascal,Raw,Name('FreeLibrary')
    IS_LoadLibraryA(*cstring),long,Pascal,Raw,Name('LoadLibraryA')
    IS_GetError(),Ulong,Raw,Pascal,Name('GetLastError')
    IS_GetProcAddress(long,*cstring),long,Raw,Pascal,Name('GetProcAddress')
        End

        PRAGMA ('project(#compile LoaderClassDynamic.clw)') !If you dont have this line, then you need to add the LoaderClassDynamic.clw to External Files in the project editor
        Module('LoaderClassDynamic')                        !otherwise the compiler will throw an Unresolved External error message. Aka this is how you link a source file in like it was a lib/dll
    IS_GetSystemDirectoryA(long,long,ulong),ulong,Proc,Name('IS_GetSystemDirectoryA')
        End
    End


LoaderClass.Construct           Procedure
    Code
    self.GetApiAddress
    IF self.ApiProcAddress
        self.GetSystemDirectoryA
    End

LoaderClass.Destruct            Procedure
Loc:RVbool  Bool
    Code
    IF self.LibraryNameHandle
        Loc:RVbool = IS_FreeLibrary(self.LibraryNameHandle)
        Assert(Loc:RVbool,'Unable to unload handle to ' & self.LibraryName )
    End

LoaderClass.GetApiAddress       Procedure
    Code
    self.LibraryName            = 'Kernel32.DLL'
    self.LibraryNameHandle      = IS_LoadLibraryA(self.LibraryName)
    IF NOT self.LibraryNameHandle
        self.WinErrorCode = IS_GetError()
        Assert(self.LibraryNameHandle,'Unable to load ' & self.LibraryName & ' Windows ErrorCode:' & self.WinErrorCode)
    Else
        self.ApiName            = 'GetSystemDirectoryA'
        self.ApiProcAddress     = IS_GetProcAddress(self.LibraryNameHandle,self.ApiName)
        IF NOT self.ApiProcAddress
            self.WinErrorCode = IS_GetError()
            Assert(self.ApiProcAddress,'Unable to get Procedure Address to api ' & self.ApiName & ' Windows ErrorCode:' & self.WinErrorCode)
        End
    End

LoaderClass.GetSystemDirectoryA  Procedure
Loc:RVulong Long
    Code
    self.SystemDirectoryAddress = Address(self.SystemDirectory)
    Loc:RVulong = IS_GetSystemDirectoryA(self.ApiProcAddress,self.SystemDirectoryAddress,Eq:MaxSize)
    IF NOT Loc:RVulong
        self.WinErrorCode = IS_GetError()
        Assert(Loc:RVulong,'Unable to fetch System Directory Windows ErrorCode:' & self.WinErrorCode)
    ElsIF Loc:RVulong = Len(self.SystemDirectory) !Should match
        Message('System Directory: ' & self.SystemDirectory)
    Else
        Assert(False,'This should not happen!')
    End

LoaderClassDynamic.clw

    Member
    Map
    GetSystemDirectoryA(long,ulong),ulong,Pascal,Type
    IS_GetSystemDirectoryA(*GetSystemDirectoryA, long, ulong),ulong,Proc,Name('IS_GetSystemDirectoryA')
    End

IS_GetSystemDirectoryA Function(pApiAddress, pLongAddressToClassSystemFolder,pSizeOfClassSystemFolder)
  CODE
  RETURN pApiAddress(pLongAddressToClassSystemFolder,pSizeOfClassSystemFolder)

Anyway an example of how to call an api on the fly from inside a class.

hth whoever in the future. :smiley:

Taking one of your comments onboard, I added this line to Jim’s calldllcl.inc …

PRAGMA ('project(#compile calla.a)')

Now I can use the class in a more conventional manner, ie: INCLUDE(calldllcl.inc),ONCE without adding the assembler file (calla.a) to the project … that’s one less thing to remember.

Yeah the problem with using the include(somefile.ext),once is some files can be treated differently by the compiler, but the pragma(‘project(#compile somefile.ext)’) gives a different way to include the source code into a project.

Because the compiler treats this procedure call as an unresolved external, the compiler is telling me it needs to be treated like a standalone dll, like a windows dll or some other dll.

It also shows you dont need to have version specific clarion dlls, which are typically used in 3rd party clarion addons.

Half the problem or puzzle is working out what the compiler is doing to the source code.

I found a situation a few days ago with group structures and classes iirc that the compiler allows when it should be throwing an error according to the docs, thats why I tend to work more off what the compiler allows and the debugger does a good job of showing what code will get called simply because the uncalled code remains black, so its easy to tell if it will work on a particular OS or not, just by loading the debugger, highlighting the code in the source window and seeing if its black or not.

You can use the slightly simpler syntax:

PRAGMA ('compile(calla.a)')

1 Like

I only used the first example because that was what was in the docs, but a quick search shows your example can be found in cwutil.inc.

I’ll update the code here.

I believe that reference variables of procedural types are more simple and reliable way to call functions by their addresses returned from GetProcAddress.

Well funnily enough, I’ve just done a template dll to generate a guid string using StringFromGUID2 function (combaseapi.h) - Win32 apps | Microsoft Learn

But whats taken the time is I’ve created a reference variable to the guid, the actual generation of the guid works perfectly, but I could not get this StringFromGuid2 api to work.

Tried a few variations in the api definition, still didnt work, so tried the StringFromCLSID function (combaseapi.h) - Win32 apps | Microsoft Learn because it says it works with guids as well. Still didnt work, the svcomdef.inc didnt show anything other than it was a Long data type, so as an off chance, I tried address(loc:myguid) and it works. So here’s me thinking its a reference variable and its, just a pointer or memory address to the actual guid. And thats where the time goes, trying to figure what MS are trying to convey.

As long as GetProcAddress is getting the api name and its case sensitive, it does work well, plus I can unload it when I like.

AppGen and some other IDE parts use UuidToString function to convert binary GUID (UUID is a synonym for GUID in RPC API) to a string form.