(也许我用烦人的细节级别解释了这个问题,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");
    }
}


这有效,并且我得到以下值:


wVersion:2

wHighVersion:514

sz说明: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定义,对该函数的首次调用应如下所示:
WSAEnumProtocols(null,wsaprotocol_info,lpdwBufferLength)
第一个参数为null,第二个参数为指向WSAPROTOCOL_INFO结构的指针,第三个为缓冲区的长度。如果该长度为零,则该函数应返回-1(SOCKET_ERROR),并且对WSAGetLastError()的调用应返回WSAENOBUFS,这意味着缓冲区不够大,无法包含WSAEnumProtocols()返回的信息,并且应设置变量lpdwBufferLength具有可以传递给WSAEnumProtocols的最小缓冲区大小,以检索所有请求的信息。
我无法使它正常工作。 WSAEnumProtocols()返回-1,但lpdwBufferLength的值未更改,WSAGetLastError()返回0而不是10055(WSAENOBUFS)

我还尝试使用WSASetLastError()并将其设置为其他错误代码,然后调用WSAGetLastError(),但它始终返回0。

tl; dr
我无法使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];

    }
}

最佳答案

您两次调用WSAStartup(),这将要求您两次调用WSACleanup()才能正确卸载WinSock。您应该只调用一次WSAStartup()

szProtocol结构的WSAPROTOCOL_INFO成员是TCHAR元素的数组。 TCHAR映射到charwchar_t取决于调用应用程序是针对ANSI / MBCS还是UNICODE编译的。因此,ws2_32.dll中没有WSAEnumProtocols()函数。相反,有单独的WSAEnumProtocolsA()(对于Ansi)和WSAEnumProtocolsW()(对于Unicode)函数。由于Java使用Unicode字符串,因此您应该使JNA代码使用WSAEnumProtocolsW()WSAStartup()不使用TCHAR,仅使用char,这就是为什么没有单独的WSAStartupA()WSAStartupW()功能的原因。

如果您的JNA代码无法正确运行WSAEnumProtocols()WSAGetLastError(),则可能是您在声明/错误使用它们,但是您没有显示任何代码,因此没有人可以确定为什么不为你工作。

更新:尝试这样的事情(我不使用JNA,因此可能需要进行一些调整,但这将为您提供总体思路):

public interface Ws2_32 extends Library {

    // I don't know how to declare fixed size arrays in JNA,
    // so you will have to adjust these Structue declarations
    // as needed...

    public static class WSAData extends Structure {
        short wVersion;
        short wHighVersion;
        byte  szDescription[257];
        byte  szSystemStatus[129];
        short iMaxSockets;
        short iMaxUdpDg;
        String lpVendorInfo;
    };

    public static class WSAPROTOCOLCHAIN extends Structure {
        int ChainLen;
        int ChainEntries[7];
    };

    public static class WSAPROTOCOL_INFOW extends Structure {
        int dwServiceFlags1;
        int dwServiceFlags2;
        int dwServiceFlags3;
        int dwServiceFlags4;
        int dwProviderFlags;
        GUID ProviderId;
        int dwCatalogEntryId;
        WSAPROTOCOLCHAIN ProtocolChain;
        int iVersion;
        int iAddressFamily;
        int iMaxSockAddr;
        int iMinSockAddr;
        int iSocketType;
        int iProtocol;
        int iProtocolMaxOffset;
        int iNetworkByteOrder;
        int iSecurityScheme;
        int dwMessageSize;
        int dwProviderReserved;
        char szProtocol[256];
    };

    Ws2_32 INSTANCE = (Ws2_32) Native.loadLibrary("ws2_32", Ws2_32.class);

    int WSAStartup(short version, WSADATA lpwsaData);
    int WSAEnumProtocolsW(int[] lpiProtocols, WSAPROTOCOL_INFOW[] lpProtocolBuffer, IntByReference lpdwBufferLength);
    int WSACleanup();
    int WSAGetLastError();
    int WSASetLastError(int iError);
}




public static void main(String[] args){
    WSADATA     wsaData = new WSADATA();
    short       version = 2;
    int         result = Ws2_32.INSTANCE.WSAStartup(version, wsaData);

    System.out.println("WSAStartup() returned: " + result);

    if(result == 0){
        System.out.println("WSADATA struct:");
        System.out.println("wVersion: " + wsaData.wVersion);
        System.out.println("wHighVersion: " + wsaData.wHighVersion);

        System.out.print("szDescription: ");
        for(byte b : wsaData.szDescription){
            System.out.print((char) b);
        }
        System.out.print("\n");

        System.out.print("szSystemStatus: ");
        for(byte b : wsaData.szSystemStatus){
            System.out.print((char) b);
        }
        System.out.print("\n");

        System.out.println("iMaxSockets: " + wsaData.iMaxSockets);
        System.out.println("iMaxUdpDg: " + wsaData.iMaxUdpDg");
    }
}




public class TestWSAEnumProtocolsA {

    public void start(){

        WSAPROTOCOL_INFOW[]     wsaprotocol_info = new WSAPROTOCOL_INFOW[1];
        WSADATA                 wsaData = new WSADATA();
        IntByReference          dwBufferLength = new IntByReference(628); // sizeof WSAPROTOCOL_INFOW, in bytes
        int                     result = 0;
        short                   version = 2;

        if((result = Ws2_32.INSTANCE.WSAStartup(version, wsaData)) != 0){
            System.out.println("Error #" + result + " at WSAStartup()");
            return;
        }

        System.out.println("WSAStartup() finished correctly.");

        if((result = Ws2_32.INSTANCE.WSAEnumProtocolsW(null, wsaprotocol_info, dwBufferLength)) == -1){
            System.out.println("WSAEnumProtocolsW() returned: " + result);
            System.out.println("dwBufferLength is: " + dwBufferLength.getValue());
            System.out.println("WSAGetLastError() returned: " + Ws2_32.INSTANCE.WSAGetLastError());
        }
    }
}

09-28 11:53