title: Refresh WiFi network list with 'WlanScan' (convert api syntax from c# to vba... or a workaround?)
body: <p><strong>I need to refresh Window's list of wireless networks.</strong> </p>
  
  <p>I'll gladly accept any workaround that I can automate (cmdline, wmi, etc) directly <em>or indirectly</em> from VBA. (I'm using Windows 7 Home 64-bit with Office 365 Pro 64-bit.)</p>
  
  <p>I can list the networks programmatically a couple ways including <code>netsh</code>, or the code below, but <strong>the list does not refresh</strong> unless I physically click the <img src="https://i.stack.imgur.com/um5P8.png" alt="net"> Network Connection icon on the taskbar's Notification area. </p>
  
  <ul>
  <li>The list does <em>not</em> auto-update every 60 seconds as some docs state.  </li>
  <li><strong>Disconnect+Reconnecting the NIC is <em>not</em> a feasible/sustainable option.</strong></li>
  </ul>
  
  <p>I think I'm <strong>not getting the handle</strong> from <a href="https://msdn.microsoft.com/27bfa0c1-4443-47a4-a374-326f553fa3bb" rel="nofollow noreferrer">WlanOpenHandle</a> as required and I'm green at converting C to VBA.</p>
  
  <p>No errors but WlanScan returns unknown code <code>1168</code>.</p>
  
  <ul>
  <li>I adapted from the VB version here: <strong><a href="https://www.pinvoke.net/default.aspx/wlanapi.wlanscan#" rel="nofollow noreferrer">wlanscan (wlanapi)</a></strong>  </li>
  <li>MSDN documentation: <strong><a href="https://docs.microsoft.com/windows/desktop/api/wlanapi/nf-wlanapi-wlanscan" rel="nofollow noreferrer">WlanScan function</a></strong>  </li>
  </ul>
  
  <hr>
  
  <blockquote>
    <h2>Related bits:</h2>
  </blockquote>
  
  <p>Here's the <strong>function declaration for <code>VB</code>,</strong> adapted:</p>
  
  <pre class="lang-vb prettyprint-override"><code>Public Shared Function WlanScan(ByVal hClientHandle As IntPtr, _
     ByRef pInterfaceGuid As Guid, ByVal pDot11Ssid As IntPtr, _
     ByVal pIeData As IntPtr, ByVal pReserved As IntPtr) As UInteger
  End Function
  </code></pre>
  
  <p>...and an example of <strong>function usage in <code>C#</code></strong>:
  </p>
  
  <pre><code>Guid g;
  //wlanHndl is the handle returned previously by calling [WlanOpenHandle]
  for (int i = 0; i &lt; infoList.dwNumberOfItems; i++)
  {
  g = infoList.InterfaceInfo[i].InterfaceGuid;
  uint resultCode=WlanScan(wlanHndl, ref g, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
  if (resultCode != 0)
      return;
  }
  </code></pre>
  
  <p>...and <strong>how to open the handle, in <code>C++</code></strong> (from <a href="https://stackoverflow.com/a/3193613/8112776">here</a>):
  </p>
  
  <pre><code>dwResult = WlanOpenHandle(dwMaxClient, NULL, &amp;dwCurVersion, &amp;hClient);
  if (dwResult != ERROR_SUCCESS) {
      wprintf(L"WlanOpenHandle failed with error: %u\n", dwResult);
      return 1;
      // You can use FormatMessage here to find out why the function failed
  }
  </code></pre>
  
  <hr>
  
  <blockquote>
    <p><sub>"Un-hidden:"</sub>  </p>
    
    <h2>Obtain (cached) list of wireless networks:</h2>
  </blockquote>
  
  <p>The code to list the networks <strong>works great</strong> - except for not refreshing on it's own. (Previously I was parsing the text output of <code>netsh wlan show networks mode=bssid</code>, which had the same issue.)</p>
  
  <p>I had previously removed this section because its lengthy and seems to work fine except for the refresh. -)</p>
  
  <pre><code>Option Explicit  'section's source: vbforums.com/showthread.php?632731
  Private Const DOT11_SSID_MAX_LENGTH As Long = 32
  Private Const WLAN_MAX_PHY_TYPE_NUMBER As Long = 8
  Private Const WLAN_AVAILABLE_NETWORK_CONNECTED As Long = 1
  Private Const WLAN_AVAILABLE_NETWORK_HAS_PROFILE As Long = 2
  
  Private Type GUID  'from cpearson.com
      Data1 As Long: Data2 As Integer:  Data3 As Integer:  Data4(7) As Byte
  End Type
  
  Private Type WLAN_INTERFACE_INFO
      ifGuid As GUID: InterfaceDescription(255) As Byte: IsState As Long
  End Type
  
  Private Type DOT11_SSID
      uSSIDLength As Long:            ucSSID(DOT11_SSID_MAX_LENGTH - 1) As Byte
  End Type
  
  Private Type WLAN_AVAILABLE_NETWORK
      strProfileName(511) As Byte:    dot11Ssid As DOT11_SSID
      dot11BssType As Long:           uNumberOfBssids As Long
      bNetworkConnectable As Long:    wlanNotConnectableReason As Long
      uNumberOfPhyTypes As Long:      dot11PhyTypes(WLAN_MAX_PHY_TYPE_NUMBER - 1) As Long
      bMorePhyTypes As Long:          wlanSignalQuality As Long
      bSEcurityEnabled As Long:       dot11DefaultAuthAlgorithm As Long
      dot11DefaultCipherAlgorithm As Long: dwflags As Long: dwReserved As Long
  End Type
  
  Private Type WLAN_INTERFACE_INFO_LIST
      dwNumberOfItems As Long: dwIndex As Long: InterfaceInfo As WLAN_INTERFACE_INFO
  End Type
  
  Private Type WLAN_AVAILABLE_NETWORK_LIST
      dwNumberOfItems As Long:  dwIndex As Long: Network As WLAN_AVAILABLE_NETWORK
  End Type
  
  Declare PtrSafe Function WlanOpenHandle Lib "Wlanapi.dll" (ByVal dwClientVersion As Long, _
                  ByVal pdwReserved As Long, ByRef pdwNegotiaitedVersion As Long, _
                  ByRef phClientHandle As Long) As Long
  
  Declare PtrSafe Function WlanEnumInterfaces Lib "Wlanapi.dll" (ByVal hClientHandle As Long, _
                  ByVal pReserved As Long, ppInterfaceList As Long) As Long
  
  Declare PtrSafe Function WlanGetAvailableNetworkList Lib "Wlanapi.dll" ( _
                  ByVal hClientHandle As Long, pInterfaceGuid As GUID, ByVal dwflags As Long, _
                  ByVal pReserved As Long, ppAvailableNetworkList As Long) As Long
  
  Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, _
                  Source As Any, ByVal Length As Long)
  
  Declare PtrSafe Sub WlanFreeMemory Lib "Wlanapi.dll" (ByVal pMemory As Long)
  
  Type WiFis
    ssid As String: signal As Single
  End Type
  
  Public Function GetWiFi() As WiFis()
  'returns an array of custom type WiFis (1st interface only)
  
      Dim udtList As WLAN_INTERFACE_INFO_LIST, udtAvailList As WLAN_AVAILABLE_NETWORK_LIST, udtNetwork As WLAN_AVAILABLE_NETWORK
      Dim lngReturn As Long, lngHandle As Long, lngVersion As Long, lngList As Long, lngAvailable As Long
      Dim lngStart As Long, intCount As Integer, ssid As String, signal As Single, wifiOut() As WiFis
      n = 0
  
      lngReturn = WlanOpenHandle(2&amp;, 0&amp;, lngVersion, lngHandle) 'get handle
      If lngReturn &lt;&gt; 0 Then
          Debug.Print "Couldn't get wlan handle (Code " &amp; lngReturn &amp; ")"
          Exit Function
      End If
  
      lngReturn = WlanEnumInterfaces(ByVal lngHandle, 0&amp;, lngList) 'enumerate &lt;*first interface only*&gt;
      CopyMemory udtList, ByVal lngList, Len(udtList)
      lngReturn = WlanGetAvailableNetworkList(lngHandle, udtList.InterfaceInfo.ifGuid, 2&amp;, 0&amp;, lngAvailable) 'get network list
      CopyMemory udtAvailList, ByVal lngAvailable, LenB(udtAvailList)
      intCount = 0
      lngStart = lngAvailable + 8
  
      Do
          CopyMemory udtNetwork, ByVal lngStart, Len(udtNetwork) ' Populate avail. network structure
          ssid = Replace(StrConv(udtNetwork.dot11Ssid.ucSSID, vbUnicode), Chr(0), "")
          If Len(ssid) &lt; 4 Then ssid = "(Unnamed)"
          signal = CSng(udtNetwork.wlanSignalQuality) / 100
          '[Signal] = 0 to 100 which represents the signal strength (100 Signal)=(-100dBm RSSI), (100 Signal)=(-50dBm RSSI)
  
          If udtNetwork.dwflags = 0 Then
              n = n + 1
              ReDim Preserve wifiOut(n)
              wifiOut(n).ssid = ssid
              wifiOut(n).signal = signal
          Else
              'skipping networks with [dwflags] &gt; 0
              'I *think* that's what I'm supposed to do
              'Returns 3 for currently connected network, 2 for networks that have profiles
          End If
  
          intCount = intCount + 1
          lngStart = lngStart + Len(udtNetwork)
      Loop Until intCount = udtAvailList.dwNumberOfItems
      WlanFreeMemory lngAvailable     'clean up memory
      WlanFreeMemory lngList
  
      GetWiFi = wifiOut   'Success! (function is populated with cached network list)
  
  End Function
  </code></pre>
  
  <hr>
  
  <blockquote>
    <p><em><sub>...and the problem:</sub></em>   </p>
    
    <h1>Refresh network list using <code>WlanScan</code>?</h1>
  </blockquote>
  
  <p>This does <em>not</em> generate a VBA error, but <em>does</em> return code <code>1168</code> (which I can't identify)/ (<a href="http://pinvoke.net/default.aspx/wlanapi.wlanscan" rel="nofollow noreferrer">Source</a>)</p>
  
  <pre class="lang-vb prettyprint-override"><code>'Added blindly:'wlanui type library (wlanui.dll) and "wlan pref iua" (wlanconn.dll)
  
  Public Type DOT11_SSID 
     uSSIDLength As LongPtr: ucSSID As String
  End Type
  
  Private Type GUID 'from cpearson.com/excel/CreateGUID.aspx
      Data1 As LongPtr: Data2 As Integer
      Data3 As Integer: Data4(0 To 7) As Byte
  End Type
  
  #If Win64 Then 'also new to Office-64bit, but seems okay
      Declare PtrSafe Function WlanScan Lib "Wlanapi.dll" _
          (ByVal hClientHandle As LongPtr, ByRef pInterfaceGuid As GUID, _
          ByVal pDot11Ssid As LongPtr, ByVal pIeData As LongPtr, _
          ByVal pReserved As LongPtr) As LongPtr
  #Else
      Private Declare WlanScan Lib "Wlanapi.dll" _
          (ByVal hClientHandle As LongPtr, ByRef pInterfaceGuid As GUID, _
          ByVal pDot11Ssid As LongPtr, ByVal pIeData As LongPtr, _
          ByVal pReserved As LongPtr) As LongPtr
  #End If
  
  Sub test_RefreshNetworkList()
      Dim hresult As LongPtr, phClientHandle As Long, pdwNegotiatedVersion As Long
      Dim retVal As Longptr, g As GUID
      hresult = WlanOpenHandle(2&amp;, 0&amp;, pdwNegotiatedVersion, phClientHandle)
      retVal = WlanScan(phClientHandle, g, 0, 0, 0)
      Select Case retVal
          Case 87: Debug.Print "ERROR_INVALID_PARAMETER"
          Case 6: Debug.Print "ERROR_INVALID_HANDLE"
          Case 8: Debug.Print "ERROR_NOT_ENOUGH_MEMORY"
          Case Else: Debug.Print "RPC_STATUS : " &amp; retVal  ' "misc errors"
      End Select
  End Sub
  </code></pre>
  
  <hr>
  
  <p>Surely there's a roundabout way to refresh the network list from VBA?  I'm cool with workarounds that I can automate... anything?! </p>
  
  <p><img src="https://i.stack.imgur.com/SyN2p.png" alt="paw">  Thanks!</p>
  
  <hr>
  
  <blockquote>
    <h1>Edit:</h1>
  </blockquote>
  
  <p>I changed <code>Long</code> to <strong><code>LongPtr</code></strong> in the applicable (I think) spots.  Same error.</p>
  
  <p>Here's the <code>WlanOpenHandle</code> and <code>WlanScan</code> definitions. </p>
  
  <pre><code>Declare PtrSafe Function WlanOpenHandle Lib "Wlanapi.dll" 
      (ByVal dwClientVersion As LongPtr, _
       ByVal pdwReserved As LongPtr, 
       ByRef pdwNegotiaitedVersion As LongPtr, _
       ByRef phClientHandle As LongPtr           ) As LongPtr
  </code></pre>
  
  <p>(...it was also my first attempt time using compiler constants.)</p>
  
  <pre><code>#If Win64 Then
      Declare PtrSafe Function WlanScan Lib "Wlanapi.dll" _
          (ByVal hClientHandle As LongPtr,
           ByRef pInterfaceGuid As GUID, _
           ByVal pDot11Ssid As LongPtr, 
           ByVal pIeData As LongPtr, _
           ByVal pReserved As LongPtr) As LongPtr
  #Else
      Private Declare WlanScan Lib "Wlanapi.dll" _
          (ByVal hClientHandle As LongPtr, 
           ByRef pInterfaceGuid As GUID, _
           ByVal pDot11Ssid As LongPtr, 
           ByVal pIeData As LongPtr, _
           ByVal pReserved As LongPtr     ) As LongPtr
  #End If
  </code></pre>