ZEMAX Users' Knowledge Base - http://www.zemax.com/kb
How Do I Write an Extension in C?
http://www.zemax.com/kb/articles/90/1/How-Do-I-Write-an-Extension-in-C/Page1.html
By Mark Nicholson
Published on 4 April 2006
 
ZEMAX has a very powerful feature which allows another Windows program to establish a communication link with ZEMAX, and for this other program to get data from ZEMAX on the lens. This is called an Extension. A program can therefore use ZEMAX to trace rays through an optical system, and then receive data back from ZEMAX.

This article shows how to write a simple "hello world" program to link a user-written C-program to ZEMAX.

This article is also available in Japanese.

Introduction

This article is also available in Japanese.

ZEMAX has a very powerful feature which allows another Windows program to establish a communication link with ZEMAX, and for this other program to get data from ZEMAX on the lens. This is called an Extension. The idea is that a program can use ZEMAX to create an optical system, trace rays through the it, and then the data is sent to the other program for further analysis or computation.

The communication between application and ZEMAX is accomplished using Dynamic Data Exchange (DDE). DDE is a protocol defined within the Windows operating system for sharing data between programs. Two programs can establish a DDE link, with one program acting as the "server" and the other the "client". The client generally requests specific data from the server, and the server sends the data back to the client. ZEMAX is intended to be the server, and any Windows program with the right modifications can be made a client.

Obviously, DDE programming for Windows is not for the faint of heart; it involves numerous programming skills and significant familiarity with message loops, DDE, pointers, atoms, and global handles. However, most of the code used to communicate with ZEMAX is standard "boiler plate" code which is common to any DDE client program. This code has all been written already, and has been placed into a single source code module program called ZCLIENT. ZCLIENT is provided in source code form with ZEMAX, and may be freely copied (as long as the copyright is retained), and used in new Extensions.

ZCLIENT handles all of the communication with ZEMAX transparently. Embedded within ZCLIENT is a call to a single function given the name "UserFunction". This function is placed in a separate C program provided by the user, and is compiled along with ZCLIENT to create the Extension executable.

This article shows how to write a simple "hello world" program to link a user-written C-program to ZEMAX using ZCLIENT. The program will get one piece of information from ZEMAX (the name of the currently loaded file) and it will make one change to the currently loaded file (it will make field point 3 have the value (x=20, y=20) to demonstrate both receiving information and sending commands.

One important point: your application program can work on a file different to that shown in the ZEMAX graphical user interface. If your program is to change lens data, and you want to see that changed data in the graphical user interface, you must "push" the copy of the lens data your application is using to the normal ZEMAX user interface. How to do this is covered in this article. However, a setting must be correctly made under File -> Preferences -> Editors to allow this:



If you want your application to be able to change the data in the editors and analysis windows of the currently open ZEMAX session, then "Allow Extensions To Push Lenses" must be checked.

This control is provided to prevent accidental erasure of data in the current user interface by an extension program.

DDE is a Conversation
The easiest way to understand communication between your program (referred to as the client in this article) and ZEMAX (called the server in this article) is as a conversation between the two programs. The client sends a string to the server, the server acts upon the contents of the string and returns another string.

The string sent by the client is called a data item. An example of a data item is the string "GetName" which tells ZEMAX to get the name of the current file in memory. For example, if you load the sample file {zemaxroot}/samples/sequential/objectives/Cooke 40 degree field.zmx, you will see that it has the title "A simple cooke triplet" :

 
ZEMAX ships with a sample Extension program called "Command Line Interface" which you can find under the "Extensions" menu. If you run this and type GetName, you will see:



The Command Line Interface program is a simple utility written in Visual Basic. Full source code is provided, in the {zemaxroot}/extend/Visual Basic sample folder. But the principle is clear: you send a command (a data item) and ZEMAX gives a response. The Extensions chapter of the ZEMAX manual gives full details of all data items and their responses.


Hello World
On the last page of this article you can download a file called Hello World.c, which is a simple c program. Here is the header of this program:



This code was copied verbatim from one of the supplied Extension sample files, and should be included in any C program you write to communicate with ZEMAX. The rest of the program is very simple:



The function MUST be called UserFunction. Communication is then performed by the function PostRequestMessage, which passes a data item to ZEMAX and receives a string variable szBuffer back with the response from ZEMAX. All the work in managing the communication is performed by ZCLIENT.c, which is supplied with ZEMAX in the {zemaxroot}/Extend folder.

ZCLIENT handles all of the communication with ZEMAX transparently. Embedded within ZCLIENT is a call to a single function given the name "UserFunction". This function is placed in a separate C program provided by the user, and is compiled along with ZCLIENT to create the Extension executable. When ZEMAX calls the Extension, execution begins within ZCLIENT. ZCLIENT establishes the DDE communication, then calls UserFunction: this is why your program must include a function called UserFunction.

In this particular program, we send a data item to ZEMAX using PostRequestMessage and then open a standard Windows Message Box to show the reply from ZEMAX. 


Compiling the Program
We will now compile this program using Microsoft Visual C++ version 6. See the article  How To Compile An Extension Using Microsoft Visual Studio 2005 for a description of how to use that compiler. Other compilers should work similarly, but we are unable to provide technical support on further compilers.

Open Visual C++, and click on File...New:



Select "Win32 Application" from the Projects list, and give your project a name: I used "Hello World". The press OK, and select to create an empty project:



Then switch the Project Explorer to FileView, right-mouse-click on Source Files and choose Add Files to Folder...


Add the files hello world.c and zclient.c to the project. Hello World.c can be downloaded from the last page of this article, and zclient.c is in {zemaxroot}/Extend.


To compile the program, click Build -> Build Hello World.exe. It should compile with no errors or warnings.


Make sure ZEMAX is running, and that Extensions are allowed to push lenses (see the first page of this article). Load the sample file {zemaxroot}/samples/sequential/objectives/Cooke 40 degrees field.zmx. You will find the compiled hello world.exe in the project folder, and it can be run simply by double-clicking it (or by pressing cntl-F5 within Visual C++). You should see the following message boxes appear:







Congratulations! Your extension is talking to ZEMAX!


What's Happening?
Here, again, is our program:



We use PostRequestMessage to send the data item GetName (note data items are case-sensitive), and ZEMAX's response is stored in the string szBuffer. We then print szBuffer in a standard Windows Message Box:

 

So, we have successfully retrieved data from ZEMAX. We then send the data item SetField (see documentation) to set field point 3 to be x= 20, y=20, weight=1, all vignetting factors = 0. In this case, ZEMAX responds with the updated settings of field point 3:



to show that execution was successful. BUT, this change is made to a copy of the lens file. In order to see this change via the user interafce, we must "push" the updated file to the user interafce. We do this with the third command, PushLens, 1 (the ,1 also updates all open analysis windows). As a consequence, the field dialog now shows:



So to tell ZEMAX to perform an action we just send the relevant data item, but to see the consequences of that action in the normal user interface we must also send a PushLens command.


Tricks and Gotchas
The most common mistake people make with Extensions is forgetting to give Extensions permission to push lenses:



The other common error is to forget that the extension sees a copy of the file loaded in the editor, and that there is no reason to be restricted to only the currently loaded file: the LoadFile data item can be used to load any file irrespective of what is being edited in the user interface. In fact, any number of DDE clients can be talking to ZEMAX at any time, while a user is using ZEMAX from the normal keyboad and mouse graphical user interface. Its only when you want to see the results of the Extension program in the user interface that you need to issue the PushLens command.

One often overlooked capability of Extensions is the ability to create text and graphic data files from analysis already supported by ZEMAX by using a single call to zclient. For example, to generate a text listing of the spot diagram, use the command



The spot diagram data will be placed in the text file "OUTPUT.TXT" in the specified directory. For more information, see “GetTextFile”  and “GetMetaFile” in the Extensions chapter of the manual. However, there is no way of knowing a priori how long such a calculation will take. As stated earlier, DDE is modeled on a conversation, and if the server does not respond to a client's request within a certain time, known as the timeout period, the DDE communication is assumed to have failed. Within ZCLIENT, the timeout value is set by default to 5000 milliseconds, on line 32:

#define DDE_TIMEOUT 5000
This can be easily modified to any desired value, and then recompiled.