I'm looking for a way to put my notebook [Win7] into hibernation via VBA [Excel 2010]. I was searching for a solution but couldnt find a handy one.
I was playing with the SendKeys method but no luck. Then the Shell "C:\WINDOWS\System32\rundll32.exe powrprof.dll,SetSuspendState", vbHide gave me Compile error: Invalid outside procedure message. The Function ExitWindowsEx gives only the choices to log off, shut down and reboot but no hibernation as far as I know. Thank you for your help in advance.
I've just successfully hibernated my comp. Here is the magnificent code:
Option Explicit
Declare Function SetSuspendState Lib "powrprof.dll" _
(ByVal Hibernate As Boolean, ByVal ForceCritical As Boolean, ByVal DisableWakeEvent As Boolean) As Boolean
Sub Yoo()
Dim value_hibernate As Integer
value_hibernate = SetSuspendState(True, False, False)
If value_hibernate <> 1 Then MsgBox "Hibernation has failed."
End Sub
Some comments said that its better going with the SetSuspendState than the PowerState one.
Have fun hibernating your machines via VBA! Yaay!
Related
I have created Drag-n-Drop form for Excel in order to capture link to file location using treeview control (code bellow). It works as intended, however problem that after I make form ShowModal = False (because user might want to move Excel window in order to reach file-to-be-dragged) after it runs it's routine, error message pops-up informing that "File format is not valid" (screen below) or notification that file might be corrupt or unsafe (second screen below).
To my understanding that happens because Excel considers file to be dropped on the sheet and tries to open it (it will be most likely .pdf file).
Is there a way to prevent that other than making form Modal? To my understanding to achieve that somehow error message should be prevented or Excel should not try to open file at all and by doing that avoid message altogether (best case).
Code for Drag-n-Drop functionality:
Private Sub TreeView1_OLEDragDrop(Data As MSComctlLib.DataObject, Effect As Long, Button As Integer, Shift As Integer, x As Single, y As Single)
'for capturing draged file path
'VBA does not have normal native functionality to do that so it is solved by using treeview widget unconventionaly
Dim LinkToPass As String
LinkToPass = Data.Files(1)
MsgBox "Thank you! Link captured.", vbInformation, "Link captured"
'Pass information to another form, where user enters all other data required
If formLoaded("NewEntry_agreement") Then
NewEntry_agreement.LinkToFile.Caption = LinkToPass
End If
CloseBtt_Click 'just call close button Sub with Unload Me inside
End Sub
EDIT: Additional info and screenshot about alternative message. Also made goal more clear - either prevent message or prevent Excel from trying to open the file and by doing that prevent error message.
Click the form to toggle modal/modeless
' Adapted from Stephen Bullen's ModelessForm.xls 1998 example
Private Declare PtrSafe Function EnableWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal fEnable As Long) As Long
' click the form to toggle modal/modeless
Private Sub UserForm_Click()
Static lMode As Long
lMode = IIf(lMode = 0, 1, 0)
EnableWindow Application.hwnd, lMode
Me.Caption = IIf(lMode, "Modeless", "Modal")
End Sub
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
' ensure the app window is reset (maybe trap the state and reset if/as necessary)
EnableWindow Application.hwnd, 1
End Sub
I understand what you describe but I don't think there's a simple solution. Easiest would be if this works -
Call DragAcceptFiles(FindWindow("ThunderDFrame", Me.Caption), 0)
.. but unfortunately it doesn't. Neither does does attempting to disable the xlApp.Hwnd the same way from accepting dropped files. Maybe need to disable one of the other bunch of windows(?), I've only tried the ones I mentioned.
Briefly two different approaches you might look into -
Add a button for the user to toggle modeless / modal before enabling drag files operation. I don't have the code to hand, but it's definitely possible albeit not supported.
Instead of the treeview's OLE-DD setup a callback with CallWindowProc to trap the WM_DROPFILES message, get the files with DragQueryFile, and prevent Excel from receiving the message with DragFinish. You'd need a window and this could be the form's first and only direct Child window. Better though to add a window'd control such as a Frame (though it doesn't directly expose its 'hwnd' so a fair bit of API work to get it). Plenty of general examples out there and I've had this approach working - but unfortunately there are several catches and I don't have anything reliable enough I'd want to post!
This won't be the answer you're looking for but it might be the best you're going to get! Though I'd be pleased to be wrong:)
I want to detect when user clicks File >> Print or CTRL+P inside ms word or excel and use this detection to run a batch file using vba code, is this possible?
This code should self start along with the program.
I tried to find similar code but was unable to find anything useful to my need.
Any help would greatly appreciated.
Thanks
The way your question is written seems ambiguous to me. At first read, it seems like you are trying to distinguish between these two methods of telling a file to print. I know of no way to do this in vba.
However, you can intercept the print event or command.
Another possible meaning is that you want your procedure to run whenever the user attempts to print. See Intercepting Events Like Save or Print by Word MVPs Dave Rado and Jonathon West. See also Application.WorkbookBeforePrint Event.
Note, this does not block screenshots or saving to another file. Do you mind sharing why you are trying to do this? What you hope to accomplish?
You can use the DocumentBeforePrint and WorkbookBeforePrint Events. Below quoted from linked pages on Intercepting Events and WorkBookBeforePrint documentation.
A DocumentBeforePrint event procedure looks like this:
Private Sub oApp_DocumentBeforePrint(ByVal Doc As Document, _
Cancel As Boolean)
'Your code here
End Sub
If you want to prevent printing from occurring in certain
circumstances, you can set the Cancel variable to True, e.g.:
Private Sub oApp_DocumentBeforePrint(ByVal Doc As Document, _
Cancel As Boolean)
Dim Result As Long
Result = MsgBox("Have you checked the " & "printer for letterhead paper?", vbYesNo)
If Result = vbNo Then Cancel = True
End Sub
From Excel documentation
This example recalculates all worksheets in the workbook before
printing anything.
Private Sub App_WorkbookBeforePrint(ByVal Wb As Workbook, _
Cancel As Boolean)
For Each wk in Wb.Worksheets
wk.Calculate
Next
End Sub
End Quoted Material
Intercepting the Command instead of the Event
Another, less effective, method is to Intercept the actual commands. You could name your procedure PrintPreviewAndPrint and have another called FilePrintQuick that calls your procedure PrintPreviewAndPrint. Earlier versions use FilePrint and FilePrintDefault. Thank #Timothy Rylatt for the command names. He adds: Note that neither of these will intercept the backstage command accessed via File | Print. For that you need to use an event.
Sub PrintPreviewAndPrint()
' Your code here
End Sub
Sub FileQuick()
FilePreviewAndPrint
End Sub
In Word, these would go in your template or in a Global Template.
In Word, you make a template a Global Template by placing it in your Word Startup Folder.
Dealing with making this Global in Excel
My understanding of how Excel handles global macros is far poorer than that for Word. To assist with this, I asked my own question in the Microsoft Answers Excel Programming forum. Here is a link to that question and the answers I received. Andreas Klinger, who is engaged in that thread, is an experienced and knowledgeable Excel programmer, which I am not.
I am having the following problem: my client updated Excel version and all user forms appear to be broken. (Described here: Excel UserForm displays at the wrong size)
There is a special default setting "Optimize for best appearance" (When using multiple displays) in newer Excel versions that causes the problem. When I switch to "Optimize for compatibility" option, the problem is gone.
Question: did someone figure out how to change this setting programmatically (in VBA code)?
I tried to record the macro and change the settings but no luck. The resulting program is empty.
I did not manage to find a workaround. Apparently when the "optimise for best appearance" option is active and multiple monitors are connected to a device, excel cannot properly build buttons, objects etc. Telling each user not to use multiple monitors or to switch this option manually off is not an option.
I am changing right now the application in the way that I do not use any objects. It sucks but there is no other way.
Well you could try to change the registry settings:
HKEY_CURRENT_USER\Software\Microsoft\Office\16.0\Excel\Options\RenderForMonitorDpi
Doing something like (Source):
Public Function RegWrite(ByVal Path As String, _
ByVal Value As String, _
Optional ByVal Typ As String = "REG_SZ") As Boolean
Dim ws As Object
On Error Goto ErrHandler
Set ws = CreateObject("WScript.Shell")
ws.RegWrite Path, Value, Typ
RegWrite = True
Exit Function
ErrHandler:
RegWrite = False
End Function
However it did not work for me. It changes the registry value but after the restart excel won't change the setting and will change the registry value back to the old value.
Well, I suppose there is always SendKeys to change the display option. :(
There was a time when COM/VBA was important and all options have Application.* properties, but sadly not any more.
I have a subroutine invoked via Application.OnTime scheduled call.
How can I determine with Excel VBA whether the subroutine's workbook is the focused application/process in Windows?
(If it is not the active application then I will run a code that blocks the process for a little bit. If it is active I don't want to do that because I may be doing work in the workbook that I don't want interrupted.)
Try this. I tested it and it seems to work.
On module level:
Declare PtrSafe Function GetActiveWindow Lib "user32" () As LongPtr
For testing purposes I wrote these 2 subs:
Sub Test()
If GetActiveWindow <> 0 Then
'Application in focus
Debug.Print "Focus"
Else
'Application not in focus
Debug.Print " No focus"
End If
End Sub
Sub Timer()
Application.OnTime Procedure:="Test", EarliestTime:=Now + TimeValue("00:00:10")
End Sub
Now, try like this:
Run Timer (from the immediate window for example).
Before the timer reaches the 10 seconds set, switch to another app like your internet browser for example. Wait a bit for the time to complete.
Look at what the console displays:
If you stay on the excel window, the console will display "Focus".
If you switch window it will say "No focus".
I've been using the function from the top answer to this question to run shell scripts within VBA (the execShell function), which uses libc.dylib:
VBA Shell function in Office 2011 for Mac
Within the function is a while loop, and it runs much slower than the popen. I'm guessing that this part is executing within the shell and popen is initialising, but I don't really understand what it's doing.
Time taken to run popen:
0.01171875 seconds
Time taken to run while loop:
0.8710938 seconds
I'm wondering if it's possible to abort or timeout the function if it's taking a long time to execute? Is there a method that could be used to handle multiple function calls? I've added timeout to the curl command in the shell script. "curl --connect-timeout 5 --max-time 10 --retry 2 --retry-delay 0 --retry-max-time 40 ", and also added 0.1 second delay before each call. If it ran smoothly it wouldn't be a problem, but at times it can seem to lock up completely. If there's a lengthy delay with a curl request, I'd prefer to abort it and continue with the script. Maybe after a few consecutive failures terminate the script completely. I thought that the function calls would be processed linearly, but I'm thinking that maybe it could be concurrently processing the shell scripts, and this could be causing the problem. Ideally I'd like to have feedback on the status of the script, which I tried to implement with a status bar, but this didn't work because of a limitation with 2011 version of Excel. At the beginning of the run, I can see the sheets being updated and everything running smoothly, but later in the script excel freezes until it completes.
The other solution would be to use the MacScript command, but this shell script contains regex with "\" and multiple quotes, and it's very difficult to translate to applescript, so my preference is to use this method as it already works. Using the system() command isn't an option as the output is needed for the VBA script.
Private Declare Function popen Lib "libc.dylib" (ByVal command As String, ByVal mode As String) As Long
Private Declare Function pclose Lib "libc.dylib" (ByVal file As Long) As Long
Private Declare Function fread Lib "libc.dylib" (ByVal outStr As String, ByVal size As Long, ByVal items As Long, ByVal stream As Long) As Long
Private Declare Function feof Lib "libc.dylib" (ByVal file As Long) As Long
Function execShell(command As String, Optional ByRef exitCode As Long) As String
Dim file As Long
file = popen(command, "r")
If file = 0 Then
Exit Function
End If
While feof(file) = 0
Dim chunk As String
Dim read As Long
chunk = Space(50)
read = fread(chunk, 1, Len(chunk) - 1, file)
If read > 0 Then
chunk = Left$(chunk, read)
execShell = execShell & chunk
End If
Wend
exitCode = pclose(file)
End Function
popen runs faster because it is written in C/C++ and is binary executable. It runs at the operating system level and not in the VBA virtual machine (VBA VM). Your While loop and everything else in VBA is a scripting language that is run in VBA VM, which has to convert your script into a p-code that is then executed by the VBA VM. The “Visual Basic for Applications” article on Wikipedia provides a more detailed explanation if you’re interested.
VBA code will always be slower than any method provided by an Excel object or an external function like popen. For example, using the Range Objects AutoFill Method will always be faster than a VBA while loop filling a range with values.
The best way to add a “timeout” to your code is by adding the VBA method DoEvents inside your while loop. Then
I’d place it in between End If and Wend something like this:
End If
VBA.DoEvents
Wend
This will cause the While loop to allow Excel and Windows to process events. With the exception of VBA Shell() function, VBA code is serially executed. Without the VBA.DoEvents, your code will continue to execute until completion or error. This Stack Overflow question thread provides a great explanation.
One thing that could speed up your code is to turn off screen updating using this command Application.ScreenUpdating = False. Remember to turn it back on at the end of the sub with Application.ScreenUpdating = True. I'd put it around the While loop like this:
Application.ScreenUpdating = False
While feof(file) = 0
.
.
.
Wend
Application.ScreenUpdating = True
Also if you’re feeling really adventurous, you could write an event handler. Chip Pearson has a great article “Events and Event Procedures in VBA”.