Copyright: Copyright ©1995
Citation: NeXT Computer, Inc.


Accessing Excel through the ORB

This section lists the main routine of the Excel application. The main routine launches Excel on an NT machine and then quit Excel, using the protocols declared in ExcelProtocols.h. The code connects to the ORB, uses the ORB to get a proxy to the Excel.Application object, and associates the ExcelApp protocol defined in ExcelProtocols.h with the proxy. The code then manipulates Excel on NT through the proxy using two mechanisms: methods defined in ExcelProtocols.h (which correspond to Excel’s exported methods, such as the ExcelApp protocol’s quit method), and the setOLEPropertyNamed: method, which maps to Excel’s own accessor functions (for more information, see “Setting and Getting Properties.”

// main.m

#include <stdio.h>

// main.m

#import <foundation/NSAutoreleasePool.h>

#import <foundation/NSUtilities.h>

#import "ExcelProtocols.h"

int main (int argc, char **argv)

{

id <NEXTORB> olebridge;

  	id <ExcelApp> excelApp;

  	id <ExcelWorkbooks> workbook;

  	NSString *hostName;

  	NSAutoreleasePool *autoreleasePool =

[[NSAutoreleasePool alloc] init];

  	hostName = [NSString stringWithCString:argv[1]];

  	olebridge = (id <NEXTORB>)[NXConnection

 	connectToName:"NEXTORB.NXDO"

onHost:[hostName cString]];

  	if (!olebridge) {

      	NSLog (@"Did not get connected...");

      	return 0;

  	} else {

      	NSLog (@"Connected to NEXTORB");

      	[olebridge setProtocolForProxy:@protocol(NEXTORB)];

  	}

  	excelApp = [olebridge

 	objectWithRegisteredName:@"Excel.Application"

protocol:@"OLE" host:hostName];

  	if (!excelApp) {

    	NSLog (@"Cannot get Excel.Application from %@", hostName);

    	return 0;

  	}

  	NSLog (@"Connected");

/* associate a protocol with your proxy object */

  	[excelApp setProtocolForProxy:@protocol(ExcelApp)];

/* set ExcelApp's Visible property to YES */

  	[excelApp setOLEPropertyNamed:@"Visible" to:NSVal(YES)];

  	NSLog (@"Done--hit return");

  	gets (alloca (10));

  	[excelApp quit];

  	[autoreleasePool release];

  	return 0;

}

This program uses the NEXTORB protocol, which, like the OLEObject protocol described in the previous section, is also declared in nxorb.h. Like OLEObject, the NEXTORB protocol includes a method for setting a protocol. It also includes the objectWithRegisteredName:protocol:host: method, which lets you get a proxy to an OLE object through the ORB.

In the previous program, the method setProtocolForProxy: (included in both the OLEObject and NEXTORB protocols) is used to associate the olebridge proxy object with the NEXTORB protocol, and to associate the excelApp proxy object with the ExcelApp protocol that was declared in ExcelProtocols.h. For type checking and convenience, it’s recommended that you use protocols to statically type objects in your code.

The above program uses the method setOLEPropertyNamed: to set the Excel Application object’s Visible property to YES.The getOLEPropertyNamed: and setOLEPropertyNamed: methods allow you to set and get properties on the OLE object to which you have a proxy.

For information on memory management when working with the NeXT ORB, click here.


Adding Custom Toolbar Buttons in Visual C++

You may want to add to the toolbar a button that allows you to quickly build your Objective-C code from within the Visual C++ environment. Given a makefile as described in the previous section, you can create such a button as follows:

1.	From the Tools menu, choose Customize.

The Customize dialog box appears, with the Toolbars tab visible.

2.	Select Tools in the Categories list box.

3.	Drag the next unused numbered button from the selected category onto the toolbar.

You can see which buttons are not currently in use by selecting the Tools tab in the Customize dialog box; the entries in the Menu Contents list correspond, in order, to the numbered tool buttons. By default, Visual C++ defines buttons 1 through 6 for you, leaving button 7 as the first unused tool button.

Figure 8.	 Custom Toolbar Buttons

4.	Select the Tools tab in the Customize dialog box.

5.	Click Add.

The Add Tools dialog box appears.

6.	Enter the command that you want to have executed whenever the custom toolbar button is clicked, and click OK (if the command is not in your path, you will need to specify a full pathname).

For instance, you would enter “make” if you have a makefile that builds your Objective-C code.

Figure 9.	 Specifying a Command

7.	Verify that the Menu Text, Command, Arguments, and Initial Directory are all appropriate for your custom toolbar button.

If your command requires command-line arguments to be specified each time it is invoked, select Prompt for Arguments. See “Using Argument Macros” in the Visual C++ Quick Reference for a list of macros that can be used in argument strings to automatically specify file names and directories.

8.	Count the entries in the Menu Contents list to verify that the command you just specified corresponds to the button you previously dragged to the toolbar.

Make any necessary adjustments by using the Move Up and Move Down buttons to reposition your command in the list as appropriate.

9.	If you want the output generated by your command to be sent to the Visual C++ output window (where it will be displayed on its own tab), select Redirect to Output Window.

10.	Choose Close in the Customize dialog box.


Bibliography and Reading List

The following books and articles are recommended for more reading on NeXT’s Portable Distributed Objects and Microsoft’s OLE2.

Brockschmidt, Kraig.  “Developing Applications with OLE2,” Microsoft Systems Developer Relations Group, September 13, 1994. Redmond, Washington: Microsoft Corporation, 1994.

Brockschmidt, Kraig. Inside OLE2. Redmond, Washington:  Microsoft Press, 1994. ISBN 1-55615-618-9

Courington, Bill, and Matt Morse. “The PDO object model,” OpenStep Solutions, 1995. Pittsfield, MA: SIGS Publications, 1995.

Gentry, Dennis. “Spreading the Wealth: DO and PDO,” NXApp,  Winter 1993. Redwood City, CA: NeXT Computer, 1993.

Raymor, Brian, and Randy Tidd. “Realities of Distributed Objects,” OpenStep Journal, Spring 1995. Redwood City, CA: NeXT Computer, 1995.

Raymor, Brian, and Randy Tidd. “Realities of Portable Distributed Objects,” OpenStep Journal, Summer 1995. Redwood City, CA: NeXT Computer, 1995.

OLE2 Programmer’s Reference series. Redmond, Washington: Microsoft Press, 1994. ISBN 1-55615-628-6 (v. 1). -- ISBN 1-55616-629-4 (v. 2)


Building and Linking the Visual C++ Calculator

The only thing to keep in mind when working with Objective-C objects packaged as a DLL is that you must dynamically link to the Microsoft Foundation Classes library.

In Visual C++, you specify this in the Project Settings dialog box:

1.	From the Project menu choose Settings.

The Project Settings dialog box appears.

2.	On the General tab, make sure that “Use MFC in a Shared Dll” is selected in the Microsoft Foundation Classes drop down list. This setting is the default.

3.	On the Link tab, specify your project-specific library modules (such as Calc.lib)

Figure 11.	 Specifying Object and Library Modules

4.	Click OK.

Having made your project settings as outlined above, you build and execute your project as described in the Microsoft Visual C++ documentation.


Building and Running the Excel Application

To build the Excel application under NEXTSTEP, use the following command line:

cc -o MyExcelApp main.m nxorb.m -lFoundation_s

Note that the file nxorb.m, which was discussed in “Writing Protocols,” is included in the command line. When you compile Objective-C programs that use NXConnection to talk to OLE objects through the ORB, you have to include the file nxorb.m in your client program. nxorb.m is available on the D’OLE release in the directory NextDeveloper/Libraries. It contains some helper classes the ORB needs to use NXDO.

Normally, if your application uses NXConnection, you must build it using nxorb. The one exception to this is if your application doesn’t use NSObjects.

To run the program, first make sure you are running machd, nmserver, and the NeXT ORB on your NT host machine. You then type the following command line on NEXTSTEP, where myHost is the name of the NT host machine to which you want to connect:

MyExcelApp myHost


Comparison of OLE and OpenStep Terminology

Though OLE/COM and OpenStep are both object models, their use of terminology varies significantly enough to sometimes be a source of confusion. The following are common terms and their different definitions under OpenStep and OLE.

Object

OLE:	In OLE an object is a unit of information that resides in a container’s compound document and whose behavior is constant no matter where it is located; the object’s behavior is defined by the object rather than by the compound document that holds it. A container application is any application that consumes compound document objects; a compound document is a self-contained programming unit that contains data of different formats.

OpenStep:	In Objective-C an object is a programming unit that groups instance variables (a data structure) and the methods (operations) that can use or affect that data.

The OLE definition of an object refers to a self-contained unit of data (such as a spreadsheet) that includes methods for operating on the data, and that one application can make available to another (for example, you can include a spreadsheet in a document and manipulate its characteristics from within the document). In this sense, the concept of an object is closely tied to the concept of an application; objects are things provided by and consumed by applications.

In Objective-C, a class defines objects of a particular type. An object of a particular class is said to be an instance of that class. The class declares the methods an object of that class can respond to. The group of methods that a class declares is called its interface.

Interface

OLE:	In OLE an interface is a grouping of semantically related functions through which one application accesses the services of another. Interfaces are the binary standard for component object interaction. OLE provides standard interfaces such as IDispatch and IUnknown, and developers can also create their own custom interfaces.

OpenStep:	In Objective-C an interface consists of the instance variables and methods a class makes public.

OLE doesn’t include a comprehensive term to describe what Objective-C developers would think of as a class’s interface (that is, the instance variables and methods a class makes public).

Probably the closest OpenStep equivalent to interfaces in OLE are Objective-C protocols. In Objective-C, a protocol is a declaration of a group of methods that aren’t associated with any particular class. Objective-C classes can declare themselves as conforming to a particular protocol or protocols; they can then provide an implementation for each protocol. Just as an Objective-C class can conform to several protocols, an OLE object may support several standard as well as custom interfaces, and it can override or add to a standard interface’s implementation. The difference is that while an Objective-C class typically has several other ingredients besides the protocols it conforms to (notably, it has its public methods and instance variables), in OLE, all of an object’s services are accessed through a set of interfaces.

Method

OLE:	In OLE a method is a member function that performs an action on an object. For example, a Document object might provide a Print method.

OpenStep:	In Objective-C a method is a procedure that can be executed, usually by an object. Unlike OLE, Objective-C doesn’t distinguish between methods that set or get an object’s characteristics (for example, methods to set and return a Text object’s font) and methods that perform non-attribute-related operations (such as a method to copy the contents of one Text object into another).

See Property.

Property

OLE:	In OLE a property represents an attribute of an object; each property has a pair of accessor functions that get and set the property’s characteristics. OLE also uses the term “property” to refer to member function pairs that set or return information about the state of an object. For example, a Font object might have a Size property.

OpenStep:	Objective-C doesn’t include the notion of properties per se, though methods that set or return an object’s “properties” are sometimes called accessor methods to distinguish them from other methods.


Compiling and Linking With the ORB

The following command can be used to compile the NSGetDllObject() code listed above:

gcc -g -dll -o color.dll *.m $NEXTROOT/NextDeveloper/Libraries/NSDllObject.o \ $NEXTROOT/NextDeveloper/Libraries/NSRegisterDll.o -lnextorb -framework Foundation

In addition to your own .m and .o files, and any frameworks that are required by your code, you must also link with the following when creating a DLL that contains the NeXT ORB (all are located in NextDeveloper/Libraries):

•	NSDllObject.o (which includes dllmain)

•	NSRegisterDll.o

•	nextorb.lib

Note that unless you explicitly create your own version of dllmain, you don’t actually have to include it in the list of object files, since the -dll option (which you must specify when creating a DLL) automatically includes it for you. For more information on dllmain, see “The DllMain Function.”


Compiling the DLL

Once you’ve written your Objective-C code you can compile it into a DLL. The gcc compiler supplied with D’OLE has a -dll switch that structures the resulting executable as a dynamic link library. If you’ve exported one or more functions as described in the previous section, the compiler will create export (.exp) and import library (.lib) files in addition to the .dll file. The export file is used by the linker when creating the DLL, while the import library is used by the Microsoft linker to resolve references to your DLL from within your Visual C++ (or other 32-bit language) code.

The following makefile compiles both Objective-C code (in .m files) and any C code (in .c files) needed to construct the .o files identified as O_FILES and then links them into a DLL named calc.dll:

O_FILES = calc.o

DLL_NAME = calc.dll

all: $(O_FILES)

gcc -g -dll -o $(DLL_NAME) $(O_FILES) \

$(NEXT_ROOT)NextDeveloper/Libraries/dllmain.o \

$(NEXT_ROOT)NextDeveloper/Libraries/NSRegisterDLL.o

.c.o .m.o :

gcc -g -c -o $@ $<


Component Object Model

COM is a specification for building modules of code (called objects) that determines how another application (that is, the object user) accesses and uses that code. The objects COM defines are independent from any particular tool or programming language. This independence is possible because COM is a binary standard; regardless of how an object is built, all users of that object see it with the same structures in memory.

These memory structures are tables of pointers to the member functions of the object (member functions in C++ are analogous to a class’s methods in Objective-C). COM calls each table an interface. An interface, in COM parlance, is a semantically related group of functions. Each interface in COM is identified with a specific name, and each name is prefaced with a capital “I.” By implementing a given interface, an object agrees to a specific contract implied by that interface, so a user of that object will know what to expect when calling functions in that interface.

Object users communicate with an object through a pointer to an interface, which gives them access to only the specific member functions of that interface. The user never has a pointer or access to the object as a whole. An object can implement as many interfaces as it wants, providing a different interface pointer—a different function table—for each supported interface.

All objects support the IUnknown interface. IUnknown is the base interface from which all other interfaces are derived. Its methods enable an application to obtain pointers to other interfaces supported by the same object and to manage the interface pointers obtained for an object. IUnknown also handles reference counting. When the reference count for a particular interface reaches zero, the interface implementation can be freed. When there are no more interface pointers on an object as a whole, the object itself can be freed.


Connecting to the ORB

To continue the switchboard analogy, connecting to the ORB is like picking up the phone and dialing the appropriate code to get an operator, who can then connect you to your ultimate destination. What constitutes the “appropriate code” depends on the environment you’re in. The ORB registers itself as “NEXTORB.OLE” in the name space of OLE objects on Windows machines, as “NEXTORB.NSDO” on an NSConnection, and “NEXTORB.NXDO” on an NXConnection. This syntax distinguishes between the old version of Distributed Objects, which prefixes class names with NX, and the new version of Distributed Objects available with the new Foundation Framework, which prefixes class names with NS. In each of the environments, you’d connect to the ORB as described below.

From Objective-C, using NEXTSTEP Release 3.3 or PDO Release 3.0:

server = [NXConnection connectToName:"NEXTORB.NXDO" onHost:hostname];

From Objective-C, using D’OLE.

server = [NSConnection proxyForRootObjectWithRegisteredName:@"NEXTORB.NSDO"

 	host:hostname]

From Visual Basic:

Set server = CreateObject ("NEXTORB.OLE")

The following graphics illustrate the process of connecting from an OLE Automation client to an Objective-C server and vice versa. The rectangular shapes represent OLE Automation objects, and the round shapes represent PDO objects. Solid objects represent the original, “real” object, and the hollow objects are proxies that reference the original object. The “combination” object shown in the Object Request Broker box represents the ORB’s dynamic translation of messages between the two environments, in which one object type (OLE or PDO) effectively masquerades as the other.

Figure 20.	 Connecting from an Objective-C Client to an OLE Automation Server

Figure 21.	 Connecting from an OLE Automation Client to an Objective-C Server

How you acquire access to objects through the ORB once you’ve connected to it is discussed in the context of the examples described below.


Connecting to the ORB Programmatically from Visual Basic

Using the orbreg tool is the most common way of giving OLE-capable applications access to your Objective-C objects. However, you can also connect to the ORB programmatically, and through that connection gain access to Objective-C objects on a NEXSTEP server.

For example, to programmatically connect to the Objective-C Calculator object from Visual Basic, you’d use the syntax:

‘ Connect to the ORB

Set server = CreateObject ("NEXTORB.OLE")

‘ Access the object

Set calcObj = server.connectTo("Calculator", "NXDO", "MyHost")

where Calculator is the name of your Objective-C object running on a NEXTSTEP server, NXDO is the protocol you’re using, and MyHost is the NEXTSTEP host to which you’re connecting.

If you instead use the orbreg tool as described above to register your Objective-C object in the Windows registration database, you can then access that object from Visual Basic without explicitly connecting to the ORB, as follows:

Set calcObj = CreateObject("MyCalc")

where MyCalc is the name by which you registered your Objective-C object in the Windows registration database using orbreg. Note that when you access Objective-C objects programmatically instead of by using orbreg, the object is not put in the Windows registration database.

For instructions on viewing information in the Windows registration database, click here.


Constructing the OLE Object

When working with D’OLE, you don’t have to concern yourself with creating OLE objects in your Objective-C code. Instead, the ORB adds the necessary OLE interfaces to make your regular Objective-C objects accessible as OLE objects. Thus, all that you need to do to make the Calc object usable from Visual Basic is to add code making the Calc object available to other applications, and to register the connection name and server with the Mach name server (this has the effect of making the Calc object available to external applications such as the ORB). Because the ORB knows how to deal with Objective-C objects, C cover functions like those that were written for the DLL in "Writing C Cover Functions" are not needed.

In order to make Calc usable by applications that support OLE Automation, you only need add a main routine, along the lines of the following example.

int main(int argc, char *argv[])

{

id calcObj;

id conn;

[NXPort worryAboutPortInvalidation];

calcObj = [[Calc alloc] init];

conn = [NXAutoreleaseConnection registerRoot:calcObj

withName:"Calculator"];

if (conn) {

fprintf(stderr, "Calculator registered.\n");

[conn run];

[calcObj free];

} else {

fprintf(stderr,"Couldn't register calculator. Exiting.\n");

 	exit(-1);

}

exit(0);

}

Note: This example uses NXAutoreleaseConnection. In order to communicate with objects running on a NEXTSTEP Release 3.3 system, you must have the version of the Foundation Framework that is supplied with the Enterprise Objects Framework Release 1.1 installed on your NEXTSTEP computer. This is shipped as a patch with D’OLE—see the release notes for installation instructions. The version of the Foundation Framework that is shipped with NEXTSTEP Developer Release 3.3 is not capable of communicating with the NeXT ORB.

The above code advertises the Calc object’s services using the name “Calculator.” To register the Calc object in the Windows registration database on NT, type the following in an NT command shell:

orbreg add -olename "MyCalc" -doname "Calculator" -protocol "NXDO" -host "tahoe"

This command registers the Objective-C object Calculator as an OLE object named “MyCalc”. The command further states that the Objective-C object is running on a machine named tahoe and is based upon an NXConnection. The syntax for the orbreg command is described in the following section.

Having completed the above two steps, Windows NT applications can now use OLE Automation to access the Calc object running on NEXTSTEP Release 3.3.


Constructing the Visual C++ Front End

After creating a DLL that implements the calculator “engine,” you can now construct a Visual C++ front end that uses the Objective-C methods encapsulated within the DLL.

Here is the user interface for the calculator, created in Visual C++:

Figure 10.	 The Visual C++ Calculator

Using the MFC ClassWizard, the +/-, decimal point, and equals sign keys have been mapped to C++ member functions (OnChangeSign, OnDecimal, and OnEquals, respectively). The remaining keys need to be mapped in groups—all of the numeric keys, for instance, should be mapped to a single member function. Since the MFC ClassWizard currently does not allow you to map more than one button to a single member function, the map table entries for these buttons have been made manually. The message map for the Visual C++ calculator is as follows:

BEGIN_MESSAGE_MAP(CCalcDialog, CDialog)

//{{AFX_MSG_MAP(CCalcDialog)

ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON1, IDC_BUTTON10, OnDigit)

ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON11, IDC_BUTTON14, OnFunction)

ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON16, IDC_BUTTON17, OnClear)

ON_BN_CLICKED(IDC_BUTTON18, OnEquals)

ON_BN_CLICKED(IDC_BUTTON19, OnDecimal)

ON_BN_CLICKED(IDC_BUTTON20, OnChangeSign)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

The actual member functions themselves are simple re-implementations of the Objective-C methods implemented in the original NEXTSTEP calculator, with calls to the appropriate DLL cover functions replacing invocations of those methods in the Calc class. For instance, here is the C++ implementation of the NEXTSTEP calculator’s clear: method, which is invoked whenever the user presses either the Clear or Clear All buttons on the calculator:

void CCalcDialog::OnClear(UINT nID)

{

m_enteredValue = 0.0;

m_result = 0.0;

m_wasValueEntered = NO;

if(nID == IDC_BUTTON16)	// BUTTON_16 is CLEAR ALL

clearAll(calcObj);

m_decimalEntered = NO;

CCalcDialog::DisplayValue(m_enteredValue);

m_lastKeyWasOperator = NO;

}

The clearAll() function used in this example covers Calc’s clearAll method. This cover function is defined in Calc.m as follows:

void clearAll(void *aCalc)

{

[(id)aCalc clearAll];

}


Contents

Copyright Notice

Introduction

Development Scenarios

The OLE/COM and OPENSTEP Object Models

The OLE/COM Object Model

Component Object Model

OLE

The OpenStep Object Model

Creating a PDO Object

Messaging a PDO Object

How OLE Automation and OpenStep Objects Interact Programmatically

Comparison of OLE and OpenStep Terminology

Getting Started

Porting a NEXTSTEP Calculator to NT

Implementation Options

Structuring Your Application

Overview of Source Files in the Visual C++ Calculator Example

Implementing the Calculator Back End as a DLL

Dynamic Link Libraries

The DllMain Function

Guidelines for Writing Your Objective-C Code

Objective-C and Memory Management

Editing Objective-C Source Files in Visual C++

Writing C Cover Functions

Exporting Your Cover Functions

Compiling the DLL

Adding Custom Toolbar Buttons in Visual C++

Constructing the Visual C++ Front End

Building and Linking the Visual C++ Calculator

Debugging Techniques

Inspecting Objective-C Objects

Setting Breakpoints by Name

Stepping into objc_msgSend

Using the NeXT Object Request Broker

Connecting to the ORB

Which ORB?

Implementing the Calculator Using OLE Automation

Overview of Source Files in the Visual Basic Calculator Example

Constructing the OLE Object

Using the orbreg Command

Connecting to the ORB Programmatically from Visual Basic

Viewing Information in the Windows Registration Database

Writing the Visual Basic Front End

Invoking the OLE Object’s Methods

How the ORB Translates Messages

Using OLE Automation from NEXTSTEP

Overview of Source Files in the Excel Example

Writing Protocols

Accessing Excel through the ORB

Setting and Getting Properties

Building and Running the Excel Application

Object Disposal and the NeXT ORB

Using Distributed OLE

Enumerating Collections

Enumerating Collections Using Visual Basic 3.0

Enumerating OLE Collections With Objective-C Code

The Value Property

Creating DLLs that Vend OLE Automation Objects

The NSGetDllObject Function

Compiling and Linking With the ORB

Registering Your Vended Objects

Debugging Your DLL

Source Code for Examples

Visual C++ Calculator Source Code

Visual Basic Calculator Source Code

Excel Spreadsheet Enumerator

Bibliography and Reading List


Copyright Notice

NeXT and the publishers have tried to make the information contained in this manual as accurate and reliable as possible, but assume no responsibility for errors or omissions. They disclaim any warranty ofany kind, whether express or implied, as to any matter whatsoever relating to this manual, including without limitation the merchantability or fitness for any particular purpose. In no event shall NeXT or thepublishers be liable for any indirect, special, incidental, or consequential damages arising out of purchase or use of this manual or the information contained herein. NeXT will from time to time revise thesoftware described in this manual and reserves the right to make such changes without obligation tonotify the purchaser.

Copyright © 1995 by NeXT Computer, Inc., 900 Chesapeake Drive, Redwood City, CA 94063.

All rights reserved.

No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without the prior written permission of the publisher or copyright owner. Printed in the United States of America. Published simultaneously in Canada.

NeXT, the NeXT logo, NEXTSTEP, the NEXTSTEP logo, D’OLE, Application Kit, Enterprise Objects, Interface Builder, and Workspace Manager are trademarks of NeXT Computer, Inc. Microsoft and Visual Basic are registered trademarks and Visual C++, Windows, and Windows NT are trademarks of Microsoft Corporation. PostScript is a registered trademark of Adobe Systems, Incorporated. Unix is a registered trademark of UNIX Systems Laboratories, Inc. ORACLE is a registered trademark of Oracle Corporation, Inc. SYBASE is a registered trademark of Sybase, Inc. All other trademarks mentioned belong to their respective owners.

Restricted Rights Legend: Use, duplication, or disclosure by the Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.227-7013 [or, if applicable, similar clauses at FAR 52.227-19 or NASA FAR Supp. 52.227-86].

This manual describes D’OLE, version 3.5

Written by Katie McCormick and Greg Wilson

Illustrations by Peggy Kunz

With help from Paul Marcos and Kresten Krab Thorup

Technical publications management by Ron Hayden


Creating DLLs that Vend OLE Automation Objects

"Getting Started" showed you how to create an Objective-C DLL that you could link into a Visual C++ application. It required you to create and export C cover functions for each of your Objective-C methods. While this is perhaps the optimal solution from a performance standpoint, the added overhead of having to create and maintain these cover functions makes this solution less than desirable in many situations.

For situations where the ultimate in performance is not required, you can link the ORB into your DLL along with your Objective-C objects. This not only eliminates the need for any cover functions, it also allows you to communicate with your DLL as if it were a collection of OLE Automation objects. Thus, the resulting DLL can be used by applications such as Visual Basic and Microsoft Excel.

Note: The DLLs produced using this technique are 32-bit; they can only be used with applications that can work with 32-bit DLLs. This includes Visual Basic 4.0, and 32-bit versions of Microsoft Excel, among others. 32-bit DLLs will not work with 16-bit applications such as Visual Basic 3.0.

As an example, the following is a short Visual Basic for Applications (VBA) subroutine that obtains a ColorList object from a DLL, enumerates the objects in the list, and places those objects into consecutive cells in an Excel spreadsheet. Note that no special operations are needed to access the Objective-C objects; because of the ORB, all of your objects can be treated exactly like OLE Automation objects.

Sub GetColors()

Dim ColorList As Object, color As Object

Dim i As Integer

'get the colorlist from the DLL

Set ColorList = CreateObject("ColorList")

If ColorList Is Nothing Then

    MsgBox ("Unable to get ColorList object"): End

End If

'enumerate the color list

i = 1

For Each color In ColorList

    Cells(i, 1).Value = color.Value

    i = i + 1

Next color

End Sub

Creating a DLL that includes the ORB is a simple process. Given a set of Objective-C objects that you wish to “vend” as OLE Automation objects, you:

•	Write a single function, NSGetDllObject(), that returns your Objective-C objects upon request.

•	Compile and link your code with the ORB.

•	Register those objects that you want to vend using orbreg.

•	Write and execute your client software.

As illustrated by the sample code above, the final step—that of writing and executing your client software—is a simple process that is entirely application-specific. The preceding steps are discussed in the following sections.


Creating a PDO Object

To create a PDO object, all you need to do is use the NXConnection class (or NSConnection, for the new DO available with the new Foundation Framework) to register an Objective-C object with the network name server. When you do this, a Mach port is created to serve the connection, and a unique object identifier (roughly equivalent to the GUID you generate for an OLE object) is dynamically generated for the object. This process is called “vending an object.”

To vend an object in PDO, you use the following syntax:

id myServer = [[PriceServer alloc] init];

id myConnection = [NXConnection registerRoot: myServer

withName: "stockPriceServer"];

This code instantiates a price server, then registers that server with the network name server as stockPriceServer.

Note: This syntax shows so-called “old” DO, which uses the class name prefix “NX”. The new DO, which is available with the new Foundation Framework, uses the prefix “NS”, as in NSConnection. The syntax is also slightly different; for more information see the NSConnection class specification in Help.


Debugging Techniques

Since gdb is currently unavailable for Windows NT, you must use the Microsoft Visual C++ debugger to debug your Objective-C code on NT. The gcc compiler on NT generates CodeView debugging information that can be used by the Microsoft Visual C++ debugger or by any other Windows debugger that supports CodeView symbols.

The Visual C++ debugger is not programmable or otherwise extensible and thus there is no way to provide the same level of debugging support as in NEXTSTEP. Some of the limitations (as compared to gdb under NEXTSTEP or PDO) are as follows:

•	You can’t invoke functions from the command line.

•	You can’t set breakpoints by method name. However, you can set breakpoints on methods using the equivalent function name generated by the compiler.

•	You can’t directly inspect an Objective-C object. However, there are ways to indirectly inspect objects.

•	When you view an object (whether global or local) you can only look at a snapshot of the object. You don’t get its superclass information by default. You can only see the scope of the one class unless you explicitly cast it.

•	If something is typed as id, you have to explicitly cast it to display information about it. Because of this, you may want to use static typing whenever possible.

•	You can’t use the debugger to debug NSThreads.

You can step into message expressions by stepping into objc_msgSend calls in the Disassembly panel. Although this technique (explained below) is not simple, it is effective. Otherwise, you can only single-step and set break points on a method (though once you set a break point on a method you can step through it).

To debug your Objective-C code in Visual C++:

1.	Build a version of your program that includes debugging information by including -g on the gcc command line (this is the makefile default).

2.	Open the resulting .exe file in Visual C++, either by dragging it onto the Visual C++ icon or by explicitly opening it from within Visual C++ by choosing Open from the File menu. If you have problems with this step, simply restart the debugger from within Visual C++.

3.	Open your source file in Visual C++.

4.	Set any breakpoints.

5.	Choose Go from the Debug menu.

The display changes to include the Locals and Watch windows. The Locals window lets you examine local variables, while the Watch window lets you examine specific variables and expressions.


Debugging Your DLL

Even though your DLL is invoked from within Microsoft Excel, you can use the Visual C++ debugger to debug your Objective-C DLL. For instance, the following procedure describes how to set a breakpoint:

1.	Start Visual C++.

2.	Choose Open from Visual C++’s File menu.

The Open dialog box appears.

3.	In the Open dialog box, navigate to the directory where Excel is installed, and select Excel.exe.

4.	Choose Settings from Visual C++’s Project menu, then click the Debug tab in the dialog that appears.

5.	Under Additional DLLs, specify the DLL that you want to debug (for example, c:\colortest\color.dll) and click OK.

6.	Choose Open from Visual C++’s File menu.

The Open dialog box appears.

7.	In the Open dialog box, navigate to the directory where the source files for your DLL are located and select the source (.m) file in which you want to set the breakpoint. For example, navigate to c:\colortest and select color.m.

8.	Set the desired breakpoint in the opened source file.

9.	Choose Go from the Debug menu.

Microsoft Excel starts up, under control of the Visual C++ debugger. You’ll be warned that Excel.exe doesn’t have debug information associated with it; you can ignore these warnings.

10.	Load your spreadsheet, and execute it.

Once execution reaches the breakpoint you set, you can proceed as you would when debugging any other piece of Objective-C code. For tips on debugging Objective-C code using the Visual C++ debugger, see “Debugging Techniques.”


Development Scenarios

D’OLE gives you great flexibility in how you develop and deploy applications. This section describes some of the typical scenarios for which D’OLE is particularly well-suited.

One of the most basic scenarios you might use would be to link a dynamic link library (DLL) containing Objective-C code into a Windows client application running on NT. This is illustrated in Figure 1. The rectangular “lozenge” shapes represent OLE Automation objects, while the “poker chip” shapes represent Objective-C objects. For example, this figure could represent an application in which a graphical user interface created in Visual C++ is linked to a DLL that contains the primary application logic, as written in Objective-C. In the example shown in Figure 1, OLE is being used to communicate between objects.

Figure 1.	 Linking an Objective-C DLL into a Windows Application

The example shown in Figure 1 can be modified so that instead of linking a Windows client application into an Objective-C DLL, the client application instead connects to a D’OLE server application running on a remote machine. Figure 2 shows this scenario. For example, you can have an application with a user interface created in Visual Basic running on NT, connected to a NEXTSTEP server application.

Figure 2.	 NT Client and D’OLE Server

Figure 3 illustrates distributed OLE, where an application running on one NT machine can use OLE Automation to communicate through the NeXT Object Request Broker (ORB) to an application running on another NT machine. For example, you can manipulate Excel on one NT machine from a Visual Basic application running on another NT machine.

Figure 3.	 Distributed OLE

Figure 4 shows a dynamic link library (DLL) containing Objective-C code linked into a Windows application running on NT. The Objective-C code is using Enterprise Objects Framework Release 1.1 for NT to communicate with a database server running on a remote machine. This is a so-called “two-tier” architecture.

Figure 4.	 Using D’OLE with Enterprise Objects Framework

Figure 5 shows a three-tier application architecture. Multi-tiered applications are possible because of D’OLE’s ability to communicate with other D’OLE servers on the network. In Figure 5, a Windows client application is connected to a D’OLE server application running on a remote machine. The server application is in turn using the Enterprise Objects Framework to communicate with a database server on a third machine.

Figure 5.	 NT Client, D’OLE Server, Using Enterprise Objects Framework


Dynamic Link Libraries

DLLs are Windows-based program modules that can be loaded and linked at run-time. DLLs are loaded once into memory, where they can be shared by multiple applications. Each process has its own copy of all of the DLL’s static data and objects.

You frequently interact with a DLL through a set of published function calls (you can also interact with some DLLs using OLE Automation; see “Creating DLLs that Vend OLE Automation Objects”). Because of this, when writing DLLs in Objective-C you need to create C “cover” functions for each Objective-C method that you wish to be able to invoke. Typically, these are simple one-line functions that just invoke the corresponding method and return the method result (if any).

You perform the following general steps to create a DLL using the gcc compiler provided with D’OLE:

1.	Write the DllMain() function needed to properly initialize your DLL (if needed; you can use the dllmain.o file provided with D’OLE if no special initialization is needed).

2.	Write the Objective-C classes and methods that make up your DLL.

3.	Write C cover functions for each method you want to access from outside the DLL, and export those cover functions using dllexport.

4.	Construct and execute a makefile to compile and link your DLL.

The following sections describe each of the above steps in detail.


Editing Objective-C Source Files in Visual C++

You can load and edit your Objective-C source files in the Visual C++ environment just like any text file. While Visual C++ can’t be configured to automatically enable syntax coloring in all files with a .m extension, you can enable syntax coloring for individual Objective-C source files. To do so, load the .m file (or make it the active window if the file is already loaded) and then select Properties from the Edit menu. In the Language list box on the Text File Properties page that appears, select C or C++ to set syntax coloring for that source file.

Figure 7.	 Enabling Syntax Coloring

Note that you have to repeat this procedure for each .m file that you edit. Also, if you close and then reopen the file, you have to re-enable syntax coloring.

For more information on syntax coloring, see “Changing Syntax Colors” in Visual C++ Books Online.


Enumerating Collections

Writing code to individually process the elements of an array is a simple task, since the indexes by which you access the array elements are limited to some known range of integers. When dealing with OLE Automation collections, however, you can’t always access the elements by a simple numeric index. Fortunately, most of the application-development tools that you use to work with OLE Automation objects provide a mechanism whereby you can sequentially access—or enumerate—each of the elements of a collection.

Visual Basic’s For Each statement (available in Visual Basic 4.0 and Visual Basic for Applications) allows you to enumerate the elements of an OLE collection or array. The NeXT ORB allows you to use this statement to similarly enumerate any Objective-C object that responds to objectEnumerator (see the NSEnumerator class description in the Foundation Framework Reference). Among objects that respond to the objectEnumerator method are the Foundation collections NSArray, NSDictionary, and NSSet (along with their mutable variants). Thus, using Visual Basic for Applications (VBA), you can access the contents of an NSSet or NSDictionary as easily as you can access each of the worksheets in an Excel workbook.

Using Objective-C, you would enumerate a Foundation collection myCollection like this:

enum = [myCollection objectEnumerator];

while (x = [enum nextObject]){

 	.

 	.

 	.

}

In Visual Basic, the equivalent code is as follows:

for each x in myCollection

 	.

 	.

 	.

next

The following Visual Basic for Applications subroutine illustrates the use of VBA’s For Each statement to enumerate a Foundation collection of NSStrings.

Sub GetColors()

Dim nextORB As Object

Dim colorList As Object, color As Object

Dim i As Integer

‘connect to the color server

Set nextORB = CreateObject("NEXTORB.OLE")

Set colorList = nextORB.connectTo("Color", "NXDO", "kushu")

If colorList Is Nothing Then

MsgBox ("Unable to connect to Color server"): End

‘enumerate the color list

i = 1

For Each color In colorList

Cells(i, 1).Value = color.Value

i = i + 1

Next color

End Sub


Enumerating Collections Using Visual Basic 3.0

Although Visual Basic 3.0 doesn’t support the For Each statement, you can still enumerate Foundation collections by invoking the Objective-C objectEnumerator and nextObject methods directly from Visual Basic. This results in code that is only slightly more complicated. For instance, you could implement the previous example in Visual Basic 3.0 as follows:

‘connect to the color server

Set nextORB = CreateObject("NEXTORB.OLE")

Set colorList = nextORB.connectTo("Color", "NXDO", "kushu")

If colorList Is Nothing Then

MsgBox ("Unable to connect to Color server"): End

‘create an enumerator object for the color list

Set enum = colorList.objectEnumerator

‘enumerate the color list

Set color = enum.nextObject

If color Is Nothing Then MsgBox ("No colors in the color list"): End

Do

List1.AddItem color    ‘takes advantage of the fact that

           ‘the ORB knows how to deal with

           ‘NSStrings

Set color = enum.nextObject

If color Is Nothing Then Exit Do

Loop


Enumerating OLE Collections With Objective-C Code

The NeXT ORB, operating bidirectionally as it does, allows you to enumerate OLE collections using Objective-C code as easily as you can enumerate Objective-C collections from Visual Basic. Through the ORB, you can use the Objective-C objectEnumerator and nextObject methods to enumerate any OLE object that supports IEnumVARIANT just as if it were a Foundation collection. This technique is illustrated in the following code excerpt, which uses NSLog to display the names of an Excel workbook’s worksheets.

wsEnum = [worksheets objectEnumerator];

while (worksheet = [wsEnum nextObject]){

[worksheet setProtocolForProxy:@protocol(ExcelWorksheet)];

 	sheetName = [[worksheet getOLEPropertyNamed:@"name"]

 	nonretainedObjectValue];

 	NSLog(@"%@", sheetName);

}

In order for the above code to work, the ExcelWorksheets protocol must include a prototype for the objectEnumerator method, as shown here:

@protocol ExcelWorksheets <OLEObject>

- (unsigned)count;

- (NSEnumerator *)objectEnumerator;

@end

Note: Be sure that you write a protocol for each OLE object you receive from the NeXT ORB. Failure to do this will result in exceptions being raised whenever you send a message to the OLE object from Objective-C. For more discussion of this subject, see “Writing Protocols.”

For a complete listing of the example from which the above excerpts were taken, see "Excel Spreadsheet Enumerator."


Excel Spreadsheet Enumerator

The following is the Objective-C source code for a program that, given the name of a Windows NT server (which is running machd, nmserver, and the NeXT ORB) and the full path to a pre-existing Microsoft Excel .XLS file, will list the names of the worksheets in the file. The techniques used in this example are discussed in “Enumerating Collections.”

ExcelProtocols.h

#import <foundation/NSObject.h>

#import <foundation/NSValue.h>

#import <remote/NXProxy.h>

#define NSVal(x) ({ typeof(x) __y = x; [NSValue value:(void*)&__y withObjCType:@encode(typeof(x))]; })

@protocol ExcelChart <OLEObject>

@end

@protocol ExcelCharts <OLEObject>

- (id <ExcelChart>)chart;

@end

@protocol ExcelRange <OLEObject>

@end

@protocol ExcelWorksheet <OLEObject>

- (id <ExcelCharts>)chartObjects:(int)num;

- (id <ExcelRange>)range:(NSString*)range;

@end

@protocol ExcelWorksheets <OLEObject>

- (unsigned)count;

- (NSEnumerator *)objectEnumerator;

@end

@protocol ExcelWorkbook <OLEObject>

- (id <ExcelWorksheets>)worksheets;

- (id <ExcelWorksheet>)worksheets:(int)number;

@end

@protocol ExcelWorkbooks <OLEObject>

- (id <ExcelWorkbook>)open:(NSString*)name;

- (id <ExcelWorksheets>)worksheets;

@end

@protocol ExcelApp <OLEObject>

- (id <ExcelWorkbooks>)workbooks;

- (id <ExcelWorksheets>)open:(NSString *)name;

- (void)quit;

@end

main.m

// compile with: cc -g -Wall -o excel main.m nxorb.m -lFoundation_s

//

// Pass the name of NT host and the name of the Excel spreadsheet as arguments

// For example: excel Tahoe d:\\test\\sample.xls

//

#include <stdio.h>

#import <foundation/NSAutoreleasePool.h>

#import <foundation/NSUtilities.h>

#import <foundation/NSException.h>

// for NXDO protocols

#import <nxorb.h>

// for Excel protocols

#import "ExcelProtocols.h"

int main (int argc, char **argv)

{

id <NEXTORB> orb;

id <ExcelApp> excelApp;

id <ExcelWorksheets> worksheets;

id <ExcelWorksheet> worksheet;

id <ExcelWorkbooks> workbooks;

id <ExcelWorkbook> workbook;

NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];

NSString *hostName, *fileName, *sheetName;

NSEnumerator *wsEnum;

if (argc == 3){

hostName = [NSString stringWithCString:argv[1]];

fileName = [NSString stringWithCString:argv[2]];

} else {

NSLog (@"Usage: excel host_name file_name");

return 1;

}

// connect to the ORB

orb = (id <NEXTORB>)[NXConnection connectToName:"NEXTORB.NXDO"

onHost:[hostName cString]];

if (!orb){

NSLog (@"Cannot connect to host %@\n; is the NeXT ORB running?",

hostName);

return 1;

}

[orb setProtocolForProxy:@protocol(NEXTORB)];

// start up Excel

excelApp = [orb objectWithRegisteredName:@"Excel.Application"

protocol:@"OLE" host:hostName];

if (!excelApp) {

NSLog (@"Cannot get Excel.Application");

return 1;

}

[excelApp setProtocolForProxy:@protocol(ExcelApp)];

// open the file and get the worksheets collection object

NS_DURING

workbooks = [excelApp workbooks];

[workbooks setProtocolForProxy:@protocol(ExcelWorkbooks)];

workbook = [workbooks open:fileName];

[workbook setProtocolForProxy:@protocol(ExcelWorkbook)];

worksheets = [workbook worksheets];

[worksheets setProtocolForProxy:@protocol(ExcelWorksheets)];

NS_HANDLER

NSLog(@"Error opening %@ on host %@", fileName, hostName);

return 1;

NS_ENDHANDLER

// enumerate the worksheets, and print out the name of each sheet

wsEnum = [worksheets objectEnumerator];

while (worksheet = [wsEnum nextObject]){

[worksheet setProtocolForProxy:@protocol(ExcelWorksheet)];

sheetName = [[worksheet getOLEPropertyNamed:@"name"]

nonretainedObjectValue];

NSLog(@"%@", sheetName);

}

[excelApp quit];

[autoreleasePool release];

return 0;

}


Exporting Your Cover Functions

Having written the C functions that cover each of your Objective-C methods, you need to export them so that the linker will be aware of the contents of your DLL. You do this using __declspec and dllexport, as documented in the Visual C++ literature.

Note: the __declspec and __cplusplus identifiers start with two underscores. __GNUC__ starts and ends with two underscores.

The preprocessor that is supplied with D’OLE correctly handles __declspec and dllexport. While the preprocessor is running, it defines the __GNUC__ identifier, allowing you to conditionally include portions of your program depending on which compiler—the gcc compiler, or a third-party compiler such as Visual C++—is processing it. Similarly, Visual C++ defines __cplusplus while it is running. These identifiers allow you to construct header files that can be imported into both your Objective-C code and your Visual C++ code. This is illustrated in the following header file, which defines the contents of the Calc DLL:

//

// integrate.h

//

#ifdef __cplusplus

extern "C" {

#endif

__declspec(dllexport) void *createCalcObject(void);

__declspec(dllexport) void *destroyCalcObject(void *);

__declspec(dllexport) void clearAll( void*);

__declspec(dllexport) double performOperationWith(void *, double);

__declspec(dllexport) double setOperationForValue(void *, int, double);

__declspec(dllexport) void changePendingOperation(void *, int);

#ifdef __cplusplus

};

#endif

When the above is processed by the gcc preprocessor, only the __declspec lines are passed on to the compiler, resulting in a definition for each function. When the Visual C++ compiler processes it, however, the entire text is passed to the compiler, thereby informing the compiler that each of the named C functions is declared elsewhere.

For more information on __declspec and dllexport, see “Extended Attribute Syntax” in Visual C++ Books Online. “Linkage to Non-C++ Functions” in the same Visual C++ on-line document contains information on linking to external C functions.


Getting Started

This chapter introduces the D’OLE programming environment by showing you how to port a small Calculator application from NEXTSTEP to Windows NT.

The chapter focuses on the strategy and tools that you use to move existing software projects from NEXTSTEP to Windows NT, or to create new applications from scratch using Objective-C running on Windows NT. Because of this focus, the chapter presents an application that represents a relatively simple scenario: linking an Objective-C dynamic link library (DLL) into an NT application. To take advantage of the OLE Automation support provided by D’OLE, you need to use the NeXT Object Request Broker (ORB), which is described in "Using the NeXT Object Request Broker."


Guidelines for Writing Your Objective-C Code

The code you write using D’OLE follows the same rules and guidelines that apply to all Objective-C code. Your code can take advantage of the kits and frameworks supplied with D’OLE, including the Foundation Framework and the Enterprise Objects Framework. (see the NextDeveloper/Headers, NextDeveloper/Libraries, and NextLibrary/Frameworks directories for the Objective-C headers and libraries that are included with D’OLE.)


How OLE Automation and OpenStep Objects Interact Programmatically

The OLE/COM and OpenStep object models have different ways of creating and registering objects that can be accessed by other applications. OLE Automation objects reside in the Windows registration database, while PDO objects are registered with the Mach Name Server. Objects that support OLE Automation can send messages to each other using the IDispatch interface, while PDO objects can send messages to each other using the methods defined in each class’s public interface. Because of the differences between the OLE/COM and OpenStep environments, without an intermediary they’d have no way of talking to each other.

D’OLE uses the NeXT Object Request Broker (ORB) to mediate between the OLE Automation and OpenStep environments. The ORB is able to dynamically translate messages sent from one environment to the other; hence, OLE and PDO objects can speak to each other in their native languages. Accessing the objects in one environment from the other requires only minor changes to your code; this is described in more detail in “Using the NeXT Object Request Broker.”


How the ORB Translates Messages

The ORB rewrites methods being dispatched from OLE to Objective-C into a colon-separated selector, with the specified method name at the beginning. For example, the Visual Basic statement:

x.foo (a, b, c)

is translated by the ORB into the equivalent of:

[x foo:a :b :c].

To access methods with more complicated signatures, two interfaces are available.  If your OLE system supports named arguments, they will be used to construct the selector. For example:

x.foo (a, withArg:=b, and:=c)

translates into

[x foo:a withArg:b and:c].

The following table gives examples of how the ORB translates Objective-C messages into their OLE equivalents, and vice-versa.

Objective-C	OLE equivalent

[app value] 	app.value

[sheet range:"B2"]	app.range("B2")

[book open:"c:/test.xls" :YES]	app.open("c:/test.xls", TRUE)

[book open:@"c:/~" :YES]	app.open("c:/~", TRUE)

[obj do:"foo" times:2]	obj.do ("foo", times:=2)

If selector arguments beyond the first are present, they are translated into named arguments.

Conversion of Arguments

The NeXT ORB automatically converts strings into either NSStrings or char*’s, as the receiver expects them. If the receiver expects any kind of Objective-C object as an argument, and anything but an OLE Automation object is given, the ORB translates the argument into an NSString. There is no support for by-reference arguments, and the only kind of pointer arguments allowed are strings and OLE objects.

While you can pass an object to Visual Basic, Visual Basic can’t pass objects back (this is a limitation of Visual Basic 3.0; Visual Basic for Applications does not have this limitation):

Set x = CreateObject("MyServer")

Set o = x.GetObject

x.PassObject o ‘ NO!! This won’t work.


Implementation Options

In this example, you will put your Objective-C code in a DLL and then link it into a Visual C++ application. However, depending on the needs of your particular application, D’OLE allows you to package your Objective-C code in a number of different ways:

•	As an executable (.exe) file. This option works for applications that don’t need to interoperate with code written using a third-party software development environment (such as Microsoft’s Visual C++). In other words, you can write, compile, and run your Objective-C program completely on NT.

•	As a DLL. If you’re working with a third-party 32-bit development environment to implement the client portion of your application (such as the user interface), you can compile your Objective-C code into a 32-bit DLL, which can then be used by your third-party development environment.

•	As a 16-bit OLE server. Using the NeXT Object Request Broker (ORB) supplied with D’OLE, you can register your Objective-C objects so that they become accessible as OLE objects. This allows you to construct your user interface using a 16-bit development environment such as Microsoft’s Visual Basic 3.0, which doesn’t allow access to 32-bit DLLs.

•	As a 32-bit in-process or local OLE server. By combine the prior two packaging methods, you wind up with a 32-bit DLL that uses OLE Automation to communicate with client applications. Note that this technique can only be used with client development environments that can communicate with 32-bit DLLs.


Implementing the Calculator Back End as a DLL

While the Windows NT version of the calculator could be written entirely in Objective-C, that would require that you write code to handle all of the UI tasks (screen drawing, detecting mouse clicks, and so forth). A simpler and more interesting— for illustrative purposes—solution is to implement the user interface in Visual C++. The Objective-C code can then either be compiled as a DLL, or it can be compiled and registered to work through the ORB as an OLE object. The following sections illustrate the DLL solution.


Implementing the Calculator Using OLE Automation

This example application uses a combination of Visual Basic and Objective-C, and unlike the Visual C++ example in "Getting Started," it uses OLE Automation from the ORB to access Objective-C objects. The basic steps in this example are as follows:

•	Write your Objective-C code

•	Compile and run your Objective-C code

•	Register your Objective-C object in the Windows registration database on NT (you can optionally connect to the ORB programmatically)

•	Create a graphical user interface in Visual Basic

•	Write Visual Basic procedures to access your Objective-C object

•	Run your project in Visual Basic

The Calc object that was accessed as a DLL in "Getting Started" can easily be written so that it can be accessed as an OLE object. The methods implemented by the Calc object can then be invoked from applications written in a language such as Visual Basic, using OLE Automation.


Inspecting Objective-C Objects

You use the text field in the Watch window to examine and change variable values. Using this field, you can inspect an Objective-C object by first casting the object to a C structure and then examining the structure’s members. Similarly, by typing the appropriate information in the QuickWatch field, you can retrieve an object’s value, its class, and its data members.

For example, suppose you have following simple application:

#import <Foundation/Foundation.h>

int main (){

id pool = [NSAutoreleasePool new];

int num = 33;

char name[256] = "Lois Lane";

id firstString;

NSString *secondString = @"Clark Kent";

id nsnum;

firstString = [NSString stringWithCString:name];

nsnum = [NSNumber numberWithInt:num];

NSLog(@"The string is %@\n", firstString);

NSLog(@"The number is %@\n", nsnum);

[pool release];

return 0;

}

Typing the following sample statements in the Watch text field would have the effects listed in the following table:

Watch Statement	Effect

nsnum	Returns the location in memory where the object was allocated.

(NSString*)firstString	Returns the statement +(NSString*)firststring = <hex address>. The plus sign indicates that the statement can be expanded to display additional information. To expand a statement, double-click it.

*(NSString*)firstString	Has the same effect as the previous statement, except that Watch represents the object’s contents as a structure ({...}). As with the above, you can expand it to display more detail about structure members.

 (char*)(NSString*)firstString+8

Casts the variable firstString to a C string and displays its value, “Lois Lane”. This works for NSInlineCStrings, but is not guaranteed to work for other string types such as NXConstantStrings (which is what secondString is).

 (char*)((struct objc_class*)firstString)->isa->name

Returns an ASCII representation of the variable string’s class. Note that this works for local variables only; instance variables must be accessed through self.

 (char*)(NSString*)secondString->isa->name

Same as the above. This works for secondString but not for firstString because secondString was statically declared to be an NSString. Because secondString is an NXConstantString, you can’t use a statement such as (char*)(NSString*)string+8 to display its contents—that only works for NSInlineCStrings.

 ((NXConstantString *)secondString)->bytes

If you have an NSCString or an NXConstantString (like secondString) you can retrieve its string value by making the casts illustrated here (after the isa pointer is a pointer to the raw bytes).

To display information about instance variables, you must access those variables through self. For example, if you have an instance variable length, you can display information for it in the Watch window by typing the following:

(char*)((struct objc_class*)self->length)->isa->name

This displays the name of the class that the instance variable length points to.


Introduction

D’OLE provides the OpenStep object model on the Windows platform, thereby allowing integration between OLE and Objective-C objects. Using D’OLE lets you access and control OpenStep objects running on powerful compute and database servers from within Windows-based custom applications.

D’OLE provides a seamless, transparent messaging interface between OLE Automation objects and OpenStep objects. OpenStep and OLE Automation objects can call each other in their native languages, and D’OLE does the necessary translations behind the scenes. Thus, integrating OpenStep’s objects into Windows applications and accessing OLE services from OpenStep is relatively trivial. The ability to arbitrarily combine OLE Automation and OpenStep objects gives you great flexibility in your application design—for example, you can manipulate a spreadsheet on NT from NEXTSTEP or connect a Visual Basic user interface to a database server running Enterprise Objects Framework.

D’OLE includes the following:

•	Objective-C run-time

•	Mach client library and emulation server

•	gcc compiler

•	gas (assembler)

•	make and related make tools

•	OpenStep Foundation Framework Release 4.0

The version of Foundation that’s supplied with OpenStep Release 4.0 (With support for both “old DO” and “new DO”)

•	OLE Automation Integration

This includes dynamic link libraries that provide the object interfaces and the Portable Distributed Objects (PDO) network protocol. It also includes the NeXT Object Request Broker (ORB), which translates messages between OLE-capable and DO-capable processes and allows you to register PDO objects as 32-bit in-process (DLL) or local (.EXE) OLE Automation servers.


Invoking the OLE Object’s Methods

You invoke the Calc object’s methods as you would those of any other OLE object. For instance, when the user presses the calculator’s Clear All key, the following Visual Basic statement is executed:

calcObj.clearAll

This simply invokes Calc’s clearAll method. Note that this method neither takes a parameter nor returns a value. When the user presses the calculator’s “equals” key, however, the performOperationWith: method is invoked, which both takes a parameter and returns a value. The Visual Basic code used to invoke this method is as follows:

result = calcObj.performOperationWith(enteredValue)

Calc’s changePendingOperation: method is invoked whenever the user presses one operator key (+, -, x, or /), and then presses a second operator key, nullifying the first key press. The Visual Basic code to invoke this method is as follows:

calcObj.changePendingOperation (tag)

Finally, methods that take more than one named argument require a slightly different means of invocation. Because Visual Basic 3.0 does not support named arguments, you need to specify the full method name along with the arguments to the ORB’s sendMsg method. Calc’s setOperation:forValue: is just such a method; it is invoked as follows:

result = calcObj.sendMsg("setOperation:forValue:", tag, operand)

If you have a method whose name is itself sendMsg, you’d have to send it as follows:

x.sendMsg ("sendMsg", ...)

Visual Basic is not case-sensitive, and it sometimes converts the case of a term back to the form in which it originally saw it. Because Objective-C is case-sensitive and its method names usually start with lowercase, the ORB converts the first letter of method names sent from Visual Basic to lowercase. If this default behavior is not sufficient and you want to be absolutely sure that the messages you send from Visual Basic have the proper case, you can use sendMsg.


Messaging a PDO Object

Once a PDO object has been vended, as described in the previous section, other applications can interact with it. To use an object that’s been vended (for example, from an OLE Automation application running on NT), a client in effect looks up the desired server object and stores a handle to it in a local proxy object. For example, this line stores the handle in theServer:

id theServer = [NXConnection connectToName:"stockPriceServer"];

If this line of code returns a non-nil value to theServer, the client may then refer to the stock price server on the server machine as if it were implemented in the client, with only a few exceptions. For example:

NSLog(@"IBM is currently at %d\n", [theServer priceFor:@"IBM"]);


OLE

OLE is a set of COM interfaces that provide:

•	Linking

•	Embedding (in-place editing)

•	Automation (dynamic method invocation)

D’OLE supports OLE Automation only. It does not support object linking and embedding.

OLE Automation

OLE Automation allows an application to take advantage of services provided by other OLE-enabled applications. These services can vary widely from application to application, but they are provided through a standard object interface.

An OLE Automation object is an instance of a class within an application that can be programmatically manipulated from outside the application (typically by using a scripting language such as Visual Basic). For example, you can invoke a command from a word processing application that sorts a range of cells in a spreadsheet application.

The standard interfaces an OLE Automation object implements are described in the following table.

Interface	Description

IUnknown	Defines a set of member functions that identify and keep track of exposed objects. All OLE objects derive from IUnknown.

IClassFactory	Defines a set of member functions to allow OLE to create objects.

IDispatch	Defines a set of member functions to allow OLE Automation to get information about your application’s objects and to invoke methods and get or set properties. D’OLE uses the IDispatch interface to message OLE Automation objects.

IEnumVARIANT (optional)	Defines a set of member functions you must implement to expose collections of objects.

Creating an OLE Automation Object

The typical way of creating an OLE Automation object entails the basic steps listed below (note that these steps, which can be cumbersome, can be largely obviated by using D’OLE):

1.	Initialize the object’s class ID by generating a global unique identifier (GUID) for the object. The GUID is the means by which the object is uniquely identified in the Windows registration database.

2.	Implement the IUnknown interface.

3.	Implement the IClassFactory interface.

4.	Implement the IDispatch interface.

5.	Create the object’s programmable interface (that is, its defined set of properties and methods).

6.	Use the object description language (ODL) to describe the object’s type information, and compile the description to create a type library.

7.	Create an OLE registration file. The Windows registration database contains a list of all of the OLE objects contained in your system. The registration file is used to merge your registration entry into the Windows registration database, so that your object can be accessed as an OLE Automation object.

Messaging an OLE Automation Object

Once an OLE Automation object has been created and put in the Windows registration database, other applications can interact with it. For example, the following Visual Basic code fragment creates a Microsoft Word document object, opens it, inserts a line into it, and then closes the file.

Sub RunWord()

Set MyWordDoc = CreateObject("Word.Basic")

MyWordDoc.FileOpen "MyFile"

MyWordDoc.Insert "This is the text I’m inserting."

MyWordDoc.FileClose

End Sub

While the above example illustrates messaging Microsoft Word using Visual Basic, D’OLE allows you to message OLE Automation objects from Objective-C code, by going through the NeXT Object Request Broker. This is described in more detail in “Using the NeXT Object Request Broker.”


Object Disposal and the NeXT ORB

The Foundation Framework established a simple policy for the disposal of objects: If you create an object, you alone are responsible for releasing it. If you didn’t create the object, you don’t own it and shouldn’t release it. This policy is not only true for your Objective-C objects, it holds when dealing with objects obtained from the NeXT ORB, as well.

Objects that you receive from the ORB are autoreleased. If you need to make use of an object outside the scope in which it was given to you, retain it, and then release it when you are done with it. Otherwise, you shouldn’t release objects that you receive from the ORB.

On the OLE side of the ORB (from Visual Basic, say), you should never call retain, release, or autorelease. Local variables will automatically be released when they go out of scope; to “retain” an object so that it will persist beyond the scope in which it was created, declare it to be Global.


Objective-C and Memory Management

The Application Kit, which handles the automatic creation and destruction of autorelease pools, is not included with D’OLE. To avoid leaking memory, your D’OLE applications must provide an autorelease solution. The following table lists the requirements for different scenarios:

Note that when you are writing Objective-C clients, you must explicitly create your own top-level autorelease pools. This usually no more difficult than inserting the following line of code into the declarations at the top of your main routine:

id pool = [NSAutoreleasePool new];

Immediately before your client program exits, your code should destroy any autorelease pools that it explicitly created. You can do this with code similar to the following:

[pool release];

For more information on creating your own autorelease pools, see the NSAutoreleasePool class specification in the Foundation Framework Reference.

For information on managing memory when working with the NeXT ORB, click here.


Overview of Source Files in the Excel Example

The Excel example includes the source files listed in the following table.

File	Description

ExcelProtocols.h	Defines protocols for the OLE methods and objects to which your application needs access.

main.m	Includes code for launching Excel on an NT machine from a NEXTSTEP client application.

nxorb.h	Declares the OLEObject protocol and defines the NSVal macro. The OLEObject protocol defines the methods you use to set protocols, as well as the methods to set and get an OLE object’s values. The NSVal macro is used to pass value parameters to OLE objects. Also declares the NEXTORB protocol, which declares methods for setting protocols and accessing OLE objects.

nxorb.m	Contains helper classes the ORB needs to talk to NXDO.

These files are described in more detail in the following sections. Sources for nxorb.h and nxorb.m can be found on-line in NextDeveloper/Headers and NextDeveloper/Libraries, respectively.


Overview of Source Files in the Visual Basic Calculator Example

The Visual Basic calculator example includes the source files listed in the following table. The table describes only the files that play special roles in the application.

File	Description

Calc.m	Includes a main routine; you can make this code available to applications that support OLE Automation by using the orbreg command (described in “Using the orbreg Command”) to register the objects created from this code in the Windows registration database. Alternatively, NT OLE applications can programmatically connect to the ORB and through it gain access to Objective-C objects that have been registered with the Mach name server. This file also includes the implementation for the Calc object; for a complete listing of the code, see "Visual Basic Calculator Source Code."

CALC.FRM	Includes Visual Basic code for accessing the Objective-C objects and methods you registered using the ORB.

These files are described in more detail in the following sections. The full sources are listed in "Source Code for Examples" and can be found on-line in the NextDeveloper/Examples/VBCalc directory.


Overview of Source Files in the Visual C++ Calculator Example

The Visual C++ calculator example includes the source files listed in the following table. The table describes only the files that play special roles in the application.

File	Description

integrate.h	Exports C cover functions that can be used to access the Objective-C code encapsulated in your DLL.

makefile	Compiles the Objective-C code and the dllmain.c file into a dynamic link library.

Calc.m	Includes the Objective-C methods that “drive” the calculator and their corresponding C cover functions.

Calc.cpp	Primary Visual C++ application source file that contains the application class CMyCalcApp; this is where you explicitly invoke __main() in the InitInstance() method to provide housekeeping tasks required by the Objective-C language.

calcdial.cpp	Contains the code needed to interact with the user interface you create in Visual C++.

These files are described in more detail in the following sections. The full sources are listed in "Source Code for Examples," and can also be found on-line in NextDeveloper/Examples/VCCalc.


Porting a NEXTSTEP Calculator to NT

This section explains how to port a simple calculator application created in NEXTSTEP to NT, by creating an Objective-C DLL and then linking that DLL into a Visual C++ application that includes the graphical user interface.

The NEXTSTEP application used in this example emulates in software the workings of a common four-function calculator. This calculator is constructed using two basic classes. The first of these, Calc, contains the “engine” that drives the calculator. It has a fairly simple interface:

//

// Calc.h

//

#import <appkit/appkit.h>

#import "objc/Object.h"

#import "DisplayManager.h"

@interface Calc:Object

{

int pendingOperation;

double xRegister;

}

- init;

- (void)clearAll;

- (double)performOperationWith:(double)value;

- (double)setOperation:(int)operator forValue:(double)value;

- (void)changePendingOperation:(int)operator;

@end

At the heart of this class are the performOperationWith: and setOperation:forValue: methods. The latter is invoked when the user enters a value and presses one of the operator keys. It completes any pending operation (ultimately returning the result of that operation), then makes a note of the requested operation. performOperationWith: is similar, but is invoked whenever the user presses the “equals” key. It completes the pending operation and resets the calculator’s internal state.

The second class, Entry, is a bit more complex. It handles the user interface, both the keyboard and the display. This class is defined as follows:

//

// Entry.h

//

#import <appkit/appkit.h>

#import "objc/Object.h"

#import "Calc.h"

@interface Entry:Object

{

id Calculator;

id Display;

BOOL decimalEntered;

double fractionMultiplier;

BOOL wasValueEntered;

double enteredValue;

double result;

BOOL lastKeyWasOperator;

}

- init;

- changeSign:sender;

- decimal:sender;

- (void)displayValue:(double)value;

- newDigit:sender;

- clear:sender;

- setOperation:sender;

- completeOperation:sender;

@end

This class consists of action methods that you normally connect to user interface controls in Interface Builder. All of the calculator’s digit keys, for instance, invoke newDigit:. The Clear and Clear All keys invoke the clear: method. The change sign (+/-) and decimal point keys invoke the changeSign: and decimal: methods, respectively. Finally, setOperation: is invoked by the four operator keys, and completeOperation: is invoked whenever the user presses the equals key.

The NEXTSTEP graphical user interface for the calculator looks like this:

Figure 6.	 NEXTSTEP Calculator


Registering Your Vended Objects

In order for Windows NT to know which DLL vends which of your objects, there needs to be an entry in the Windows registration database for each object, identifying the appropriate DLL. You add these entries using the dll option with the orbreg command. The general format you use to associate Objective-C objects with the DLL that vends them is:

orbreg add -olename "ole_object_name" -dll dll_filename

•	ole_object_name is the name that your object will be known by in the OLE namespace.

•	dll_filename is the name of the DLL that vends an object (or nil) when passed ole_object_name.

For example, to register the fact that color.dll vends the object ColorList, you would type the following command:

orbreg add -olename "ColorList" -dll color.dll

Because there needs to be an entry in the registration database for each object vended by a DLL, you need to run orbreg multiple times if your DLL vends more than a single object.


Setting Breakpoints by Name

You can set breakpoints on methods by name, but not by the Objective-C name that identifies them in your source code. You must use the function name that the compiler assigns to the method. For instance, suppose you have the following in your code:

@interface Controller(DataSource)

- openFile:(id)file withFlags:(int flags);

@end

The compiler transforms the Objective-C method openFile:withFlags: into a function as follows:

When there’s no category, two underscores are between the class and the method. Underscores are substituted for the colons terminating method keywords.

To set a breakpoint on a method by name, type it with the appropriate function format into the Location field of the Breakpoints panel and click Add. The application must be compiled with the -g flag specified.


Setting and Getting Properties

In OLE, a property represents an attribute of an object. For example, an Excel.Application object has properties such as Height, Creator, Name, and Selection. In OLE properties have pairs of accessor functions that are used to get and set the property value.

The following are typical examples of how you'd set and retrieve properties in Visual Basic:

Text1.Text = "This is my string."

Text2.Text = Text1.Text

NewString = Text2.Text

If an object has a property and a method with the same name, you can differentiate the method by adding () after it with no arguments, for example:

x.foo	'property

x.foo()	'method

The ORB maps property statements into the getOLEPropertyNamed: and setOLEPropertyNamed:to: methods, which you implement as follows:

- (NSValue*) getOLEPropertyNamed: (NSString*)name

Returns the value of the property identified by name. The return value must be wrapped in an NSValue object using [NSValue value:(void*) withObjCType:(char*)].   The ORB will unwrap it before presenting it to the requestor.

- (void)setOLEPropertyNamed: (NSString*)name to:(NSValue*)value

Your implementation should set the named property to the value wrapped in value. Use getValue: and objcType to extract the real value from the NSValue object.

The ORB looks for implementations of these methods in your Objective-C code when it’s accessing that code from an OLE Automation-capable application. For example, the ORB translates a Visual Basic statement such as:

x.y

into:

[x getOLEPropertyNamed:@"y"];


Source Code for Examples

This section lists all of the source code for the examples described in this document:

Visual C++ calculator

Visual Basic calculator

Excel spreadsheet enumerator


Stepping into objc_msgSend

Using the Visual C++ Disassembly panel, you can step into an invoked method via the objc_msgSend call and “exit” into the source code implementing that method. The methods susceptible to this technique can only be your own, not methods in libraries for which you don’t have source code, such as the Foundation Framework. To step into methods:

1.	Set a breakpoint on or near the invocation of the method you want to step into.

2.	Run the program to that breakpoint.

3.	Choose Step Over from the Debug menu (or click the associated icon) until the cursor is on the line containing the method you want to step into.

Figure 12.	 Setting a Breakpoint

4.	Choose Disassembly from the Debug menu.

5.	In the Disassembly panel, step over lines until you get to the first objc_msgSend.

Figure 13.	 Inside objc_msgSend

6.	If you want to verify the associated selector at this point, follow the instructions in “Identifying Selectors,” below.

7.	Choose Step Into from the Debug menu (or click the associated icon) to step into the objc_msgSend call.

8.	Step over lines until you come to jmp    eax; step into it. This assembly directive actually switches the thread of execution to the invoked method.

Figure 14.	 Switching the Thread of Execution

9.	You’re now in the portion of assembly code related to the invoked method’s first line of source code. Choose Disassembly from the Debug menu to toggle off the Disassembly panel and pop up the source code file

Figure 15.	 Viewing Your Source Code

Identifying Selectors

To identify the method selector associated with an objc_msgSend in the Disassembly panel, do the following:

1.	In the Disassembly panel, before you step into objc_msgSend, locate the selector stack just before it. Selector stacks hold the selector for the next objc_msgSend call or for a series of objc_msgSend calls if messages are nested.

Figure 16.	 The Selector Stack

2.	Double-click the address in the parentheses to the right of the selector stack to select it, then drop it to the Memory panel

Figure 17.	 Dropping the Address into the Memory Panel

3.	Since the address you drop onto the Memory panel is a pointer to a pointer, you must dereference the result to find the selector. For convenience, the Memory panel should be set to display long hexadecimal; if it’s not, choose

Options from the Tools menu, select the Debug display, and set the format to be Long Hex. Select the first hex address associated with the selector stack address, drag it slightly within the Memory panel, and drop it. This deferences the pointer and displays the selector name.

Figure 18.	 Displaying the Selector Name


Structuring Your Application

In writing an application in the D’OLE environment, one of the major considerations is the tool you want to use to create your graphical user interface. Using a 32-bit development environment such as Visual C++ allows you to talk to a 32-bit DLL, which results in a faster program. However, Visual Basic 3.0 offers simplicity and ease of user interface creation, even though as a 16-bit application it can’t talk to a 32-bit DLL (and so you must use OLE Automation).

The calculator was intentionally designed so that the code needed to handle the user interface is isolated in a single class. Limiting all user interface-specific code to a small number of classes simplifies the porting process, since that is the only code that need be rewritten to take advantage of the user interface capabilities of a particular development environment. The Calc class, which does the bulk of the work, need not be rewritten at all. It can either be compiled as a DLL or accessed through the ORB as an OLE object.

Because the ORB operates bidirectionally, you can not only make your Objective-C objects look like OLE objects from the Windows NT side, you can also write Objective-C programs that can talk to applications that support OLE Automation, such as Microsoft Word or Microsoft Excel. The ORB is described in detail in "Using the NeXT Object Request Broker."

The remainder of this chapter shows you how to take the NEXTSTEP calculator described above and re-implement it with a Visual C++ front end."Using the NeXT Object Request Broker." tells you how to implement it using the ORB and a Visual Basic front end. The Visual C++ version implements the equivalent of the Entry class in Visual C++, and employs the Calc class in the form of a DLL. The Visual Basic version re-implements the equivalent of the Entry class in Visual Basic, but it communicates with Calc, which is running on a NEXTSTEP release 3.3 machine, as an OLE object.


The DllMain Function

A single user-defined function, DllMain(), is invoked from the Microsoft run-time code to handle DLL initialization and termination. This function gives you the ability to allocate or release resources as needed. It’s invoked in four different situations: process attach, process detach, thread attach, and thread detach.

Before an application can access a DLL that was created using gcc, it must make a call to NSRegisterDLL(). The easiest way to guarantee that this function is invoked is by invoking it from within DllMain(). For example:

//

// dllmain.c

//

#include <winnt-pdo.h>

#include <windows.h>

#include <NSRegisterDLL.h>

int APIENTRY DllMain( HANDLE hModule,

                        DWORD ul_reason_for_call,

                        LPVOID lpReserved )

{

    switch( ul_reason_for_call ) {

    case DLL_PROCESS_ATTACH:

NSRegisterDLL(hModule);

break;

    case DLL_THREAD_ATTACH:

break;

    case DLL_THREAD_DETACH:

break;

    case DLL_PROCESS_DETACH:

break;

    }

    return TRUE;

}

For most applications, the above code is sufficient. If you don’t need to make any additional changes to dllmain, you can simply include dllmain.o (found in NextDeveloper/Libraries) along with your modules when linking. If you do need to make changes to dllmain, make a copy of NextDeveloper/Examples/dll/dllmain.c, alter it as necessary for your particular application, and compile it along with your other source files. Be sure to include NSRegisterDLL.o (from NextDeveloper/Libraries) when linking your DLL.


The NSGetDllObject Function

The ORB funnels all object requests originating from the OLE Automation side through a single function named NSGetDllObject(). NSGetDllObject() takes a single parameter—an NSString that represents the name of an object—and returns an autoreleased Objective-C object corresponding to that parameter. Your implementation of NSGetDllObject() can also return nil if it isn’t appropriate to return an object for a particular object request.

How you write this “object vendor” is up to you; you can do any processing you wish in order to create the object that will be returned. You can even ignore the incoming NSString, if your code already knows the type of object to return. The only requirement is that you return either an autoreleased Objective-C object or nil.

As an example, the following is a simple version of NSGetDllObject() that returns an NSArray of NSStrings when a ColorList is requested, and returns nil upon any other request:

#import <Foundation/Foundation.h>

id NSGetDllObject(NSString *objectName){

if([objectName isEqualToString:@"ColorList"]){

NSArray *colorObj =

[NSArray arrayWithObjects:@"Red", @"Green", @"Blue",

    nil];

return colorObj;

} else {

return nil;

}

}

After implementing NSGetDllObject() and registering each of the vended objects with orbreg, any OLE-Automation-capable application will receive OLE proxies to your Objective-C objects upon request. For instance, the following line of Visual Basic code requests an object named ColorList:

Set ColorList = CreateObject("ColorList")

When this line of code is executed, the following happens:

1.	Using the string passed in the invocation of CreateObject(), OLE looks in the Windows registration database for the name of the corresponding DLL.

2.	If the DLL isn’t already loaded, OLE loads it. It then invokes the DLL and passes to it the name of the requested object.

3.	The ORB calls NSGetDllObject(), passing the name of the requested object as an NSString.

4.	NSGetDllObject() either returns an autoreleased Objective-C object, or nil.

5.	The ORB constructs an OLE Automation “proxy” to whatever was returned by NSGetDllObject(), and passes the proxy back to CreateObject().

6.	CreateObject() returns the OLE Automation proxy back to your code. Note that if the returned object “is Nothing”, then NSGetDllObject() returned nil.


The OLE/COM Object Model

Object Linking and Embedding (OLE) is Microsoft’s object layer. It’s based on the Component Object Model (COM), which defines a binary interface for objects that is independent of any programming language. OLE is a set of object services built on top of COM.


The OLE/COM and OPENSTEP Object Models

D’OLE brings together two object models: Microsoft’s OLE/COM, and OpenStep.

This chapter gives a brief overview of each of these models and describes how they interact programmatically. It also includes a discussion of how terminology is used differently in OLE and OpenStep.

What is an Object Model?

An object model describes the “look and feel” of objects from a developer’s perspective. It describes the built-in properties of objects and the programming language or languages that provide access to the model’s facilities. To be effective, an object model should also describe general-purpose services such as distribution and interoperation. Both Microsoft’s OLE/COM and OpenStep are object models; another example of an object model is the Object Management Group’s Common Object Request Broker Architecture (CORBA).


The OpenStep Object Model

OpenStep’s Portable Distributed Objects (PDO) system provides a simple way for applications to communicate with one another by allowing them to send Objective-C messages between objects in different applications—even applications running on different machines (including machines running non-NEXTSTEP systems) across a network. Distributed objects are particularly useful for implementing client-server and cooperative applications.

PDO includes the following components:

•	The Objective-C compiler and related tools, which you use to create your custom objects.

•	The Foundation Framework—a set of core object classes that implement a number of basic computing constructs including strings, arrays, enumerators and exceptions.

•	NeXT’s Distributed Objects System—a set of object classes that manages the brokering of server objects and provides transparent connections to remote objects in the network.

•	An independent transport mechanism/protocol for communicating between objects over a network (this mechanism is built into the Distributed Objects classes).

•	A portable version of the Mach Network Name Server, which is used by NEXTSTEP’s Distributed Objects implementation to provide basic network object naming.

Using PDO, you can build non-NEXTSTEP Objective-C applications that can distribute their objects to the NEXTSTEP computers attached to the network (and vice versa). For example, a NEXTSTEP system could communicate with database objects on a file server on a non-NEXTSTEP system.


The Value Property

Every OLE Automation object has a Value property. This property either returns an actual value, or, if “Value” has no meaning for the object, it returns a description of the object. When programming in Visual Basic, you should be aware that there are some situations in which it will silently reference an object’s Value property.

Suppose you have the following assignment statement in a Visual Basic program:

Set x = myObject

After this statement is executed, x contains a reference to myObject (as opposed to a copy of it). If you omit the word “set,” however, Visual Basic does something very different. Instead of setting x to point to myObject, Visual Basic assigns to x the Value property of myObject. Thus, removing the word “set” from the above statement is equivalent to:

x = myObject.Value

Similarly, when you pass an OLE object as a parameter to another object’s method, Visual Basic actually passes the object’s Value property instead of a reference to the object. Assume that the following line of code is executed:

myObject.aFunction(p)

The above is equivalent to the following:

myObject.aFunction(p.Value)

Note that if p.value results in an object reference, this will cause a run-time error in Visual Basic 3.0 (but will work in Visual Basic 4.0).

Because Value is an OLE object property, if the OLE object being accessed is a proxy to an Objective-C object—that is, it was obtained from the NeXT ORB—the ORB will translate the Value property reference into a getOLEPropertyNamed: message.

As an example, suppose you have the following statement in a Visual Basic program:

x = myObject

If myObject is really a proxy to an Objective-C object, the above will assign to x the return value of:

[myObject getOLEPropertyNamed:@"Value"]


Using Distributed OLE

The preceding sections described how to use the ORB to communicate between OLE Automation objects and Objective-C objects. You can also use the ORB to perform “distributed OLE”— that is, to communicate between two OLE-capable applications, even if they’re running on different machines. Performing distributed OLE entails two simple steps: connecting to the ORB, and then through that connection, gaining access to an OLE object which you can then message. For example:

‘ Connect to the ORB

Set theORB = CreateObject("NEXTORB.OLE")

‘ Access the object

Set wordApp = theORB.connectTo("Word.Basic", "OLE", "RemoteHost")

where "Word.Basic" is the programmatic identifier for Microsoft Word (a programmatic identifier is a unique name that is put in the Windows registration database when an OLE application is installed), "OLE" is the protocol, and "RemoteHost" is the name of the machine on which the Word.Basic object resides. Once you have a local proxy to the remote Microsoft Word application object, you can message it using OLE Automation. For example:

wordApp.FileNew

wordApp.EditSelectAll

wordApp.FontSize 12

wordApp.Insert "Some text"

The following Visual Basic for Applications (VBA) code example is run from an Excel spreadsheet. From Excel, it opens a Microsoft Word document on a remote machine RemoteHost. It then writes the contents of a named range of spreadsheet cells to the document.

Const SheetName = "MainSheet"

Const ORBName = "NEXTORB.OLE"

Global mainSheet As Object

Global theRange As Object

Sub writeDataToWord()

Dim wordApp As Object

Dim SaveStatusBar As Integer

Dim I As Integer

Dim count As Integer

Dim startRow As Integer

Dim startCol As Integer

Set mainSheet = Sheets(SheetName)

Set theRange = mainSheet.Range("theRange")

On Error GoTo handleError

startRow = theRange.Row

startCol = theRange.Column

count = 3

SaveStatusBar = Application.DisplayStatusBar

Application.DisplayStatusBar = True

Application.StatusBar = "Connecting to Word..."

Dim theORB As Object

Set theORB = CreateObject("NEXTORB.OLE")

Set wordApp = theORB.connectTo("Word.Basic", "OLE", "amazon")

wordApp.FileNew

wordApp.EditSelectAll

wordApp.FontSize 12

wordApp.Insert "Spreadsheet Values:" & Chr(13)

For I = 0 To count - 1

    Dim theLine As String

    theLine = mainSheet.Cells(startRow + I, startCol).Value

    wordApp.Insert theLine & Chr(13)

Next I

wordApp.FileSave

Application.StatusBar = False

Application.DisplayStatusBar = SaveStatusBar

Exit Sub

handleError:

    Application.StatusBar = False

    Application.DisplayStatusBar = SaveStatusBar

    MsgBox "Error " & Err & " occurred: " & Error$()

End Sub


Using OLE Automation from NEXTSTEP

The Visual Basic calculator example discussed above illustrated connecting to a NEXTSTEP server from an OLE-capable application on NT. This section describes how to go in the other direction: from a NEXTSTEP client application to an NT server. The example described in this section consists of a simple Objective-C program that launches Excel on an NT machine and then quits Excel.


Using the NeXT Object Request Broker

This chapter describes how to use the NeXT Object Request Broker’s services to communicate between applications that support OLE Automation and NeXT’s Distributed Objects.

The NeXT Object Request Broker (ORB) is a server that runs on Windows NT whose job it is to translate messages between Objective-C and OLE Automation objects. Because of how you use it to communicate between OLE Automation objects and NeXT’s Distributed Objects, the ORB can be thought of as a switchboard. To access objects in the other environment (whether OLE or Objective-C) you first connect to the ORB, which can then connect to other objects on your behalf. Because of how it mediates between the two environments, in effect the NeXT ORB is both an OLE server and an Objective-C server.

Figure 19 illustrates the role the ORB plays in D’OLE. The “poker chip” shapes on the PDO Client/Server side represent Objective-C objects, while the rectangular “lozenge” shapes on the OLE Client/Server side represent OLE Automation objects. The ORB dynamically translates the messages sent from one environment to the other, so that Objective-C and OLE Automation objects can talk to each other in their native languages.

Figure 19.	 The ORB

This chapter uses two different examples to illustrate how you use the ORB. The first example is a variation on the calculator examples presented in "Getting Started"; it uses a Visual Basic front end that connects to a PDO server using the ORB. The second example illustrates using the ORB to launch and interact with Excel on an NT machine from a NEXTSTEP client.


Using the orbreg Command

orbreg is a command line tool that puts Objective-C objects in the Windows registration database and thus makes them visible to applications running on NT.

The command line usage for orbreg is as follows:

orbreg [add|remove|list] options

The options are described in the following table.

Option	Description

-olename <name>	where name is the name by which the object is known in OLE.

-doname <name>	where name is the name of the DO object, as registered with the Mach name server.

-dll <name>	where name is the name by which an Objective-C DLL is known in OLE.

-protocol [NSDO|NXDO|OLE]	where NSDO means to connect using an NSConnection (new DO), NXDO means to connect using an NXConnection (old DO), and OLE means to connect to the object as an OLE object.

-host <hostname>	where hostname is the name of the host to connect to. If this parameter is not specified, the local host is used by default.

If you specify the add command, a new entry is created in the registration database for the object. If no entry exists, a new GUID (Globally Unique Identifier) is automatically generated for you. If an entry for this object already exists, its GUID is used and the rest of the entry is overwritten. When you’re adding an object, the options olename, doname, and protocol must be specified. When you’re adding a DLL, the options olename and dll must be specified, where olename is the name of an object vended by the DLL. For more discussion of this subject, see “Registering Your Vended Objects.” The host is optional and the local host is the default.

If you specify the list command, no options are required. Using the olename option lets you list the information for a specific object. If no olename is given, the output is a listing of all Objective-C objects that have been registered as OLE objects. This command is the default.

The remove command removes a specified entry from the registration database. You use the olename option to specify the object you want to remove.


Viewing Information in the Windows Registration Database

If you ever suspect that an object wasn’t registered successfully, you can use the Registry Editor or the OLE2 View Tool provided on NT to examine the contents of the registration database.

Using the Registry Editor

To use the Registry Editor:

1.	On NT, choose Run from the Program Manager File menu, and type regedt32 in the dialog box.

2.	Display the HKEY_CLASSES_ROOT window; this is where the objects you register using orbreg can be examined.

3.	Look for the object you registered, which will be listed by name (for example, “MyCalc”).

4.	Double-click the object’s folder to open it, and then open its CLSID folder to view the object’s CLSID (Class ID).

CLSIDs are the mechanism by which OLE uniquely identifies objects and type libraries within the system.

5.	Compare this CLSID to those listed in the top-level CLSID directory. There should be a corresponding entry for it here; if there isn’t, the object wasn’t registered successfully.

Objects that were registered using the orbreg tool appear in the Registry Editor with the description “OLE object masquerading as DO object named name,” where name is the object’s DO name.

Using the OLE2 View Tool

Another Microsoft tool you can use to examine the contents of the registration database is the OLE2 Object View application.

The OLE2 Object View application resides in the Visual C++ BIN directory. When you launch it, it displays a list of all of the objects in the registration database, such as Microsoft Excel 5.0 Worksheet, and so on. Red icons indicate that an object supports OLE Automation, while blue icons indicate that an object supports OLE linking and embedding. You use the View Tool’s menus and controls to obtain information about these objects, such as their CLSIDs and their interfaces.

To display information about the objects you put into the registration database, choose Show NonInsertable Objects from the Objects menu. You can run the Registry Editor from the OLE2 Object View by choosing Run Registry Editor from the File menu.


Visual Basic Calculator Source Code

The following two files, Calc.[hm] constitute the Objective-C calculator server. This server should be compiled and run on a NEXTSTEP 3.3 system. The techniques used in this example are discussed in “Implementing the Calculator Using OLE Automation.”

Calc.h

#include <stdio.h>

#include <stdlib.h>

#include <signal.h>

#include <ctype.h>

#if defined (NeXT)

#	include <libc.h>

#endif

#import <remote/NXProxy.h>

#import <foundation/NSNotification.h>

#import <foundation/NSAutoreleasePool.h>

#import <foundation/NXAutoreleaseConnection.h>

#import <machkit/NXPort.h>

#import <appkit/appkit.h>

#import "objc/Object.h"

@interface Calc:Object

{

int pendingOperation;

double xRegister;

}

- init;

- (void)clearAll;

- (double)performOperationWith:(double)value;

- (double)setOperation:(int)operator forValue:(double)value;

- (void)changePendingOperation:(int)operator;

@end

Calc.m

#import "Calc.h"

//digits are 0 through 9

#define DIVIDE 10

#define MULTIPLY 11

#define SUBTRACT 12

#define ADD 13

#define NOOP 20

@implementation Calc

- init

{

[super init];

[self clearAll];

return self;

}

- (void)clearAll

{

xRegister = 0.0;

pendingOperation = NOOP;

}

- (double)performOperationWith:(double)value

{

switch (pendingOperation){

case ADD:

xRegister += value;

break;

case SUBTRACT:

xRegister -= value;

break;

case MULTIPLY:

xRegister *= value;

break;

case DIVIDE:

xRegister /= value;

break;

default:

xRegister = value;

break;

}

pendingOperation = NOOP;

    return xRegister;

}

- (double)setOperation:(int)operator forValue:(double)value

{

xRegister = [self performOperationWith:value];	// do any pending stuff

pendingOperation = operator;

    return xRegister;

}

- (void)changePendingOperation:(int)operator

{

pendingOperation = operator;

}

@end

int main(int argc, char *argv[])

{

id calcObj;

id conn;

[NXPort worryAboutPortInvalidation];

    calcObj = [[Calc alloc] init];

conn = [NXAutoreleaseConnection registerRoot:calcObj

withName:"Calculator"];

if (conn) {

fprintf(stderr, "Calculator registered\n");

[conn run];

[calcObj free];

} else {

fprintf(stderr, "Couldn't register calculator - exiting.\n");

    exit(-1);

}

exit(0);

}

The following files make up the Visual Basic front-end to the calculator. They were written using Microsoft Visual Basic 3.0.

general

Dim decimalEntered As Integer   'BOOL

Dim fractionMultiplier As Double

Dim wasValueEntered As Integer  'BOOL

Dim enteredValue As Double

Dim result As Double

Dim lastKeyWasOperator As Double    'BOOL

Dim calcObj As object   'OLE object

Const NO = 0

Const YES = 1

Sub Form_Unload (Cancel As Integer)

    Set calcObj = Nothing

End Sub

Sub DisplayValue (value As Double)

    Dim formatString As String

    If decimalEntered Then

        formatString = "0." + String$(Len(Str$(fractionMultiplier)) - 2, "0")

    Else

        formatString = "General Number"

    End If

    Label1.Caption = Format$(value, formatString)

End Sub

Command1()

Sub Command1_Click (Index As Integer)

    If decimalEntered Then

        fractionMultiplier = fractionMultiplier / 10#

        enteredValue = enteredValue + Index * fractionMultiplier

    Else

        enteredValue = enteredValue * 10# + Index

    End If

    DisplayValue (enteredValue)

    wasValueEntered = YES

    lastKeyWasOperator = NO

End Sub

Command2

Sub Command2_Click ()

    If wasValueEntered Then

        enteredValue = -enteredValue

    Else

        enteredValue = -result

    End If

    DisplayValue (enteredValue)

    wasValueEntered = YES

    lastKeyWasOperator = NO

End Sub

Command3

Sub Command3_Click ()

    If Not decimalEntered Then

        fractionMultiplier = 1#

        decimalEntered = YES

    End If

    wasValueEntered = YES

    lastKeyWasOperator = NO

End Sub

Command4()

Sub Command4_Click (Index As Integer)

    Dim operand As Double

    Dim tag As Integer

    tag = Index + 10    'divide=10, multiply=11, etc.

    If lastKeyWasOperator Then

        calcObj.changePendingOperation (tag)

    Else

        If wasValueEntered Then

            operand = enteredValue

        Else

            operand = result

        End If

        result = calcObj.sendMsg("setOperation:forValue:", tag, operand)

        DisplayValue (result)

        enteredValue = 0#

        wasValueEntered = NO

        decimalEntered = NO

    End If

    lastKeyWasOperator = YES

End Sub

Command5()

Sub Command5_Click (Index As Integer)

    enteredValue = 0#

    result = 0#

    wasValueEntered = NO

    If Index = 0 Then     'CA=0

        calcObj.clearAll

    End If

    decimalEntered = NO

    DisplayValue (enteredValue)

    lastKeyWasOperator = NO

End Sub

Command6

Sub Command6_Click ()

    result = calcObj.performOperationWith(enteredValue)

    DisplayValue (result)

    enteredValue = 0#

    wasValueEntered = NO

    decimalEntered = NO

    lastKeyWasOperator = NO

End Sub

Form

Sub Form_Load ()

    wasValueEntered = NO

    enteredValue = 0#

    decimalEntered = NO

    lastKeyWasOperator = NO

'The following assumes that "orbreg" was used to register MyCalc

    Set calcObj = CreateObject("MyCalc")

    If calcObj Is Nothing Then

        keyPressed = MsgBox("Unable to connect to Calculator server.", 16, "Calc Error")

        End

    End If

End Sub

Sub Form_Unload (Cancel As Integer)

    Set calcObj = Nothing

End Sub


Visual C++ Calculator Source Code

This section includes only the Objective-C code and Visual C++ code that provides custom functionality; that is, it doesn’t include the code files generated by Visual C++, nor does it include the source code for dllmain (which can be found on-line in NextDeveloper/Examples/dll.  The techniques used in this example are discussed in “Porting a NEXTSTEP Calculator to NT.”

Calc.h

#import "objc/Object.h"

@interface Calc:Object

{

int pendingOperation;

double xRegister;

}

- init;

- (void)clearAll;

- (double)performOperationWith:(double)value;

- (double)setOperation:(int)operator forValue:(double)value;

- (void)changePendingOperation:(int)operator;

@end

Calc.m

#import "Calc.h"

#include "integrate.h"

//digits are 0 through 9

#define DIVIDE 10

#define MULTIPLY 11

#define SUBTRACT 12

#define ADD 13

#define NOOP 20

@implementation Calc

- init

{

[super init];

[self clearAll];

return self;

}

- (void)clearAll

{

xRegister = 0.0;

pendingOperation = NOOP;

}

- (double)performOperationWith:(double)value

{

switch (pendingOperation){

case ADD:

xRegister += value;

break;

case SUBTRACT:

xRegister -= value;

break;

case MULTIPLY:

xRegister *= value;

break;

case DIVIDE:

xRegister /= value;

break;

default:

xRegister = value;

break;

}

pendingOperation = NOOP;

    return xRegister;

}

- (double)setOperation:(int)operator forValue:(double)value

{

xRegister = [self performOperationWith:value];  // do any pending stuff

pendingOperation = operator;

    return xRegister;

}

- (void)changePendingOperation:(int)operator

{

pendingOperation = operator;

}

@end

void *createCalcObject()

{

Calc *aCalc = [[Calc alloc] init];

return (void *)aCalc;

}

void clearAll(void *aCalc)

{

[(id)aCalc clearAll];

}

double performOperationWith(void *aCalc, double value)

{

return [(id)aCalc performOperationWith:value];

}

double setOperationForValue(void *aCalc, int operator, double value)

{

return [(id)aCalc setOperation:operator forValue:value];

}

void changePendingOperation(void *aCalc, int operator)

{

[(id)aCalc changePendingOperation:operator];

}

void *destroyCalcObject(void *aCalc)

{

return [(id)aCalc free];

}

integrate.h

#ifndef __GNUC__

extern "C" {

#endif

__declspec(dllexport) void *createCalcObject(void);

__declspec(dllexport) void *destroyCalcObject(void *);

__declspec(dllexport) void clearAll( void*);

__declspec(dllexport) double performOperationWith(void *, double);

__declspec(dllexport) double setOperationForValue(void *, int, double);

__declspec(dllexport) void changePendingOperation(void *, int);

#ifndef __GNUC__

};

#endif

calcdial.h

// calcdial.h : header file

//

/////////////////////////////////////////////////////////////////////////////

// CCalcDialog dialog

class CCalcDialog : public CDialog

{

// Construction

public:

CCalcDialog(CWnd* pParent = NULL);   // standard constructor

~CCalcDialog();	// standard destructor

// Dialog Data

//{{AFX_DATA(CCalcDialog)

enum { IDD = IDD_DIALOG1 };

CString	m_Display;

//}}AFX_DATA

BOOL m_decimalEntered;

double m_fractionMultiplier;

BOOL m_wasValueEntered;

double m_enteredValue;

double m_result;

BOOL m_lastKeyWasOperator;

   	void *calcObj;

// Overrides

// ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CCalcDialog)

protected:

virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

//}}AFX_VIRTUAL

// Implementation

protected:

// Generated message map functions

//{{AFX_MSG(CCalcDialog)

afx_msg void OnDigit(UINT);

afx_msg void OnFunction(UINT);

afx_msg void OnClear(UINT);

afx_msg void OnEquals();

afx_msg void OnChangeSign();

afx_msg void OnDecimal();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

void DisplayValue(double);

};

calcdial.cpp

// calcdial.cpp : implementation file

//

#include "stdafx.h"

#include "MyCalc.h"

#include "calcdial.h"

#include "integrate.h"

#define NO FALSE

#define YES TRUE

#ifdef _DEBUG

#undef THIS_FILE

static char BASED_CODE THIS_FILE[] = __FILE__;

#endif

/////////////////////////////////////////////////////////////////////////////

// CCalcDialog dialog

CCalcDialog::CCalcDialog(CWnd* pParent /*=NULL*/)

: CDialog(CCalcDialog::IDD, pParent)

{

//{{AFX_DATA_INIT(CCalcDialog)

m_Display = _T("");

//}}AFX_DATA_INIT

m_enteredValue = 0.0;

m_wasValueEntered = NO;

m_decimalEntered = NO;

m_lastKeyWasOperator = NO;

calcObj = createCalcObject();

}

CCalcDialog::~CCalcDialog()

{

destroyCalcObject(calcObj);

}

void CCalcDialog::DoDataExchange(CDataExchange* pDX)

{

CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CCalcDialog)

DDX_Text(pDX, IDC_Display, m_Display);

//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CCalcDialog, CDialog)

//{{AFX_MSG_MAP(CCalcDialog)

ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON1, IDC_BUTTON10, OnDigit)

ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON11, IDC_BUTTON14, OnFunction)

ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON16, IDC_BUTTON17, OnClear)

ON_BN_CLICKED(IDC_BUTTON18, OnEquals)

ON_BN_CLICKED(IDC_BUTTON19, OnDecimal)

ON_BN_CLICKED(IDC_BUTTON20, OnChangeSign)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////

// CCalcDialog message handlers

void CCalcDialog::OnDigit(UINT nID)

{

int tag = nID - IDC_BUTTON1 + 1;

if (tag == 10) tag = 0;

if(m_decimalEntered){

m_fractionMultiplier /= 10.0;

m_enteredValue += tag * m_fractionMultiplier;

} else

m_enteredValue = m_enteredValue * 10 + tag;

CCalcDialog::DisplayValue(m_enteredValue);

m_wasValueEntered = YES;

m_lastKeyWasOperator = NO;

}

void CCalcDialog::OnFunction(UINT nID)

{

double operand;

int tag = nID - IDC_BUTTON11 + 10;	// +10 puts it into the right range

if(m_lastKeyWasOperator)

changePendingOperation(calcObj, tag);

else {

operand = m_wasValueEntered ? m_enteredValue : m_result;

m_result = setOperationForValue(calcObj, tag, operand);

CCalcDialog::DisplayValue(m_result);

m_enteredValue = 0.0;

m_wasValueEntered = NO;

m_decimalEntered = NO;

}

m_lastKeyWasOperator = YES;

}

void CCalcDialog::OnClear(UINT nID)

{

int tag = nID - IDC_BUTTON16;	// BUTTON_16 is CLEAR ALL

m_enteredValue = 0.0;

m_result = 0.0;

m_wasValueEntered = NO;

if(tag == 0)

clearAll(calcObj);

m_decimalEntered = NO;

CCalcDialog::DisplayValue(m_enteredValue);

m_lastKeyWasOperator = NO;

}

void CCalcDialog::OnEquals()

{

m_result = performOperationWith(calcObj, m_enteredValue);

CCalcDialog::DisplayValue(m_result);

m_enteredValue = 0.0;

m_wasValueEntered = NO;

m_decimalEntered = NO;

m_lastKeyWasOperator = NO;

}

void CCalcDialog::OnChangeSign()

{

m_enteredValue = m_wasValueEntered ? -m_enteredValue : -m_result;

CCalcDialog::DisplayValue(m_enteredValue);

m_wasValueEntered = YES;

m_lastKeyWasOperator = NO;

}

void CCalcDialog::OnDecimal()

{

if(!m_decimalEntered){

m_fractionMultiplier = 1.0;

m_decimalEntered = YES;

}

m_wasValueEntered = YES;

m_lastKeyWasOperator = NO;

}

void CCalcDialog::DisplayValue(double value)

{

char buf[256];

sprintf(buf, "%15.10g", value);

m_Display = buf;

CCalcDialog::UpdateData(FALSE);

}


Which ORB?

D’OLE provides two different versions of the ORB: an .EXE version that’s registered in the Windows registration database as NEXTORB.OLE.Server and a .DLL version that’s registered in the Windows registration database as both NEXTORB.OLE.Client and NEXTORB.OLE. The version of the ORB you use depends on the characteristics of your application.

When you connect to the ORB from a Windows application that supports OLE Automation, you must use the .EXE (server) version of the ORB in the following cases:

•	If the application from which you’re connecting is 16-bit.

•	If your application vends OLE objects that need to receive messages.

When possible, however, you should use the .DLL version of the ORB for its superior performance.


Writing C Cover Functions

When structuring your Objective-C code as a DLL, the interface your code presents to the outside world must consist entirely of C functions since there is no way for Visual C++ code to invoke Objective-C methods directly. This does not prevent you from passing Objective-C objects between your Visual C++ and Objective-C code, however; simply declare any parameters that return or receive Objective-C objects as void *. For instance, here are two functions that create and destroy Objective-C objects:

void* createObject()

{

Calc* aCalc = [[Calc alloc] init];

return (void*)aCalc;

}

void* destroyObject( void* aCalc )

{

return [(id)aCalc free];

}

The createObject() function returns an Objective-C object that is then passed as an argument to each of the cover functions. For example, the following function acts as a cover for Calc’s setOperation:forValue: method:

double setOperationForValue(void *aCalc, int operator,

double value)

{

return [(id)aCalc setOperation:operator forValue:value];

}


Writing Protocols

In a running application, sending messages from Objective-C to an OLE Automation object is similar to sending messages from OLE to Objective-C, which was described in the Visual Basic calculator example. Regardless of whether you’re going from NEXTSTEP to NT or in the other direction, you connect to the ORB, it connects to the other object on your behalf, and it translates the messages you send into a format that can be understood by the recipient.

The primary difference in connecting from NEXTSTEP to NT is in how you set up your application. To send messages from OLE to Objective-C in the Visual Basic calculator example, you used the orbreg tool to register your Objective-C object in the registration database. OLE-capable processes on NT then had access to your object. However, when you’re going in the other direction (connecting to NT using a DO client application on NEXTSTEP), you need to specify an interface that defines, in Objective-C terms, the OLE objects and methods to which you want access. You do this by declaring an Objective-C protocol for the OLE object whose methods you want to invoke.

The following file, ExcelProtocols.h, declares protocols for performing operations on an Excel Application object (these protocols are pretty skeletal; in a real-world application, you’d supply a more complete set of methods). For example, the file declares that you want access to OLE objects such as ExcelWorksheet and ExcelApp. It further declares the methods associated with each OLE object that you want to use in your application, such as the chartObjects: and range: methods for ExcelWorksheet.

As you can see, all of the protocols declared in ExcelProtocols.h derive from the OLEObject protocol. The OLEObject protocol is declared in nxorb.h, and it includes methods for setting a protocol and for setting and getting the properties of the real OLE object represented by an <OLEObject> proxy.

//ExcelProtocols.h

#import <foundation/NSObject.h>

#import <foundation/NSValue.h>

#import <remote/NXProxy.h>

#import <nxorb.h>

@protocol ExcelChart <OLEObject>

@end

@protocol ExcelCharts <OLEObject>

- (id <ExcelChart>)chart;

@end

@protocol ExcelRange <OLEObject>

@end

@protocol ExcelWorksheet <OLEObject>

- (id <ExcelCharts>)chartObjects:(int)num;

- (id <ExcelRange>)range:(NSString*)range;

@end

@protocol ExcelWorkbook <OLEObject>

- (id <ExcelWorksheet>)worksheets:(int)number;

@end

@protocol ExcelWorkbooks <OLEObject>

- (id <ExcelWorkbook>)open:(NSString*)name;

@end

@protocol ExcelApp <OLEObject>

- (id <ExcelWorkbooks>)workbooks;

- (void)quit;

@end


Writing the Visual Basic Front End

You can use Visual Basic to create a graphical user interface for the calculator, and to write the code necessary to access your Objective-C Calc object running on NEXTSTEP.

Figure 22.	 The Visual Basic Calculator

Accessing an Objective-C object from Visual Basic through the NeXT ORB is almost completely transparent. Connecting to the object, for instance, is as simple as the following (assuming you’ve used the orbreg tool to register your Objective-C object in the registration database):

Set calcObj = CreateObject("MyCalc")

When invoked, CreateObject starts up the ORB as a separate task that will continue to execute until your Visual Basic program completes.

Because Visual Basic automatically disposes of objects upon program completion, you normally don’t have to dispose of them manually. However, if you need to manually dispose of an object created with CreateObject—to minimize your program’s use of memory, for instance—you can do so by setting your object to “Nothing,” as follows:

Set calcObj = Nothing

Visual Basic also disposes of variables when they go out of scope. Thus, if you want to “retain” an object so that it continues to exist even after the subroutine in which is was created has completed, declare the variable that is on the left side of the assignment statement as Global. This is especially important for objects that you pass as arguments to other objects, since your code could wind up trying to access an object that no longer exists.

Note: If CreateObject fails because the object hasn’t been put in the Windows registration database, or because the corresponding NEXTSTEP code isn’t running, the variable in the CreateObject statement is assigned a value of Nothing. If machd isn’t running, CreateObject generates a run-time error (“OLE Automation server cannot create object”), which can be caught with a Visual Basic error trap. If machd and nmserver are running but you still have trouble accessing an Objective-C object through the ORB, check to be sure that the object was successfully registered, as described in  “Viewing Information in the Windows Registration Database.”