记一次使用WIN api中的wifi有关操作并借助CLR生成链接库供C#的WPF程序调用
最后带有完整代码
WlanConnect的参数
DWORD WlanConnect(
[in] HANDLE hClientHandle,
[in] const GUID *pInterfaceGuid,
[in] const PWLAN_CONNECTION_PARAMETERS pConnectionParameters,
PVOID pReserved
);
其参数如下,主要的参数就是pConnectionParameters,它的其中一个成员wlanConnectionMode有许多模式可供选择,网上找的别人写的范例都是将其设为wlan_connection_mode_profile ,但每次运行都抛出参数错误提示,也就是返回ERROR_INVALID_PARAMETER错误
在WlanConnect的返回值说明中,ERROR_INVALID_PARAMETER错误有12种之多,其中一条写道:
- The wlanConnectionMode member of the structure pointed to by pConnectionParameters is set to wlan_connection_mode_profile, and the strProfile member of the same structure is NULL or the length of the profile exceeds WLAN_MAX_NAME_LENGTH.
就是说如果设置为wlan_connection_mode_profile 模式,相应的配置文件strProfile不能为空,并且不能超过WLAN_MAX_NAME_LENGTH(该值在我的机器上定义为256),而我的配置文件早就超过了这个值。好几天后,尝试将连接模式设置为wlan_connection_mode_temporary_profile,才能成功执行WlanConnect函数。
感觉微软的文档有时候说明了怎么做不对,却没有说明要具体怎么做(可能我水平太低)
最后虽然能够完成连接网络的代码,但是windows状态栏点开wifi图标后却显示没有连接,这个真的不知道问题出在哪里,而用cmd的wlan命令没有这个问题
静态编译(将dll文件整合到exe程序中)
上述连接wifi代码编译后还附加几个dll,毕竟只是一个小文件,移植到别人的还要用压缩包,停麻烦的,遂整合成一个文件。
我使用VS编译代码,按网上说的将项目属性>>C++>>代码生成 中的运行库选项更改为/MT,但是并不能奏效,因为我还使用了除winapi外的库,仅仅更改此选线没用。
笔者使用vcpkg安装编译其他库,而vcpkg具有在编译阶段生成静态库的功能(详见vcpkg文档网页中搜索static),而项目属性中也有Use Static Library选项,结合/MT,确实将dll文件整合到一个exe中了,但是移植到我同学的电脑上时,运行又失败了,就是那个另外引入的库运行失败,不知道具体原因。
网上还有用winrar打包的,没使用过,以后可以试试看
(中间还尝试过使用.def文件生成dll,但是自己写的导出函数都报ERROR LINK,只好换个方向)
C++通过CLR编译为动态库供WPF使用
建议在创建项目的时候选择CLR类库

并且不要改乱改适用的.NET版本,否则虽然dll文件生成了却无法调用。而且wpf项目使用的.NET版本应该和库文件使用的一样(也不一定,dll使用.NETCore3.1,wpf使用.Net5.0我也成功运行了),Release版本的dll应该对应Release版本的WPF,并且同时x64或x86
编写适用与CLR的C++代码时,有些winapi内置类型像句柄,在取址的时候,会自动转换成托管类型(属于C#的类型,但是winapi无法接收),而且我没找到方法去转换,但微软文档里写道:
An interior pointer can only be declared on the stack. An interior pointer cannot be declared as a member of a class.
一个interior指针(C#中的托管类型)只能在栈中被声明,该指针不能被声明为类成员
也就是说,不能在类中定义这些HANDLE之类的变量,但可以声明为全局变量。
生成后的dll文件在引用后可以在WPF工程项目的对象浏览器中查看导出的函数,但使用dumpbin或Dependencies工具查不到导出的函数
该方法相比每个函数都使用dllimport,对原C++文件和C#代码都没有太多的附加操作,简便了许多
不知道真正的C++写内核,C#写界面是什么样的,欢迎讨论和交流
CLR项目头文件
//CLR项目头文件 ClassLibrary2.h
#pragma once
#include<Windows.h>
#include<wlanapi.h>
#include <stdio.h>
#include<curl/curl.h>
#include<locale>
using namespace System;
HANDLE hClientHandle;
PWLAN_INTERFACE_INFO_LIST pInterfaceList;
DWORD* dwDataSize;
VOID* pData;
WLAN_OPCODE_VALUE_TYPE* opcodeValueType;
WLAN_AVAILABLE_NETWORK_LIST* pAvailableNetworkList;
PWLAN_PROFILE_INFO_LIST pProfileList;
//校园网,无密码,连接后需要网络登录验证;有密码的导出自己本机wifi配置替换strProfileXml中的数据,详细看微软文档
LPCWSTR strProfileXml=TEXT(R"(<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
<name>NIIT-WIFI</name>
<SSIDConfig>
<SSID>
<hex>WIFI名称的hex值,可以不填</hex>
<name>WIFI名称</name>
</SSID>
</SSIDConfig>
<connectionType>ESS</connectionType>
<connectionMode>auto</connectionMode>
<MSM>
<security>
<authEncryption>
<authentication>open</authentication>
<encryption>none</encryption>
<useOneX>false</useOneX>
</authEncryption>
</security>
</MSM>
<MacRandomization xmlns="http://www.microsoft.com/networking/WLAN/profile/v3">
<enableRandomization>false</enableRandomization>
<randomizationSeed>3036961407</randomizationSeed>
</MacRandomization>
</WLANProfile>
)");;
namespace ClassLibrary2 {
public ref class myWlan
{
public:
myWlan();
~myWlan();
BOOL openWlanService();
BOOL enumWlanInterfaces();
BOOL queryInterfacePara();
BOOL setWlanInterface(int state);
BOOL scanWlan();
BOOL getAvailableNetList();
BOOL setWlanProfile(LPCWSTR strProfileXml);
BOOL getWlanProfile();
BOOL connectWlan(LPCWSTR strProfileXml);
BOOL loginWeb();
};
}
CLR代码实现
#include "pch.h"
#include "ClassLibrary2.h"
namespace ClassLibrary2
{
myWlan::myWlan()
{
hClientHandle = (HANDLE)WlanAllocateMemory(sizeof(HANDLE));
pInterfaceList = (WLAN_INTERFACE_INFO_LIST*)WlanAllocateMemory(sizeof(WLAN_INTERFACE_INFO_LIST*));
opcodeValueType = (WLAN_OPCODE_VALUE_TYPE*)WlanAllocateMemory(sizeof(WLAN_OPCODE_VALUE_TYPE*));
pAvailableNetworkList = (WLAN_AVAILABLE_NETWORK_LIST*)WlanAllocateMemory(sizeof(WLAN_AVAILABLE_NETWORK_LIST*));
pProfileList = (PWLAN_PROFILE_INFO_LIST)WlanAllocateMemory(sizeof(PWLAN_PROFILE_INFO_LIST));
}
myWlan::~myWlan()
{
WlanFreeMemory(hClientHandle);
WlanFreeMemory(pInterfaceList);
WlanFreeMemory(opcodeValueType);
WlanFreeMemory(pAvailableNetworkList);
WlanFreeMemory(pProfileList);
}
BOOL myWlan::openWlanService()
{
DWORD dwNegotiatedVersion;
DWORD dwResult = WlanOpenHandle(WLAN_API_VERSION, NULL, &dwNegotiatedVersion, &hClientHandle);
if (ERROR_SUCCESS != dwResult)
{
switch (dwResult)
{
case ERROR_INVALID_PARAMETER:
wprintf(L"%s\n", L"pdwNegotiatedVersion is NULL, phClientHandle is NULL, or pReserved is not NULL.");
break;
case ERROR_NOT_ENOUGH_MEMORY:
wprintf(L"%s\n", L" Failed to allocate memory to create the client context.");
break;
case ERROR_REMOTE_SESSION_LIMIT_EXCEEDED:
wprintf(L"%s\n", L"Too many handles have been issued by the server.");
break;
default:
dwResult = GetLastError();
wprintf(L"WlanOpenHandle Fail:%d\n", dwResult);
break;
}
return false;
}
return true;
}
BOOL myWlan::enumWlanInterfaces()
{
DWORD dwResult = WlanEnumInterfaces(hClientHandle, NULL, &pInterfaceList);
if (ERROR_SUCCESS != dwResult)
{
switch (dwResult)
{
case ERROR_INVALID_PARAMETER:
wprintf(L"%s", L"A parameter is incorrect.\
This error is returned if the hClientHandle or ppInterfaceList parameter is NULL. \
This error is returned if the pReserved is not NULL. \
This error is also returned if the hClientHandle parameter is not valid.");
break;
case ERROR_INVALID_HANDLE:
wprintf(L"%s", L" The handle hClientHandle was not found in the handle table.");
break;
case ERROR_NOT_ENOUGH_MEMORY:
wprintf(L"%s", L"Not enough memory is available to process this request and allocate memory for the query results.");
break;
default:
dwResult = GetLastError();
wprintf(L"WlanEnumInterfaces failed,%d", dwResult);
break;
}
return false;
}
return true;
}
BOOL myWlan::queryInterfacePara()
{
WLAN_INTERFACE_INFO interfaceInfo = pInterfaceList->InterfaceInfo[0];
DWORD dwResult = WlanQueryInterface(hClientHandle, &interfaceInfo.InterfaceGuid, wlan_intf_opcode_bss_type,
NULL, dwDataSize, &pData, opcodeValueType); //为pData释放内存
if (ERROR_SUCCESS != dwResult)
{
dwResult = GetLastError();
wprintf(L"WlanEnumInterfaces failed,%d", dwResult);
return false;
}
return true;
}
BOOL myWlan::setWlanInterface(int state)
{
WLAN_PHY_RADIO_STATE phyRadioState{};
phyRadioState.dwPhyIndex = wlan_interface_type_native_802_11;
if (1 == state)
{
phyRadioState.dot11SoftwareRadioState = dot11_radio_state_on;
phyRadioState.dot11HardwareRadioState = dot11_radio_state_on;
}
else
{
phyRadioState.dot11SoftwareRadioState = dot11_radio_state_off;
phyRadioState.dot11HardwareRadioState = dot11_radio_state_off;
}
PVOID pData = &phyRadioState;
DWORD dwResult = WlanSetInterface(hClientHandle, &pInterfaceList->InterfaceInfo[0].InterfaceGuid,
wlan_intf_opcode_radio_state, sizeof(WLAN_PHY_RADIO_STATE), pData, NULL);
if (ERROR_SUCCESS != dwResult)
{
dwResult = GetLastError();
wprintf(L"%s", L"setWlanInterface failed");
return false;
}
return true;
}
BOOL myWlan::scanWlan()
{
DWORD dwResult = WlanScan(hClientHandle, &pInterfaceList->InterfaceInfo[0].InterfaceGuid, NULL, NULL, NULL);
if (ERROR_SUCCESS != dwResult)
{
switch (dwResult)
{
case ERROR_INVALID_PARAMETER:
wprintf(L"%s", L"hClientHandle is NULL or invalid, pInterfaceGuid is NULL, or pReserved is not NULL.");
break;
case ERROR_INVALID_HANDLE:
wprintf(L"%s", L"The handle hClientHandle was not found in the handle table.");
break;
case ERROR_NOT_ENOUGH_MEMORY:
wprintf(L"%s", L"Failed to allocate memory for the query results.");
break;
default:
dwResult = GetLastError();
wprintf(L"scanWlan failed,%d", dwResult);
break;
}
return false;
}
return true;
}
BOOL myWlan::getAvailableNetList()
{
DWORD dwResult = WlanGetAvailableNetworkList(hClientHandle, &pInterfaceList->InterfaceInfo[0].InterfaceGuid,
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES, NULL, &pAvailableNetworkList);
if (ERROR_SUCCESS != dwResult)
{
switch (dwResult)
{
case ERROR_INVALID_PARAMETER:
wprintf(L"%s\n", L"A parameter is incorrect.\
This error is returned if the hClientHandle, pInterfaceGuid, \
or ppAvailableNetworkList parameter is NULL. This error is returned \
if the pReserved is not NULL. This error is also returned if the dwFlags parameter value\
is set to value that is not valid or the hClientHandle parameter is not valid.");
break;
case ERROR_INVALID_HANDLE:
wprintf(L"%s\n", L" The handle hClientHandle was not found in the handle table.");
break;
case ERROR_NDIS_DOT11_POWER_STATE_INVALID:
wprintf(L"%s\n", L"The radio associated with the interface is turned off.\
There are no available networks when the radio is off.");
break;
case ERROR_NOT_ENOUGH_MEMORY:
wprintf(L"%s\n", L"Not enough memory is available to process\
this request and allocate memory for the query results.");
break;
default:
dwResult = GetLastError();
wprintf(L"WlanOpenHandle Fail:%d\n", dwResult);
break;
}
return false;
}
return true;
}
BOOL myWlan::setWlanProfile(LPCWSTR strProfileXml)
{
DWORD dwReasonCode;
DWORD dwResult = WlanSetProfile(hClientHandle, &pInterfaceList->InterfaceInfo[0].InterfaceGuid, 0, strProfileXml, NULL,
TRUE, NULL, &dwReasonCode);
if (ERROR_SUCCESS != dwResult)
{
switch (dwResult)
{
case ERROR_ACCESS_DENIED:
wprintf(L"%s\n", L"The caller does not have sufficient permissions to set the profile.");
break;
case ERROR_ALREADY_EXISTS:
wprintf(L"%s\n", L" strProfileXml specifies a network that already exists.");
break;
case ERROR_BAD_PROFILE:
{
wprintf(L"%s\n", L"The profile specified by strProfileXml is not valid.");
PWCHAR pStringBuffer = (PWCHAR)calloc(128, sizeof(WCHAR));
WlanReasonCodeToString(dwReasonCode, 128, pStringBuffer, NULL);
setlocale(LC_ALL, "chs");
printf("%ls", pStringBuffer);
printf("%ls", strProfileXml);
break;
}//在case中定义变量要在大括号中完成
case ERROR_INVALID_PARAMETER:
wprintf(L"%s\n", L"One of the following conditions occurred:");
break;
case ERROR_NO_MATCH:
wprintf(L"%s\n", L"The interface does not support one or more of the capabilities specified in the profile. ");
break;
default:
dwResult = GetLastError();
wprintf(L"WlanOpenHandle Fail:%d\n", dwResult);
break;
}
return false;
}
//printf("setprofile成功\n");
return true;
}
BOOL myWlan::getWlanProfile()
{
//不用get……list找其他的配置了,只找一个wifi的配置
LPWSTR strProfileXML;
DWORD dwFlags = WLAN_EXECUTE_ACCESS;
DWORD dwGrantedAccess = WLAN_READ_ACCESS;
DWORD dwResult = WlanGetProfile(hClientHandle, &pInterfaceList->InterfaceInfo[0].InterfaceGuid,
L"WIFI名称", NULL, &strProfileXML, &dwFlags, &dwGrantedAccess);//设置pdwFlags可以查找密码
if (ERROR_SUCCESS != dwResult)
{
wprintf(L"failed");
return false;
}
setlocale(LC_ALL, "chs");
//printf("%ls",strProfileXML);
return true;
}
BOOL myWlan::connectWlan(LPCWSTR strProfileXml)
{
//printf("%ls\n", strProfileXml);
WLAN_CONNECTION_PARAMETERS connectionParameters{};
connectionParameters.wlanConnectionMode = wlan_connection_mode_temporary_profile;
connectionParameters.strProfile = strProfileXml;
connectionParameters.pDot11Ssid = NULL;
connectionParameters.pDesiredBssidList = NULL;
connectionParameters.dot11BssType = dot11_BSS_type_infrastructure;
connectionParameters.dwFlags = WLAN_CONNECTION_HIDDEN_NETWORK;
DWORD dwResult = WlanConnect(hClientHandle, &pInterfaceList->InterfaceInfo[0].InterfaceGuid,
&connectionParameters, NULL);
if (ERROR_SUCCESS != dwResult)
{
switch (dwResult)
{
case ERROR_INVALID_PARAMETER:
wprintf(L"Para is NULL\n");
break;
case ERROR_INVALID_HANDLE:
wprintf(L"Failed to INVALID HANDLE \n");
break;
case ERROR_ACCESS_DENIED:
wprintf(L"The caller does not have sufficient permissions. \n");
break;
default:
dwResult = GetLastError();
wprintf(L"WlanConnect Fail: %d\n", dwResult);
break;
}
wprintf(L"FAILED");
return false;
}
wprintf(L"SUCCESS");
return true;
}
BOOL myWlan::loginWeb()
{
CURL* curl;
CURLcode res;
char* jsonString = (char*)R"(登录的JSON请求)";
/* In windows, this will init the winsock stuff */
curl_global_init(CURL_GLOBAL_ALL);
/* get a curl handle */
curl = curl_easy_init();
if (curl) {
/* First set the URL that is about to receive our POST. This URL can
just as well be a https:// URL if that is what should receive the
data. */
curl_easy_setopt(curl, CURLOPT_URL, "请求网址");
/* Now specify the POST data */
curl_slist* plist = curl_slist_append(NULL,
"Content-Type:application/json;charset=UTF-8");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, plist);
// 设置要POST的JSON数据
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, jsonString);
/* Perform the request, res will get the return code */
res = curl_easy_perform(curl);
/* Check for errors */
if (res != CURLE_OK)
{
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(res));
wprintf(L"\n登录校园网失败,请重试\n");
Sleep(1000);
return FALSE;
}
/* always cleanup */
curl_easy_cleanup(curl);
}
curl_global_cleanup();
return TRUE;
}
}
CLR代码基本和普通的C++代码差不多,删除类声明中的public ref class myWlan再按编译器提示改一下就行了,主程序中依次调用类中函数可以实现wlanapi的功能。至于WPF代码主要是界面,我就不列了,C#我也没接触多少。

浙公网安备 33010602011771号