K8062 DLL corrupt

Hi,

I am attempting to write JNI (Java Native Interface) layer ontop of the supplied DLL to enable K8062 control from my Java application. I have written a number of these JNI layers previosuly all without significant issues. Note, I am using the K8062D.DLL file that came on the installation CD with the kit, from the folder “:\K8062 & VM116 DMX light controller\SDK tools\Borland C++Builder\K8062D.DLL” this file is dated “17/11/2008 10:00” (there are several copies of this DLL in the various example directories, but they all appear to be the same size and date so I’m assuming they are identical). I am using “Microsoft Visual C++ 2010 Express v10.0.30319.1”. I am running on Windows 7 64bit.

In case you are not familar with the JNI process I’ll briefly outline it.

  1. create C header file from Java class which contains native method stubs
  2. create new Win32 DLL project in VC++
  3. copy header file from step 1 into project directory and add to project
  4. copy native library (K8062D.DLL) into project directory
  5. add native library (K8062D.DLL) to linker ‘additional libraries’ path in VC++ properties
  6. add native library (K8062D.DLL) to VC++ project libraries path in VC++ properties
    8 ) copy native library header file to project directory and add to VC++ project includes path (I am using K8052D_DLL.h from same path as I got the DLL, see above)
    9 ) implement interface methods defined in header from step one by calling methods identified in native header file from setp 7
  7. build project and obtain the new JNI interface DLL

I am having problems at steps 8 and 10.

At step 8 VC++ complains that the function definitions in the native header file are invalid, specifically in the header file 4 lines like the the following are found:

FUNCTION __stdcall StartDevice();

VC++ doesn’t like this as no explict return value is declared, I get around this by creating a new copy of this header file and change all the affected lines to read like this:

FUNCTION void __stdcall StartDevice(); - note the addition of void return value
VC++ is now happy with the header file.

I now try to build the project and VC++ fails with the following linker error:

I have tried recopying the DLL from the CD, I get the same error. I have tried downloading a new DLL from the downloads section of the main Velleman site (this new DLL is dated 11/12/2003) and I get the same error. I have tried downloading a new DLL from a link posted on these forums (this DLL is dated 02/06/2004) and get the same error. I have found a fourth DLL link on this thread but the link is now out of date and doesn’t work.

Can anyone advise as to whats going on here? I can open all of these DLLs using a third party DLL viewer and they look fine, but VC++ allways reports them as corrupt with the same error message.

Any help would be very greatfully recieved :smiley:

FYI this is my code for the various files - I don’t think any of these are really pertenant to the problem as I think it’s some sort of versioning, or compatability issue with VC++ and the supplied K8062D.DLL file, but for the sake of completeness here they are:

My NEW K8062D.h file:

[code]#ifdef __cplusplus
extern “C” { /* Assume C declarations for C++ */
#endif

#define FUNCTION __declspec(dllimport)

FUNCTION void __stdcall StartDevice();
FUNCTION void __stdcall SetData(long Channel, long Data);
FUNCTION void __stdcall SetChannelCount(long Count);
FUNCTION void __stdcall StopDevice();

#ifdef __cplusplus
}
#endif[/code]

My JNI interface headre file (generated by java)

[code]/* DO NOT EDIT THIS FILE - it is machine generated /
#include <jni.h>
/
Header for class com_dyteqta_control_jni_DMXControl */

#ifndef _Included_com_dyteqta_control_jni_DMXControl
#define _Included_com_dyteqta_control_jni_DMXControl
#ifdef __cplusplus
extern “C” {
#endif
/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: startDevice_native
  • Signature: ()V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_startDevice_1native
    (JNIEnv *, jobject);

/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: stopDevice_native
  • Signature: ()V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_stopDevice_1native
    (JNIEnv *, jobject);

/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: setChannel_native
  • Signature: (I)V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_setChannel_1native
    (JNIEnv *, jobject, jint);

/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: setData_native
  • Signature: (II)V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_setData_1native
    (JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif
[/code]

My JNI interface cpp file:

[code]// DMXControl.cpp : Defines the exported functions for the DLL application.
//
#include <jni.h>
#include “stdafx.h”
#include “DMXControl.h”
#include “K8062D.h”

/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: startDevice_native
  • Signature: ()V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_startDevice_1native (JNIEnv * env, jobject obj){
    StartDevice();
    }

/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: stopDevice_native
  • Signature: ()V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_stopDevice_1native (JNIEnv * env, jobject jobj){
    StopDevice();
    }

/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: setChannel_native
  • Signature: (I)V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_setChannel_1native (JNIEnv * env, jobject jobj, jint count){
    SetChannelCount(count);
    }

/*

  • Class: com_dyteqta_control_jni_DMXControl
  • Method: setData_native
  • Signature: (II)V
    */
    JNIEXPORT void JNICALL Java_com_dyteqta_control_jni_DMXControl_setData_1native (JNIEnv * env, jobject jobj, jint channel, jint data){
    SetData(channel, data);
    }[/code]

Right, I have made some significant progress since my original query and can now call the DLL succesfully. I will document my findings here for any that follow and might be helped out;

It would appear that there are several DLL library formats; Visual C++ uses the COFF library format while Borland uses OMF (no idea what the differences are, but they are different and that’s all thats matters). This means that you can’t simply add a Borland generated import library to a Visual C++ project. The DLL supplied with the K8062 kit is Borland (hence OMF). I guess I’ve just been lucky in the past and only come across COFF DLLs before.

The actually DLLs can still be called successfully you just can’t add them into the Visual project. In order to call them you have to explicity load them at runtime. This post from MSDN explains the code to do so. Essentially you load the DLL using LoadLibrary() and then grab a function pointer for each funtion you want to access, using GetProcAddress(). There are a couple of gotchas here for those using Visual Studio 2010. VS by default uses __cdecl calling convention, the methods in the K8062D.dll use __stdcall, so you have to make sure your function pointers declare __stdcall, the following code snippet gives the correct typedefs for the K8062D function pointers:

typedef void (__stdcall *StartDevice)(); typedef void (__stdcall *StopDevice)(); typedef void (__stdcall *SetChannelCount)(int); typedef void (__stdcall *SetData)(int, int);
The second gotcha is that VS uses unicode by default so you have to prepend any string literals with L, for example the LoadLibrary call looks like this:

LoadLibrary(L"MyDLL");

The rest of the process is pretty much as described in the MSDN example.

The only other thing which tripped me up is that the K8062D.DLL IS NOT STANDALONE, it depends on the K8062e.exe and FASTTime32.dll being present (I just dropped them into same directory as the K8062D.dll, which is on my path so everything gets loaded). It looks like the DLL actually starts up the exe as I can see a window flash into existance and then bugger off smartish; seems a bit Heath-Robinson, but there you go. This dependancy isn’t documented as far as I can tell.

So once you’ve coded up the explicit library loader, written your JNI interface code, and deployed the JNI dll, the K8062D.dll, the FASTTime32.dll and the K8062e.exe, everything is ready to go. Oh, you only need load your JNI dll from within Java, all the other dependencies are sorted out at runtime.

Hope that helps someone, somewhere.

It’s good to see you found the solution!
I tried the steps 2-10 of your first post in Visual C++ 2010 Express - without any luck.
I didn’t use the .lib file. The .lib file is only for the Borlabd C++Builder and is not compatible with Microsoft C++, as you noted.

Indeed, the K8062D.DLL starts up the K8062E.EXE.
The K8062E.EXE is using the functions from the FASTTime32.dll.

[quote]Hope that helps someone, somewhere.[/quote]I’m sure it will help!

That would have been my first guess. You can’t load the functions in a 3rd party DLL unless you get the lib file or use dynamic linking. The trick with lib files is that they indeed have two formats, one from Borland, one from Microsoft. What’s annoying about this is that they’re are incompatible, so therein lies your problem.

Another thing to note is that C in Visual Studio uses the cdecl calling conventions when calling functions in a DLL unless specified otherwise. But, Windows in general uses the stdcall calling convention, and so does our DLL.

You can speficy stdcall by specifying the __stdcall prefix when importing functions. You can see this in the Microsoft SDK as well. Well, actually, they don’t use __stdcall, but rather a macro around it called WINAPI. By the name you can also see that the Windows API (kernel32.dll, user32.dll, etc) uses the stdcall calling convention.

<windef.h>

...
#define WINAPI      __stdcall
...

For the K8062 DLL this would mean:

#include <windows.h> // includes everything you need, including windef.h

typedef VOID (WINAPI *StartDevice)();
typedef VOID (WINAPI*SetChannelCount)(DWORD);

Also noticed the use of the macro VOID instead of the built-in void type. This is best practice when using Visual Studio C and programming Windows specific libraries. ‘windows.h’ defines macros for all data types abstracting them from you and preventing you from having to know their exact size. This avoids confusion about whether (for example) a 16-bit integer or a 32-bit integer is expected.

Examples are: WORD, DWORD, BYTE, VOID, …

Since everyone knows a DWORD is 32-bits wide, there can be no confusion about the type. This is not true for the built-in types (int, long, etc).

Even strings have their own ‘abstracted’ type: LPSTR, LPCSTR, … These types take unicode into account by using wchar_t or char depending on whether the UNICODE identifier is defined. There are actually two identifiers to select the use of unicode but i won’t get into the specifics of that. I’ll give you an example for LoadLibrary:

<winbase.h>

...
WINBASEAPI
__out
HMODULE
WINAPI
LoadLibraryA(
    __in LPCSTR lpLibFileName
    );
WINBASEAPI
__out
HMODULE
WINAPI
LoadLibraryW(
    __in LPCWSTR lpLibFileName
    );
...
#ifdef UNICODE
#define LoadLibrary  LoadLibraryW
#else
#define LoadLibrary  LoadLibraryA
#endif // !UNICODE
...

So the correct way to support unicode in your Visual C code is like this:

#undef UNICODE
#include <windows.h>

#undef _UNICODE
#include <tchar.h> // unicode helpers

// ...
LoadLibrary(TEXT("mydll.dll"));

The TEXT macro will prefix your string with an L depending on whether unicode is enabled. So it is actually better than always using the L prefix when using the Windows API.

A good classic book about all this is ‘Programming Windows’ by Charles Petzold

I can’t tell you anything about why fasttime32 is used because my colleague wrote the dll. Anyway, good job on getting the dll to work in JNI, you seem like a very decent programmer, that’s why I thought to give you some extra info. Keep up the good work!

Thank you for the additional information.

I have never really written anything for Windows before other than several JNI DLL wrappers, so all that best practice advice if very welocme :slight_smile:. I have now made the changes you suggested.

You might be able to advise on an another issue I am looking at. Currently each of my wrapper methods looks like the following:

[code]void startDevice_DMX(){
HINSTANCE hDLL; // Handle to DLL
StartDevice pFunc; // Function pointer

hDLL = LoadLibrary(TEXT("K8062D.dll"));
if (hDLL != NULL)
{
   pFunc = (StartDevice)GetProcAddress(hDLL, "StartDevice");
   if (pFunc){
	  // call the function
	  pFunc();
   }
   // release the library
   FreeLibrary(hDLL);   
}

}[/code]
Now as you can see the library is loaded and freed on each function call, I am assuming that this is quite inefficient. Assuming that it is inefficient I was thinking of the following solution, loading the library inside the startDevice warpper function and storing it in some gloabl variabel and then freeing it inside the stopDevice wrapper function. Can you see any obvious problems with this? Also, what happens to loaded libraries if the process that owns them dies, do they automatically get freed or might I end up with some sort of resource leak?

Thanks for any help you can provide.

Indeed, global variables are the way to go. Don’t call LoadLibrary/FreeLibrary more than necessary (1 time) for each process (process!, not card). I’d also load every function with GetProcAddress at the time you load the library, which will once more save you from doing things more than once.

Just to clarify:

wrapper.h

#ifndef _WRAPPER_H_
#define _WRAPPER_H_

typedef VOID (WINAPI*LPFNSETCHANNELCOUNT)(DWORD);

extern HANDLE hK8062lib;
extern LPFNSETCHANNELCOUNT SetChannelCount;
// ...

BOOL K8062_Load();
BOOL K8062_Unload();

#endif /* _WRAPPER_H_ */

wrapper.c

#include "wrapper.h"

HMODULE hK8062lib = NULL;
LPFNSETCHANNELCOUNT SetChannelCount;
// ...

BOOL K8062_Load()
{
    // ... LoadLibrary ...
    // ...
    // ... GetProcAddress for all functions into their global variables ...
    // ... For example:
    SetChannelCount = (LPFNSETCHANNELCOUNT)GetProcAddress(hK8062lib, _T("GetChannelCount"));
}

BOOL K8062_Unload()
{
    // ... FreeLibrary ...
}

The extern keyword is absolutely needed here, in case two other files include this header. Also, do not forget to put include guards in all your header files.

application.c

#include "wrapper.h"

int main() // or whatever function you like
{
    K8062_Load(); // once when your application starts

    // because i was clever in naming my variables i can now use the K8062 functions
    // as if they had been declared statically
    SetChannelCount(5); // whoop!

    K8062_Unload(); // once when the application exits
}

If you’ve ever programmed winsock, this would be similar in usage as having to call WSAStartup/WSACleanup before and after using any winsock functions.

About memory usage, if I remember correctly, any and all memory is released when a process dies. So there is never a possibility of a memory leak after a process terminates, only for running processes. This doesn’t mean that you shouldn’t use FreeLibrary though! But yes, Windows does clean up after a process.

A good classic book on advanced topics like memory management is ‘Advanced Windows’ by Jeffrey Richter. It’s basically the next book you should read after ‘Programming Windows’.

Always glad to help

Thanks again for the assistance, very clear and exactly what I was after. :smiley: