Как мне правильно отобразить CERT_SELECT_STRUCT в JNA

#java #winapi #structure #jna

#java #winapi #структура #jna

Вопрос:

Я использую jna-4.5.1 в своем Java-проекте. Это структура cryptdlg CERT_SELECT_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 resu<
                }
            }

            @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

    ...
    }
}
  

Когда я вызываю этот метод, я получаю «java.lang.Ошибка: недопустимый доступ к памяти «при вызове dll 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)
    
  

Я не уверен, почему мы получаем исключение. Я последовал примеру, упомянутому здесь.

[ОБНОВИТЬ]

Как бы то ни было, когда я изменяю тип «setArrayCertStore» из указателя на HCERTSTORE [], я не получаю никаких исключений, но сертификат не извлекается.Нет сертификата

Это заставляет меня задуматься, правильно ли инициализирован 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 resu<
                }
            }

            @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);

    ...
    }
}
  

Комментарии:

1. В вашем обновлении вы изменили тип (в структуре) setArrayCertStore from Pointer на HCERTSTORE[] , но это неверно. Указатель на массив java отличается от собственного указателя. Вам нужно сохранить Pointer определение в структуре. Просто обновите setArrayCertStore() метод. Выполните присвоение массиву, а затем вернитесь getPointer() из 0 элемента массива, например, pCertSelectInfo.arrayCertStore = cStoreArray[0].getPointer();

2. Раздражает обсуждение JNA list и stackoverflow. Тем более, что ответ на рассылку был очень коротким.

Ответ №1:

Есть несколько потенциальных проблемных областей.

Во-первых, вы смешиваете версии ANSI и Unicode.

CertSelectCertificate() Функция имеет два варианта, один из которых заканчивается на A , а другой на W . -A Функциями являются ANSI (8-разрядные символы), в то время как -W варианты предназначены для Unicode / Wide-string (16-разрядные символы). Основное различие в методах заключается в количестве символов в строках в CERT_SELECT_STRUCT структуре., которая аналогично имеет два варианта, CERT_SELECT_STRUCT_A и CERT_SELECT_STRUCT_W .

JNA автоматически сопоставляет вас с правильной версией ( -W версией почти во всех случаях в современных операционных системах), используя свое средство сопоставления типов по умолчанию. Вероятно, вам следует явно добавить этот тип mapper при загрузке вашей библиотеки, используя параметры по умолчанию для библиотек Win32:

 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);
  

Кроме того, вы можете найти определения структур более упрощенными в JNA 5.x (в настоящее время 5.6.0), которые включают @FieldOrder аннотацию в качестве предпочтительного метода создания списка порядка полей.

Комментарии:

1. Спасибо, Дэниел, я попытался, удалив конструктор, имеющий ASCII, и все равно получаю ту же ошибку «Недопустимый доступ к памяти», я не уверен, правильно ли я сопоставил значения структуры в методе CertSelect ().

2. Просто обновил свой ответ еще несколькими возможностями. Ваши сопоставления в структуре хороши, но ваши методы, устанавливающие некоторые значения, нуждаются в некоторых изменениях.

3. Я последовал предложению, но у меня это не сработало. Я обновил вопрос, указав, нашел ли я тоже.