CIBC:Project:Windows

From NCRR Biomedical Software Development, Engineering, and Dissemination Wiki

Jump to: navigation, search

This is a document hoping to document any windows-specific issues seen in the last few years.

Contents

DLL Programming

Perhaps the most painful part of maintaining a software for package is interfacing with DLLs. Basically, it comes down to: Every symbol in a DLL you want access to in another DLL (or the main exe) needs to be marked with __declspec(dllexport), and every symbol you need to read from another DLL needs to be marked with __declspec(dllimport).

Shortcut

In SCIRun, this system is shortcut with the SCISHARE macro, which is typically used in header files so it can be imported and exported. The shortcut is created by saying “If I’m build XXX DLL, then make everything that says ‘SCISHARE’ that is part of that library dllexport. Otherwise make it be dllimport. We control that statement via the share.h include file. Here is an example:

#undef SCISHARE
#if defined(_WIN32 ) && !defined(BUILD_SCIRUN_STATIC) && defined(BUILD_Core_Thread)
#  define SCISHARE __declspec(dllexport)
#else
#  define SCISHARE
#endif

and we in any Thread .h file, include share.h. Although, notice that <Core/Thread/share.h> is always the last thing included. This is because Thread.h could include Exception.h, which could include its own share.h, so Thread’s SCISHARE would need to be reset.

Then we need to tell the make system that when building Thread source files, to define BUILD_Core_Thread, which you can probably see at most of the SCIRun CMakeLists.txt files.

Placement

The most common and easy way to do this is to export/import an entire class. To do this, do:

class SCISHARE Thread { … };

Global functions:

SCISHARE void doJunk();

Global variables:

SCISHARE PackageDB* packageDB;

(this will often be marked with an extern)

With global variables it is often the case to define it in a .cc files and reference it in a .cc file. In this case, you must fully qualify it:

.cc file defining:

#ifdef _WIN32
#  define SCISHARE __declspec(dllexport)
#endif

SCISHARE int dude;

.cc file using:

#ifdef _WIN32
#  define SCISHARE __declspec(dllimport)
#endif
SCISHARE extern int dude;

When NOT to do it Never SCISHARE templated code or inlines. If there is an inline function inside a class, it’s still fine to SCISHARE the entire class.

Cygwin/MinGW compilers

If you ever decide to use these again, you should be aware that you cannot SCISHARE an entire class. There are, however, compiler options to export all symbols, but with this option, you still have to mark global variables.

A side note about cygwin: if you don’t want to depend on cyg1.dll, compile with –mno-cygwin.


Finding DLLs

Windows has no LD_LIBRARY_PATH and no -rpath path storing. Windows finds DLLs in the following order:

  • directory of the executable
  • a directory in the PATH variable
  • windows system directory

OR if the DLL was linked in via a manifest, it will look in a specific location.

CMake's location of binary files

CMake, as per a visual studio convention, creates a bindir/Debug and/or a bindir/Release depending on what you make. This is because one project/solution will let you select debug or release in the same configuration but output in different directories.

However, because of Dynamic Compilation, we are restricted to one mode, since at code time, there is no way to differentiate among the configurations at code time.

Also, there are cases when scirun expects output files (like in the case of convert/utils) to not be in the release/debug dir, but in the bin dir. In these cases, there are CMake rules to copy these programs to the correct location.

Windows system calls

If you use a system call, it’s going to different on windows. Please keep this in mind if adding system calls to your code. If you don’t know the windows equivalent, please ask somebody who does (or who can figure it out quickly), and please do this before committing your code.

C Runtime Libraries

Windows has basically two methods of running C runtime code – static or DLL. The general rule is to pick one and make sure that every single library in your application is using it. I believe it’s okay to mix versions of the same type (ala 2003 vs. 2005) as long as there not debug. I don’t recommend it if you can avoid it, though.

Compiler Options

  • /MD – dynamic CRT – most common, especially among apps with lots of DLLs
  • /MT – static CRT
  • /MDd – dynamic debug CRT – don’t distribute apps with this (it’s illegal).
  • /MTd – static debug CRT

So you’ll notice every release of Seg3D has come with a msvcr71.dll and msvcp71.dll. These are the C runtime libraries. I could have made static ones, but to do so I would have had to hack ITK’s make system to do /MT instead of /MD, and that would just confuse everybody.

Visual Studio 2005 CRT

Starting with Visual Studio 2005, Visual Studio has linked against the CRT with a manifest. This basically says “look for this version of a DLL in this place”. I have yet to discover how to distribute msvcr80.dll such that the installed app on a different machine will identify it. Once this is discovered, we can stop using VS 2003 altogether.

OpenGL on windows

OpenGL is a little different on windows. On Windows XP and earlier, compile-time functionality is locked to version 1.1, and on Vista it is (I believe) 1.4. If you want greater functionality, you need to query its address with extensions. Glew is highly recommended, as you are probably already are using it to determine available opengl functionality in unix programs.

If OpenGL comes with less functionality than expected, and futher digging reveals that you have “Microsoft OpenGL 1.1”, you probably need to upgrade your video card driver. Microsoft OpenGL on windows is bypassed via some client channel set up by your video driver.


Hash_map

As hash_map is not standard, the implementation is slightly different on windows. See the

#if defined(__ECC) || defined(_MSC_VER)

sections in SCIRun/src/Core/Datatypes

Quirky macro expansion

If you don't want the windows compiler to yell at you about using min and max as variables or function names, you need to define NOMINMAX on the command line. (This happens in SCIRun's root CMakeLists.txt file). These become macros when you include stdlib.h otherwise.

If you don't want the windows compiler to yell at you for using vars or functions called rad1, rad2, ..., grp1, grp2, ..., you need to define WIN32_LEAN_AND_MEAN on the command line. These are macros that happen when dlgs.h is included, which is included from windows.h unless you define WIN32_LEAN_AND_MEAN.

Solutions and Projects

Instead of constructing Makefiles, Visual Studio uses Solutions and Projects. A solution is a runtime environment containing one or more projects. You can open it up in Visual Studio, or compile it on the command line:

devenv /build solution-file
or
devenv /build solution-file /project project-file

In the case of the VS Express, you can use VCExpress instead of devenv.

Unix features not available on windows

Neither fork nor any derivative of it exists on windows. Sorry. There are a variety of CreateProcess or SpawnProcess functions which are helpful in executing external jobs, but no fork.

Installers

I’ve played with a variety of installers. They’re all pretty straightforward – I am using Inno Setup right now, and have an example script file checked into the Seg3D/scripts directory. I had some problems using the MSI installer (that comes with Visual Studio) on several machines – it just crashes when the wizard closes and it starts to do real work. Then again, Inno failed on a machine as well. Googling the error messages didn’t provide much insight, it blamed the problem on viruses and such.

Windows Thirdparty

The Windows thirdparty is not (presently) integrated into the thirdparty install script. Instead do the following:

 1) Download the thirdparty source, currently in http://www.sci.utah.edu/~worthen/3P/3P-win-source.zip.
 2) Open the thirdparty.sln or thirdparty.2005.sln.  Everything with VS 2005 has 2005 appended.
 3) Choose either Debug, Release, StaticDebug, or StaticRelease, in either VS 2003 or 2005, and select Build from the Build menu.
 4) The resulting package will be in debug, release, static-debug, or static-release (with an 05 appended if you used 2005).
 5) Point SCIRun at that directory, or zip or copy it wherever you want.  (you can do the same thing on unix if you follow the right set of instructions, but nobody believes me.)

Constructing the package was kind of a pain. If stuff gets added to thirdparty, each package will have to be added to this solution as its own project. Some of the packages came with their own project (like wx widgets, teem, freetype, zlib, and png do), and some don’t (like all of tcl). Adding the right files has been difficult, but once added, you never have to do it again (unless you upgrade your tcl version or something).

Windows shell (batch) programs, and system() command

Windows shells do not operate like sh or tcsh. Many features are unavailable, like back-quote evaulation and '&&' to execute a second command. Environment variables are referenced with %VAR%. For more information on batch files, you can see MS batch file docs

As in other OSs, the system command executes one command is if it were in the shell.

Windows filesystem

Windows files are set up as follows:

<drive letter>:dir1\dir2\dir with space\file1.

where a given drive letter is a mount point.

That being said, MOST windows system calls function correctly with either / or \, and the drive letter and : may be omitted if the file you're accessing is on the current drive.

There are however, a few system calls that require \, and the primary one that comes to mind is mkdir.

For conversion between unix and windows, there are convertToWindowsPath and convertToUnixPath functions in Core/Util/FileUtils. I recommend converting everything to a Unix path and using a windows path where necessary - primarily because having \ in a string has been known to be treated as an escape character when interpreting the string literally.

SCIRun-specific

Dynamic compilation

With windows, I needed to execute a separate process to run dynamic compilation, as I couldn't execute cd dir && make in system(), so I in essence ran that in a batch file. Instead of forcing the user to specify additional information (like path to NMake), I opted to make this batch file emulate make, without dependencies.

CreateTclIndex

On windows, createTclIndex is a batch file that calls itclsh on the directory where the tcl files are.

Installation

A SCIRun installation requires scirun.exe, the SCIRun dlls, the thirdparty DLLs (and the CRT DLLs), all the TCL and XML files in their relative structure. Since scirun sets up the environment with a hardcoded objdir and srcdir, I (with an installer) set up registry variables to override the location.

So I have

SCIRun
  src
    Core
      GUI/tcl files with tclIndex
    Dataflow
      GUI/tcl files with tclIndex
      XML/xml files
    Packages/Teem,Biopse,etc. with tcl and xml files in Dataflow/{GUI,XML}
  bin
    on-the-fly-libs (see below)
    scirun.exe
    All the DLLs (including CRT)

Then IF you support dynamic compilation, you need to supply a million headers (which ones they are, I'm not sure) in src in their paths, and then find a way to have the user specify the path to the compiler (THIS IS NOT DONE YET).

If you don't support dynamic compilation, you need to turn it off by setting the CMake ENABLE_DYNAMIC_COMPILATION to off. Optionally you can distribute as many on-the-fly-libs as you want in on-the-fly-libs.

Preparing for 64bit

I don't know that much about 64bit windows, but from what I've read, it appears that that main concerns are:

  • making sure you know how big your sizes are (long, size_t, etc.) It seems that this is already being addressed in 64bit linux
  • In 64bit windows, the SetWindowLong and GetWindowLong in Core/Geom/Win32OpenGLContext should be SetWindowLongPtr and GetWindowLongPtr.
Personal tools