Tuesday, January 23, 2018

VBA Pomodoro Timer

While learning about how I can improve my knowledge and myself, I discovered something called the pomodoro technique. Pomodoro is the italian word for Tomato, and the technique was named such because the creator, Francesco Cirillo, used a tomato shaped timer.

If you are unfamiliar with this technique, it is pretty simple. You write a list of things you wish to get done. After that you set a timer for 25minutes and work on a single one of your items until the timer is finished. If you finish your task before the timer is up, you continue to work on bettering yourself or improving your work until the timer is up. Once the timer is up you take a short, 3-5 minute break. Every fourth break, however, is a longer break--about 15-30 minutes. You then reset the timer and move on to your next item. This is repeated until all of your items are complete (or your work day is over).

Well, oddly enough, this technique is nearly identical to the technique I have found for myself and used through college. I was using a proven time management technique without even knowing it!  After discovering the pomodoro technique, I decided to write some VBA code to create my very own timer.

The code file can be downloaded HERE. (Right-Click -> Save As)

The code is simple, I created a new module and 4 subs. First, I start with some global variables.


 Public iSets As Integer  
 Public iPomodoros As Integer  
 Public nextTime  


Note: These do not have to be public, that will depend on you. You could alternatively use static variables in your subroutines as well.

Then I create the four subs:

  1. start
  2. incrementSet
  3. incrementPoms
  4. Finish

The method I use to set off the timer is application.ontime. I set it to run 25 minutes after the user clicks the OK button on the messagebox. After 25 minutes, my incrementPoms routine is run. There the user is prompted to take a short or long break (depending on how many Pomodoros they have done).

Once the user is done, they simply run the "Finish" sub. This will stop the process and display how many Sets of 4 Pomodoros was completed along with the number of Pomodoros left over.

To run this macro, you can either type in the immediate window, use the Macro dialog box in Excel, or create your own fancy buttons which refer to each macro (One for start, the other for Finish).

That's it!



 '--------------------------------------------------  
 '--------------------------------------------------  
 'Pomodoro technique timer. Written by Lance in VBA  
 'use to increase your productivity.  
 '--------------------------------------------------  
 '--------------------------------------------------  
 '  
 '  
 '       Directions for use  
 '__________________________________________________  
 '1. Create a list of items you wish to complete  
 '2. Run the "start" subroutine  
 '3. Begin working on the first item and work on  
 '  that item exclusively until the timer is up  
 '4. When the timer finishes, take a break as  
 '  directed. Move on to another item on your  
 '  list. If you have not finished the work on  
 '  your current item, come back to it during  
 '  another pomodoro.  
 '5. When your list is complete, run the "Finish"  
 '  subroutine.  
 '6. Enjoy having completed your To Do list!  
 '__________________________________________________  
 Public iSets As Integer  
 Public iPomodoros As Integer  
 Public nextTime  
 Sub start()  
   'reset our variables  
   iPomodoros = 0  
   iSets = 0  
   'prepare the user  
   ans = MsgBox("Are you ready? Make sure you have your list of items!", vbYesNo + vbQuestion, _  
          "Ready?")  
   'if yes is clicked then  
   If ans = vbYes Then  
     'prepare, OK will start the timer  
     MsgBox "Your time will begin when you click OK!"  
     'set our next time value  
     nextTime = Now() + TimeValue("00:25:00")  
     'schedule our next pomodoro  
     Application.OnTime nextTime, "incrementPoms"  
   Else  
     'when the user clicks no  
     MsgBox "Okay, Come back when you are ready to begin"  
   End If  
 End Sub  
 Sub incrementSet()  
   'reset the pomodoros  
   iPomodoros = 0  
   'increment our sets  
   iSets = iSets + 1  
   'take our 15min. break  
   MsgBox "Take a 15-30min. Break. Then switch to a new task and click OK to start the timer again"  
 End Sub  
 Sub incrementPoms()  
   'increase the count  
   iPomodoros = iPomodoros + 1  
   'if we have a new set then make it  
   If iPomodoros = 4 Then  
     incrementSet  
   Else  
     'let us know we need a small break  
     MsgBox "Take a short break, 3-5minutes." & _  
         "Then switch to a new task and click OK to start the timer"  
   End If  
   'set our next time variable  
   nextTime = Now() + TimeValue("00:25:00")  
   'schedule the next pomodoro  
   Application.OnTime nextTime, "incrementPoms"  
 End Sub  
 Sub Finish()  
   'Let's see how well we did!  
   MsgBox "Congratulations! You completed " & iSets & " sets plus " & iPomodoros & " Pomodoros"  
   'cancel the next pomodoro  
   Application.OnTime nextTime, "incrementPoms", schedule:=False  
 End Sub  



Monday, January 22, 2018

Auto Zoom in VBA

We Excel users can often be pretty technologically minded. We like multiple monitors, or big wide screens.

Sometimes we create a superb workbook and we decide to share it with someone else. The problem is, though, that sometimes their monitor does not display the page as well as we would like it to.

There is a neat trick to automatically set the zoom to the appropriate size based on the screen size.
------------------------------------

The first step is to create our workbook. Make it whatever size you think looks nice on your screen. If you think it looks best full screen, make it full screen.

Next, you need to find out what the window size is for your workbook. In the immediate window, type the following:

?application.height

?application.width


Make note of the numbers that result from it. For our example, let's say we got the results, 600 height 400 width.

Next we create a new VBA module and place the following code in it. After the code has been inserted, you must change the variables "orig_Height" and "orig_Width" with the numbers we got in the previous step. In our case this is 600 and 400 respectively.


Public Sub autoZoom()
    'Checks the size of the application and scales it
    'according to the size of the original workbook
    
    'Resume next on error because
    'this subroutine will be run
    'at various times and we don't
    'want it to interrupt the use
    'of our workbook
    On Error Resume Next
    
    Dim orig_Height
    Dim orig_Width
    Dim cur_Height
    Dim cur_Width
    Dim diff_height As Single
    Dim diff_width As Single
    
    'change this to the proper height
    'and width at 100% Zoom
    orig_Height = 600
    orig_Width = 400
    
    'These are set based on the current information
    cur_Height = Application.Windows.Item(ThisWorkbook.Name).Height
    cur_Width = Application.Windows.Item(ThisWorkbook.Name).Width
    
    'this checks to make sure we are using the
    'current workbook. This can be changed if
    'you want, but it's helpful so that we don't
    'scale the wrong workbook
    If ActiveWorkbook.Name = ThisWorkbook.Name Then
        'this is where we automatically change the zoom
        'We have to base it on either height or width
        'So we run a quick calculation to see which one
        'to use
        diff_height = Abs(orig_Height - cur_Height)
        diff_width = Abs(orig_Width - cur_Width)
        
        'if the height difference is less than the width
        'difference, then we use the height to scale the
        'window. Otherwise, we use the width
        If diff_height < diff_width Then
            'set the zoom
            ActiveWindow.Zoom = (cur_Height / orig_Height) * 100
        Else
            'set the zoom
            ActiveWindow.Zoom = (cur_Width / orig_Width) * 100
        End If
    End If
End Sub

Now you need to navigate to the workbook module and create a new event workbook_open (or whatever other event you wish to link this to) Then you simply type autoZoom wherever you want the code to automatically set the zoom.

Viola!
--------------------------------
This code works by creating a ratio of your current window size in comparison to the window size that looks good on the original monitor. It multiplies this ratio by 100 to set the zoom to the proper number.

Friday, January 19, 2018

Optimize Your VBA Code

Have you ever created a VBA module only to find that the subroutines and functions therein were incredibly slow?

There are a few things you want to look for in order to speed up your code.  Using a simply google search, you can find information about more advanced things like Storing data in a variant array (don't loop through cells!). However, I'm going to show you a few simpler techniques that I use when coding in VBA.

The first thing I do is use optimizing code. I have written two very simple subroutines which I call at the beginning and end of my code respectively.

First:

 Sub Optimize ()  
   'Run this before the slow code runs  
   Application.ScreenUpdating = False  
   Application.Calculation = xlCalculationManual  
   Application.DisplayAlerts = False  
   Application.AskToUpdateLinks = False  
 End Sub  

and then:

 Sub deOptimize()  
   'Stops Optimization, run before exiting sub or function  
   '(after code runs)  
   Application.ScreenUpdating = True  
   Application.Calculation = xlCalculationAutomatic  
   Application.DisplayAlerts = True  
   Application.AskToUpdateLinks = True  
 End Sub  

The important thing to note here is that you MUST run the deOptimize sub after  you are done. Otherwise, the user will run into some very annoying problems.

Another thing I do is look for points where my code calls for something else to happen. That could be running additional subroutines or functions, or calls to the windows API.

When you run code that is process intensive, Excel has a tendency to hang. A user might think that Excel is unresponsive and choose to end it.  Sometimes while Excel is hanging, it is also trying to complete another task in addition to your code.  (This can happen during long loops, for example.)

The solution is simple, but you must be careful with how often you use it. It is a simple line:

 DoEvents  

This will pause your code for a brief moment while the computer uses its processing power to process other things it is doing.  This will then open up more processing power for your code and can speed up certain processes. When used excessively, however, it can have the opposite effect and slow it down.

There have been big speed problems when it comes to Pivot Tables and Camera objects. If you need to use these things, you may not have a good way of speeding things up. Make sure you aren't calculating your Pivot Tables each time the smallest update occurs. Camera objects have no mercy, and it is better to avoid them and go with better "Dashboard" tools instead.

Friday, January 12, 2018

Create Animations in Excel (VBA)


Sometimes you are creating a nice workbook for work and realize that you need some sort of animation. Yes you can take the simple route and simply move objects around instantly, but what about times that you really need some sort of movement.

 Let's go ahead and build a simple expanding textbox containing some instructions which we don't want shown all the time. To make this textbox, we will insert a rectangle shape into the worksheet.



You can add some text by clicking the "Text Box" button on the ribbon.


Great! Now we have a neat little textbox. Let's rename it. Click on the Textbox and go to the box to the left of the formula bar. Let's name it "AniBox" (short for animation box).

Now let's find out how big this rectangle is by using the immediate window. Click on your rectangle and go to the immediate window (in the VBE. Get there by pressing Alt + F11) and type ?selection.height

Here, my height is a nice 273. I'll remember that.  Now I'll go back to my rectangle and manually make the height small enough so all you can see is the title.
Now we Right-Click on our rectangle and click "Assign Macro." A dialog box will open up where we can click on "New" on the right side. 
We will be brought to the visual basic editor. Now we can type some code in.  The code is simple. We will run through a for loop to change the size of the rectangle so the height is tall enough to see the instructions (273 in my case, as we found out earlier). 

Before we get too far, we do want to make sure we know what the "Closed" height is. Let's go back to the immediate window and type ?selection.height again after selecting our rectangle.  My height is 30.

Use the code below in your new VBA macro you created. Make sure to change your openHeight and closedHeight properties to whatever your box is. Also, if you want it to go faster, change the howSmooth to a lower number, or if you want it smoother, make it bigger.

Here is the code!  You can also download the example workbook Here: http://hidemyvba.com/AnimationExample.xlsb

 Sub AniBox_Click()  
      'Run the Animation  
      Dim isOpen As Boolean  
      Dim i As Integer  
      Dim endSize As Integer  
      Dim openHeight As Integer  
      Dim closedheight As Integer  
      Dim howSmooth As Integer  
      Dim myTimer  
      'make sure you set these to your open and closed  
      'height as we found using ?selection.height in  
      'the immediate window  
      openHeight = 273  
      closedheight = 30  
      'Make this bigger to have the animation be smoother  
      'but slower, make it small to make the animation  
      'faster I use 15 as a default  
      howSmooth = 15  
      'find out if we need to open or close the box  
      If ActiveSheet.Shapes("AniBox").Height = 30 Then  
           'we need to open it  
           isOpen = False  
           endSize = openHeight  
      Else  
           'we need to close it  
           isOpen = True  
           endSize = closedheight  
      End If  
      'let's actually do the work  
      For i = 1 To howSmooth  
           'now let's change the height  
           If isOpen Then  
                'let's close it  
                ActiveSheet.Shapes("AniBox").Height = _  
                ActiveSheet.Shapes("AniBox").Height - ((openHeight - closedheight) / howSmooth)  
           Else  
                'let's open it  
                ActiveSheet.Shapes("AniBox").Height = _  
                ActiveSheet.Shapes("AniBox").Height + ((openHeight - closedheight) / howSmooth)  
           End If  
           'this allows us to actually see the changes  
           myTimer = Timer  
           Do  
                DoEvents  
           Loop Until Timer - myTimer > 0.01   
      Next i  
      'If someone clicks on the box too many times  
      'we run into errors, so we set the end result here  
      ActiveSheet.Shapes("AniBox").Height = endSize  
 End Sub  

Thursday, January 11, 2018

Cursors.io Script. Write anything!

Ever got frustrated in http://cursors.io game and wanted to yell at another person?  Well now you can!

This script requires you to have Excel installed, otherwise it will not work.

Have fun with it! Either copy and paste the code into notepad and save as a .vbs file, or download the file here.

 Public Const MOUSEEVENTF_LEFTDOWN = &H2  
 Public Const MOUSEEVENTF_LEFTUP = &H4  
 Public Const MOUSEEVENTF_RIGHTDOWN = &H8  
 Public Const MOUSEEVENTF_RIGHTUP = &H10  
 On Error Resume Next  
    ' GetCursorPos requires a variable declared as a custom data type  
    ' that will hold two integers, one for x value and one for y value  
    Class POINTAPI  
      Public X_Pos  
      Public Y_Pos  
    End Class  
  Set Excel = WScript.CreateObject("Excel.Application")  
  Set Orig = New POINTAPI  
  Set Start = New POINTAPI  
  Dim scode1,owb1,omod1, iSize, iDelay, iRpt, text  
  Set owb1=Excel.workbooks.add  
  iSize = InputBox("What size do you want? (1-50: Recommended 7)")  
  If iSize = "" Then  
  iSize = 7  
  End If  
  iDelay = InputBox("What delay do you want? (Recommended 90)")  
  If iDelay = "" Then  
  iDelay = 90  
  End If  
  iRpt = InputBox("How many times would you like to repeat the text?")  
  If iRpt = "" Then  
  iRpt = 1  
  End If  
  scode1="Public Declare PtrSafe Sub mouse_event Lib ""user32"" (ByVal dwFlags As Long, ByVal dx As Long, ByVal dy As Long, ByVal cButtons As Long, ByVal dwExtraInfo As Long)" & vbCrLf  
  scode1= scode1 & "Declare PtrSafe Function GetCursorPos Lib ""user32"" (lpPoint As POINTAPI) As Long" & vbCrLf  
  scode1= scode1 & "Public Declare PtrSafe Sub keybd_event Lib ""user32"" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)" & vbCrLf  
  scode1= scode1 & "Public Declare PtrSafe Function MapVirtualKey Lib ""user32"" Alias ""MapVirtualKeyA"" (ByVal wCode As Long, ByVal wMapType As Long) As Byte" & vbCrLf  
  scode1= scode1 & "Type POINTAPI" & vbcrlf & "X_Pos As Long" & vbcrlf & "Y_Pos As Long" & vbcrlf & "End Type" & vbCrLf  
  scode1= scode1 & "Public Const KEYEVENTF_EXTENDEDKEY = &H1" & vbCrLf  
  scode1= scode1 & "Public Const KEYEVENTF_KEYUP = &H2" & vbCrLf  
  scode1 = scode1 & "Sub doMouse (Event1)" & vbcrlf  
  scode1 = scode1 & "mouse_event Event1, 0, 0, 0, 0" & vbCrLf  
  scode1 = scode1 & "End Sub" & vbCrLf  
  scode1 = scode1 & "Function getX ()" & vbcrlf  
  scode1 = scode1 & "Dim myPos as POINTAPI" & vbcrlf  
  scode1 = scode1 & "GetCursorPos myPos" & vbcrlf  
  scode1 = scode1 & "getX = myPos.X_Pos" & vbcrlf  
  scode1 = scode1 & "End Function" & vbcrlf  
  scode1 = scode1 & "Function getY ()" & vbcrlf  
  scode1 = scode1 & "Dim myPos as POINTAPI" & vbcrlf  
  scode1 = scode1 & "GetCursorPos myPos" & vbcrlf  
  scode1 = scode1 & "getY = myPos.Y_Pos" & vbcrlf  
  scode1 = scode1 & "End Function" & vbCrLf  
  scode1 = scode1 & "Sub myShiftKey (isDown as Boolean)" & vbcrlf  
  scode1 = scode1 & "If isDown then" & vbcrlf  
  scode1 = scode1 & "keybd_event &H10, MapVirtualKey(&H10, 0), 0, 0" & vbcrlf  
  scode1 = scode1 & "else" & vbCrLf  
  scode1 = scode1 & "keybd_event &H10, MapVirtualKey(&H10, 0), KEYEVENTF_KEYUP, 0" & vbCrLf  
  scode1 = scode1 & "End If" & vbCrLf  
  scode1 = scode1 & "End Sub" & vbCrLf  
  Set omod1=owb1.vbproject.vbcomponents.add(1)  
  omod1.codemodule.addfromstring scode1  
 For iL = 1 To iRpt  
  Move_Cursor  
  SetCursorPos1 start.X_Pos, Start.Y_Pos  
  start.x_pos = start.x_pos + 1  
 Next  
 'cleanup  
  Set omod1=nothing  
  owb1.saved=true  
  owb1.close  
  Set owb1=nothing  
  Excel.quit  
  Set Excel=Nothing  
  If Err.Number <> 0 Then  
  MsgBox "Error Occurred: " & Err.Description  
  End If  
 Sub Move_Cursor()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'Dim text As String  
   'Dim i As Integer  
   'you must always start and end in the upper left corner  
   If start.X_Pos = 0 Then  
    text = InputBox("What Do you want to write with the mouse?")  
    WScript.Sleep 4000  
   End If  
   GetCursorPos1 orig  
   If start.X_Pos = 0 Then  
    GetCursorPos1 Start  
   End If  
   For i = 1 To Len(text)  
    shiftDown True  
    WScript.Sleep iDelay/2  
     mouse_event1 MOUSEEVENTF_LEFTDOWN   
     Select Case LCase(Mid(text, i, 1))  
       Case "a"  
         down  
         down  
         up  
         right  
         up  
         left  
         right  
         down  
         down  
         up  
         up  
         left  
       Case "b"  
         right  
         down  
         left  
         up  
         down  
         down  
         right  
         up  
         up  
         left  
       Case "c"  
         right  
         left  
         down  
         down  
         right  
         left  
         up  
         up  
       Case "d"  
         right  
         down  
         down  
         left  
         up  
         up  
       Case "e"  
         right  
         left  
         down  
         right  
         left  
         down  
         right  
         left  
         up  
         up  
       Case "f"  
         right  
         left  
         down  
         right  
         left  
         down  
         up  
         up  
       Case "g"  
         right  
         down  
         down  
         left  
         right  
         up  
         left  
         up  
       Case "h"  
         down  
         right  
         up  
         down  
         down  
         up  
         left  
         down  
         up  
         up  
       Case "i"  
         right  
         down  
         down  
         left  
         right  
         right  
         left  
         up  
         up  
         right  
         left  
       Case "j"  
         right  
         down  
         down  
         left  
         up  
         down  
         right  
         up  
         up  
         left  
       Case "k"  
         down  
         diagupRight  
         diagdownLeft  
         diagdownRight  
         diagupLeft  
         down  
         up  
         up  
       Case "l"  
         down  
         down  
         right  
         left  
         up  
         up  
       Case "m"  
         down  
         down  
         up  
         up  
         right  
         down  
         up  
         right  
         down  
         down  
         up  
         up  
         left  
       Case "n"  
         down  
         up  
         right  
         down  
         up  
         left  
       Case "o"  
         down  
         right  
         up  
         left  
       Case "p"  
         down  
         down  
         up  
         right  
         up  
         left  
       Case "q"  
         down  
         right  
         up  
         left  
         right  
         down  
         down  
         right  
         left  
         up  
         up  
       Case "r"  
         down  
         down  
         up  
         diagdownRight  
         diagupLeft  
         right  
         up  
         left  
       Case "s"  
         right  
         left  
         down  
         right  
         down  
         left  
         right  
         up  
         left  
         up  
       Case "t"  
         right  
         down  
         down  
         up  
         up  
         right  
         left  
       Case "u"  
         down  
         right  
         up  
         down  
         left  
         up  
       Case "v"  
         diagdownRight  
         diagupRight  
         diagdownLeft  
         diagupLeft  
       Case "w"  
         diagdownRight  
         diagupRight  
         diagdownRight  
         diagupRight  
         diagdownLeft  
         diagupLeft  
       Case "x"  
         diagdownRight  
         diagdownRight  
         diagupLeft  
         diagupRight  
         diagdownLeft  
         diagdownLeft  
         diagupRight  
         diagupLeft  
       Case "y"  
         diagdownRight  
         diagupRight  
         diagdownLeft  
         diagdownLeft  
         diagupRight  
         diagupLeft  
       Case "z"  
         right  
         diagdownLeft  
         right  
         left  
         diagupRight  
         left  
     End Select  
     mouse_event1 MOUSEEVENTF_LEFTUP  
     right  
     right  
   Next   
   shiftDown False  
 End Sub  
 Sub SetCursorPos1(x, y)  
  Excel.ExecuteExcel4Macro ("CALL(""user32"",""SetCursorPos"",""JJJ""," & x & "," & y & ")")  
 End Sub  
 Function GetCursorPos1(ByRef myPoint)  
  myPoint.X_Pos=Excel.run("getX")  
  myPoint.Y_Pos=Excel.run("getY")  
 End Function  
 Sub left()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos - iSize, orig.Y_Pos  
   orig.X_pos = orig.x_pos - iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub right()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos + iSize, orig.Y_Pos  
   orig.X_pos = orig.x_pos + iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub down()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos, orig.Y_Pos + iSize  
   orig.y_pos = orig.y_pos + iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub up()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos, orig.Y_Pos - iSize  
   orig.Y_pos = orig.y_pos - iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub diagdownRight()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos + iSize, orig.Y_Pos + iSize  
   orig.X_pos = orig.x_pos + iSize  
   orig.y_pos = orig.y_pos + iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub diagupRight()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos + iSize, orig.Y_Pos - iSize  
   orig.X_pos = orig.x_pos + iSize  
   orig.y_pos = orig.y_pos - iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub diagdownLeft()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos - iSize, orig.Y_Pos + iSize  
   orig.X_pos = orig.x_pos - iSize  
   orig.y_pos = orig.y_pos + iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub diagupLeft()  
   'Dim Hold As POINTAPI  
   Set Hold = New POINTAPI  
   'GetCursorPos1 Hold  
   SetCursorPos1 orig.X_Pos - iSize, orig.Y_Pos - iSize  
   orig.X_pos = orig.x_pos - iSize  
   orig.y_pos = orig.y_pos - iSize  
   WScript.Sleep iDelay  
 End Sub  
 Sub mouse_event1 (myEvent)  
  Excel.run("DoMouse(" & myEvent & ")")  
 End Sub  
 Sub shiftDown (isDown)  
  Excel.run("myShiftKey(" & isDown & ")")  
 End Sub  

VBS Special! Monitor Network Files

Here's a special one for you!  If you are ever at work and need to monitor a particular file for changes, I have a script for you!

I created this small script to monitor a log file for which I am an administrator. This log file was an indicator if a user was beta testing a piece of software that I wrote. I needed to know when the file was being used, so I knew when to anticipate bug reports and whether or not I would receive feedback.

The method of using this is easy. Simply copy and paste this code into notepad, and save the file with the .vbs extension.

The file will first ask you to choose a file you wish to monitor.  Once you chose the file, you can choose how many seconds you want to monitor it. It is not limited to a certain amount of time, you can enter enough seconds to cover hours, or even days. The script will silently run in the background, and as soon as your chosen file is modifies, you will get a messagebox popup!

---

 dim iSize  
 'create objects  
 set objFSO = createobject("Scripting.FileSystemObject")  
 set objFile = objFSO.GetFile(strFile)  
 Set wShell=CreateObject("WScript.Shell")  
 'Allow a user to select a file with the file dialog box  
 Set oExec=wShell.Exec("mshta.exe ""about:""")  
 'get the file into a string variable  
 strFile = oExec.StdOut.ReadLine  
 'ask the user for input  
 iLength = inputBox("Please Enter the number of seconds you wish to Monitor this file")  
 'validate input, set default if invalid  
 if not isnumeric(iLength) then  
  msgbox "Error! Invalid Time Amount, using default, 1800 seconds (half an hour)"  
  iLength = 1800  
 end if  
 'set original file size  
 iSize = clng(objfile.size)  
 'show the current size  
 msgbox "Current Size " & iSize  
 'loop for designated number of seconds  
 for i = 1 to clng(iLength)  
  'Check if the size has changed (once per second)  
  if clng(objfile.size) <> isize then  
   'notify user of the change  
   msgbox objfile.Name & " Modified!"  
   'end  
   wscript.quit  
  end if  
  'wait a second (or so)  
  wscript.sleep 999  
 next   
 'If no changes detected and timer has run out, notify user  
 msgbox "End of Checking Changes... File Size: " & clng(objFile.Size) & "Kb"  

Place Secret Messages in Excel

Ever want to send a secret message to your friend?  Want it to be secure and encrypted and not immediately obvious to anyone who looks at it?  Well how about using Excel and VBA to create a workbook with a hidden message!

Simply Create a new VBA module and paste the following code in it.

 Public Function encrypt(sText As String) As String  
   Dim i As Long  
   Dim Order() As Variant  
   Dim myLetter As Integer  
   Dim newString As String  
   sText = LCase(sText)  
   Order = Array(97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, _  
          110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122)  
   For i = 1 To Len(sText)  
     myLetter = Asc(Mid(sText, i, 1)) - 96  
     If myLetter &lt; 0 Or myLetter &gt; 26 Then  
       newString = newString &amp; Chr(myLetter + 96)  
     Else  
       newString = newString &amp; Chr(Order(myLetter - 1))  
       algorithm Order, myLetter  
     End If  
   Next  
   encrypt = newString  
 End Function  
 Function decrypt(sText As String) As String  
   Dim i As Long  
   Dim NormOrder() As Variant  
   Dim Order() As Variant  
   Dim myLetter As Integer  
   Dim newString As String  
   sText = LCase(sText)  
   NormOrder = Array(97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, _  
          110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122)  
   Order = Array(97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, _  
          110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122)  
   For i = 1 To Len(sText)  
     myLetter = Asc(Mid(sText, i, 1)) - 96  
     If myLetter &lt; 0 Or myLetter &gt; 26 Then  
       newString = newString &amp; Chr(myLetter + 96)  
       GoTo nexti  
     End If  
     If i = 1 Then  
       myLetter = Asc(Mid(sText, i, 1)) - 96  
       newString = Mid(sText, i, 1)  
       algorithm Order, myLetter  
     Else  
       myLetter = Asc(Mid(sText, i, 1))  
       myLetter = getPlace(myLetter, Order)  
       newString = newString &amp; Chr(NormOrder(myLetter))  
   'Debug.Assert Chr(NormOrder(myLetter)) &lt;&gt; "i"  
       'cycle the order again  
       myLetter = NormOrder(myLetter) - 96  
       algorithm Order, myLetter  
     End If  
 nexti:  
   Next  
   decrypt = newString  
 End Function  
 Function getPlace(chrNum As Integer, Order() As Variant) As Integer  
   Dim i As Integer  
   For i = LBound(Order) To UBound(Order)  
     If Order(i) = chrNum Then  
       getPlace = i  
       Exit Function  
     End If  
   Next i  
 End Function  
 Function algorithm(Order() As Variant, letterNum As Integer)  
   Dim i  
   Dim j As Double  
   Dim newOrder() As Variant  
   Dim temp  
   Dim offset As Double  
   ReDim newOrder(LBound(Order) To UBound(Order))  
   letterNum = letterNum - 1  
   'initial switch  
   temp = Order(UBound(Order))  
   Order(UBound(Order)) = Order(letterNum)  
   Order(letterNum) = temp  
   offset = Round(Exp(letterNum) / 2)  
 reTest:  
   If offset &gt; UBound(Order) Then  
     offset = offset - (UBound(Order) * Application.WorksheetFunction.RoundDown(offset / (UBound(Order)), 0))  
     GoTo reTest  
   End If  
   'shift entire array  
   For i = LBound(Order) To UBound(Order)  
     j = i + offset  
     If j &gt; 25 Then j = j - 26  
     newOrder(UBound(Order) - i) = Order(j)  
   Next i  
   'replace the order variable  
   For i = LBound(newOrder) To UBound(newOrder)  
     Order(i) = newOrder(i)  
   Next i  
 End Function  

The idea is simple. Instead of storing text in the workbook, this will store the ASCII character codes in the form of font sizes in your workbook!

In addition to this simple method of hiding a message in a workbook, this code also encrypts the ascii character codes by offsetting the characters.


To dig deeper:


Give each letter a number. For example:

A = 1, B = 2, C = 3,...,Z = 26


get it?


Let's say your message is "Hi":


Your first letter would be H, so the number 8.

well now, we don't want to simply store the letter i as number 9, instead, we will offset the entire alphabet by the letter H--AKA the number 8.


so now, the letter i becomes 9 + 8 = 17


so the encoded message would be the numbers 8, 17.


The result are two cells in the Excel Sheet with font sizes 8 and 17.


Have fun with it!


VBA Add an animated Notification Box to your Excel Program

For those of us who create programs and add-ins in Excel, we are very, very familiar with the message box.  The message box gives us the opp...