cCDECL.cls
上传用户:jnjasmy
上传日期:2015-01-04
资源大小:637k
文件大小:9k
源码类别:

GDI/图象编程

开发平台:

Visual Basic

  1. VERSION 1.0 CLASS
  2. BEGIN
  3.   MultiUse = -1  'True
  4.   Persistable = 0  'NotPersistable
  5.   DataBindingBehavior = 0  'vbNone
  6.   DataSourceBehavior  = 0  'vbNone
  7.   MTSTransactionMode  = 0  'NotAnMTSObject
  8. END
  9. Attribute VB_Name = "cCDECL"
  10. Attribute VB_GlobalNameSpace = False
  11. Attribute VB_Creatable = True
  12. Attribute VB_PredeclaredId = False
  13. Attribute VB_Exposed = False
  14. ' Following is from Paul Caton, no modifications made by myself except value of MAX_ARG
  15. '--------------------------------------------------------------------------
  16. '
  17. ' cCDECL - Class that enables the user to call cdecl dynamic link libraries.
  18. '          Supports cdecl style variable argument lists and bas module
  19. '          callbacks.
  20. '
  21. '031029 First cut.................................................... v1.00
  22. '
  23. Option Explicit
  24. Option Base 0
  25. 'API declarations
  26. Private Declare Sub RtlMoveMemory Lib "kernel32" (Destination As Any, Source As Any, ByVal Length As Long)
  27. Private Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
  28. Private Declare Function GetLastError Lib "kernel32" () As Long
  29. Private Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
  30. Private Declare Function LoadLibraryA Lib "kernel32" (ByVal lpLibFileName As String) As Long
  31. Private Declare Sub SetLastError Lib "kernel32" (ByVal dwErrCode As Long)
  32. 'Private constants
  33. Private Const ERR_SRC       As String = "cCDECL"    'Error source name
  34. Private Const ERR_NUM       As Long = vbObjectError 'cCDECL error number base
  35. Private Const MAX_ARG       As Long = 5&            'Maximum number of parameters, you can change this if required
  36. Private Const PATCH_01      As Long = 15&           'CDECL patch, CDECL function address
  37. Private Const PATCH_02      As Long = 10&           'Callback patch, bas mod function address patch
  38. Private Const PATCH_03      As Long = 16&           'Callback patch, stack adjustment patch
  39. Private Const CODE_CDECL    As String = "538B5C240C8B0BE305FF348BE2FBE8<fix 01>8B0BC1E10201CC5B8B54240C890231C0C20C00"
  40. Private Const CODE_WRAPPER  As String = "E8000000005A8F4219E8<fix 02>81EC<fix 03>E8000000005AFF7205C300000000"
  41. 'Parameter block
  42. Private Type tParamBlock
  43.   ParamCount                As Long                 'Number of parameters to be forwarded to the cdecl function
  44.   Params(0 To MAX_ARG - 1)  As Long                 'Array of parameters to be forwarded to the cdecl function
  45. End Type
  46. 'Private member
  47. Private m_LastError         As Long                 'Last error private member
  48. 'Private variables
  49. Private bNewDLL             As Boolean              'Flag to indicate that the loaded DLL has changed
  50. Private pMe                 As Long                 'vtable address
  51. Private hMod                As Long                 'DLL module handle
  52. Private nAddr               As Long                 'Cache the previous cdecl function's address
  53. Private nEntry              As Long                 'vtable entry index
  54. Private pCode               As Long                 'Pointer to the CDECL code
  55. Private sLastFunc           As String               'Cache the previous cdecl function's name
  56. Private sCode()             As String               'Code buffer string array...
  57. Private pb                  As tParamBlock          'Parameter block instance
  58. 'Replace the stub proc (z_DO_NOT_CALL) with machine-code to handle the cdecl function
  59. 'stored in sCode(0). Load the wrapper code into sCode(1)
  60. Private Sub Class_Initialize()
  61.   'ObjPtr returns the address of me, at that address is the address of the vtable, copy it to pMe.
  62.   Call RtlMoveMemory(pMe, ByVal ObjPtr(Me), 4)
  63.   Call Redirect(CODE_CDECL, True)                   'CDECL code
  64.   pCode = StrPtr(sCode(0&))                         'Remember the address of the CDECL code
  65.   
  66.   Call Redirect(CODE_WRAPPER, False)                'Callback wrapper code, vtable not patched... we don't call it
  67. End Sub
  68. 'Convert the passed string of hex character pairs to bytes stored in an ASCII
  69. 'string buffer. If indicated, patch the appropriate vtable entry to point to the byte codes
  70. Private Sub Redirect(ByVal sHexCode As String, ByVal bPatch As Boolean)
  71.   Dim i     As Long
  72.   Dim nLen  As Long
  73.   Dim s     As String
  74.   
  75.   nLen = Len(sHexCode)
  76.   
  77.   For i = 1 To nLen Step 2
  78.     s = s & ChrB$(Val("&H" & Mid$(sHexCode, i, 2)))
  79.   Next i
  80.   
  81.   ReDim Preserve sCode(0 To nEntry)
  82.   sCode(nEntry) = s
  83.   
  84.   If bPatch Then
  85.     'Patch the vtable entry to point to the code
  86.     Call RtlMoveMemory(ByVal pMe + &H1C + (nEntry * 4), StrPtr(sCode(nEntry)), 4)
  87.   End If
  88.   
  89.   nEntry = nEntry + 1                               'In case another patch is added to the class
  90. End Sub
  91. Public Function z_DO_NOT_CALL(ByVal nAddrParamBlock As Long) As Long
  92. 'This sub is replaced by machine code in sCode(0) at class instance creation...
  93. 'IT MUST ONLY be called internally by CallFunc. It can't be made Private as it wouldn't
  94. 'then use the vtable. Being the first public method in this class, we know that the vtable
  95. 'pointer to this function will be located at [vtable + &H1C]
  96. End Function
  97. 'Purpose:
  98. ' Call the named cdecl function with the passed parameters
  99. '
  100. 'Arguments:
  101. ' sFunction - Name of the cdecl function to call
  102. ' ParmLongs - ParamArray of parameters to pass to the named cdecl function
  103. '
  104. 'Return:
  105. '  The return value of the named cdecl function
  106. '
  107. Public Function CallFunc(ByVal sFunction As String, ParamArray ParmLongs() As Variant) As Long
  108.   Dim i As Long
  109.   Dim j As Long
  110.   
  111.   'Check that the DLL is loaded
  112.   If hMod = 0& Then
  113.     
  114.     'If in the IDE just stop, programmer may have On Error Resume Next protection and miss the problem.
  115.     Debug.Assert False
  116.     Call Err.Raise(ERR_NUM + 0, ERR_SRC, "DLL not loaded")
  117.   End If
  118.   'Check to see if we're calling the same cdecl function as the previous call to CallFunc
  119.   If (StrComp(sLastFunc, sFunction) <> 0) Or bNewDLL Then
  120.     
  121.     'Get the address of the function
  122.     nAddr = GetProcAddress(hMod, sFunction)
  123.     If nAddr = 0 Then
  124.       
  125.       'If in the IDE just stop, programmer may have On Error Resume Next protection and miss the problem.
  126.       Debug.Assert False
  127.       Call Err.Raise(ERR_NUM + 1, ERR_SRC, "Failed to locate function: " & sFunction)
  128.     End If
  129.     'Patch the code buffer to call the relative address to the cdecl function
  130.     Call RtlMoveMemory(ByVal pCode + PATCH_01, nAddr - pCode - (PATCH_01 + 4), 4)
  131.     bNewDLL = False
  132.     sLastFunc = sFunction
  133.   End If
  134.   
  135.   With pb
  136.     j = UBound(ParmLongs)
  137.     If j >= MAX_ARG Then
  138.       
  139.       'If in the IDE just stop, programmer may have On Error Resume Next protection and miss the problem.
  140.       Debug.Assert False
  141.       Call Err.Raise(ERR_NUM + 2, ERR_SRC, "Too many parameters")
  142.     End If
  143.     
  144.     'Fill the parameter block
  145.     For i = 0 To j
  146.       .Params(i) = ParmLongs(i)
  147.     Next i
  148.     
  149.     .ParamCount = i                                         '(j + 1)
  150.   End With
  151.   
  152.   Call SetLastError(0)                                      'Clear the error code
  153.   CallFunc = z_DO_NOT_CALL(VarPtr(pb))                      'Execute the code buffer passing the address of the parameter block
  154.   m_LastError = GetLastError()                              'Get error code
  155. End Function
  156. 'Load the DLL
  157. Public Function DllLoad(ByVal sName As String) As Boolean
  158.   hMod = LoadLibraryA(sName)
  159.   If hMod <> 0 Then
  160.     DllLoad = True
  161.     'It's remotely possible that the programmer could change the dll and then call a function
  162.     'in the new dll with exactly the same name as the previous CallFunc to the previous DLL. This would
  163.     'defeat the caching scheme and result in the old function in the old dll being called. An unlikely
  164.     'scenario, but stranger things have happened. Soooo, explicitly indicate that we're using a new dll
  165.     bNewDLL = True
  166.   End If
  167.   
  168.   'If in the IDE just stop on failure, programmer may not be checking the return value.
  169.   Debug.Assert DllLoad
  170. End Function
  171. 'It's not important to do this, but, if you've finished with a DLL there's no harm in releasing
  172. 'its memory. Don't bother at app end... it will be dealt with automatically when the process ends.
  173. Public Function DllUnload() As Boolean
  174.   If hMod <> 0 Then
  175.     DllUnload = (FreeLibrary(hMod) <> 0)
  176.     hMod = 0
  177.   End If
  178.   
  179.   'If in the IDE, get the programmer's attention
  180.   Debug.Assert DllUnload
  181. End Function
  182. 'Return the cdecl function's error code
  183. Public Property Get LastError() As Long
  184.   LastError = m_LastError
  185. End Property
  186. 'Purpose:
  187. ' Setup a wrapper so that a bas module function can act as a cdecl callback
  188. '
  189. 'Arguments:
  190. ' nModFuncAddr - The address of the bas module function to act as a cdecl callback (use AddressOf)
  191. ' nParms       - The number of parameters that will be passed to the bas module function
  192. '
  193. 'Return:
  194. '  The address to pass to the cdecl function as the callback address
  195. '
  196. Public Function WrapCallback(ByVal nModFuncAddr As Long, ByVal nParms As Long) As Long
  197.   Dim nStackAdjust As Long                                  'The number of bytes to adjust the stack
  198.   
  199.   WrapCallback = StrPtr(sCode(1))                           'Address of the callback wrapper
  200.   nStackAdjust = nParms * 4                                 'Four bytes per parameter
  201.   'Patch the code buffer to call the vb bas module callback function
  202.   Call RtlMoveMemory(ByVal WrapCallback + PATCH_02, nModFuncAddr - WrapCallback - (PATCH_02 + 4), 4)
  203.   
  204.   'Patch the code buffer to apply the necessary stack adjustment
  205.   Call RtlMoveMemory(ByVal WrapCallback + PATCH_03, nStackAdjust, 4)
  206. End Function