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.