如何在jna中正确Mapcert\u select\u struct

gk7wooem  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(381)

我在java项目中使用jna-4.5.1。这是我要复制的cryptdlg structure cert\u select\u struct。

typedef struct tagCSSA {
  DWORD           dwSize;
  HWND            hwndParent;
  HINSTANCE       hInstance;
  LPCSTR          pTemplateName;
  DWORD           dwFlags;
  LPCSTR          szTitle;
  DWORD           cCertStore;
  HCERTSTORE      *arrayCertStore;
  LPCSTR          szPurposeOid;
  DWORD           cCertContext;
  PCCERT_CONTEXT  *arrayCertContext;
  LPARAM          lCustData;
  PFNCMHOOKPROC   pfnHook;
  PFNCMFILTERPROC pfnFilter;
  LPCSTR          szHelpFileName;
  DWORD           dwHelpId;
  HCRYPTPROV      hprov;
} CERT_SELECT_STRUCT_A, *PCERT_SELECT_STRUCT_A;

我的项目的示例java代码。

public class Crypto {
    public interface Cryptdlg extends Library {
        Cryptdlg INSTANCE = (Cryptdlg) Native.loadLibrary("Cryptdlg", Cryptdlg.class, W32APIOptions.DEFAULT_OPTIONS);

        public boolean CertSelectCertificate(CERT_SELECT_STRUCT pCertSelectInfo);

        public static class CERT_SELECT_STRUCT extends Structure {

            private static final List<String> fieldOrder = createFieldsOrder("dwSize", "hwndParent", "hInstance",
                    "pTemplateName", "dwFlags", "szTitle", "cCertStore", "arrayCertStore", "szPurposeOid",
                    "cCertContext", "arrayCertContext", "lCustData", "pfnHook", "pfnFilter", "szHelpFileName",
                    "dwHelpId", "hprov");

            public static class ByReference extends CERT_SELECT_STRUCT implements Structure.ByReference {
            }

            public int dwSize = size();
            public HWND hwndParent;
            public HINSTANCE hInstance;
            public String pTemplateName;
            public int dwFlags;
            public String szTitle;
            public int cCertStore;
            public Pointer arrayCertStore;
            public String szPurposeOid;
            public int cCertContext;
            public Pointer arrayCertContext;
            public WinDef.LPARAM lCustData;
            public Pointer pfnHook = null;
            public Pointer pfnFilter = null;
            public String szHelpFileName;
            public int dwHelpId;
            public HCRYPTPROV hprov;

            public CERT_SELECT_STRUCT() {
                super();
            }

            public WinCrypt.CERT_CONTEXT[] getArrayCertContext() {
                WinCrypt.CERT_CONTEXT[] elements = new WinCrypt.CERT_CONTEXT[cCertContext];
                for (int i = 0; i < elements.length; i++) {
                    elements[i] = (WinCrypt.CERT_CONTEXT) Structure.newInstance(WinCrypt.CERT_CONTEXT.class,
                            arrayCertContext.getPointer(i * Native.POINTER_SIZE));
                    elements[i].read();
                }
                return elements;
            }

            public void setArrayCertContext(WinCrypt.CERT_CONTEXT[] arrayCertContexts) {
                if (arrayCertContexts == null || arrayCertContexts.length == 0) {
                    arrayCertContext = null;
                    cCertContext = 0;
                } else {
                    cCertContext = arrayCertContexts.length;
                    Memory mem = new Memory(Native.POINTER_SIZE * arrayCertContexts.length);
                    for (int i = 0; i < arrayCertContexts.length; i++) {
                        mem.setPointer(i * Native.POINTER_SIZE, arrayCertContexts[i].getPointer());
                    }
                    arrayCertContext = mem;
                }
            }

            public void setArrayCertStore(WinCrypt.HCERTSTORE[] stores) {
                if (stores == null || stores.length == 0) {
                    arrayCertStore = null;
                    cCertStore = 0;
                } else {
                    Memory mem = new Memory(Native.POINTER_SIZE * stores.length);
                    for (int i = 0; i < stores.length; i++) {
                        mem.setPointer(i * Native.POINTER_SIZE, stores[i].getPointer());
                    }
                    cCertStore = stores.length;
                    arrayCertStore = mem;
                }
            }

            public WinCrypt.HCERTSTORE[] getArrayCertStore() {
                if (arrayCertStore == null || cCertStore == 0) {
                    return new WinCrypt.HCERTSTORE[0];
                } else {
                    WinCrypt.HCERTSTORE[] result = new WinCrypt.HCERTSTORE[cCertStore];
                    for (int i = 0; i < result.length; i++) {
                        result[i] = new WinCrypt.HCERTSTORE(arrayCertStore.getPointer(i * Native.POINTER_SIZE));
                    }
                    return result;
                }
            }

            @Override
            protected List<String> getFieldOrder() {
                return fieldOrder;
            }
        }
    }

    public void CertSelect() {
       Cryptdlg cryptdlg = Cryptdlg.INSTANCE;

     ...// parentHwnd and hCertStore are initalized and passed to this method
     Cryptdlg.CERT_SELECT_STRUCT certSel = new Cryptdlg.CERT_SELECT_STRUCT();
     WinCrypt.CERT_CONTEXT[] pContexts = new WinCrypt.CERT_CONTEXT[1];
     certSel.hwndParent = parentHwnd;
     certSel.szTitle = "title";
     certSel.cCertStore = 1;
     certSel.setArrayCertStore(new WinCrypt.HCERTSTORE[] {hCertStore});
     pCertSelectInfo.cCertContext = 1;
     pContexts[0] = new WinCrypt.CERT_CONTEXT.ByReference();
     certSel.setArrayCertContext(pContexts);
     cryptdlg.CertSelectCertificate(certSel); //line 60

    ...
    }
}

当我调用这个方法时,在dll调用中得到“java.lang.error:invalid memory access” cryptdlg.CertSelectCertificate(certSel) 在上面第60行。

java.lang.Error: Invalid memory access
at com.sun.jna.Native.invokeInt(Native Method)
    at com.sun.jna.Function.invoke(Function.java:419)
    at com.sun.jna.Function.invoke(Function.java:354)
    at com.sun.jna.Library$Handler.invoke(Library.java:244)
    at com.sun.proxy.$Proxy13.CertSelect(Unknown Source)
    at com.project.Crypto.CertSelect(Crypto.java:60)

我不知道为什么我们会有例外。我遵循了这里提到的例子。
[更新]
值得一提的是,当我从指向hcertstore[]的指针修改“setarraycertstore”的类型时,我没有得到任何异常,但是没有得到任何证书。

它让我思考arraycertstore初始化是否正确。

WinCrypt.HCERTSTORE[] cStoreArray = new WinCrypt.HCERTSTORE[1];
pCertSelectInfo.cCertStore = 1;
cStoreArray[0] = hCertStore;
pCertSelectInfo.arrayCertStore = cStoreArray;

结构定义更改如下

public WinCrypt.HCERTSTORE[] arrayCertStore;

以及 HCRYPTPROV 定义为

public static class HCRYPTPROV extends BaseTSD.ULONG_PTR {

      public HCRYPTPROV() {}
      public HCRYPTPROV(long value) {
      super(value);
      }
    }

==================================
[编辑]与丹尼尔和其他人讨论后。下面是最新的代码

public class Crypto {
    public interface Cryptdlg extends Library {
        Cryptdlg INSTANCE = (Cryptdlg) Native.loadLibrary("Cryptdlg", Cryptdlg.class, W32APIOptions.DEFAULT_OPTIONS);

        public boolean CertSelectCertificate(CERT_SELECT_STRUCT pCertSelectInfo);

        public static class CERT_SELECT_STRUCT extends Structure {

            private static final List<String> fieldOrder = createFieldsOrder("dwSize", "hwndParent", "hInstance",
                    "pTemplateName", "dwFlags", "szTitle", "cCertStore", "arrayCertStore", "szPurposeOid",
                    "cCertContext", "arrayCertContext", "lCustData", "pfnHook", "pfnFilter", "szHelpFileName",
                    "dwHelpId", "hprov");

            public static class ByReference extends CERT_SELECT_STRUCT implements Structure.ByReference {
            }

            public int dwSize;
            public HWND hwndParent;
            public HINSTANCE hInstance;
            public String pTemplateName;
            public int dwFlags;
            public String szTitle;
            public int cCertStore;
            public Pointer arrayCertStore;
            public String szPurposeOid;
            public int cCertContext;
            public Pointer arrayCertContext;
            public WinDef.LPARAM lCustData;
            public Pointer pfnHook = null;
            public Pointer pfnFilter = null;
            public String szHelpFileName;
            public int dwHelpId;
            public HCRYPTPROV hprov;

            public CERT_SELECT_STRUCT() {
                super();
            }

            public WinCrypt.CERT_CONTEXT[] getArrayCertContext() {
                WinCrypt.CERT_CONTEXT[] elements = new WinCrypt.CERT_CONTEXT[cCertContext];
                for (int i = 0; i < elements.length; i++) {
                    elements[i] = (WinCrypt.CERT_CONTEXT) Structure.newInstance(WinCrypt.CERT_CONTEXT.class,
                            arrayCertContext.getPointer(i * Native.POINTER_SIZE));
                    elements[i].read();
                }
                return elements;
            }

            public void setArrayCertContext(WinCrypt.CERT_CONTEXT[] arrayCertContexts) {
                if (arrayCertContexts == null || arrayCertContexts.length == 0) {
                    arrayCertContext = null;
                    cCertContext = 0;
                } else {
                    cCertContext = arrayCertContexts.length;
                    Memory mem = new Memory(Native.POINTER_SIZE * arrayCertContexts.length);
                    for (int i = 0; i < arrayCertContexts.length; i++) {
                        mem.setPointer(i * Native.POINTER_SIZE, arrayCertContexts[i].getPointer());
                    }
                    arrayCertContext = mem;
                }
            }

            public void setArrayCertStore(WinCrypt.HCERTSTORE[] stores) {
                if (stores == null || stores.length == 0) {
                    arrayCertStore = null;
                    cCertStore = 0;
                } else {
                    Memory mem = new Memory(Native.POINTER_SIZE * stores.length);
                    for (int i = 0; i < stores.length; i++) {
                        mem.setPointer(i * Native.POINTER_SIZE, stores[i].getPointer());
                    }
                    cCertStore = stores.length;
                    arrayCertStore = mem;
                }
            }

            public WinCrypt.HCERTSTORE[] getArrayCertStore() {
                if (arrayCertStore == null || cCertStore == 0) {
                    return new WinCrypt.HCERTSTORE[0];
                } else {
                    WinCrypt.HCERTSTORE[] result = new WinCrypt.HCERTSTORE[cCertStore];
                    for (int i = 0; i < result.length; i++) {
                        result[i] = new WinCrypt.HCERTSTORE(arrayCertStore.getPointer(i * Native.POINTER_SIZE));
                    }
                    return result;
                }
            }

            @Override
            public void write() {
                this.dwSize = size();
                super.write();
            }

            @Override
            protected List<String> getFieldOrder() {
                return fieldOrder;
            }
        }
    }

    public void CertSelect() {
     Cryptdlg cryptdlg = Cryptdlg.INSTANCE;
     Cryptdlg.CERT_SELECT_STRUCT certSel = new Cryptdlg.CERT_SELECT_STRUCT();
     certSel.hwndParent = parentHwnd;
     certSel.szTitle = "title";
     certSel.cCertStore = 1;
     certSel.setArrayCertStore(new WinCrypt.HCERTSTORE[] {hCertStore});
     pCertSelectInfo.cCertContext = 1;
     pCertSelectInfo.arrayCertContext = new Memory(Native.POINTER_SIZE);
     pCertSelectInfo.arrayCertContext.setPointer(0, Pointer.NULL);
     cryptdlg.CertSelectCertificate(certSel);

    ...
    }
}
dojqjjoe

dojqjjoe1#

有几个潜在的问题领域。
首先,您混合了ansi和unicode版本。
这个 CertSelectCertificate() 函数有两个变量,一个以 A 一个在里面 W . 这个 -A 函数是ansi(8位字符),而 -W 变体用于unicode/宽字符串(16位字符)。这些方法的主要区别在于 CERT_SELECT_STRUCT 结构,同样有两个变体, CERT_SELECT_STRUCT_A 以及 CERT_SELECT_STRUCT_W .
jna会自动将您Map到正确的版本(即 -W 在现代操作系统中,几乎所有情况下都使用默认类型Map器。您可能应该使用win32库的默认选项在库加载中显式添加该类型Map器:

Cryptdlg INSTANCE = (Cryptdlg) Native.loadLibrary("Cryptdlg", Cryptdlg.class,
    W32APIOptions.DEFAULT_OPTIONS);

但是,您有一个显式调用来使用 -A 在您的 CERT_SELECT_STRUCT() 施工单位:

public CERT_SELECT_STRUCT() {
  super(W32APITypeMapper.ASCII);
}

这迫使它使用-a版本的结构,它的字符串中每个字符只有8位。你应该给我打个电话 super() .
第二种可能性,虽然从文件上看不明显,但第一个因素, dwSize ,应该是结构的大小。你应该把它放在你的构造器里:

this.dwSize = this.size();

当您设置 arrayCertContext 现场。记录如下:
指向数组的指针 CERT_CONTEXT 结构。
将java端的数组定义为一个结构(它有自己的指针),并手动将其设置到内存中,而不是填充 CERT_CONTEXT 你把一个 Pointer 在那里:

pContexts[0] = new WinCrypt.CERT_CONTEXT.ByReference();

最后用刚创建的指针地址填充“结构”的前8个字节,其中的前4个字节分配给 dwCertEncodingType 接下来的4个字节(加上4个空字节)指向 ByteByReference() 现场。
此外,与内存分配相比,结构数组的分配可以这样做:

WinCrypt.CERT_CONTEXT[] pContextArray = 
    (WinCrypt.CERT_CONTEXT[]) new WinCrypt.CERT_CONTEXT().toArray(1);

另外,您可能会发现JNA5.x(目前为5.6.0)中的结构定义更加精简,其中包括 @FieldOrder 注解作为创建字段顺序列表的首选方法。

相关问题