#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
fromPointer
на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. Я последовал предложению, но у меня это не сработало. Я обновил вопрос, указав, нашел ли я тоже.