Create script that handles registry variables

Get help with using AutoHotkey and its commands and hotkeys
Thouldre
Posts: 42
Joined: 25 May 2017, 14:29

Create script that handles registry variables

27 May 2017, 05:31

I heard good things about the AutoHotKey community so I am hoping you guys can help me with this :) :

How to create a script that changes the location (Category) of all network profiles from "public" (0) to "private" (1) and handles variables like the random generated folder names for each network?
Also the amount of folders will increase with time as the device will connect to more networks...
Attachments
Unbenannt.png
Unbenannt.png (77.61 KiB) Viewed 3038 times
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: Create script that handles registry variables

27 May 2017, 07:12

Thanks to Lexikos and HotKeyIt who showed me the proper way to do this

Run this as administrator:

Code: Select all

#NoEnv
#NoTrayIcon
SetBatchLines, -1

NetworkListManager := ComObjCreate("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}")
IEnumNetworkConnections := NetworkListManager.GetNetworks(NLM_ENUM_NETWORK_ALL := 3)
for INetwork in IEnumNetworkConnections
		INetwork.SetCategory(1) ; NLM_NETWORK_CATEGORY_PRIVATE
Using the COM interface to do this is better than manipulating the registry directly because:
  • it's an actual documented way(!)
  • if you're connected to a network at the time of running the script, the active network's designated category will change on-the-fly - when changing the active network's Registry key, IIRC reconnection or a restart is required to effect change
BoBo
Posts: 2519
Joined: 13 May 2014, 17:15

Re: Create script that handles registry variables

27 May 2017, 14:09

qwerty12 wrote:Thanks to Lexikos and HotKeyIt who showed me the proper way to do this

Run this as administrator:
Spoiler
Using the COM interface to do this is better than manipulating the registry directly because:
  • it's an actual documented way(!)
  • if you're connected to a network at the time of running the script, the active network's designated category will change on-the-fly - when changing the active network's Registry key, IIRC reconnection or a restart is required to effect change
GMX (a German email provider) offers to connect to a members media center using WebDav.
After mapping the network-drive (to Z:) I've searched the registry for any changes and came up with this:

Code: Select all

Windows Registry Editor Version 5.00

[HKEY_USERS\S-1-5-21-1277220127-497109434-765616382-1000\Network\Z]
"RemotePath"="https://webdav.mc.gmx.net"
"UserName"="<[email protected]>"
"ProviderName"="Web Client Network"
"ProviderType"=dword:002e0000
"ConnectionType"=dword:00000001
"DeferFlags"=dword:00000001
Question: would qwerty12's method allow that on-the-fly change/setting, instead of doing any RegWrite/explorer-refresh workaround?
Thx for listening. :)
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: Create script that handles registry variables

27 May 2017, 14:26

BoBo wrote:Question: would qwerty12's method allow that on-the-fly change/setting, instead of doing any RegWrite/explorer-refresh workaround?
Thx for listening. :)
No, that's something different. I've not tested it, but DriveMap should allow you to map a WebDAV server to a drive letter from an AutoHotkey script (and the changes should take effect then and there).
BoBo
Posts: 2519
Joined: 13 May 2014, 17:15

Re: Create script that handles registry variables

27 May 2017, 14:34

qwerty12 wrote:
BoBo wrote:Question: would qwerty12's method allow that on-the-fly change/setting, instead of doing any RegWrite/explorer-refresh workaround?
Thx for listening. :)
No, that's something different. I've not tested it, but DriveMap should allow you to map a WebDAV server to a drive letter from an AutoHotkey script (and the changes should take effect then and there).
Thx :thumbup:
Thouldre
Posts: 42
Joined: 25 May 2017, 14:29

Re: Create script that handles registry variables

28 May 2017, 04:10

qwerty12 wrote:Thanks to Lexikos and HotKeyIt who showed me the proper way to do this

Run this as administrator:

Code: Select all

#NoEnv
#NoTrayIcon
SetBatchLines, -1

NetworkListManager := ComObjCreate("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}")
IEnumNetworkConnections := NetworkListManager.GetNetworks(NLM_ENUM_NETWORK_ALL := 3)
for INetwork in IEnumNetworkConnections
		INetwork.SetCategory(1) ; NLM_NETWORK_CATEGORY_PRIVATE
Using the COM interface to do this is better than manipulating the registry directly because:
  • it's an actual documented way(!)
  • if you're connected to a network at the time of running the script, the active network's designated category will change on-the-fly - when changing the active network's Registry key, IIRC reconnection or a restart is required to effect change
Thank you very much! But there still is a problem: I am connected to a VPN via the TAP Adapter which uses the "public" profile and my firewall is precisely configured that way.
So maybe there is a way to make an exception for OpenVPN but force everything else to use "private"?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: Create script that handles registry variables

28 May 2017, 11:22

Thouldre wrote: So maybe there is a way to make an exception for OpenVPN but force everything else to use "private"?
You could avoid specific connections by connection name:

Code: Select all

for INetwork in ComObjCreate("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}").GetNetworks(3) { ; NLM_ENUM_NETWORK_ALL
		if (INetwork.GetName() == "<name of specific connection name>") { ; use MsgBox % INetwork.GetName() inside a loop like this one if you want to find out your available choices
			INetwork.SetCategory(0) ; NLM_NETWORK_CATEGORY_PUBLIC
			; or comment the above line to leave the setting as it is, instead of forcing the Public category to be set, and just
			continue
		} else {
			INetwork.SetCategory(1) ; NLM_NETWORK_CATEGORY_PRIVATE
		}
}
For connected connections only, you can try this if you want to filter by adapter name:

Code: Select all

VarSetCapacity(adapterGuid, 16), VarSetCapacity(adapterGuidStr, 140), adapters := GetAdaptersAddresses()

for INetwork in ComObjCreate("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}").GetNetworks(1) { ; NLM_ENUM_NETWORK_CONNECTED
	for k, v in INetwork.GetNetworkConnections() {
		try if ((INetworkConnection := ComObjQuery(k, "{DCB00005-570F-4A9B-8D69-199FDBA5723B}"))) {
			if (DllCall(NumGet(NumGet(INetworkConnection+0)+12*A_PtrSize), "Ptr", INetworkConnection, "Ptr", &adapterGuid) == 0) { ; ::GetAdapterId
				if (DllCall("ole32\StringFromGUID2", "Ptr", &adapterGuid, "WStr", adapterGuidStr, "Int", 68)) {
					if (adapters[adapterGuidStr].Description == "<name of network adapter>") { ; also look at FriendlyName instead of Description
						INetwork.SetCategory(0) ; NLM_NETWORK_CATEGORY_PUBLIC
						noop := "or comment the above line to leave the setting as it is, instead of forcing the Public category to be set" ; for lack of a better option in AutoHotkey...
					} else {
						INetwork.SetCategory(1) ; NLM_NETWORK_CATEGORY_PRIVATE
					}
				}
			}
			ObjRelease(INetworkConnection)
		}
	}
}

; just me: https://autohotkey.com/boards/viewtopic.php?t=18768
GetAdaptersAddresses()
{
	; initial call to GetAdaptersAddresses to get the size needed
	If (DllCall("iphlpapi.dll\GetAdaptersAddresses", "UInt", 2, "UInt", 0, "Ptr", 0, "Ptr", 0, "UIntP", Size) = 111) ; ERROR_BUFFER_OVERFLOW
		If !(VarSetCapacity(Buf, Size, 0))
			Return "Memory allocation failed for IP_ADAPTER_ADDRESSES struct"

	; second call to GetAdapters Addresses to get the actual data we want
	If (DllCall("iphlpapi.dll\GetAdaptersAddresses", "UInt", 2, "UInt", 0, "Ptr", 0, "Ptr", &Buf, "UIntP", Size) != 0) ; NO_ERROR
		Return "Call to GetAdaptersAddresses failed with error: " . A_LastError

	Addr := &Buf
	Adapters := {}
	While (Addr) {
		AdapterName := StrGet(NumGet(Addr + 8, A_PtrSize, "Uptr"), "CP0")
		Description := StrGet(NumGet(Addr + 8, A_PtrSize * 7, "UPtr"), "UTF-16")
		FriendlyName := StrGet(NumGet(Addr + 8, A_PtrSize * 8, "UPtr"), "UTF-16")
		Adapters[AdapterName] := {Description: Description, FriendlyName: FriendlyName}
		Addr := NumGet(Addr + 8, "UPtr") ; *Next
	}
	Return Adapters
}
(Also, if anybody knows the magic incantation to retrieve a GUID through INetworkConnection's IDispatch interface, compared to me going for the more simple option of calling GetAdapterId directly from the vtable, please let me know)
Thouldre
Posts: 42
Joined: 25 May 2017, 14:29

Re: Create script that handles registry variables

28 May 2017, 13:41

Thank you, thank you, thank you! I really appreciate your time! It's working as hoped! :dance:
I decided to use a combination of the first and the third script. First one always at startup and the third as a scheduled task triggered when connected to a new network.

I hope you can answer this question: How to execute a script automatically at startup before a user logs in?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: Create script that handles registry variables

28 May 2017, 14:05

Thouldre wrote:Thank you, thank you, thank you! I really appreciate your time! It's working as hoped! :dance:
I decided to use a combination of the first and the third script. First one always at startup and the third as a scheduled task triggered when connected to a new network.
Good to hear (and thanks for the Task Scheduler idea) :-)
I hope you can answer this question: How to execute a script automatically at startup before a user logs in?
Hmm, what are you trying to do? I wrote a mishmash of functions for running AutoHotkey on the logon screen's desktop, but that might be overkill for your purposes - you might be able to get away with just having Task Scheduler to just start a simple script in the services session...
Thouldre
Posts: 42
Joined: 25 May 2017, 14:29

Re: Create script that handles registry variables

29 May 2017, 10:32

I've converted the .ahk to .exe and created an scheduled task to run it when a user logs in. That works, but I need to run the .exe at startup not at login which means before a user even has to login.

I ticked "run whether user is logged on or not" and switched the trigger from user logon to startup but unfortunately that is not working... Why?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: Create script that handles registry variables

29 May 2017, 13:01

Thouldre wrote:I ticked "run whether user is logged on or not" and switched the trigger from user logon to startup but unfortunately that is not working... Why?
Assuming it's this script you want to run and that your task scheduler settings set to run the script as a user with enough privileges, Task Scheduler might be starting your script before the Network List Service service is started. I don't really know enough about what's going on here to be able to give you a definitive answer. Other than waiting for the service to start, you can use your original idea of manipulating the registry directly (not tested):

Code: Select all

Loop, Reg, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList\Profiles, K
{
	currKey := A_LoopRegKey . "\" . A_LoopRegSubKey "\" . A_LoopRegName
	RegRead, ProfileName, %currKey%, ProfileName
	if (!ErrorLevel) {
		if (ProfileName != "<name of profile>") {
			RegWrite, REG_DWORD, %currKey%, CategoryType, 0
			RegWrite, REG_DWORD, %currKey%, Category, 1 ; private network: set to 0 for a public designated one
		}
	}
}
Thouldre
Posts: 42
Joined: 25 May 2017, 14:29

Re: Create script that handles registry variables

30 May 2017, 03:11

Thanks, the script is working but unfortunately not as a scheduled task. At least now I know the issue lies with task scheduler!

Thank you for your time and work! You were a huge help! Thanks!
Thouldre
Posts: 42
Joined: 25 May 2017, 14:29

Re: Create script that handles registry variables

30 May 2017, 05:07

I still have a small question if you don't mind: How to completely disable or hide the following question of Windows or maybe just click automatically yes?
Attachments
asdasd.jpg
asdasd.jpg (25.15 KiB) Viewed 2899 times
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: Create script that handles registry variables

30 May 2017, 05:27

Thouldre wrote:How to completely disable or hide the following question of Windows or maybe just click automatically yes?
Sorry, I don't know - I keep my UAC level at the highest, which disables that window automatically, but, erm, doing that might be a bit drastic for that purpose....
Thouldre
Posts: 42
Joined: 25 May 2017, 14:29

Re: Create script that handles registry variables

30 May 2017, 07:22

I've created a scheduled task to take care of that which means this prompt is not only unnecessary it's also a security risk if accidentally clicked.

Found the solution! If somebody has or is going to have the same issue, just add the following registry key:

Code: Select all

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Network\NewNetworkWindowOff
eekhelpspike
Posts: 16
Joined: 05 Jul 2015, 17:51

Re: Create script that handles registry variables

28 Aug 2017, 18:01

I have a slightly different problem, and this looks like a good starting point for fixing it. I have a 2003 DC and all my PCs are beating the DC to booting, and not properly connecting to the domain. Their connections show up as "Network #" and a profile of "public". If I disable/re-enable the connection, the connection connects with the proper "Domain" profile (contoso.local). I guess if there doesn't exist a "domain" connection, reset all network adapters? Does the GetNetworks() differentiate between "private" and "domain"? I tried for INetwork in ComObjCreate("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}").GetNetworks(2) and it returns the same value as "3" (all the public connections).
eekhelpspike
Posts: 16
Joined: 05 Jul 2015, 17:51

Re: Create script that handles registry variables

28 Sep 2017, 15:20

Ok, so I got this far:
Spoiler
But GetName nor GetDescription return what the disable/enable commands are expecting. Any way to disable/enable connections with inetwork?
qwerty12
Posts: 468
Joined: 04 Mar 2016, 04:33
GitHub: qwerty12

Re: Create script that handles registry variables

29 Sep 2017, 08:19

Sorry, I didn't see this. Look at the second script here. If you replace what's inside the if (DllCall("ole32\StringFromGUID2", "Ptr", &adapterGuid, "WStr", adapterGuidStr, "Int", 68)) { block with MsgBox % adapters[adapterGuidStr].FriendlyName you should, hopefully, see the name netsh is expecting.

EDIT: No problem, eekhelpspike. I'm glad you managed to devise a solution. Sadly, running as admin is annoying, but IMHO, it's for the better in this case
Last edited by qwerty12 on 29 Sep 2017, 09:58, edited 1 time in total.
eekhelpspike
Posts: 16
Joined: 05 Jul 2015, 17:51

Re: Create script that handles registry variables

29 Sep 2017, 09:19

qwerty12 wrote:Sorry, I didn't see this. Look at the second script here. If you replace what's inside the if (DllCall("ole32\StringFromGUID2", "Ptr", &adapterGuid, "WStr", adapterGuidStr, "Int", 68)) { block with MsgBox % adapters[adapterGuidStr].FriendlyName you should, hopefully, see the name netsh is expecting.
No problem! Thanks for responding.
Excellent. Got this working:

Code: Select all

VarSetCapacity(adapterGuid, 16), VarSetCapacity(adapterGuidStr, 140), adapters := GetAdaptersAddresses()

for INetwork in ComObjCreate("{DCB00C01-570F-4A9B-8D69-199FDBA5723B}").GetNetworks(1) { ; NLM_ENUM_NETWORK_CONNECTED
	if (INetwork.GetCategory() <> 2) {
		for k, v in INetwork.GetNetworkConnections() {
			try if ((INetworkConnection := ComObjQuery(k, "{DCB00005-570F-4A9B-8D69-199FDBA5723B}"))) {
				if (DllCall(NumGet(NumGet(INetworkConnection+0)+12*A_PtrSize), "Ptr", INetworkConnection, "Ptr", &adapterGuid) == 0) { ; ::GetAdapterId
					if (DllCall("ole32\StringFromGUID2", "Ptr", &adapterGuid, "WStr", adapterGuidStr, "Int", 68)) {
						NetworkToReset := adapters[adapterGuidStr].FriendlyName
						;MsgBox % NetworkToReset
						Sleep,500
						RunWait, netsh interface set interface "%NetworkToReset%" DISABLED,,hide
						Sleep,5000
						RunWait, netsh interface set interface %NetworkToReset% ENABLED,,hide
						Sleep,2000
					}
				}
				ObjRelease(INetworkConnection)
			}
		}
	}
}

GetAdaptersAddresses()
{
	; initial call to GetAdaptersAddresses to get the size needed
	If (DllCall("iphlpapi.dll\GetAdaptersAddresses", "UInt", 2, "UInt", 0, "Ptr", 0, "Ptr", 0, "UIntP", Size) = 111) ; ERROR_BUFFER_OVERFLOW
		If !(VarSetCapacity(Buf, Size, 0))
			Return "Memory allocation failed for IP_ADAPTER_ADDRESSES struct"

	; second call to GetAdapters Addresses to get the actual data we want
	If (DllCall("iphlpapi.dll\GetAdaptersAddresses", "UInt", 2, "UInt", 0, "Ptr", 0, "Ptr", &Buf, "UIntP", Size) != 0) ; NO_ERROR
		Return "Call to GetAdaptersAddresses failed with error: " . A_LastError

	Addr := &Buf
	Adapters := {}
	While (Addr) {
		AdapterName := StrGet(NumGet(Addr + 8, A_PtrSize, "Uptr"), "CP0")
		Description := StrGet(NumGet(Addr + 8, A_PtrSize * 7, "UPtr"), "UTF-16")
		FriendlyName := StrGet(NumGet(Addr + 8, A_PtrSize * 8, "UPtr"), "UTF-16")
		Adapters[AdapterName] := {Description: Description, FriendlyName: FriendlyName}
		Addr := NumGet(Addr + 8, "UPtr") ; *Next
	}
	Return Adapters
}
I've got to run as admin, but I'll pick my battles. Probably fine if I'm going to schedule it like the previous posters.
Thank you so much, qwerty12!

Return to “Ask For Help”

Who is online

Users browsing this forum: Albireo, bennybroseph, Bing [Bot], theholyscripture and 51 guests