How to compile a User-Defined DLL

This article will show how to compile a User-Defined DLL in Visual Studio. It will also briefly go over what User-Defined DLLs are. This tutorial was built with Visual Studio Community 2017 version 15.9 but these steps will work with previous versions of the program as well. Changes between versions are noted.  It will also discuss other compilers that are available. Please note that this article does not show how to write a new DLL.

Authored By Alexandra Culler, Michael Humphreys, Sandrine Auriol

Introduction

OpticStudio Professional and Premium allow users to link their own optical components through a C or C++ program. Any program can be used to compile the C/C++ code, but one of the most readily available is Visual Studio. This article will provide a step-by-step guide on how to compile a DLL.

What is a User-Defined DLL?

OpticStudio can be customized with User-Defined DLLs.

• In Sequential Mode, a surface defines the boundaries between optical materials. Surfaces can be refractive, reflective, diffractive and gradient index. OpticStudio supports more than 65 different surface types, including very general surface shapes like Polynomial surfaces and Biconic Zernikes.
Yet, it is often the case that users wants something tailored specifically to their own needs. This is where the User-Defined surface is extremely useful and powerful as OpticStudio gives an interface to do it.
• In Non-Sequential Mode, an object defines the boundaries between materials. Objects have a geometry, are made of a material, can have faces that diffract, scatter light. The material can scatter light. The object can be a source. OpticStudio has built-in objects and properties for all of these. In Non-Sequential, all of these can be tailored by separate DLLs: User-defined objects, GRIN profiles, Diffraction algorithms, Surface scattering algorithms, Bulk scattering algorithms, User-defined sources.

64-bit requirement

In the past, OpticStudio was released as either a 32-bit or 64-bit application, meaning that both 32-bit or 64-bit DLLs could be generated based on the properties of the installed version of OpticStudio. Now, OpticStudio is solely a 64-bit application. If the following error message pops-up when using a DLL, then the source code has to be recompiled using the steps outlined in this article.

Note: For older versions of Visual Studio, a separate 64-bit compiling package may need to be downloaded before working through this article: Windows SDK 7.1. Once downloaded, navigate to Tools…Options…Environment…Projects and Solutions. Under that window, enable “Show advanced build configurations” and ensure the settings are as follows:

(Tools…Options is not the same as the “Project Properties” dialog.)

Compile a DLL: General settings

A Dynamic Link Library (DLL) file is an output type of a project within Visual Studio. In order to make a DLL, let's first create a project space that houses the parent code.
Navigate to File…New…Project in Visual Studio.

Choose a Windows Desktop Wizard project type. This can be found under the Visual C++ option.

Then make the Application type “Dynamic Link Library (.dll):

Once the project has been created, the first step is to change the build type. This can be done by navigating to Build…Configuration Manager…

In the Configuration Manager, let's set the solution configuration as “Release” to get a DLL output when the code is run. The platform needs to be set to “x64”. This means that the code will be compatible with 64-bit systems and applications.

Next, add a Source File. The Source File must be a CPP type, so select “New Item…” and pick a C++ File.

There are several sample files to base the User-Defined code off of in the {Zemax}\DLL folder. To use one of those files, copy its code into the newly-created C++ Source File.

Now that the sample file is loaded in, some of the project settings have to be changed. Right-click on the project name and click “Properties”.

Under Configuration Properties…General, ensure that the “Configuration Type” to “Dynamic Library (.dll)”, as shown below. Usually, set “Character Set” to “Use Multi-Byte Character Set”.

Then, under Configuration Properties…C/C++…Code Generation, choose the “Runtime Library”. Typically, it is recommended to use Multi-threaded (/MT) or Multi-threaded DLL (/MD).

The Multi-threaded vs Multi-threaded DLL choice is somewhat complex, but it mostly comes down to compatibility vs. ease of use. If the User-Defined DLL doesn’t depend on interfacing with other libraries, then using the Multi-Threaded runtime libraries means there is no need to worry about installing the C++ redistributable on the machine that will use the DLL. If other libraries are used from within the DLL however, it will need to use the same runtime libraries, which is almost always the Multi-Threaded DLL runtime.

For simple DLL’s that don’t use other third-party libraries, the Multi-threaded runtime is probably the best choice.

Once those settings are declared, click OK to exit the Properties dialog.

Checking for code errors

Now, let's check and make sure that everything is recognized in the parent code. If the DLL is a User-Defined Surface and the source file is housed outside of the {Zemax}\DLL\Surfaces folder, then the heading “usersurf.h” is unrecognized (denoted by the red underline).

To fix this, simply copy “usersurf.h” from {Zemax}\DLL\Surfaces and paste it into the same directory that the source code is located in. Now load in the newly-copied header file by right-clicking “Header Files” and importing the existing code.

Once the header file is loaded into the right directory, there should be no more red underlines in your code. If anything is unrecognized, then the DLL may not compile. If it does, it may not run as expected.

Using a C++ compiler

The majority of Zemax-provided sample files are written in C. Because Visual Studio is a C++ compiler, it means that some modifications have to be made in order to properly compile them.

If it’s not already added, place “extern “C” {}” around the initialization functions at the beginning of the code. Also make sure to comment out the line “BOOL WINAPI DllMain”:

In a C++ compiler, the function names are usually modified “behind the scenes” so that each function has a unique identifier. If these names are changed, then OpticStudio will not be able to run the DLL as it looks for a specific name (i.e. UserDefinedSurface, UserObjectDefinition, etc.) Making the above modifications forces the compiler to keep the function names exactly as they were in the C code and to ignore any errors that may cause.

Similarly, there will likely be a need to suppress warnings caused by the minor differences between C and C++. One such warning is the following:

C4996: ‘srtcpy’: This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS.

If this is the only warning received when compiling, the following code will circumnavigate the compatibility issues:

#pragma warning ( disable : 4996 )

This line allows the compiler to offer machine/operating system-specific features while retaining overall compatibility with the C and C++ languages.

Compile the code by selecting Build…Rebuild Solution or by hitting “Ctrl+F5” on the keyboard. A successful build will output the following:

The DLL can be found in the solution folder. For the project “My_Surface”, the DLL is found in {Project directory}\My_Surface\x64\Release.

Place the DLL in the appropriate {Zemax}\DLL\ folder. OpticStudio will now be able to use it.

Compilers

The following versions of Visual Studio are known to compile OpticStudio User-Defined DLLs:

• 2005, 2008
• Express 2010, Express 2012
• Community 2017, Community 2019. Please see their website for conditions of usage.

Apart from Visual Studio, any other 64-bit C compiler can be used, providing it can create a multi-threaded Windows DLL project. There are a huge number of available compilers. If there are difficulties when using some other compiler, it is recommended to contact the program's technical support and ask the question, "How do I create an empty multi-threaded Win32 DLL project?"

References

The following website can help in understanding the programming commands used within the sample codes: http://www.cplusplus.com/reference/.

KA-01787