How to use a function pointer in VBA.
VB6 and VBA come with no support for function pointers.
In
the previous chapter we tried to execute a pointer to a function with no return value or argument.
In this chapter we also try using pointers of functions with arguments and return values.
Example.2
The source code on this page needs to be pasted into the standard module. If you paste it into Sheet1 or ThisWorkBook, a compile error will occur.
Option Explicit
'You can use a sample code for this site freely.
'Though this is not a duty, I am grateful that you describe that you reffered this site
'(https://akihitoyamashiro.blogspot.com/2020/07/how-to-use-function-pointer-in-vba-2.html),
'when you present this sample code in your web site.
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByVal pvargResult As LongPtr) As Long
Enum tagCALLCONV
CC_FASTCALL = 0
CC_CDECL = 1
CC_MSCPASCAL = CC_CDECL + 1
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL = CC_PASCAL + 1
CC_STDCALL = CC_MACPASCAL + 1
CC_FPFASTCALL = CC_STDCALL + 1
CC_SYSCALL = CC_FPFASTCALL + 1
CC_MPWCDECL = CC_SYSCALL + 1
CC_MPWPASCAL = CC_MPWCDECL + 1
CC_MAX = CC_MPWPASCAL
End Enum
Public Function SixParamOneReturn( _
ByVal longVal As Long, ByRef longRef As Long, _
ByVal byteVal As Byte, ByRef byteRef As Byte, _
ByVal strVal As String, ByRef strRef As String _
) As Long
'ByVal...String in VB is equivalent to BSTR in VC++.
'ByRef...String in VB is equivalent to BSTR* in VC++.
Debug.Print "SixParamOneReturn"
Debug.Print "longVal = " & longVal
Debug.Print "longRef = " & longRef
Debug.Print "byteVal = " & byteVal
Debug.Print "byteRef = " & byteRef
Debug.Print "strVal = " & strVal
Debug.Print "strRef = " & strRef
Debug.Print
SixParamOneReturn = longVal + longRef
longVal = 100
longRef = 200
byteVal = 10
byteRef = 20
strVal = "strVal"
strRef = "strRef"
MsgBox "EightParamOneReturn is called."
End Function
Public Sub Test2()
'The annotations that were explained in Test1 have been omitted.
Dim lDispCallFuncResult As Long
Dim vFuncResult As Variant
vFuncResult = Empty
Dim lVal As Long, vlVal As Variant
Dim lRef As Long, vlRef As Variant
Dim bVal As Byte, vbVal As Variant
Dim bRef As Byte, vbRef As Variant
Dim sVal As String, vsVal As Variant
Dim sRef As String, vsRef As Variant
lVal = 12
lRef = 34
bVal = 56
bRef = 78
sVal = "string1"
sRef = "string2"
'DispCallFunc method
'The variables passed to the called function all have to
'be passed in Variant form so all the variables are
'declared as Variant variables.
'It is easier to create a Variant array and assign the
'array but to show there is no need for it to be an
'array, we will not create an array.
'Calling by reference, as instructed by VB's ByRef,
'involves not declaring variables, but we have to assign
'pointers to variables.
vlVal = lVal 'Because it is the ByVal variable, set a value.
vlRef = VarPtr(lRef) 'It is the ByVal variable so assign
'pointers. In C it corresponds to &IRef.
vbVal = bVal
vbRef = VarPtr(bRef)
vsVal = sVal
vsRef = VarPtr(sRef)
'After declaring the Variant variables
'along with set each Variant variable's type as an integer array.
'we set each Variant variable's address as a long array.
Dim iVarTypes(0 To 5) As Integer 'hard to tell, but
'first letter is a small letter i.
Dim lVarPtrs(0 To 5) As LongPtr
iVarTypes(0) = VarType(vlVal) 'VbVarType.vbLong is assigned
lVarPtrs(0) = VarPtr(vlVal) '"VarPtr(vlVal)" corresponds to
'"&vlVal" in C.
iVarTypes(1) = VarType(vlRef) 'The pointer is in Long form
'so VbVarType.vbLong is assigned.
lVarPtrs(1) = VarPtr(vlRef)
iVarTypes(2) = VarType(vbVal) 'VbVarType.vbByte is assigned.
lVarPtrs(2) = VarPtr(vbVal)
iVarTypes(3) = VarType(vbRef) 'The pointer is in Long form
'so VbVarType.vbLong is assigned.
lVarPtrs(3) = VarPtr(vbRef)
iVarTypes(4) = VarType(vsVal) 'VbVarType.vbString is assigned.
lVarPtrs(4) = VarPtr(vsVal)
iVarTypes(5) = VarType(vsRef) 'The pointer is in Long form
'so VbVarType.vbLong is assigned.
lVarPtrs(5) = VarPtr(vsRef)
'The returned value's type is long so we set the 4th argument
'(the returned value's type) as vbLong(=3).
'There are 6 variables, we set the 5th argument to 6.
'For the 6th argument we set the first address in the
'integer array that stores each variant variable's inner type.
'For the 7th argument we set the first address in the
'long array that stores each variant variable's address.
lDispCallFuncResult = DispCallFunc( _
0, _
AddressOf SixParamOneReturn, _
tagCALLCONV.CC_STDCALL, _
VbVarType.vbLong, _
6, _
VarPtr(iVarTypes(0)), _
VarPtr(lVarPtrs(0)), _
VarPtr(vFuncResult) _
)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'In this section, if the declaration of the DispCallFunc
'is as follows, then the 6th, 7th and 8th arguments VarPtr are
'unnecessary.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByRef prgvt As Integer, _
ByRef prgpvarg As LongPtr, _
ByRef pvargResult As Variant) As Long
'Regarding the 6th, 7th and 8th arguments, we change
'ByVal pointer's type to ByRef variable's type
'6th argument ByVal ... Long to ByRef ... Integer
'7th argument ByVal ... LongPtr to ByRef ... LongPtr
'8th argument ByVal ... Long to ByRef ... Variant
'Because of this getting the address value is done
'automatically by VBA, and we can do the DispCallFunc
'part in the following way
'lDispCallFuncResult = DispCallFunc( _
0, _
AddressOf SixParamOneReturn, _
tagCALLCONV.CC_STDCALL, _
VbVarType.vbLong, _
6, _
iVarTypes(0), _
lVarPtrs(0), _
vFuncResult _
)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Debug.Print "Test2"
Debug.Print "lDispCallFuncResult = " & lDispCallFuncResult
Debug.Print "vFuncResult = " & vFuncResult
Debug.Print "lVal = " & lVal 'lVal value is left as is.
Debug.Print "lRef = " & lRef 'lRef value is passed to a pointer
'so the original value is affected
'by the changes where it is called.
Debug.Print "bVal = " & bVal
Debug.Print "bRef = " & bRef
Debug.Print "sVal = " & sVal
Debug.Print "sRef = " & sRef
End Sub
Until now, I assumed you understand DispCallFunc's call procedure. Actually, there are not many cases where functions within VBA is called with DispCallFunc.
Also, because standard WIN32API are created with the STDCALL calling convention, you can call them if you do a Declare Function. In such a case, there are few cases where calls are made with DispCallFunc.
Thus I will try to call functions created with the CDECL calling convention in the next section.
Friday, July 17, 2020
How to use a function pointer in VBA #1 (function without arguments or return value)
VB6 and VBA come with no support for function pointers.
Also, when you wish to execute a function in a dll using the Declare function, you can only call functions created by the stdcall calling convention.
These constraints can be avoided by using the DispCallFunc API.
The DispCallFunc is widely used in VB6 when erasing the history of IE.
Although the DispCallFunc is known as API for calling the IUnknown interface, in fact, you can also perform other functions other than COM by passing the NULL to the first argument.
As explained in the http://msdn.microsoft.com/en-us/library/ms221473(v=vs.85).aspx , the DispCallFunc argument is as follows.
Use of the argument sixth and seventh is very peculiar, and there are few descriptions about it available. Therefore, I'll try to summarize it this time around.
First argument, pvInstance
In this time, I am going to call functions other than COM. Thus, always setting at 0.
Second argument, oVft
Passing the address of a function.
Third argument, cc
Passing the function's calling convention.
For CALLCONV type, the following values are defined for the OAIdl.h.
For a general WIN32API function, CC_STDCALL (= 4) is used.
For a function that is specifically designed for language C, CC_CDECL (= 1) is used.
Fourth argument, vtReturn
Setting the value of VbVarType enumerated type that indicates the type of the function's return value.
In the event where the function does not return a value (the Sub procedure referred to in VB), setting the vbEmpty.
VARTYPE type as defined in WTypes.h is shown below.
By ignoring 'unsigned', it is equivalent to the Integer type in VB.
Fifth argument, cActuals
Setting the number of function arguments.
Sixth argument, prgvt
Storing in an array of Integer type VbVarType enumerated values that indicate the type of the function's arguments, and then passing the address of the beginning of the array.
However, when the number of the arguments is 0, passing 0.
Seventh argument, prgpvarg
Once again, storing each function's argument in Variant type, and storing the address of each of the variant variable in the Long type array that is prepared separately, then passing the address of the beginning of the array.
However, when the number of arguments is 0, then passing 0.
Then, the return value of the DispCallFunc API is, if a call to the function is done successfully in the Long type, S_OK (= 0) is returned.
As an example, the API declaration and the declaration of the enumeration in VB are as follows.
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByVal pvargResult As LongPtr) As Long
Enum tagCALLCONV
CC_FASTCALL = 0
CC_CDECL = 1
CC_MSCPASCAL = CC_CDECL + 1
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL = CC_PASCAL + 1
CC_STDCALL = CC_MACPASCAL + 1
CC_FPFASTCALL = CC_STDCALL + 1
CC_SYSCALL = CC_FPFASTCALL + 1
CC_MPWCDECL = CC_SYSCALL + 1
CC_MPWPASCAL = CC_MPWCDECL + 1
CC_MAX = CC_MPWPASCAL
End Enum
First, I tried the simplest way with a pointer to a function with no defined arguments in a standard module of VBA.
In VBA, as long as functions that are defined in a standard module, you can obtain those addresses using AddressOf operator.
The source code on this page needs to be pasted into the standard module. If you paste it into Sheet1 or ThisWorkBook, a compile error will occur.
Example.1
'You can use a sample code for this site freely.
'Though this is not a duty, I am grateful that you describe that you reffered this site
'(https://akihitoyamashiro.blogspot.com/2020/07/how-to-use-function-pointer-in-vba-1.html),
'when you present this sample code in your web site.
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByVal pvargResult As LongPtr) As Long
Enum tagCALLCONV
CC_FASTCALL = 0
CC_CDECL = 1
CC_MSCPASCAL = CC_CDECL + 1
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL = CC_PASCAL + 1
CC_STDCALL = CC_MACPASCAL + 1
CC_FPFASTCALL = CC_STDCALL + 1
CC_SYSCALL = CC_FPFASTCALL + 1
CC_MPWCDECL = CC_SYSCALL + 1
CC_MPWPASCAL = CC_MPWCDECL + 1
CC_MAX = CC_MPWPASCAL
End Enum
Public Sub NoParamNoReturn()
MsgBox "NoParamNoReturn is called."
End Sub
Public Sub Test1()
Dim lDispCallFuncResult As Long
'Declare a Variant variable that will store the return value of the function called.
'This variable is needed even if the function does not return a value.
Dim vFuncResult As Variant
'In this case, there is no need to initialize the variable after declaring it.
'However, when using a Variant variable, it can be initialized like this
'by setting it to Empty.
vFuncResult = Empty
'You can get the address of a function in VBA by using the AddressOf operator.
'Enter the name of the enumerated type; at the point when you type in the period
'you will be able to use VBA's input support functionality.
'This time we are calling a sub procedure so there is no value to be returned.
'Hence we set the data type of the return variable to be vbEmpty(=0).
lDispCallFuncResult = DispCallFunc(0, AddressOf NoParamNoReturn, _
tagCALLCONV.CC_STDCALL, VbVarType.vbEmpty, _
0, 0, 0, VarPtr(vFuncResult))
'When the call to the DispCallFunc API is successful, it returns 0.
'However, this only means that the call to the DispCallFunc API was successful,
'and it does not indicate whether the target function call was successful or not.
Debug.Print lDispCallFuncResult
End Sub
In the next section we also try using pointers of functions defined in VBA's standard module.
However, in the next function there are arguments and return value.
Also, when you wish to execute a function in a dll using the Declare function, you can only call functions created by the stdcall calling convention.
These constraints can be avoided by using the DispCallFunc API.
The DispCallFunc is widely used in VB6 when erasing the history of IE.
Although the DispCallFunc is known as API for calling the IUnknown interface, in fact, you can also perform other functions other than COM by passing the NULL to the first argument.
As explained in the http://msdn.microsoft.com/en-us/library/ms221473(v=vs.85).aspx , the DispCallFunc argument is as follows.
HRESULT DispCallFunc( void *pvInstance, ULONG_PTR oVft, CALLCONV cc, VARTYPE vtReturn, UINT cActuals, VARTYPE *prgvt, VARIANTARG **prgpvarg, VARIANT *pvargResult );
Use of the argument sixth and seventh is very peculiar, and there are few descriptions about it available. Therefore, I'll try to summarize it this time around.
First argument, pvInstance
In this time, I am going to call functions other than COM. Thus, always setting at 0.
Second argument, oVft
Passing the address of a function.
Third argument, cc
Passing the function's calling convention.
For CALLCONV type, the following values are defined for the OAIdl.h.
enum tagCALLCONV
{ CC_FASTCALL = 0,
CC_CDECL = 1,
CC_MSCPASCAL = CC_CDECL + 1,
CC_PASCAL = CC_MSCPASCAL,
CC_MACPASCAL = CC_PASCAL + 1,
CC_STDCALL = CC_MACPASCAL + 1,
CC_FPFASTCALL = CC_STDCALL + 1,
CC_SYSCALL = CC_FPFASTCALL + 1,
CC_MPWCDECL = CC_SYSCALL + 1,
CC_MPWPASCAL = CC_MPWCDECL + 1,
CC_MAX = CC_MPWPASCAL + 1
} CALLCONV;
For a general WIN32API function, CC_STDCALL (= 4) is used.
For a function that is specifically designed for language C, CC_CDECL (= 1) is used.
Fourth argument, vtReturn
Setting the value of VbVarType enumerated type that indicates the type of the function's return value.
In the event where the function does not return a value (the Sub procedure referred to in VB), setting the vbEmpty.
VARTYPE type as defined in WTypes.h is shown below.
typedef unsigned short VARTYPE;
By ignoring 'unsigned', it is equivalent to the Integer type in VB.
Fifth argument, cActuals
Setting the number of function arguments.
Sixth argument, prgvt
Storing in an array of Integer type VbVarType enumerated values that indicate the type of the function's arguments, and then passing the address of the beginning of the array.
However, when the number of the arguments is 0, passing 0.
Seventh argument, prgpvarg
Once again, storing each function's argument in Variant type, and storing the address of each of the variant variable in the Long type array that is prepared separately, then passing the address of the beginning of the array.
However, when the number of arguments is 0, then passing 0.
Then, the return value of the DispCallFunc API is, if a call to the function is done successfully in the Long type, S_OK (= 0) is returned.
As an example, the API declaration and the declaration of the enumeration in VB are as follows.
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByVal pvargResult As LongPtr) As Long
Enum tagCALLCONV
CC_FASTCALL = 0
CC_CDECL = 1
CC_MSCPASCAL = CC_CDECL + 1
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL = CC_PASCAL + 1
CC_STDCALL = CC_MACPASCAL + 1
CC_FPFASTCALL = CC_STDCALL + 1
CC_SYSCALL = CC_FPFASTCALL + 1
CC_MPWCDECL = CC_SYSCALL + 1
CC_MPWPASCAL = CC_MPWCDECL + 1
CC_MAX = CC_MPWPASCAL
End Enum
First, I tried the simplest way with a pointer to a function with no defined arguments in a standard module of VBA.
In VBA, as long as functions that are defined in a standard module, you can obtain those addresses using AddressOf operator.
The source code on this page needs to be pasted into the standard module. If you paste it into Sheet1 or ThisWorkBook, a compile error will occur.
Example.1
'You can use a sample code for this site freely.
'Though this is not a duty, I am grateful that you describe that you reffered this site
'(https://akihitoyamashiro.blogspot.com/2020/07/how-to-use-function-pointer-in-vba-1.html),
'when you present this sample code in your web site.
Private Declare PtrSafe Function DispCallFunc Lib "OleAut32.dll" _
(ByVal pvInstance As LongPtr, _
ByVal oVft As LongPtr, _
ByVal cc As Long, _
ByVal vtReturn As Integer, _
ByVal cActuals As Long, _
ByVal prgvt As LongPtr, _
ByVal prgpvarg As LongPtr, _
ByVal pvargResult As LongPtr) As Long
Enum tagCALLCONV
CC_FASTCALL = 0
CC_CDECL = 1
CC_MSCPASCAL = CC_CDECL + 1
CC_PASCAL = CC_MSCPASCAL
CC_MACPASCAL = CC_PASCAL + 1
CC_STDCALL = CC_MACPASCAL + 1
CC_FPFASTCALL = CC_STDCALL + 1
CC_SYSCALL = CC_FPFASTCALL + 1
CC_MPWCDECL = CC_SYSCALL + 1
CC_MPWPASCAL = CC_MPWCDECL + 1
CC_MAX = CC_MPWPASCAL
End Enum
Public Sub NoParamNoReturn()
MsgBox "NoParamNoReturn is called."
End Sub
Public Sub Test1()
Dim lDispCallFuncResult As Long
'Declare a Variant variable that will store the return value of the function called.
'This variable is needed even if the function does not return a value.
Dim vFuncResult As Variant
'In this case, there is no need to initialize the variable after declaring it.
'However, when using a Variant variable, it can be initialized like this
'by setting it to Empty.
vFuncResult = Empty
'You can get the address of a function in VBA by using the AddressOf operator.
'Enter the name of the enumerated type; at the point when you type in the period
'you will be able to use VBA's input support functionality.
'This time we are calling a sub procedure so there is no value to be returned.
'Hence we set the data type of the return variable to be vbEmpty(=0).
lDispCallFuncResult = DispCallFunc(0, AddressOf NoParamNoReturn, _
tagCALLCONV.CC_STDCALL, VbVarType.vbEmpty, _
0, 0, 0, VarPtr(vFuncResult))
'When the call to the DispCallFunc API is successful, it returns 0.
'However, this only means that the call to the DispCallFunc API was successful,
'and it does not indicate whether the target function call was successful or not.
Debug.Print lDispCallFuncResult
End Sub
In the next section we also try using pointers of functions defined in VBA's standard module.
However, in the next function there are arguments and return value.
Monday, May 28, 2018
Get an error messsage from Err.LastDllError using FormatMessage in VBA
This is how you can retrieve a message string from a Windows api error code.
Private Declare PtrSafe Function FormatMessage Lib "Kernel32.dll" Alias "FormatMessageW" ( _
ByVal dwFlags As Long, _
ByVal lpSource As LongPtr, _
ByVal dwMessageId As Long, _
ByVal dwLanguageId As Long, _
ByVal lpBuffer As LongPtr, _
ByVal nSize As Long, _
ByVal Arguments As LongPtr) As Long
Private Declare PtrSafe Sub RtlMoveMemory Lib "kernel32" ( _
ByVal Destination As LongPtr, ByVal Source As LongPtr, ByVal Length As Long)
Private Declare PtrSafe Function SysAllocString Lib "OleAut32.dll" (ByVal psz As LongPtr) As LongPtr
Private Declare PtrSafe Function LocalFree Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Function GetApiErrorText(ByVal Code As Long) As String
Const FORMAT_MESSAGE_ALLOCATE_BUFFER As Long = &H100&
Const FORMAT_MESSAGE_FROM_SYSTEM As Long = &H1000&
Const FORMAT_MESSAGE_IGNORE_INSERTS As Long = &H200&
'calculated value of MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)
Const MAKELANGID_LANG_NEUTRAL_SUBLANG_DEFAULT As Long = &H400
Dim lpMsgBuf As LongPtr
FormatMessage _
FORMAT_MESSAGE_ALLOCATE_BUFFER Or _
FORMAT_MESSAGE_FROM_SYSTEM Or _
FORMAT_MESSAGE_IGNORE_INSERTS, _
0, _
Code, _
MAKELANGID_LANG_NEUTRAL_SUBLANG_DEFAULT, _
VarPtr(lpMsgBuf), _
0, _
0
Dim lpNewString As LongPtr
GetApiErrorText = vbNullString
lpNewString = SysAllocString(lpMsgBuf)
LocalFree lpMsgBuf
RtlMoveMemory VarPtr(GetApiErrorText), VarPtr(lpNewString), LenB(lpNewString)
End Function
Sub Sample()
'I put numeric number 1 , instead of Err.LastDllError
Debug.Print GetApiErrorText(1)
End Sub
Saturday, May 26, 2018
MAKELANGID in VBA
I wonder how I can translate 'MAKELANGID' declared in winnt.h into VBA.
MAKELANGID is declared as follows in winnt.h.
#define MAKELANGID(p, s) ((((WORD )(s)) << 10) | (WORD )(p))
Here's the answer.
MAKELANGID is declared as follows in winnt.h.
#define MAKELANGID(p, s) ((((WORD )(s)) << 10) | (WORD )(p))
Here's the answer.
-----------------------------------------
Function MAKELANGID(p As Integer, s As Integer) As Integer
Dim lngTemp As Long
lngTemp = (ShortToLong(s) * 1024) And &HFFFF&
MAKELANGID = (lngTemp + ShortToLong(p)) And &HFFFF&
End Function
Function ShortToLong(ByVal Short As Integer) As Long
ShortToLong = Short And &HFFFF&
End Function
Tuesday, September 18, 2012
How to check whether Cancel has been pressed in the InputBox.
This is a method to identify that the cancel button has been pressed in the InputBox. InputBox returns vbNullString if the cancel button is clicked. If the string is vbNullString, StrPtr will be 0.
Sub main()
Dim sTemp As String
sTemp = InputBox("What's the value?")
If StrPtr(sTemp) = 0 Then
MsgBox "Cancel was clicked"
End If
End Sub
How to retrieve the Window handle of the UserForm in VBA.
Here is how to get the window handle of UserForm with VBA. Unlike VB6, VBA forms do not have the hWnd property. Therefore, in general, you will use the FindWindow API to reverse lookup from the form's Caption. In general, you can get your own hWnd in a userform like this:
Private Declare PtrSafe Function FindWindow Lib "User32.dll" Alias "FindWindowA" _
(ByVal ClassName As String, ByVal WindowName As String) As LongPtr
Public Property Get hWnd() As LongPtr
hWnd = FindWindow("ThunderDFrame", Me.Caption)
End Property
However, strictly speaking, in the case where a UserForm with the same Caption exists, there is no guarantee that this code could get the hWnd of the desired UserForm. The same is true when multiple instances of Excel.exe open the same workbook. You probably don't need to consider these rare cases, but if you do, the code below will do the trick.
Private Declare PtrSafe Function WindowFromAccessibleObject Lib "oleacc.dll" _
(ByVal IAcessible As Object, ByRef HWND As LongPtr) As Long
Public Property Get HWND() As LongPtr
WindowFromAccessibleObject Me, HWND
End Property
*
FindWindow function has specifications at https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-findwindowa .
WindowFromAccessibleObject function has specifications at https://docs.microsoft.com/ja-jp/windows/win32/api/oleacc/nf-oleacc-windowfromaccessibleobject .
Monday, May 31, 2010
Late Binding MSForms.DataObject to access the ClipBoard from VBA.
Unlike VB6, VBA does not have the ClipBoard object.
Instead of VB6's ClipBoard object, you can use MSForms 2.0's DataObject object.
To use the object , you should set reference to 'Microsoft Forms 2.0 Object Library'.
If your VBA project has a UserForm, the reference is automatically set.
Then you can use the following code:
Dim Clip as MSForms.DataObject
Set Clip = New MSForms.DataObject
You may think that you can write the following code to use the object without the reference.
Dim Clip as Object
Set Clip = CreateObject("MSForms.DataObject")
Unfortunately the code does not run if your VBA project does not have a UserForm
because "MSForms.DataObject" is not registered under the 'Classes' registry key.
Here's a workaround snippet.
'Though this is not a duty, I am grateful that you describe that you reffered this site(https://akihitoyamashiro.blogspot.com/2010/05/late-binding-msformsdataobject-to.html),
'when you present this sample code in your web site.
Sub Main()
'This is ok when you use a UserForm.
'Dim Clip As MSForms.DataObject
'Set Clip = New MSForms.DataObject
'You cannot write a code like this.
'Dim Clip As Object
'Set Clip = CreateObject("MSForms.DataObject")
'This is a workaround when you do not use a UserForm.
Dim Clip As Object
Set Clip = CreateObject( _
"new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
Clip.GetFromClipboard
MsgBox Clip.GetText, vbOKOnly, "What's in the ClipBoard?"
Clip.SetText "Test"
Clip.PutInClipboard
Set Clip = Nothing
End Sub
Instead of VB6's ClipBoard object, you can use MSForms 2.0's DataObject object.
To use the object , you should set reference to 'Microsoft Forms 2.0 Object Library'.
If your VBA project has a UserForm, the reference is automatically set.
Then you can use the following code:
Dim Clip as MSForms.DataObject
Set Clip = New MSForms.DataObject
You may think that you can write the following code to use the object without the reference.
Dim Clip as Object
Set Clip = CreateObject("MSForms.DataObject")
Unfortunately the code does not run if your VBA project does not have a UserForm
because "MSForms.DataObject" is not registered under the 'Classes' registry key.
Here's a workaround snippet.
'Though this is not a duty, I am grateful that you describe that you reffered this site(https://akihitoyamashiro.blogspot.com/2010/05/late-binding-msformsdataobject-to.html),
'when you present this sample code in your web site.
Sub Main()
'This is ok when you use a UserForm.
'Dim Clip As MSForms.DataObject
'Set Clip = New MSForms.DataObject
'You cannot write a code like this.
'Dim Clip As Object
'Set Clip = CreateObject("MSForms.DataObject")
'This is a workaround when you do not use a UserForm.
Dim Clip As Object
Set Clip = CreateObject( _
"new:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
Clip.GetFromClipboard
MsgBox Clip.GetText, vbOKOnly, "What's in the ClipBoard?"
Clip.SetText "Test"
Clip.PutInClipboard
Set Clip = Nothing
End Sub
Subscribe to:
Posts (Atom)