(也许我用烦人的细节解释了这个问题,tl;dr 版本在底部)
我一直在尝试使用 JNA 在 Java 中创建一个简单的 Winsock 测试应用程序。我调用的第一个函数是 WSAStartup(),使用以下代码:
public interface Ws2_32 extends Library {
Ws2_32 INSTANCE = (Ws2_32) Native.loadLibrary("ws2_32", Ws2_32.class);
int WSAStartup(short version, LPWSADATA lpwsaData);
}
public static void main(String[] args){
LPWSADATA lpwsaData = new LPWSADATA();
short version = 2;
int result = Ws2_32.INSTANCE.WSAStartup(version,lpwsaData);
System.out.println("WSAStartup() returned: " + result);
if((resultado = Ws2_32.INSTANCE.WSAStartup(version,lpwsaData)) == 0){
System.out.println("LPWSADATA struct:");
System.out.println("wVersion: " + lpwsaData.wVersion);
System.out.println("wHighVersion: " + lpwsaData.wHighVersion);
System.out.print("szDescription: ");
for(byte b : lpwsaData.szDescription){
System.out.print((char) b);
}
System.out.print("\n");
System.out.print("szSystemStatus: ");
for(byte b : lpwsaData.szSystemStatus){
System.out.print((char) b);
}
System.out.print("\n");
System.out.println("iMaxSockets: " + lpwsaData.iMaxSockets);
System.out.println("iMaxUdpDg: " + lpwsaData.iMaxUdpDg");
}
}
这有效,我得到以下值:
w版本:2
wHighVersion:514
szDescription: WinSock 2.0
szSystemStatus:正在运行
iMaxSockets: 0
iMaxUdpDg: 0
WSAStartup() 成功返回后,我尝试调用 WSAEnumProtocols() 但出现以下错误:
线程“主”java.lang.UnsatisfiedLinkError 中的异常:查找函数“WSAEnumProtocols”时出错:找不到指定的过程。
然后我使用 Dependency Walker 打开 ws2_32.dll,我发现没有同名的函数。我只找到了 3 个名称相似的:WSAEnumProtocolsA()、WSAEnumProtocolsW() 和 WSCEnumProtocols()。我将使用 WSAEnumProtocolsA() 作为示例,但我对所有 3 个都使用了下一个过程并得到了相同的结果:
首先我调用了 WSAStartup() 并且没有返回错误。根据 WSAEnumProtocols 的 MSDN 定义,第一次调用该函数应该是这样的:缓冲区。如果该长度为零,则函数应返回 -1 (SOCKET_ERROR),并且对 WSAGetLastError() 的调用应返回 WSAENOBUFS,这意味着缓冲区不够大,无法包含 WSAEnumProtocols() 返回的信息,应设置变量 lpdwBufferLength具有可以传递给 WSAEnumProtocols 以检索所有请求信息的最小缓冲区大小。我不能让它工作。
我还尝试使用 WSASetLastError() 并将其设置为其他错误代码,然后调用 WSAGetLastError(),但它始终返回 0。
tl;博士 我无法让 WSAEnumProtocols(null, wsaprotocol_info, lpdwBufferLength) 工作。WSAEnumProtocols() 返回 -1 但 lpdwBufferLength 的值没有改变, WSAGetLastError() 返回 0 而不是 10055 (WSAENOBUFS)
更新:
这是我用来声明 Winsock 函数的接口
public interface Ws2_32 extends Library {
Ws2_32 INSTANCE = (Ws2_32) Native.loadLibrary("ws2_32", Ws2_32.class);
int WSAStartup(short version, LPWSADATA lpwsaData);
int WSAEnumProtocolsW(int[] lpiProtocols, WSAPROTOCOL_INFO lpProtocolBuffer, int lpdwBufferLength);
int WSACleanup();
int WSAGetLastError();
int WSASetLastError(int iError);
}
这是我调用函数的代码:
public class TestWSAEnumProtocolsA {
public void start(){
WSAPROTOCOL_INFO wsaprotocol_info = new WSAPROTOCOL_INFO();
LPWSADATA lpwsaData = new LPWSADATA();
int lpdwBufferLength = -2;
int result = 0;
short version = 514;
if((result = Ws2_32.INSTANCE.WSAStartup(version, lpwsaData)) != 0){
System.out.println("Error #" + result + " at WSAStartup()");
return;
} else {
System.out.println("WSAStartup() finished correctly.");
if((result = Ws2_32.INSTANCE.WSAEnumProtocols(null, wsaprotocol_info, lpdwBufferLength)) == -1){
System.out.println("WSAEnumProtocolsW() returned: " + result);
System.out.println("lpdwBufferLength is: " + lpdwBufferLength);
System.out.println("WSAGetLastError() returned: " + Ws2_32.INSTANCE.WSAGetLastError());
System.out.println("Now I'm setting it to 10004");
Ws2_32.INSTANCE.WSASetLastError(10004);
System.out.println("WSAGetLastError() returned: " + Ws2_32.INSTANCE.WSAGetLastError());
}
}
}
}
该代码产生以下输出:
WSAStartup() 正确完成。
WSAEnumProtocolsW() 返回:-1
lpdwBufferLength 为:-2
WSAGetLastError() 返回:0
现在我将其设置为 10004
WSAGetLastError() 返回:0
这就是我定义调用这些函数时使用的结构的方式:
public class WinSock2_structs {
public static class LPWSADATA extends Structure{
public short wVersion;
public short wHighVersion;
public byte szDescription[] = new byte[256+1];
public byte szSystemStatus[] = new byte[128+1];
public short iMaxSockets;
public short iMaxUdpDg;
public char lpVendorInfo;
}
public static class WSAPROTOCOLCHAIN extends Structure{
public int ChainLen;
public int ChainEntries[] = new int[7];
}
public static class GUID extends Structure{
public int Data1;
public short Data2;
public short Data3;
public short Data4;
public byte Data5[] = new byte[8];
}
public static class WSAPROTOCOL_INFO extends Structure{
public int dwServiceFlags1;
public int dwServiceFlags2;
public int dwServiceFlags3;
public int dwServiceFlags4;
public int dwProviderFlags;
public GUID ProviderId;
public int dwCatalogEntryId;
public WSAPROTOCOLCHAIN ProtocolChain;
public int iVersion;
public int iAddressFamily;
public int iMaxSockAddr;
public int iMinSockAddr;
public int iSocketType;
public int iProtocol;
public int iProtocolMaxOffset;
public int iNetworkByteOrder;
public int iSecurityScheme;
public int dwMessageSize;
public int dwProviderReserved;
public char szProtocol[] = new char[256];
}
}