Search code examples
javac++structdeclarationjna

How do I use a more specific struct type in place of a more general one?


I am working on a windows device manager which will work with java. I stick on trying to pass without error SetupDiSetClassInstallParams function. (I am trying disable an device.)

I am running exact same structure(necessary way) in C++ and I do not have any problem.

I am getting ERROR_INVALID_USER_BUFFER error. When I tried get this error in C++ I need to change SP_PROPCHANGE_PARAMS structs values with wrong ones.

My struct declerations:

    public static class SP_CLASSINSTALL_HEADER extends Structure {

    public static class ByReference extends SP_CLASSINSTALL_HEADER implements Structure.ByReference {
        public ByReference() {
        }

        public ByReference(Pointer memory) {
            super(memory);
        }
    }

    public SP_CLASSINSTALL_HEADER() {
        cbSize = size();
    }

    public SP_CLASSINSTALL_HEADER(Pointer memory) {
        super(memory);
        read();
    }

    public int cbSize;
    public long InstallFunction; **/* <-- this should be int or else buffer size changes, dll cannot place variables on right places. */**

    protected List getFieldOrder() {
        return Arrays.asList(new String[] { "cbSize", "InstallFunction" });
    }
}

public static class SP_PROPCHANGE_PARAMS extends Structure {

    public static class ByReference extends SP_PROPCHANGE_PARAMS implements Structure.ByReference {
        public ByReference() {
        }

        public ByReference(Pointer memory) {
            super(memory);
        }
    }

    public SP_PROPCHANGE_PARAMS() {
    }

    public SP_PROPCHANGE_PARAMS(Pointer memory) {
        super(memory);
        read();
    }

    public SP_CLASSINSTALL_HEADER ClassInstallHeader = new SP_CLASSINSTALL_HEADER();
    public int StateChange;
    public int Scope;
    public int HwProfile;

    protected List getFieldOrder() {
        return Arrays.asList(new String[] { "ClassInstallHeader", "StateChange", "Scope", "HwProfile" });
    }
}

My function decleration:

boolean SetupDiSetClassInstallParams(WinNT.HANDLE hDevInfo, Pointer deviceInfoData, Pointer classInstallHeader, int size);

How do I calling this function:

    SP_PROPCHANGE_PARAMS spPropChangeParams = new SP_PROPCHANGE_PARAMS(); 
    spPropChangeParams.ClassInstallHeader.InstallFunction = DISetupApi.DIF_PROPERTYCHANGE;
    spPropChangeParams.Scope = DISetupApi.DICS_FLAG_GLOBAL;
    spPropChangeParams.HwProfile = 0;
    spPropChangeParams.StateChange = DISetupApi.DICS_DISABLE;
    int spPropChangeParamsSize = spPropChangeParams.size();
    SP_CLASSINSTALL_HEADER classInstallHeaderReference = new SP_CLASSINSTALL_HEADER(spPropChangeParams.getPointer());

    setupApi.SetupDiSetClassInstallParams(hDevInfo, device.getSPDeviceInfoData().getPointer(), classInstallHeaderReference.getPointer(),
            spPropChangeParamsSize);

How It works in c++:

SP_PROPCHANGE_PARAMS spPropChangeParams;    
spPropChangeParams.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
spPropChangeParams.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
spPropChangeParams.Scope = DICS_FLAG_GLOBAL;
spPropChangeParams.HwProfile = 0; 
spPropChangeParams.StateChange = DICS_DISABLE;

SetupDiSetClassInstallParams(hDeviceInfo, &device.getDeviceInfoData(), (SP_CLASSINSTALL_HEADER*)&spPropChangeParams, sizeof(spPropChangeParams));

Actually I mixed and matched too many ways these structs and function I changed variable types of structs and parameter types of function at the end I could not get anything but error. I cannot find what is my mistake. Could you please help me solve this.

Thanks in advance!


Solution

  • When you're passing around a Structure, don't use Structure.getPointer() unless you have to. When you do that, JNA can't automatically synch the native and Java data, and it's error-prone to remember where to do that yourself. In your case, whatever is in the Java fields never gets copied to native memory in your call to setupApi.SetupDiSetClassInstallParams.

    Change your function mapping to this:

    boolean SetupDiSetClassInstallParams(WinNT.HANDLE hDevInfo, SP_DEVINFO_DATA deviceInfoData, SP_CLASSINSTALL_HEADER classInstallHeader, int size);
    

    and change the invocation to this:

    setupApi.SetupDiSetClassInstallParams(hDevInfo, device.getSPDeviceInfoData(), classInstallHeaderReference,             spPropChangeParamsSize);
    

    EDIT

    If you stick to your original struct definition (where SP_CLASSINSTALL_HEADER is a field), you need to add a function mapping to the interface (extend the interface and create your own instance of the native library):

    public interface MySetupApi extends SetupApi {
        MySetupApi INSTANCE = (MySetupApi)Native.loadLibrary(MySetupApi.class, W32APIOptions.DEFAULT_OPTIONS);
        boolean SetupDiSetClassInstallParams(WinNT.HANDLE hDevInfo, SP_DEVINFO_DATA deviceInfoData, SP_PROPCHANGE_PARAMS propChangeParams, int size);
    }