So far, this book has focused on how to work with VBA to perform actions within a VBA host application, such as Word or Access.
But you might sometimes need to communicate between applications as well. This chapter demonstrates four tools you can use to contact and manipulate one application from another: Automation, data objects, Dynamic Data Exchange (DDE), and SendKeys
.
The Office applications that this chapter uses as examples offer several tools for communicating with other applications:
SendKeys)
.SendKeys
The oldest and most primitive method of communicating between applications, SendKeys
relies on sending keystroke equivalents to the other application. It's an attempt to pretend that someone is typing on the keyboard. But this approach can cause timing and other issues. Nonetheless, even though it's rudimentary by comparison to Automation and DDE, SendKeys
can still be effective in some situations. So you need to at least know it's available should the other two approaches be impractical.Beyond these three communications tools, this chapter discusses the DataObject
object, which you can use to store information and to transfer information to and from the Windows Clipboard.
Automation is the most powerful and efficient way to communicate between applications, so we'll look at it first. Each application that supports Automation offers one or more Component Object Model (COM) objects that you can access programmatically—usually an object representing the application, an object representing the various types of files the application uses, objects representing its major components, and so on.
For any Automation transaction, there's a server application that provides the information or tools and a client application that receives or employs them. (There's also another pair of terms that distinguish between two communicating applications: The server application is also sometimes known as the object application, and the client application is known as the controlling application.)
Automation lets the client application harness the built-in capabilities of the server application. For example, Excel has better calculation features than Word and can generate useful charts, data maps, and so on based on its calculations and data. By using Automation, Word can borrow Excel's calculation engine and then insert the results into a Word document. Or, Word could use Excel to create a chart that it then inserts into a document as well. Word can also take more-limited actions, such as causing Excel to open a workbook, copy a group of cells from a spreadsheet in it, and paste-link them into a document.
To use Automation through VBA, you create an object in VBA that references the application you want to work with. You use the CreateObject
function to create a new object in the other application and the GetObject
function to retrieve an existing object from the other application.
When using Automation, you can choose whether to display the server application or keep it hidden from the user. For some procedures, you'll need to display it—for example, the user might need to choose a file or a folder or make another choice that requires live intervention. In other situations, it can be best to keep the server application hidden so that the user isn't distracted by an application suddenly launching itself spontaneously and robotically carrying out actions in front of the user's startled eyes. This can make some users uneasy, and conclude that the computer has gotten out of control.
But even if you decide to hide a server application from the user when the procedure runs, in most cases it's helpful to display the server application to yourself while you're writing and testing the procedure. That can make it easier to see what's going wrong if your code doesn't work as expected.
When you use Automation to access another application, you can choose which type of binding to use—that is, how to establish the connection between the client application and the server application.
Early binding involves adding a reference to the application's object library by using the References dialog box (Tools ➢ References) and then declaring an object at the start of the code by using a Dim
statement that declares the specific object class type rather than declaring the object generically As Object
.
For example, the following code connects to a slide within a PowerPoint presentation by using early binding:
Dim myPowerPoint As PowerPoint.Application
Dim myPresentation As Presentation
Dim mySlide As Slide
Set myPowerPoint = CreateObject("PowerPoint.Application")
Set myPresentation = myPowerPoint.Presentations.Add
Set mySlide = myPresentation.Slides.Add(Index:=1, Layout:=ppLayoutTitleOnly)
With late binding, you create an object that references the other application on the fly when the code executes. If you declare the object explicitly, you declare it as a generic object—As Object
—rather than declare it as a specific object class type.
For example, the following statements declare the Object variable myOutlook
and then assign to it a reference to an Outlook.Application
object:
Dim myOutlook As Object
Set myOutlook = CreateObject("Outlook.Application")
If the server application you're using supports early binding, use it in preference to late binding. There are three advantages to early binding:
On the other hand, late binding can avoid object-library issues such as having to make the right references and other library-version problems.
The CreateObject
function creates and returns a reference to an Automation object exposed to other applications. The syntax is as follows:
CreateObject(class [,servername])
Here, class
is a required argument specifying the class (the formal definition) of the object to create. The class
argument consists of the name of the library that will provide the object and the type of object to be provided, so it looks like this:
applicationname.objecttype
For example, to specify the Excel Application
object as a class, use a class
argument of Excel.Application
. Here, Excel
is the name of the application that provides the object, and Application
is the type of object that we want Excel to provide. Likewise, Excel.Sheet
would specify a worksheet object in Excel.
servername
is an optional string Variant that specifies the name of the network server on which to create the object. If you merely want to connect to an application located on the user's machine (in other words, if both applications—the client and server applications—are located on the same hard drive), omit servername
or specify an empty string. To connect with an application located on a remote server machine, you must have DCOM (the Distributed Component Object Model) installed, and the object on the server computer must be configured to allow remote creation.
Typically, you'll use a CreateObject
function with a Set
statement to assign to an object variable the object that you create. For example, the following statements declare an object variable named myNewSheet
and assign an Excel worksheet object to it:
Dim myNewSheet As Object
Set myNewSheet = CreateObject("Excel.Sheet")
The GetObject
function returns a reference to an existing Automation object. The syntax is as follows:
GetObject([pathname] [, class])
You can provide either argument—but you must provide one of them. Here, pathname
is an optional string Variant specifying the full path and name of the file that contains the object you want to retrieve. pathname
is optional, but if you don't specify it, you must specify the class
argument. class
(which is optional if you specify pathname
, but required if you don't) is a string Variant specifying the class of the object you want to return.
As with CreateObject
, typically, you'll use a GetObject
function with a Set
statement to assign to an object variable the object that you return with the GetObject
function. For example, in the second of the following statements, the GetObject
function returns an object consisting of the workbook Z:FinanceRevenue.xlsm
. The Set
statement assigns this object to the object variable named Revenue
declared in the first statement:
Dim Revenue As Object
Set Revenue = GetObject("Z:FinanceRevenue.xlsm")
Here, the workbook is associated with Excel. When this code runs, VBA starts Excel if it isn't already running and activates the workbook. You can then reference the object by referring to its object variable; in this example, you could manipulate the Revenue
object to affect the Z:FinanceRevenue.xlsm
workbook.
The following sections show three examples of using Automation with Office applications.
This example transfers information from an Excel spreadsheet to a Word document.
First, you need to add to the target Word project (the client project that will contain the code that accesses Excel) a reference to the Excel object library. Follow these steps:
Normal.dotm
template, select the Normal project in the Project Explorer before adding the reference. Or, just choose Insert ➢ Module to create a brand-new module to play around with.Once you've added the reference, you can use the VBA Editor's Object Browser to browse Excel objects. Display the Object Browser as usual by pressing F2 or choosing View ➢ Object Browser, and then choose Excel in the Object Browser's Project/Library drop-down list. The Object Browser will display the contents of the Excel object library, as shown in Figure 29.1. You can display the help (code examples, syntax) for a selected Excel object by clicking the Help button (the question-mark icon) in the Object Browser.
To create and test the next code example, first set up in Excel the preconditions that this procedure expects: namely, a range object named SalesTotal
. To do this,
It's this value that your macro in Word will pluck from this workbook.
Book1.xlsx
in your C: emp
subdirectory. (Note that you're saving it as an .xlsx
file.)It won't matter because your macro will open the file on the hard drive.
Okay, now in Word's VBA Editor, add the code. Because you used early binding, you have available the Editor's IntelliSense assistance and code-completion features. Create the procedure shown in Listing 29.1. This procedure uses the GetObject
function to retrieve the information from the specified cell in the Excel spreadsheet you previously created and inserts this data in the active Word document at the current insertion point (where the blinking cursor is).
This macro retrieves one piece of information from an Excel spreadsheet that's on the hard drive in the C: emp
directory. Here's what happens in the macro:
mySpreadsheet
of the type Excel.Workbook
.strSalesTotal
.Set
statement and the GetObject
function to make mySpreadsheet
reference the spreadsheet C:TempBook1.xlsm
.strSalesTotal
the Value
property (the actual data) of the Range
object cell A1 You defined the SalesTotal
range as a single cell, so strSalesTotal
receives the value of that cell.mySpreadsheet
object the special value Nothing
, releasing the memory it occupied.
Because the procedure ends almost immediately afterward, this statement isn't necessary here. VBA will destroy it at the end of execution of the procedure. But, it's good practice to free the memory assigned to an object when you no longer need to use the object, just to get into the habit.
TypeText
method of the Selection
object in Word to enter a string of text and the strSalesTotal
string at the current selection.TypeParagraph
method to insert a paragraph after the text.If you have trouble getting this example to work, double-check the following:
Set mySpreadsheet = _
GetObject("C:TempBook1.xlsx")
Either you've mistyped this path in your code (such as typing C:Docs
rather than C:Temp
) or you have not saved an Excel file named Book1.xlsx
to this folder.
strSalesTotal = mySpreadsheet.Application.ActiveSheet.Range("A1").Value
If this code fails, you've got a typo in the code, such as specifying the wrong range cell. Ensure it's A 1 (the number 1 not the letter L). Then resave the workbook. Remember, the workbook is opened by this macro as a file on the hard drive—not accessed in the Excel application itself.
We managed to send data from Excel to Word in the previous section. Now let's go the other way.
This next procedure (Listing 29.2) runs as a macro in Word. The procedure requires that Excel be currently running, so the macro checks for the possibility that Excel isn't executing and, if necessary, handles the problem itself by starting Excel. The procedure creates a new Excel workbook and then transfers the current document's word count from Word to the workbook.
For this example to work, you must store a Word .docm
file named test.docm
in your C: emp
directory.
As before, you'll find creating this procedure easier if you first add to the current Word project a reference to the Excel object library. (See the previous section for instructions.)
Here's what happens in Listing 29.2:
Later, in line 33, this same variable assigns its value to an Excel cell.
strPath
that will hold the file path to the Word document.strFile
that will hold the Word document's filename.Document
variable docCurDoc
; it will point to the Word document when it is opened using the Open
method of the Documents
object.Excel.Application
object variable myXL
, and line 8 declares an Excel.Workbook
object variable myXLS
.errExcelNotRunning
, setting its value to 429
. This error number indicates that the procedure attempted to manipulate Excel while no instance of Excel was currently executing.errDocNotAvailable
, setting its value to 5174
. This error number indicates that the Word document your procedure attempted to open could not be found.Handle
in the event of an error.strPath
, a backslash, and strFile
, assigning the document object to the docCurDoc
variable.
If the document isn't available, an error occurs and execution is transferred to the error-handler code that starts in line 43. This error number matches the constant defined in the procedure as errDocNotAvailable
, so a message box informs the user that the Word document wasn't found. Then execution is transferred to the Shutdown
label where the two object variables are destroyed and the procedure is exited.
myXL
. However, if this attempt fails, execution is transferred to the Handle
label.
If Excel isn't running at this point, error 429 (“ActiveX component cannot create object”) occurs, so line 44 in the error handler checks for this error by using the constant errExcelNotRunning
. If it matches the error number, line 46 assigns to myXL
a new instance of Excel that it creates by using the CreateObject
function. Line 47 then uses an Err.Clear
statement to clear the error, and line 48 contains a Resume Next
statement to cause VBA to resume execution back up at the next statement following the offending statement.
myXL
refers to a running instance of Excel.Visible
property of myXL
to True
so that it appears onscreen.myXLS
a new workbook created by using the Add
method of the Workbooks
object in myXL
.myXL
the text Word Count
.WordCount
. This value is accessed by using the wdPropertyWords
property from the BuiltInDocumentProperties
collection of docCurDoc
.Exit Sub
statement to exit the procedure at this point—to avoid permitting execution to continue down into the zone where the error-handling statements are. Using an Exit Sub
like this is common when a procedure includes an error handler at the end.The next procedure shows how to communicate between PowerPoint and Outlook. This procedure, run from PowerPoint, returns the existing instance of Outlook or (if there is none) creates a new instance. The procedure then uses PowerPoint to send a message that gives details drawn from the presentation.
Listing 29.3 shows the procedure. There's one complication: Because PowerPoint doesn't have a central macro storage project like Word's Normal.dotm
or Excel's Personal Macro Workbook, the code must be stored in an open presentation. This could be the presentation that is the subject of the email, but it is much more convenient to maintain a code-only presentation that you open at the beginning of all PowerPoint sessions that require the use of code. This becomes your own personal macro-storage system.
In any case, you need some slides from which to pick information that will be sent (and you also need to provide your email address), so follow these steps to set up the necessary preconditions for the upcoming example.
First, prepare the target PowerPoint project (the project that will contain the code that accesses Outlook and will contain the slides you're accessing):
Now enter the code from Listing 29.3 into the module you inserted in step 3. Be sure to replace my email address in line 23 with your email address.
Here's what happens in Listing 29.3:
Presentation
object variable named myPresentation
.strPresentationFilename
, which is used for storing the path and filename of the presentation.strPresentationTitle
, which is used to store the title of the presentation. Line 6 declares a String variable named strPresentationPresenter
, which is used to store the name of the presenter of the presentation.Outlook.Application
object variable named myOutlook
that is used to represent the Outlook application.Outlook.MailItem
object variable named myMessage
that is used to represent the message that the procedure creates.errOutlookNotRunning
and assigns to it the number 429
, the error number returned if no instance of Outlook is available when the GetObject
function tries to access it.ErrorHandler
(in line 36) in the event of an error.myPresentation
object variable.With
structure that works with myPresentation
:
FullName
property of myPresentation
to strPresentationFilename
.strPresentationTitle
the Text
property of the TextRange
object in the TextFrame
object in the third Shape
object on the first Slide
object—in other words, the text from the first placeholder shape on the first slide in the presentation.strPresentationPresenter
the text from the second shape on the second slide.myOutlook
the current instance of Outlook, which it returns using the GetObject
function. If Outlook isn't running at this point, error 429 (“ActiveX component cannot create object”) occurs, so
errOutlookNotRunning
.myOutlook
a new instance of Outlook that it creates by using the CreateObject
function.Err.Clear
statement to clear the error.Resume Next
statement to cause VBA to jump back up in the code and resume execution where it left off (at the statement after the offending statement).CreateItem
method of the Outlook Application
object (represented by myOutlook
) to create a new mail item (a new email), which it assigns to myMessage
.With
structure that works with myMessage
.To
property. (You should change this line to your own email address so you can test this code and receive the message it sends.)Subject
property..BodyFormat = olFormatHTML
).Body
property.Send
method to send the message.Quit
method to close myOutlook
.myMessage
to Nothing
, releasing the memory it occupied.myOutlook
to Nothing
.Else
statement in line 41, and line 42 displays a message box that gives the error number and description.If you test this example, be sure to remember to change line 23 from my email address to your email address. When the procedure finishes execution, look in your Inbox in Outlook for the new email message.
Instead of using the CreateObject
function to start an application and return a reference to it, you can use the Shell
function to run an application. Shell
can run any executable program, and its syntax is straightforward:
Shell(pathname[,windowstyle])
Here, pathname
is the file path and program name of the program you want the Shell
command to execute. Also include in the pathname
any necessary command-line switches or arguments required by that program.
This example opens Internet Explorer, maximizes its window, and then switches the focus to it:
Sub OpenIE()
Dim id
id = Shell("c:program filesinternet exploreriexplore.exe", vbMaximizedFocus)
End Sub
windowstyle
is an optional integer Variant that you use to specify the type of window in which to run the application and to switch focus to the newly launched application. Table 29.1 lists the constants and values for windowstyle
.
TABLE 29.1: Constants and values for the windowstyle argument
CONSTANT | VALUE | WINDOW STYLE |
vbHide |
0 |
Minimized and hidden, but with focus |
vbNormalFocus |
1 |
Normal (“restored”) with focus |
vbMinimizedFocus |
2 |
Minimized with focus (the default) |
vbMaximizedFocus |
3 |
Maximized with focus |
vbNormalNoFocus |
4 |
Normal (“restored”) without focus |
vbMinimizedNoFocus |
6 |
Minimized without focus |
As you've seen so far in this book, you can store information in many places using VBA. But there's also a uniquely useful data object with the ability to copy information to, and retrieve information from, the Clipboard. This chapter is all about ways to communicate between applications, and the Clipboard is one such way.
A data object is attached to a UserForm
object in the Microsoft Forms object model, but you can use a data object by itself with no user form displayed. This is similar to the way that you can create and manipulate a hidden Access database with no visible interface displayed to the user. (This technique is described in the section titled “Opening Multiple Databases at Once” in Chapter 28 .)
A data object, which is represented in VBA by the DataObject
object, is used to store data. Each data object can hold multiple pieces of text information, and each piece must be in a defined format. You can create and use multiple data objects to store multiple pieces of data in the same format, or you can be tricky and tell VBA that information is in a different format when really it's not.
At any given time, the Clipboard can contain one text item and one item in another format, such as a picture. If you copy another text item to the Clipboard, that item will overwrite the previous text item, but any graphical item on the Clipboard will remain unscathed. Likewise, if you copy a picture to the Clipboard, it will overwrite any previous graphical item (or indeed any type of non-text item) stored in the Clipboard, but any text item in the Clipboard will be unaffected.
The data object works in a way similar to the Clipboard. However, a data object can't store graphics. It can store multiple pieces of text information, each defined as being in a different format.
To create a data object, declare an object variable of the DataObject
type and then use a Set
statement to assign a new DataObject
object to it. For example, the following statements declare a DataObject
variable named myDObj
and assign a new DataObject
to it:
Dim myDObj As DataObject
Set myDObj = New DataObject
To store information in a data object, use the SetText
method, which has the following syntax:
object.SetText(StoreData [,format])
The components of the syntax are as follows:
object
is a required argument specifying a valid object.StoreData
is a required argument specifying the data to store in the data object.format
is an optional argument containing an Integer value or a String specifying the format of the information in StoreData
. A value of 1
indicates text format; a value other than 1
or a String indicates a user-defined format.For example, the following statement stores the text Sample text string
in the DataObject
named myDObj
:
myDObj.SetText "Sample text string"
The following statement stores the text Sample formatted text string
in the DataObject
named myDObj
, defining and using the custom format myFormat
:
myDObj.SetText "Sample formatted text string", "myFormat"
Once the custom format has been defined and stored in the data object, you can access the data stored in that format by specifying the format. In this case, no formatting is actually involved—the code simply uses the format
argument to create and identify a different data slot in the data object so that the new string doesn't overwrite the existing text string. It's a trick.
To return information from a data object, use the GetText
method of the DataObject
object. The GetText
method has the following syntax:
object.GetText([format])
The components of the syntax are as follows:
object
is a required argument specifying a valid object.format
is an optional argument containing a String or an Integer specifying the format of the data to retrieve.For example, the following statement displays a message box containing the plain-text string stored in the DataObject
named myDObj
:
MsgBox myDObj.GetText
The following statement assigns to the String variable strTemp
the text stored with the myFormat
format in the DataObject
named myDObj
:
strTemp = myDObj.GetText("myFormat")
Here's a working code example that illustrates how to create a data object and then uses it to store and retrieve information. First, choose Tools ➢ References in the Editor to ensure that the check box next to Microsoft Forms 2.0 Object Library is checked. Note that it's likely this library will not be in its correct alphabetic location in the list of libraries in the References dialog box. Instead, it will probably be checked already by default and, thus, found in the first 10 or so libraries at the top of the References list. Recall that once a library has been selected, it moves to the top of the list.
Type this working example into an application's VBA Editor, and press F5 to see it execute:
Sub StoreText()
Dim myDObj As DataObject
Set myDObj = New DataObject
myDObj.SetText "Sample text string"
MsgBox myDObj.GetText
End Sub
To put text into the Clipboard from a data object, use the PutInClipboard
method of the DataObject
. For example, the following example creates a new data object named myDO
, assigns to it the text Nasta Nasta Gomes
, and then assigns that text to the Clipboard:
Sub ManageClipboard()
Dim myDO As New MSForms.DataObject
myDO.SetText "Nasta Gomes"
myDO.PutInClipboard
myDO.GetText
MsgBox myDO.GetText
End Sub
To fetch whatever text information is in the Clipboard and store it in a data object, use the GetFromClipboard
method of the DataObject
object. The previous example uses the data object referenced by the variable myDO
, assigns to it the text from the Clipboard, and then displays the text.
To return formatted information from the Clipboard and store it in a data object, use the GetFormat
method of the DataObject
object.
To find out whether a data object contains a given format, use the GetFormat
method of the DataObject
object. The syntax for the GetFormat
method is as follows:
object.GetFormat(format)
Here are the components of the syntax:
object
is a required argument that returns a valid DataObject
object.format
is an Integer or String specifying the format you're looking for. If the DataObject
contains the format, GetFormat
returns True
; if not, GetFormat
returns False
.For example, the following statement checks to see if the DataObject
named myDO
contains the format myHTML
and assigns the format's contents to the string strHTMLText
if it does:
If myDO.GetFormat("myHTML") = True Then _
strHTMLText = myDO.GetText(Format:="myHTML")
If the application with which you want to communicate doesn't support Automation, you can try Dynamic Data Exchange (DDE). DDE is a protocol that establishes a channel between two applications through which they can automatically exchange data. DDE can be tricky to set up, but once you get it working, it is usually reliable.
Not all applications support DDE. Among the Office applications, Word, Excel, and Access support DDE, but PowerPoint and Outlook do not. What's more, Microsoft warns that DDE is not a secure technology. So use it only in situations where you aren't vulnerable to outside intrusion, where the data is innocuous, or where it's public information like today's weather.
In the following descriptions of DDE statements, I'll use the term method in its more generic, non-OOP sense. Long, long ago when DDE was introduced (in Windows 3.0!), object-oriented programming wasn't yet fashionable.
A typical DDE conversation can contain the following actions:
DDEInitiate
method to start a DDE connection and establish the channel on which the connection operatesDDERequest
method to return text from the other application or the DDEPoke
method to send text to the other applicationDDEExecute
method to execute a command in the other applicationDDETerminate
method to close the current DDE channel or using the DDETerminateAll
method to close all the DDE channelsTo start a DDE connection, you use the DDEInitiate
method, which employs the following syntax:
expression.DDEInitiate(App, Topic)
The components of the syntax are as follows:
expression
is an optional expression specifying an Application
object.App
is a required String argument specifying the name of the application with which the DDE connection is to be started.Topic
is a required String argument specifying the DDE topic (such as an open file) in the application. To discover the list of topics available for an application, you send a DDE request (via the DDERequest
method, discussed in the next section) to the System
object in the application.DDEInitiate
returns the number of the DDE channel established. You then use this number for subsequent DDE calls.
For example, the following statements declare the Long variable lngDDEChannel1
and assign to it a DDE channel established with the workbook Sales Results.xlsm
in Excel:
Dim lngDDEChannel1 As Long
lngDDEChannel1 = DDEInitiate("Excel", "Sales Results.xlsm")
To return a string of text from another application, you use the DDERequest
method, which has the following syntax:
expression.DDERequest(Channel, Item)
The components of the syntax are as follows:
expression
is an optional expression that returns an Application
object.Channel
is a required Long argument specifying the DDE channel to use for the request.Item
is a required String argument specifying the item requested.To get the list of topics available via DDE, request the Topics
item from the System
topic. For example, the following statements establish a DDE channel to FrontPage (by using DDEInitiate
) and return the list of DDE topics, assigning the list to the String variable strDDETopics
:
Dim lngDDE1 As Long
Dim strDDETopics As String
lngDDE1 = DDEInitiate(App:="FrontPage", Topic:="System")
strDDETopics = DDERequest(Channel:=lngDDE1, Item:="Topics")
Open Excel, click the File tab on the Ribbon, and then click the New option. In the search field at the top, type Monthly Family Meal Planner. Click that template to select it, and then click the Create button.
Now open Word's VBA Editor and type in the following procedure. The following statements establish a DDE channel to the workbook SalesReport1.xlsm
in Excel and return the contents of cell C7 (R7C3) in the String variable strResult
:
Sub DDEtoExcel()
Dim lngDDEChannel1 As Long, strResult As String
lngDDEChannel1 = DDEInitiate("Excel", "Monthly family meal planner1")
strResult = DDERequest(lngDDEChannel1, "R11C4")
MsgBox strResult
DDETerminateAll
End Sub
Don't save this workbook. Press F5 to test this, and you should see a message box displaying “Beef and Mushroom Skillet Supper,” which sounds pretty nasty.
For DDE to work, you have to use the correct, full name of the target document as it appears in the title bar of the application. In this case, your target document is an Excel workbook named Monthly family meal planner1.
The previous code works only if you haven't yet saved the Monthly family meal planner1
workbook because before it's saved, a new workbook has no filename extension appended to its name. However, if you have already saved this workbook, you must append whatever filename extension you employed, such as .xlsm
. Here's an example:
lngDDEChannel1 = DDEInitiate("Excel", "Monthly family meal planner1.xlsx")
The DDETerminateAll
statement is explained shortly.
To send text to another application, use the DDEPoke
method, which has the following syntax:
expression.DDEPoke(Channel, Item, Data)
The components of the syntax are as follows:
expression
is an optional expression that returns an Application
object.Channel
is a required Long argument specifying the DDE channel to use.Item
is a required String argument specifying the item to which to send the data.Data
is a required String argument specifying the data to be sent.Continuing to use the previous example, the following statements use the DDEPoke
method to assign the data Potato Salad Surprise to cell R11 C4 in the worksheet:
Sub DDEPokeExcel()
Dim lngDDEChannel1 As Long, strResult As String
lngDDEChannel1 = DDEInitiate("Excel", "Monthly family meal planner1")
strResult = DDERequest(lngDDEChannel1, "R11C4")
DDEPoke Channel:=lngDDEChannel1, Item:="R11C4", _
Data:="Potato Salad Surprise"
DDETerminateAll
End Sub
Now look at the Excel worksheet and you'll see that “Beef and Mushroom Skillet Supper” has been replaced with the even more dubious-sounding “Potato Salad Surprise.”
To execute a command in another application, use the DDEExecute
method, which has the following syntax:
expression.DDEExecute(Channel, Command)
The components of the syntax are as follows:
expression
is an optional expression that returns an Application
object.Channel
is a required Long argument specifying the DDE channel to use.Command
is a required String argument specifying the command or series of commands to execute.For example, the following statements establish a DDE channel to Excel and issue a Close
command to close the active workbook:
Sub DDEExec()
Dim lngMyChannel
lngMyChannel = DDEInitiate(App:="Excel", Topic:="System")
DDEExecute lngMyChannel, Command:="[Close]"
End Sub
If the workbook you're closing has unsaved data, Excel will display a message box prompting you to save it first—thus, preventing it from closing until the prompt is satisfied.
When you've finished a DDE communication, use the DDETerminate
method to close the DDE channel you opened. The syntax for the DDETerminate
method is as follows:
expression.DDETerminate(Channel)
Here are the components of the syntax:
expression
is an optional expression that returns an Application
object.Channel
is a required Long argument specifying the DDE channel to close.The following statements employ the previous example, closing the DDE channel that was opened:
Dim lngMyChannel
lngMyChannel = DDEInitiate(App:="Excel", Topic:="System")
DDEExecute lngMyChannel, Command:="[Close]"
DDETerminate lngMyChannel
To close all open DDE channels, use the DDETerminateAll
method:
DDETerminateAll
Because VBA doesn't automatically close DDE channels when a procedure ends, it's a good idea to use a DDETerminateAll
statement to make sure you haven't inadvertently left any DDE channels open.
The SendKeys
statement is a basic and limited form of communication between applications. You may find SendKeys
useful if neither Automation nor DDE works with the target application. But SendKeys
does have shortcomings, as you'll see shortly.
SendKeys
transmits specified keystrokes to the destination application. It impersonates someone typing at the keyboard.
For example, to use SendKeys
to send the command to create a new file in Notepad, you send the keystrokes for Alt+F, N (to execute Notepad's File ➢ New command), and Notepad reacts as if you had pressed the keys manually. In Office 2019 applications, Alt+F opens the File tab on the Ribbon.
SendKeys
works only with currently running Windows applications: You can't use SendKeys
to start another application running (for that you need to use the Shell
command, as discussed earlier in this chapter).
The syntax for the SendKeys
statement is as follows:
SendKeys string[, wait]
Here, string
is a required String expression specifying the keystrokes to be sent to the destination application. wait
is an optional Boolean value specifying whether to wait after sending the keystrokes until the application has executed them (True
) or to immediately return control to the procedure sending the keystrokes (False
, the default setting). The True
setting, however, can prevent some kinds of timing problems.
Typically, string
consists of a series of keystrokes (rather than a single keystroke). All alphanumeric characters that appear on the regular keyboard are represented by the characters themselves: To send the letter H, you specify H in the string, and to send the word Hello, you specify Hello in the string. To denote the movement (arrow) and editing keys, SendKeys
uses keywords enclosed within braces ({}
), as described in Table 29.2.
TABLE 29.2: SendKeys keywords for movement and editing keys
KEY | CODE |
Down arrow | {DOWN} |
Left arrow | {LEFT} |
Right arrow | {RIGHT} |
Up arrow | {UP} |
Backspace | {BACKSPACE} , {BS} , or {BKSP} |
Break | {BREAK} |
Caps Lock | {CAPSLOCK} |
Delete | {DELETE} or {DEL} |
End | {END} |
Enter | {ENTER} |
Esc | {ESC} |
F1, F2, etc. | {F1} , {F2} , etc. (up to {F16} ) |
Help | {HELP} |
Home | {HOME} |
Insert | {INSERT} or {INS} |
NumLock | {NUMLOCK} |
Page Down | {PGDN} |
Page Up | {PGUP} |
Print Screen | {PRTSC} |
Scroll Lock | {SCROLLLOCK} |
Tab | {TAB} |
To send Shift, Control, and Alt, use the symbols shown in Table 29.3.
TABLE 29.3: SendKeys symbols for meta keys
KEY | CODE |
Shift | + |
Ctrl | ^ |
Alt | % |
SendKeys
automatically assigns the keystroke after the meta key to the meta key, thereby imitating pressing and holding the Alt key—for example, while simultaneously pressing S.
In other words, to send a Ctrl+O keystroke combination, you would specify ^O, and SendKeys
imitates holding down Ctrl while pressing O. Then, the next keystroke after the O is considered to be struck separately. If you need to assign multiple keystrokes to the meta key, enter the keystrokes in parentheses after the meta key. For example, to send Alt+F, I, I, you'd write %(FII), not %FII.
As you can see, SendKeys
has special uses for the plus sign (+), caret (^), percent sign (%), and parentheses (). The tilde (˜) gets special treatment as well. To use these characters to merely represent themselves instead of their special uses, enter them within braces: {=}
sends a regular = sign, {^}
a regular caret, {%}
a percent sign, {˜} a tilde, and {()}
parentheses. Likewise, you must enclose brackets (which have a special meaning in DDE in some applications) within braces; braces themselves also go within braces.
Using SendKeys
is much less complex than these details initially make it appear—but with that reassurance, there's one more trick you should know: To repeat a key, enter the key and the number of repetitions in braces. For example, to send five up-arrow keystrokes, you'd specify {UP 5}
; to send 10 zeros, you'd specify {0 10}
.
Listing 29.4 shows an example of how to use SendKeys
to send some text to Notepad after first starting it with the Shell
command.
Here's how the code works:
Send_to_Notepad
procedure starts by declaring (in lines 2, 3, and 4) three String variables—strLogDate
, strSaveLog
, and strMsg
—and (in line 5) one Variant variable, appNotepad
.strMsg
a sample string of text.strLogDate
a date built of the Day
, Month
, and Year
values for Now
(which returns the current date and time).
For example, if the date is July 11, 2019, Month(Now)
will return 7
, Day(Now)
will return 11
, and Year(Now)
will return 2019
, so the strLogDate
string will contain 7-11-2019
.
strSaveLog
string (which will be used to supply the filename for the log file) text describing the file, the strLogDate
string, and the .txt
filename extension (to continue our example, Log file for 7-11-2019.txt
).Shell
statement to run Notepad in a “normal” (not maximized or minimized) window with focus and storing the task ID of the Notepad session in the variable appNotepad
.AppActivate
statement to activate Notepad.SendKeys
statement to send to Notepad the following:
strMsg
.strSaveLog
String variable, which is entered in the File Name text box.When you run this procedure (again, remember that you need to run the procedure by pressing F5 rather than stepping into it with F8), you'll see the following:
Msg
string appear in the Notepad window..txt
file is saved to the currently active folder on your hard drive.
To locate the currently active folder, open Notepad by hand and choose File ➢ Save As. You might also find that timing problems have changed the name of the file from the intended Log file for 1-13-2019.txt to something like g file for 1-13-2019.txt.
Because SendKeys
was historically most often employed to open an application's menus and select an option from the menus (the way that Notepad still behaves), you might think that applications since Windows Vista—which are largely menu-free and employ the Ribbon instead—would seriously curtail the flexibility of the SendKeys
technique. However, this isn't true. Many of the features of the Ribbon, for example, are accessible via key combinations. Try pressing the sequence Alt, W, Q, 2, and the Enter key in Word; it will switch to the View tab on the Ribbon, select the Zoom option, and switch to a 200 percent zoom.
The difference here is that instead of employing the traditional approach of simultaneously pressing the Alt key while pressing other keys (such as Alt+V to open a View menu), in current Windows operating systems you press and release Alt by itself and then you press the W key to switch to the View tab on the Ribbon. At this point, additional keystrokes are possible to activate the various options on the View tab. To exit from this mode, press Esc.
Here's another code example, which illustrates how to manipulate Ribbon-based applications. This time Excel, not Notepad, is the target, and the Ribbon, not a menu, is manipulated. The code sends an Alt key by itself (this activates the shortcut key feature on the Ribbon and the Quick Access Toolbar as well, displaying a variety of keys you can choose from). Then the code switches to the View tab (a W does that), and finally full-screen mode is turned on by sending an E:
Sub Send_to_Excel()
Dim appExcel As Variant
appExcel = Shell("Excel.exe", vbNormalFocus)
AppActivate appExcel
SendKeys "%", True 'send Alt by itself
SendKeys "W", True 'W for the View tab
SendKeys "E", True 'E for full screen mode
End Sub
Before pressing F5 to test this code, close Excel.
VBA is not limited to its own library of functions. In this chapter, you've seen how to use the Editor's Tools ➢ References feature to make Office applications' object libraries available to VBA's built-in capabilities. But wait! There's more.
VBA can also access the entire Windows API (application programming interface). This isn't as simple as adding a library via Tools ➢ References. And the necessary code is verbose. But if you want to have complete control over Windows' internals to, for example, perfectly manage timing issues such as waiting for an outside application to complete its task and other advanced techniques, the Windows API functions are up to such jobs (and plenty more besides).
Windows API programming is beyond the scope of this book, but if you're interested, copy and paste the sample code from this MSDN web page:
https://msdn.microsoft.com/en-us/library/office/bb258148(v=office.12).aspx
That sample code works fine in Word's or Access's VBA Editors. And the links provided on that web page are your doorways into further, deeper study of the topic. If, like me, you have major geek tendencies, it's great fun to wander around and experiment in an immense compendium like the API. You can make Windows do things you wouldn't believe.
Shell
function to run an application. Although the Shell
function can prove useful in a variety of inter-application communication situations, Shell
can also present the programmer with a timing problem.
Shell
function raises, and describe a good solution to this problem.SendKeys
. Using SendKeys
is a fairly simple, but rather awkward and limited, way to communicate between applications. It imitates typing in keystrokes, thereby allowing your code to manipulate an application by accessing some of its features using, for example, Alt+key combinations such as Alt+F to open the File tab on the Ribbon.
SendKeys
was historically most often employed to open an application's menus and select an option from the menus. Since Vista, Windows applications have largely done away with traditional menus, so is SendKeys
of even more limited use now than in the past?