3

我已经映射了 C++ 函数(来自 WLanapi.dll):

    DWORD WINAPI WlanHostedNetworkQueryStatus(
      _In_        HANDLE hClientHandle,
      _Out_       PWLAN_HOSTED_NETWORK_STATUS *ppWlanHostedNetworkStatus,
      _Reserved_  PVOID pvReserved
    );

到以下 C# 代码:

    [DllImport("Wlanapi.dll", SetLastError = true)]
    static extern UInt32 WlanHostedNetworkQueryStatus(
        [In] IntPtr hClientHandle,
        [Out] out _WLAN_HOSTED_NETWORK_STATUS ppWlanHostedNetworkStatus,
        [In, Out] IntPtr pvReserved
        );

我还映射了所需的所有结构和枚举以及其他内容(例如,获取 clientHandle 指针并启动托管网络)。
_WLAN_HOSTED_NETWORK_STATUS 映射如下:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct _WLAN_HOSTED_NETWORK_STATUS
    {
        public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
        public Guid IPDeviceID;
        public _DOT11_MAC_ADDRESS wlanHostedNetworkBSSID;
        public _DOT11_PHY_TYPE dot11PhyType;
        public UInt32 ulChannelFrequency;
        public UInt32 dwNumberOfPeers;
        public _WLAN_HOSTED_NETWORK_PEER_STATE[] PeerList;
    }

现在执行该功能时,我不确定如何正确使用 ppWlanHostedNetworkStatus 等。该函数返回 ERROR_SUCCESS (0) 这意味着我已经调用它并正确传递了参数:

    _WLAN_HOSTED_NETWORK_STATUS netStatus = new _WLAN_HOSTED_NETWORK_STATUS();

    WlanHostedNetworkQueryStatus(clientHandle, out netStatus, IntPtr.Zero);

但是在查询 ppWlanHostedNetworkStatus 的值(如网络状态或已连接对等点的数量)时,我得到了一些奇怪的长整数(我会说内存地址,但我不确定),例如调用:

netStatus.HostedNetworkState.ToString();

退货

11465720

HostedNetworkState 是一个枚举,定义如下:

    public enum _WLAN_HOSTED_NETWORK_STATE
    {
        wlan_hosted_network_unavailable,
        wlan_hosted_network_idle,
        wlan_hosted_network_active
    }

所以 .toString() 应该从枚举中返回这些字符串之一,对吧?
我很确定这与指针等有关,因为在 _WLAN_HOSTED_NETWORK_STATUS 的文档(MS 文档)中说,在调用该函数之前,ppWlanHostedNetworkStatus 应该为 NULL,并且它本身就是指向结构体...

我该如何调试它?我正在用 C#、VS 2012 编写代码...

谢谢你的帮助。

-----编辑-----
我进一步尝试使用 IntPtr 作为参数来映射函数,传递 IntPtr.Zero 和 Marshal.PtrToStruct,但是在尝试这样做时我得到了 AccessViolationException...

    [DllImport("Wlanapi.dll", SetLastError = true)]
    static extern UInt32 WlanHostedNetworkQueryStatus(
        [In] IntPtr hClientHandle,
        [Out] out IntPtr ppWlanHostedNetworkStatus,
        [In, Out] IntPtr pvReserved
        );

进而:

    IntPtr ppStatus = IntPtr.Zero;

    WlanHostedNetworkQueryStatus(clientHandle, out ppStatus, IntPtr.Zero);

    _WLAN_HOSTED_NETWORK_STATUS netStatus = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ppStatus, typeof(_WLAN_HOSTED_NETWORK_STATUS));

------编辑2------

根据 Fermat2357 的建议,我取消了要映射的结构的部分注释,并更改以下内容以计算指向指针的指针:

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct _WLAN_HOSTED_NETWORK_STATUS
    {
        public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
        public Guid IPDeviceID;
        public _DOT11_MAC_ADDRESS wlanHostedNetworkBSSID;
        public _DOT11_PHY_TYPE dot11PhyType;
        public UInt32 ulChannelFrequency;
        public UInt32 dwNumberOfPeers;
        //public _WLAN_HOSTED_NETWORK_PEER_STATE[] PeerList;
    }

我这样称呼它:

    IntPtr ppStatus = IntPtr.Zero;
    WlanHostedNetworkQueryStatus(clientHandle, out ppStatus, IntPtr.Zero);
    IntPtr ppStatus2 = new IntPtr(ppStatus.ToInt32());
    _WLAN_HOSTED_NETWORK_STATUS stat = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ppStatus2, typeof(_WLAN_HOSTED_NETWORK_STATUS));
    netStatus = stat.HostedNetworkState.ToString();

这终于给了我正确的网络状态(启动后处于活动状态)......现在我必须找到一种方法来编组该动态数组......
到目前为止感谢Fermat2357的帮助

4

2 回答 2

2

您的映射不正确。看一下 API 函数WlanHostedNetworkQueryStatus的定义。

DWORD WINAPI WlanHostedNetworkQueryStatus(
  _In_        HANDLE hClientHandle,
  _Out_       PWLAN_HOSTED_NETWORK_STATUS *ppWlanHostedNetworkStatus,
  _Reserved_  PVOID pvReserved
);

请注意,参数ppWlanHostedNetworkStatus是指向WLAN_HOSTED_NETWORK_STATUS结构指针的指针。深入了解您会发现的函数的文档

ppWlanHostedNetworkStatus [出]

在输入时,此参数必须为 NULL。

在输出时,如果对 WlanHostedNetworkQueryStatus 函数的调用成功,则此参数接收指向无线托管网络当前状态的指针。当前状态在 WLAN_HOSTED_NETWORK_STATUS 结构中返回。

如果您提供一个NULL指针(如文档中所述),则底层 API 将为您分配一个缓冲区来保存结构并初始化指向该缓冲区的指针。稍后不要忘记通过调用来释放它WlanFreeMemory。否则你会在这里发生资源泄漏。

但是,该功能的文档似乎不是 100% 完整的。在我的测试(Win7 32Bit)中,如果您将指针初始化为足够大的内存缓冲区,API 将不会为您分配内存。WlanFreeMemory在这种情况下,似乎没有必要稍后调用。WLAN_HOSTED_NETWORK_PEER_STATE但在这种情况下,很难猜测以下结构需要多少内存。出于这个原因,这似乎无论如何都无法使用。

这是我用于测试的 C 代码

#include <Wlanapi.h>

int _tmain(int argc, _TCHAR* argv[])
{
    DWORD dwRes;
    HANDLE hHandle;
    DWORD negotiatedVersion;

    dwRes = WlanOpenHandle(1, NULL, &negotiatedVersion, &hHandle);
    if (ERROR_SUCCESS == dwRes)
    {
        PWLAN_HOSTED_NETWORK_STATUS pStatus = NULL;

        dwRes = WlanHostedNetworkQueryStatus(hHandle, &pStatus, NULL);
        if (ERROR_SUCCESS == dwRes)
        {
            if (wlan_hosted_network_unavailable != pStatus->HostedNetworkState)
            {
                // Do something with the result
            }

            WlanFreeMemory(pStatus);
        }
        else
        {
            // handle Error
        }

        WlanCloseHandle(hHandle, NULL);
    }
    else
    {
        // handle Error
    }

    return 0;
}

如果您想让您的示例工作,您需要修改编组结构的方式。

编辑

要正确编组,您可以尝试以下操作:

...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
    public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
    public Guid IPDeviceID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 6)]
    public string wlanHostedNetworkBSSID;
    public _DOT11_PHY_TYPE dot11PhyType;
    public UInt32 ulChannelFrequency;
    public UInt32 dwNumberOfPeers;
    public _WLAN_HOSTED_NETWORK_PEER_STATE PeerList;
}

...

IntPtr ptr = new IntPtr();
uint hostedNetworkQueryStatusSuccess = WlanHostedNetworkQueryStatus(clientHandle, out ptr, IntPtr.Zero);
if (openHandleSuccess == 0)
{
    var netStat = (_WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(ptr, typeof(_WLAN_HOSTED_NETWORK_STATUS));
    Console.WriteLine(netStat.HostedNetworkState);

    if (netStat.HostedNetworkState != _WLAN_HOSTED_NETWORK_STATE.wlan_hosted_network_unavailable)
    {
        IntPtr offset = Marshal.OffsetOf(typeof(_WLAN_HOSTED_NETWORK_STATUS), "PeerList");

        for (int i = 0; i < netStat.dwNumberOfPeers; i++)
        {
            var peer = (_WLAN_HOSTED_NETWORK_PEER_STATE)Marshal.PtrToStructure(
            new IntPtr(ptr.ToInt64() + offset.ToInt64()), 
            typeof(_WLAN_HOSTED_NETWORK_PEER_STATE));

            System.Console.WriteLine(peer.PeerMacAddress);

            offset += Marshal.SizeOf(peer);
        }
    }
}

想法:我在这里使用了一个结构,我可以确定它一直都是正确编组的。后来在dwNumberOfPeers 成员的知识下(如果它是有效的),我一步一步地得到了所有的内部结构。

于 2012-12-10T16:51:52.033 回答
0

编辑:要尝试的一件事是我用来帮助调试的技巧 - 用字节数组替换你的结构,然后看看你拉回了什么(好的,在这种情况下是一个 uint 数组):

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType=UnmanagedType.U4, SizeConst = 48)]
    public uint[] scratch;
}

但是,当我在本地运行它时,我得到了非常奇怪的值 - 第一个 (10x4) = 40 字节被清零:

0 0 0 0 
0 0 0 0 
0 0 4D454D4C 28 
16DCD870 0 787F6447 80000020 
...omitted...

试试这组 P/Invoke 声明:

(在 LINQPad 中完成,因此相应地替换“转储”方法)

void Main()
{
    IntPtr clientHandle;
    int negotiatedVersion;
    if(WlanOpenHandle(2, IntPtr.Zero, out negotiatedVersion, out clientHandle) != 0)
    {
        throw new InvalidOperationException("Could not open handle");
    }
    Console.WriteLine("Negotiated version:{0}", negotiatedVersion);
    IntPtr pNetStatus = IntPtr.Zero;
    if(WlanHostedNetworkQueryStatus(clientHandle, out pNetStatus, IntPtr.Zero) != 0)
    {
        throw new InvalidOperationException("Could not query network status");
    }
    var netStatus = (WLAN_HOSTED_NETWORK_STATUS)Marshal.PtrToStructure(pNetStatus, typeof(WLAN_HOSTED_NETWORK_STATUS));
    Console.WriteLine(netStatus.PeerList[0]);
    WlanFreeMemory(pNetStatus);
    WlanCloseHandle(clientHandle, IntPtr.Zero);
}


[DllImport("Wlanapi.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool WlanOpenHandle(
  [In] int dwClientVersion,
  IntPtr pReserved,
  [Out] out int pdwNegotiatedVersion,
  [Out] out IntPtr phClientHandle
);
[DllImport("Wlanapi.dll", SetLastError = true)]
[return:MarshalAs(UnmanagedType.Bool)]
public static extern bool WlanCloseHandle(
  [In] IntPtr hClientHandle,
  IntPtr pReserved
);
[DllImport("Wlanapi.dll", SetLastError = true)]
static extern UInt32 WlanHostedNetworkQueryStatus(
    [In] IntPtr hClientHandle,
    [Out] out IntPtr ppWlanHostedNetworkStatus,
    [In, Out] IntPtr pvReserved
);

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_STATUS
{
    public _WLAN_HOSTED_NETWORK_STATE HostedNetworkState;
    public Guid IPDeviceID;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)]
    public string wlanHostedNetworkBSSID;
    public _DOT11_PHY_TYPE dot11PhyType;
    public UInt32 ulChannelFrequency;
    public UInt32 dwNumberOfPeers;
    public IntPtr PeerList;
}
public enum _WLAN_HOSTED_NETWORK_STATE 
{ 
  wlan_hosted_network_unavailable,
  wlan_hosted_network_idle,
  wlan_hosted_network_active
}
public enum _DOT11_PHY_TYPE : uint
{ 
  dot11_phy_type_unknown     = 0,
  dot11_phy_type_any         = 0,
  dot11_phy_type_fhss        = 1,
  dot11_phy_type_dsss        = 2,
  dot11_phy_type_irbaseband  = 3,
  dot11_phy_type_ofdm        = 4,
  dot11_phy_type_hrdsss      = 5,
  dot11_phy_type_erp         = 6,
  dot11_phy_type_ht          = 7,
  dot11_phy_type_IHV_start   = 0x80000000,
  dot11_phy_type_IHV_end     = 0xffffffff
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct _WLAN_HOSTED_NETWORK_PEER_STATE 
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst=6)]
    public string PeerMacAddress;
    _WLAN_HOSTED_NETWORK_PEER_AUTH_STATE PeerAuthState;
}
public enum _WLAN_HOSTED_NETWORK_PEER_AUTH_STATE 
{ 
  wlan_hosted_network_peer_state_invalid,
  wlan_hosted_network_peer_state_authenticated
}
于 2012-12-10T17:06:02.983 回答