1

I'm using JNA to call SystemParametersInfo from user32. This is my JNA interface method:

boolean SystemParametersInfo(
        int uiAction,
        int uiParam,
        Pointer pvParam,
        int fWinIni
);

And here's how I use it:

User32.INSTANCE.SystemParametersInfo(SPI_SETMOUSESPEED, 0,
    new IntByReference(2).getPointer(),
    SPIF_UPDATEINIFILE | SPIF_SENDCHANGE | SPIF_SENDWININICHANGE);

This should set the mouse speed to 2 (out of 20) but it has no effect and the method is always returning false.

These are the flag values that I use:

private static final int SPI_GETMOUSESPEED = 0x70;
private static final int SPI_SETMOUSESPEED = 0x0071;
private static final int SPIF_UPDATEINIFILE = 0x01;
private static final int SPIF_SENDCHANGE = 0x02;
private static final int SPIF_SENDWININICHANGE = 0x02;
Jire
  • 9,680
  • 14
  • 52
  • 87

1 Answers1

3

The return value of SystemParametersInfo() is a BOOL, aka an alias for a 4-byte int. So use int instead of boolean on the Java side for the return value.

That aside, the reason SystemParametersInfo() is failing is because you are not passing in the speed value correctly. Read the SPI_SETMOUSESPEED documentation carefully:

SPI_SETMOUSESPEED
0x0071
Sets the current mouse speed. The pvParam parameter is an integer between 1 (slowest) and 20 (fastest). A value of 10 is the default. This value is typically set using the mouse control panel application.

Compare that to the SPI_GETMOUSESPEED documentation:

SPI_GETMOUSESPEED
0x0070
Retrieves the current mouse speed. The mouse speed determines how far the pointer will move based on the distance the mouse moves. The pvParam parameter must point to an integer that receives a value which ranges between 1 (slowest) and 20 (fastest). A value of 10 is the default. The value can be set by an end-user using the mouse control panel application or by an application using SPI_SETMOUSESPEED.

So, even though the pvParam parameter is declared as a pointer, SPI_SETMOUSESPEED wants the actual integer value, not a pointer to an integer that holds the value, like you are currently passing by using IntByReference.getPointer(). This is confirmed in the answer to this question (though for C++, not Java):

Mouse speed not changing by using SPI_SETMOUSESPEED

In C/C++, the solution is like this:

SystemParametersInfo(SPI_SETMOUSESPEED, 0,
    (void*)2,
    SPIF_UPDATEINIFILE | SPIF_SENDCHANGE | SPIF_SENDWININICHANGE);

In Java, the equivalent is more like this:

User32.INSTANCE.SystemParametersInfo(SPI_SETMOUSESPEED, 0,
    Pointer.createConstant(2),
    SPIF_UPDATEINIFILE | SPIF_SENDCHANGE | SPIF_SENDWININICHANGE);
Community
  • 1
  • 1
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks for this! The method still works with a `boolean` as the return type, I did not fully read the documentation. Did not know about `Pointer.createConstant` either! Thanks! – Jire Jul 20 '15 at 21:04
  • Since you've already added your own mapping for `SystemParametersInfo`, you might as well just add a new function declaration that takes an `int` as the third parameter and dispense with the artificial `Pointer` construct. – technomage Jul 20 '15 at 21:27
  • 1
    @technomage: There are operations (like `SPI_GETMOUSESPEED`) where a real pointer is expected, and some (like `SPI_SETMOUSESPEED`) were an integer are expected instead. Can you overload the `SystemParametersInfo()` declaration in Java to handle both cases? I do not know. – Remy Lebeau Jul 20 '15 at 22:04
  • 1
    You can add as many unique signatures as are supported by the underlying call, thus adding some degree of type safety to an otherwise unsafe system. – technomage Jul 21 '15 at 11:09