VBA delay millisecond timer using API - vba

I need a VBA millisecond (~100) delay timer and tried using the API:
Public Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As LongPtr)
Then use Sleep(100) in a sub.
But, when I try using it I get a compiler error:
"Constants, fixed-length strings, arrays, user-defined types and
declare statements not allowed as Public members of object modules"
I get the same error if I change it to 'Private'.
Any clues as to how I can get this to work?
Thanks for any help provided.

The relevant part of the error is this (emphasis mine):
"Constants, fixed-length strings, arrays, user-defined types and
declare statements not allowed as Public members of object modules"
In other words, you apparently can't have a Public Declare statement in a class module.
Add a new standard/procedural (.bas) module, and move the Declare statement(s) there. Or, make it Private if that's the only module it's used in.
Should "just work" ;-)

Related

Access .dll library MODULE function in Excel (VBA)

I have a library called PIT_math_tools (written in vb.net) containing math functions which is split up into different modules.
In this case, the function I want to access is located in a Module called geometry_ellipsen_module:
Namespace geometry_ellipsen
Public Module geometry_ellipsen_module
...
Public Function calc_distanz_ellipse_kreis(...) As Double
Now I want to access the Function calc_distanz_ellipse_kreis() in VBA.
I used the following declare statement to accomplish this:
Private Declare PtrSafe Function calc_distanz_ellipse_kreis Lib "C:\Users\path...\PIT_math_tools.dll" (...) As Double
So far, everything looks fine.
When trying to call that function, however, I get runtime error '453'
"DLL-Entry Point calc_distanz_ellipse_kreis in 'PathToLibrary' not found."
When using the function in other .Net code, it could be found either by using the prefix
PIT_math_tools.geometry_ellipsen.geometry_ellipsen_module.calc_distanz_ellipse_kreis()
or an import statement identical to the prefix above.
Based on that, I tried extending the Function name in the Declare Statement by the same prefix:
Private Declare PtrSafe Function geometry_ellipsen.geometry_ellipsen_module.calc_distanz_ellipse_kreis Lib ...
However, this results in a compiler error "Expected: Lib".
How do I get the vba code to find the function inside the library module?
Thanks in advance.
The VBA syntax...
Private Declare Function SomeMethod() Lib "Some.dll"
... is used to prototype standard Win32 DLLs, e.g. Win32 API. Which .NET DLLs (aka "Assemblies") are not.
What you need to do in VB.NET is to make that assembly COM-visible. VB.NET creates a typelib (*.tlb) for the assembly.
In VBA you then add a COM reference (point to that typelib) to your project like you would do with other COM DLLs (ADODB, MSXML etc.). And then you do your standard
Dim myObject As Namespace.ClassName
Set myObject = New Namespace.ClassName
With myObject
.ThisProperty = 123
Dim lRet As Long
lRet = .ThisMethod("abc")
End With

Experiencing problems with copy/paste excel objects/shapes with VBA

I have some complex code which takes some user inputs (names of shapes to copy) then copies said shapes from one sheet to another multiple times. The items are grouped shapes drawn in Excel and all named correctly and uniquely.
I receive copy and paste errors intermittently "Method 'Paste' of object _Worksheet' failed" and "Method 'copy' Of Object '_worksheet' Failed. Through researching the problem we understand that it is fairly common and has something to do with programmes which conflict with Excel when they are accessing the clipboard.
So far, my colleague and I have deduced that 2 programmes in particular interfere the most with the copy/paste operations - Adobe Reader and Autodesk Powershape. (Autodesk and Adobe both have Reference Libraries available within VBA, not sure if this is a coincidence?)
The problem used to occur very frequently whilst we had the programmes open, so we wrote the following macros/functions to try and stablise the code:-
Public Sub CopyShape(ItemName, CopyDestination)
Call ClearClipboard Sheets(CopyDestination).Shapes(ItemName).Copy
Do Until IsClipboardEmpty = False DoEvents Loop
End Sub
Where "ClearClipboard" is:-
Public Function ClearClipboard()
OpenClipboard (0&)
EmptyClipboard
CloseClipboard
End Function
and the function IsClipboardEmpty is:-
Function IsClipboardEmpty() As Boolean
IsClipboardEmpty = (CountClipboardFormats() = 0)
End Function
with the following public declarations:-
Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
Declare Function EmptyClipboard Lib "user32" () As Long
Declare Function CloseClipboard Lib "user32" () As Long
Public Declare Function CountClipboardFormats Lib "user32" () As Long
This code works quite a lot of the time (far better than trying to use "DoEvents" after the copy operation which just failed miserably) as it forces the code to check if the copied item is in the clipboard before trying to paste it, but it doesn't always work - something in the background still messes up the code.
Is there any way of either:-
locking and unlocking the clipboard using VBA or APIs?
using a completely different method of copying and pasting the shapes?
Any and all solutions welcome and of course happy to answers any questions.
Thanks

VBA - invoke method of native object without type information (e.g. call IUnknown::Release)

I'm invoking a few native functions via VBA, which at times return pointers to certain objects. For example, I'm calling CreateStreamOnHGlobal(), which would return a pointer to an IStream interface.
But - is there any way in VBA to call a method of this object? For example, when I'm done using that, I would like to release the stream via a call to IUnknown::Release. Given I don't have any type information whatsoever though, I can't do that directly.
Declare Function CreateStreamOnHGlobal Lib "ole32" (ByVal hGlobal As LongPtr, ByVal fDeleteOnRelease As Long, ppstm As Any) As Long
Declare Function GlobalAlloc Lib "kernel32" (ByVal uFlags As Long, ByVal dwBytes As Long) As Long
Const GMEM_MOVEABLE = &H2
Sub test()
Dim pMem, pStream AS LongPtr
pMem = GlobalAlloc(GMEM_MOVEABLE,0)
CreateStreamOnHGlobal(pMem, 1, pStream)
' .. do stuff with the stream ..
' now call .Release() on the IStream object pointed to by pStream - but how??
End Sub
Is there any way to call an instance method of a type of which VBA doesn't have any type information?
I'm not well-versed in C++ / lower level languages, so I'm not quite sure how and where interface functions are represented in memory, but maybe there's some way to get a function pointer (GetProcAddr maybe?) and invoke this (CreateTread?) somehow? Appreciate any ideas!
Thanks
You have two big problems here.
First, in VBA, Set something = Nothing is what you'd do to call IUnknown::Release on the object. But you have a pointer, not something known to the VBA, so you can't use it like an object.
Once you've went with pointers, you must go with the pointers all the way. You really can't just go "I wanna use it like an object but I wanna a pointer sometime".
The next problem is that because you got a pointer, you are now limited in VBA with what you can do with it. VBA cannot have a Declare statement that references namespace scoped functions like IUnknown::Release -- that basically belongs to a class, and can't be called directly from a Declare statement because you need to have an instance and do a offset from it. But you can't go to the offset, say "let's run whatever is there" in VBA, either.
You might be able to use CopyMemory API to copy the pointer to a Object variable, but I really don't recommend this since it usually ends in crashes, heartbreaks and tears.
In short, I believe we have a XY problem here. Instead of asking on how to do IUnknown::Release in VBA, why don't you state what you are actually trying to solve? There might be an easier solution that doesn't involve all those API messes.

VB6 ActiveX DLL - Public Declare Lib

I have a line of code I need to compile into a (ActiveX) DLL in VB6, unfortunately I am always greeted with a, "Declare statements not allowed as public members of object modules".
The line looks like this:
Public Declare Sub Example Lib "kernel32"
Is there any work around? I saw somewhere you can do this with, Property Set, or Property Let. I'm just not sure how. Any help would be appreciated.
Thanks.
See if this works for you:
Private Declare Sub Example Lib "kernel32"
Public Sub CallExample()
Call Example
End Sub
Here is a MSFT reference that gives more info:
http://msdn.microsoft.com/en-us/library/office/gg278767.aspx

VBA in Word: Getting the script execution time

how do I get out the time my VBA script takes to execute?
I know from PHP that there is something like microtime() which is called once before the script and once after in order to be able to calculate the difference from this values...
Is there an VBA equivalent?
It's a sample code that I used in one of my VBA projects to measure the performance of my script temporarily.
Also you can find ample of resources to optimize your script's performance
Public Sub generate(ByRef generators() As Generator)
Dim startTime As Double
OptimizePerformance doc
'/////////////////below is the line that matters
startTime = Timer
'////////// your code that is to be measured (in time) here //////////
MsgBox Format(Timer - startTime, "00.00") & " seconds"
removeOptimization doc
End Sub
There's a function called Timer() that returns time in seconds since midnight. Includes milliseconds. I don't know of a micro-second resolution timer in VBA.
An article on About.com suggests that it's possible to write your own microtimer by making Win32 API calls directly from VBA.
In case you need more accurate timing information, I would recommend using one of the following functions the get the start and end times
#If Win64 Then
Private Declare PtrSafe Function GetTickCount Lib "kernel32" () As LongLong
Private Declare PtrSafe Function timeGetTime Lib "winmm.dll" () As LongLong
#Else
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Declare Function timeGetTime Lib "winmm.dll" () As Long
#End If
They should return more precise information, depending on systems. Personally, on Win7 64bit / Office 2010/2013 32bit environments I prefer timeGetTime
Note that the absolute values of timeGetTime are not recommended to be used, but the DIFFERENCE (e.g. endTime-startTime) is a quite accurate value in miliseconds