【问题标题】:fetching windows current user with java native interfaces使用java本机接口获取windows当前用户
【发布时间】:2018-04-25 19:18:08
【问题描述】:

我正在尝试在 cpp 程序中获取 Windows 用户名,然后使用 Java 本机接口 (JNI) 从 Java 代码中调用该程序来进行概念验证。现在我所拥有的是一个示例 JNI hello world 程序,它能够编译和打印 Hello world 或我设置的任何参数。现在在一个单独的 cpp sn-p 中,我能够获取当前用户名并且它也可以工作;如下所示:

#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 库或领导者。但我不知道如何解决这个问题。任何机构有什么好主意吗?请让我开心!感谢您抽出宝贵时间:D 我查看了Linking of dll file in JNI,这与我的问题非常相关。但不确定这些解决方案是否适用于我。

编辑

error: LNK1120: 5 unresolved externals 这个问题有类似的问题,它似乎有与 Remy 建议的与 Advapi32.lib 链接的相同解决方案。至少现在我学到了一些新东西,比如#pragma cmets 和 Advapi32 库的作用。非常感谢!

【问题讨论】:

  • 您的 C++ 代码需要链接到 Advapi32.lib,要么通过项目选项中的链接器参数,要么通过 C++ 代码本身中的 #pragma comment(lib) 语句。否则,您可以使用 Win32 API LoadLibrary()GetProcAddress() 函数在运行时直接从 Advapi32.dll 获取指向 GetUserNameA() 的指针,而不是在编译时链接到 Advapi32.lib
  • 另一方面,为什么在 C++ 中使用 GetUserName()? Java 对用户名有自己的访问权限:Get login username in java
  • 我在其他问题中看到了这个解决方案。但是我自己不知道这个 Advapi32.lib 做了什么。你能告诉我为什么这对我来说很重要,我在哪里可以找到执行任务的确切命令/代码。我是 cpp 和 JNI 的超级菜鸟,都很抱歉。 :(
  • 这与JNI无关。这就是 C++ 编译器\链接器的一般工作方式。 Advapi32.libAdvapi32.dll 的导入库,它告诉链接器从 DLL 导出哪些函数以及如何导出。链接器使用导入库来解析 C++ 代码进行的 DLL 函数调用。
  • 我给你贴了更详细的答案

标签: java c++ winapi java-native-interface


【解决方案1】:

您的代码编译很好。您实际上得到的是 linker 错误,而不是 compiler 错误。

从错误消息中,您可以看到您的 C++ 代码正在尝试调用 GetUserNameA() 函数。 GetUserName()winbase.h(包含在 windows.h 中)中的预处理器 #define 宏,当 UNICODE 未定义时解析为 GetUserNameA()

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

由于您的 C++ 代码中没有实现 GetUserNameA() 函数,但至少有一个可用的声明(来自 winbase.h),编译器会发出在外部引用 GetUserNameA() 的机器代码(通过名为 @987654333 的符号@)。当编译完成后调用链接器时,链接器会输出“未解决”错误,因为它无法找到任何GetUserNameA 函数的实现来满足该引用。

GetUserNameA() 是在Advapi32.dll 中实现的 Win32 API 函数。您需要从编译器的 Windows SDK 链接到 Advapi32.lib,以便链接器可以找到 GetUserNameA() 的实现。

一种方法是在您的 C++ 代码中添加 #pragma comment(lib) 语句,例如:

#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() 需要modified UTF-8 格式的输入字符串。但是,GetUserNameA() 输出 ANSI 格式(因此在其名称中使用 A),而不是 UTF-8,更不用说 modified UTF-8。 Windows 根本没有 modified 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() {}

【讨论】:

  • 非常感谢您的详细解答。
猜你喜欢
  • 2023-03-31
  • 2010-10-19
  • 2012-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多