загрузка текущего пользователя windows с нативными интерфейсами java - PullRequest
0 голосов
/ 25 апреля 2018

Я пытаюсь проверить концепцию, когда в программе cpp я получаю имя пользователя Windows, а затем вызываю эту программу из кода Java, используя собственный интерфейс Java (JNI).Теперь у меня есть пример программы JNI hello world, которая может скомпилировать и напечатать Hello world или какой-либо другой параметр, который я настроил.Теперь в отдельном фрагменте cpp я могу получить текущее имя пользователя, и оно также работает;выглядит следующим образом:

#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
    char acUserName[100];
    DWORD nUserName = sizeof(acUserName);
    if (GetUserName(acUserName, &nUserName)) {
        cout << "User name is " << acUserName << "." << endl;
        cin.get();
    }
    return 0;
}

При компиляции и выполнении с использованием моего G ++ в командной строке он правильно печатает текущее имя пользователя.

Теперь я просто хочу объединить две программы.Когда я делаю это следующим образом

public class Sample1
 {
    public native String fetchCurrentUserName();

    public static void main(String[] args)
    {
     System.loadLibrary("Sample1");
     Sample1 sample = new Sample1();     
     String  userName   = sample.fetchCurrentUserName();       
     System.out.println("userName: " + userName);

    }
 }

Это java-часть, а cpp-часть выглядит следующим образом:

 #include "Sample1.h"
 #include <string.h>
 #include <iostream>
 #include <windows.h>
 using namespace std;


  JNIEXPORT jstring JNICALL Java_Sample1_fetchCurrentUserName
   (JNIEnv *env, jobject obj) {

    char acUserName[100];
    DWORD nUserName = sizeof(acUserName);
    if (GetUserName(acUserName, &nUserName)) {
        cout << "User name is " << acUserName << "." << endl;
        cin.get();
    }

    const char* userName = acUserName;
    //const char* userName = "acUserName";
    return env->NewStringUTF(userName);
 }

 void main(){}

Это больше не компилируется, оно говорит

Sample1.obj : error LNK2019: unresolved external symbol __imp_GetUserNameA referenced in function Java_Sample1_fetchCurrentUserName
Sample1.dll : fatal error LNK1120: 1 unresolved externals

Я использовал следующую команду для компиляции кода JNI.

cl -I "C: \ Program Files \ Java \ jdk1.8.0_131 \ include" -I "C: \ Program Files \ Java\ jdk1.8.0_131 \ include \ win32 "-LD Sample1.cpp -FeSample1.dll

согласно учебнику из https://www.ibm.com/developerworks/java/tutorials/j-jni/j-jni.html. Теперь из нескольких других вопросов переполнения стека кажется, что моя программа не в состояниисвязать библиотеку Windows или лидер как-то.Но я понятия не имею, как это можно решить.У какого тела есть какие-нибудь яркие идеи?Пожалуйста, сделайте мой день!Спасибо за потраченное время: я посмотрел Связывание файла dll в JNI , что очень важно для моего вопроса.Но не уверен, что решения применимы ко мне.

Редактировать

Ошибка: LNK1120: 5 неразрешенных внешних проблем Этот вопрос имеет аналогичную проблему, и, похоже, имеет такое же решение, котороекак предложено Реми, чтобы связаться с Advapi32.lib.По крайней мере, теперь я узнал несколько новых вещей, таких как комментарии #pragma и роль библиотеки Advapi32.Большое спасибо!

1 Ответ

0 голосов
/ 25 апреля 2018

Ваш код компилируется отлично.На самом деле вы получаете ошибку linker , а не ошибку .

Из сообщения об ошибке видно, что ваш код C ++ пытается вызвать GetUserNameA() функция.GetUserName() - это макрос препроцессора #define в winbase.h (который входит в windows.h), который преобразуется в GetUserNameA(), когда UNICODE не определен:

WINADVAPI
BOOL
WINAPI
GetUserNameA (
    __out_ecount_part_opt(*pcbBuffer, *pcbBuffer) LPSTR lpBuffer,
    __inout LPDWORD pcbBuffer
    );
WINADVAPI
BOOL
WINAPI
GetUserNameW (
    __out_ecount_part_opt(*pcbBuffer, *pcbBuffer) LPWSTR lpBuffer,
    __inout LPDWORD pcbBuffer
    );
#ifdef UNICODE
#define GetUserName  GetUserNameW
#else
#define GetUserName  GetUserNameA // <-- HERE!
#endif // !UNICODE

Поскольку нетGetUserNameA() функция реализована в вашем коде C ++, но по крайней мере доступно объявление (от winbase.h), компилятор генерирует машинный код, который ссылается на GetUserNameA() извне (через символ с именем __imp_GetUserNameA).Когда линкер вызывается после завершения компиляции, он выводит «неразрешенную» ошибку, потому что он не может найти реализацию какой-либо функции GetUserNameA для удовлетворения этой ссылки.

GetUserNameA() являетсяФункция Win32 API реализована в Advapi32.dll.Вам нужно сделать ссылку на Advapi32.lib из Windows SDK вашего компилятора, чтобы компоновщик мог найти реализацию GetUserNameA().

. Один из способов сделать это - добавить оператор #pragma comment(lib) в код C ++, например::

#include "Sample1.h"
#include <windows.h>
#include <iostream>

using namespace std;

#pragma comment(lib, "Advapi32.lib") // <-- add this!

JNIEXPORT jstring JNICALL Java_Sample1_fetchCurrentUserName(JNIEnv *env, jobject obj) {
    char acUserName[100] = {};
    DWORD nUserName = sizeof(acUserName);
    if (GetUserNameA(acUserName, &nUserName)) {
        cout << "User name is " << acUserName << "." << endl;
    }
    else {
        DWORD dwErr = GetLastError();
        cout << "Unable to get User name, error " << dwErr << "." << endl;
    }

    return env->NewStringUTF(acUserName);
}

void main() {}

В качестве альтернативы:

#include "Sample1.h"
#include <windows.h>
#include <iostream>
#include <vector>

using namespace std;

#pragma comment(lib, "Advapi32.lib")

JNIEXPORT jstring JNICALL Java_Sample1_fetchCurrentUserName(JNIEnv *env, jobject obj) {
    DWORD nUserName = 100, dwErr;
    std::vector<char> acUserName(nUserName);
    do {
        if (GetUserNameA(&acUserName[0], &nUserName)) {
            cout << "User name is " << &acUserName[0] << "." << endl;
            break;
        }
        dwErr = GetLastError();
        if (dwErr != ERROR_INSUFFICIENT_BUFFER) {
            cout << "Unable to get the User name, error " << dwErr << "." << endl;
            break;
        }
        acUserName.resize(nUserName);
    }
    while (true);

    return env->NewStringUTF(&acUserName[0]);
}

void main() {}

При этом для NewStringUTF() требуется строка ввода в измененном UTF-8 формате,Однако GetUserNameA() выводит формат ANSI (отсюда и название A), а не в UTF-8, не говоря уже о , измененном UTF-8.В Windows вообще нет понятия модифицированного UTF-8.Ваш код будет работать правильно только в том случае, если имя пользователя не содержит никаких символов, отличных от ASCII.

Ваш код C ++ действительно должен вместо этого вызывать GetUserNameW(), который выводит в формате UTF-16.Тогда вы можете использовать NewString() вместо NewStringUTF() (строки Java в любом случае используют UTF-16), например:

#include "Sample1.h"
#include <windows.h>
#include <iostream>

using namespace std;

#pragma comment(lib, "Advapi32.lib")

JNIEXPORT jstring JNICALL Java_Sample1_fetchCurrentUserName(JNIEnv *env, jobject obj) {
    WCHAR acUserName[100];
    DWORD nUserName = 100;
    if (GetUserNameW(acUserName, &nUserName)) {
        --nUserName; // ignore the null terminator
        wcout << L"User name is " << acUserName << L"." << endl;
    }
    else
    {
        nUserName = 0;
        DWORD dwErr = GetLastError();
        cout << "Unable to get the User name, error " << dwErr << "." << endl;
    }

    return env->NewString(reinterpret_cast<jchar*>(acUserName), nUserName);
}

void main() {}

В качестве альтернативы:

#include "Sample1.h"
#include <windows.h>
#include <iostream>
#include <vector>

using namespace std;

#pragma comment(lib, "Advapi32.lib")

JNIEXPORT jstring JNICALL Java_Sample1_fetchCurrentUserName(JNIEnv *env, jobject obj) {
    DWORD nUserName = 100, dwErr;
    std::vector<WCHAR> acUserName(nUserName);
    do {
        if (GetUserNameW(&acUserName[0], &nUserName)) {
            --nUserName; // ignore the null terminator
            wcout << L"User name is " << &acUserName[0] << L"." << endl;
            break;
        }
        dwErr = GetLastError();
        if (dwErr != ERROR_INSUFFICIENT_BUFFER) {
            nUserName = 0;
            cout << "Unable to get the User name, error " << dwErr << "." << endl;
            break;
        }
        acUserName.resize(nUserName);
    }
    while (true);

    return env->NewString(reinterpret_cast<jchar*>(&acUserName[0]), nUserName);
}

void main() {}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...