Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Best practice how to set callingConvention for a certain platform #531

Closed
rPraml opened this issue Nov 5, 2015 · 8 comments
Closed

Best practice how to set callingConvention for a certain platform #531

rPraml opened this issue Nov 5, 2015 · 8 comments
Labels

Comments

@rPraml
Copy link

rPraml commented Nov 5, 2015

Hello,

in my current application I have to access the Lotus Notes C-API.
This API is available for various platforms and JNA looks perfect to access the Notes API on different platforms. (in my case: Linux/32/64 + Windows/32/64)

Although the JNA API was new for me, it was not very difficult to do this. My implementation seems t works on all platform EXCEPT on Win/32 with this problem:

Either the VM crashes with a segfault(protected mode off) or I got a "Invalid Memory Access"

Then I spent a lot of time with debugging, reading documentation, source, header files and so on to figure out what is different with Win32.
I finally figured out that I am using the wrong calling-convention for my JNA callbacks and this messed up the stack. (All Notes API functions are declared as __stdcall on Windows)

While reading the JNA sources I came over the AltCallingConvention interface, and it seems that this solves my problem. Win/32 works now - but Linux/64 (and maybe others) did not work any longer, as the __stdcall calling convention is not available there.

So I tried to change the initializer to

    Map<String, Object> options = new HashMap<String, Object>();
    options.put(Library.OPTION_CLASSLOADER, NNotes.class.getClassLoader());

    if (Platform.getName().startsWith("Windows") && Platform.getArchitecture().equals("x86")) {
        WIN_32 = true;
        options.put(Library.OPTION_CALLING_CONVENTION, Function.ALT_CONVENTION);
    } else {
        WIN_32 = false;
    }
    String libName = Platform.getLibName("notes");
    NativeLibrary lib = NativeLibrary.getInstance(libName, options);
    Native.register(NNotes.class, lib);

in the hope that ALT_CONVENTION is also used for callbacks that are passed to the Notes-API. But the flag does not count in CallbackReference.getFunctionPointer() :(
It seems that currently the only way to change the calling convention is to tag it with AltCallingConvention

So I ended up with the following workaround code:

    interface NSFNoteOpenCallback extends Callback {
        public short noteOpen(final Pointer param, final int hNote, final int noteId, final int status);
    }

    protected class OnNoteOpen implements NSFNoteOpenCallback {
        public short noteOpen(final Pointer param, final int hNote, final int noteId, final int status) {
            // do something - called by nnotes.dll
        }
    }

    // Dummy, required to tag OnNoteOpen with AltCallingConvention
    protected class OnNoteOpenWin32 extends OnNoteOpen implements AltCallingConvention {
    }

    public void doit() {    
        OnNoteOpen noteOpenCallback;
        if (WIN_32) {
            noteOpenCallback = new OnNoteOpenWin32();
        } else {
            noteOpenCallback = new OnNoteOpen();
        }

        // invoke JNA method
        NSFDbGetNotes(..., noteOpenCallback, ...);
    }

The good thing: My code works on all platforms now.
The bad thing: This inflates my code with a lot of dummy classes and WIN_32 checks.

So is it possible to change the calling-convention programmatically for certain platforms, or are there plans to implement this? (maybe something like this: options.put(Library.OPTION_CALLBACK_CALLING_CONVENTION, Function.ALT_CONVENTION);)

Best regards
Roland Praml

@dblock
Copy link
Member

dblock commented Nov 6, 2015

I don't have a good answer for the above, but I think the world would benefit from an open-source wrapper of the Lotus Notes API. Just saying :)

@dblock dblock added the question label Nov 6, 2015
@twall
Copy link
Contributor

twall commented Nov 8, 2015

There’s already a Platform.isWindows(), so you don’t have to roll your own.

The calling convention on callbacks looks like a bug; the convention should be pulled from a containing library option if available.

If you could write a failing test for that, and put it into a PR, it’d be helpful. That’s a Java-only fix.

On Nov 5, 2015, at 1:50 PM, Roland Praml notifications@github.com wrote:

Hello,

in my current application I have to access the Lotus Notes C-API.
This API is available for various platforms and JNA looks perfect to access the Notes API on different platforms. (in my case: Linux/32/64 + Windows/32/64)

Although the JNA API was new for me, it was not very difficult to do this. My implementation seems t works on all platform EXCEPT on Win/32 with this problem:

Either the VM crashes with a segfault(protected mode off) or I got a "Invalid Memory Access"

Then I spent a lot of time with debugging, reading documentation, source, header files and so on to figure out what is different with Win32.
I finally figured out that I am using the wrong calling-convention for my JNA callbacks and this messed up the stack. (All Notes API functions are declared as __stdcall on Windows)

While reading the JNA sources I came over the AltCallingConvention interface, and it seems that this solves my problem. Win/32 works now - but Linux/64 (and maybe others) did not work any longer, as the __stdcall calling convention is not available there.

So I tried to change the initializer to

Map<String, Object> options = new HashMap<String, Object>

();
options
.put(Library.OPTION_CLASSLOADER, NNotes.class.
getClassLoader());

if (Platform.getName().startsWith("Windows") && Platform.getArchitecture().equals("x86"
)) {

WIN_32 = true
;
options
.put(Library.OPTION_CALLING_CONVENTION, Function.ALT_CONVENTION
);
}
else
{

WIN_32 = false
;
}

String libName = Platform.getLibName("notes"
);

NativeLibrary lib = NativeLibrary.
getInstance(libName, options);

Native.register(NNotes.class, lib);
in the hope that ALT_CONVENTION is also used for callbacks that are passed to the Notes-API. But the flag does not count in CallbackReference.getFunctionPointer() :(
It seems that currently the only way to change the calling convention is to tag it with AltCallingConvention

So I ended up with the following workaround code:

interface NSFNoteOpenCallback extends Callback

{

public short noteOpen(final Pointer param, final int hNote, final int noteId, final int status
);
}

protected class OnNoteOpen implements NSFNoteOpenCallback
{

public short noteOpen(final Pointer param, final int hNote, final int noteId, final int status
) {

// do something - called by nnotes.dll

    }
}

// Dummy, required to tag OnNoteOpen with AltCallingConvention

protected class OnNoteOpenWin32 extends OnNoteOpen implements AltCallingConvention
{
}

public void
doit() {

OnNoteOpen
noteOpenCallback;

if (WIN_32
) {
noteOpenCallback
= new OnNoteOpenWin32
();
}
else
{
noteOpenCallback
= new OnNoteOpen
();
}

// invoke JNA method

    NSFDbGetNotes(

..., noteOpenCallback, ...
);
}

The good thing: My code works on all platforms now.
The bad thing: This inflates my code with a lot of dummy classes and WIN_32 checks.

So is it possible to change the calling-convention programmatically for certain platforms, or are there plans to implement this?

Best regards
Roland Praml


Reply to this email directly or view it on GitHub.

@twall
Copy link
Contributor

twall commented Nov 8, 2015

I'm wondering if it'd be easier to have platforms which haven't defined AltCallingConvention to just ignore it (since it's a special value distinct from an actual libffi-based value.

@rPraml
Copy link
Author

rPraml commented Nov 10, 2015

I also had the idea to introduce a Win32AltCallingConvention tag, that switch CC on Win32 only.
But if Win32 is the only platform that has an AltCallingConvention, I will agree with you.
It also seems that there is already such a workaround for Win64/WinCE in callback.c#L213 and dispatch.c#L590.

I'm not 100% sure if I got that piece of code. On Linux64 I expected to get an "Unrecognized calling convention"-error with an AltCallingConvention tagged interface. But the VM segfaults.
(But I'm not sure if VM crashed and the the error was generated anyway)

In my debugging session I tracked down the problem to CallbackReference.getCallback#L131. Maybe you can take a look at this, if this should be also fixed.

Can I download the 4.2.1-SNAPSHOT maven artifact somewhere from the travis-ci? If so, it should be easy for me to test your fixes.

@twall
Copy link
Contributor

twall commented Nov 13, 2015

I’ll push a staging artifact when I get a chance, or one of the other admins can do it.

@lgoldstein - I need to get you set up with java.net maven repo access.

T.

@MadaraUchiha
Copy link

@twall I'm fairly sure have the wrong Lyor. I know the Lyor you pinged, he isn't a Java dev 😆

@twall
Copy link
Contributor

twall commented Nov 13, 2015

oops, I should know better than to do that via email :)

thanks

On Nov 13, 2015, at 9:44 AM, Madara Uchiha notifications@github.com wrote:

@twall I'm fairly sure have the wrong Lyor. I know the Lyor you pinged, he isn't a Java dev


Reply to this email directly or view it on GitHub.

@matthiasblaesing
Copy link
Member

This commit demonstrates setting calling convention based on platform:
matthiasblaesing/javatellstick@e7e14ce
Given that the last activity here was three years ago, I'll close it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants