1

I have a problem with code that creates a DLL (or mayby the compiler settings).

I'm trying to create a DLL that works as an extension to a software package (ZEMAX) running under windows 64 - although the software it OLD (2005) - upgrading it would cost >$5000 and it works fine so it makes sense to try.

The software (ZEMAX) uses dll files to extend what it can do and expects these DLLs to have given functions which take give parameters and return given values. Example code is given, I can compile it to a DLL but the software just cannot find the functions.

Example code is given by ZEMAX to which I made minor modifications. I want to create four_angle.dll. Key lines in the example code (whic is all in C# and I'd rather not re-write it all) are:

int __declspec(dllexport) APIENTRY UserParamSourceDefinition(double *data);
int __declspec(dllexport) APIENTRY UserParamNames(char *data);

BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
{
    return TRUE;
}

and later on

int __declspec(dllexport) APIENTRY UserParamNames(char *data)
{
    strcpy_s(data[1],16,'hello');
}

int __declspec(dllexport) APIENTRY UserSourceDefinition(double *data)
{
    data[30] = (double)data[2] + 1.0;
}

Visual studio compiles with no errors. Is there any reason these functions cannot be called by the software which loads the DLL at runtime? I am new to making dlls so I have no idea on how those definition statements and options for DLLMain work.

PS - I notice that visual studio also created a DLLmain which I commented out as you cannot define something twice. Again I've only so much of an idea how it works and if I'm meant to fill in some of the case statements.

#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call,LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

PPS I've been following the instructions here - but with no luck: http://customers.zemax.com/os/resources/learn/knowledgebase/how-to-compile-a-user-defined-surface.aspx

Mathieu
  • 8,840
  • 7
  • 32
  • 45
SRD
  • 141
  • 1
  • 1
  • 9
  • 1
    It is operating system specific. You probably need some `winapi` or `windows` tag – Basile Starynkevitch May 31 '18 at 14:52
  • How is `APIENTRY` defined? What are the compiler/linker options? Do you check the calling convention? – Mathieu May 31 '18 at 14:52
  • You can see how functions are exported with [dependency walker](http://www.dependencywalker.com/) and compare with existing Zemax plugins – Mathieu May 31 '18 at 14:53
  • Being new to DLLs I'm unsure how the calling procedure works on windows. As for the APIENTRY I think I came across something that said it was an alias for WINAPI – SRD May 31 '18 at 15:07
  • As for the compiler options there are many I may have missed (being a gui system) but I followed the steps further down the webpage I put a link to: – SRD May 31 '18 at 15:10
  • At lease some of them are : Platform=win32, configuration=release, I could not find anything on multithreading, but I built it for my system – SRD May 31 '18 at 15:13
  • So dependancy walker it is....(thanks for that tip)...a working one gives: _UserParamNames@4 as a function and the non-working one has : ?UserParamNames@@YGHPAD@Z One difference between them is that the working one has C and the other C++ by it. A lot of errors were generated by both but the C / C++ difference could be key (maybe?) – SRD May 31 '18 at 15:33
  • Check https://stackoverflow.com/questions/30581837/linker-error-when-calling-a-c-function-from-c-code-in-different-vs2010-project (especially the `extern "C"` part that should enclose the 2 functions from your 1st snippet). How do you build your code? Do you have a *VStudio* project? Which version are you using? Your functions are in a *.cpp* file? You should include the *Dependency Walker* info in the question, as it's crucial. – CristiFati May 31 '18 at 16:22

1 Answers1

1

Considering that:

  • Both WINAPI and APIENTRY are just #defines to __stdcall
  • According to Dependency Walker, UserParamNames function is exported by the .dll version that:
    • Works: as _UserParamNames@4
    • Doesn't work: as ?UserParamNames@@YGHPAD@Z

the following conclusions emerge:

  • Code is compiled (and functions exported) as C++ (check [MSDN]: Decorated Names)
  • Target architecture is 32bit (Win32 or x86)

I am only speculating here, but most likely you placed the function definitions in a .cpp file (which is compiled by default as C++).

In order to fix your problem, export your functions as C. Your 1st code snippet modified (you'll have to do the same for every function that has this problem). You can do it as below, or add extern "C" for each function individually.

#if defined(__cplusplus)
extern "C" {
#endif

    __declspec(dllexport) int APIENTRY UserParamSourceDefinition(double *data);
    __declspec(dllexport) int APIENTRY UserParamNames(char *data);

#if defined(__cplusplus)
}
#endif

For more details (info, code, resources), check:

CristiFati
  • 38,250
  • 9
  • 50
  • 87
  • 1
    Thanks a lot from all those that helped out, dependancy walker was great to know about - changing the file names in Visual Studio to end in .c not .cpp was the key - still a few runtime bugs but I thnk I've got those, I also went with win32(sys) as a build option (instead of win32) – SRD May 31 '18 at 17:13
  • Yes, it's an option as well as explained in the 3rd URL (that I've just added) you can check there you can change the compile flags without changing the file name, but the `extern "C"` variant will work eitherway (it's the most general). – CristiFati May 31 '18 at 17:30