Jump to content
ViciousXUSMC

Automatic Reassignment of COMM Port

Recommended Posts

Another project idea that I am trying to get working.

A USB GPS device needs to be assigned COMM Port 25 when it is plugged into the computer.  It obviously gets the first open port when plugged in the first time and if I manually change it to 25 it will get a new port again if somebody plugs the device into a new USB port.

So I want to make a script that can detect the device, and move/configure its port automatically.

I started off finding this UDF: https://www.autoitscript.com/forum/topic/128546-serial-port-com-port-udf/?page=32

It was useful for finding the device is plugged in and what port its on, but it does not have the ability to actually change the port.

I think that will have to be done via registry, or perhaps via Objects.

I just want to double check my facts here and get a grip on what I need to do.

Registry has the following keys that change:
HKLM\HARDWARE\SERIALCOMM - \Device\ProlificSerial0
This key just lists the COMM Port for the device, it changes based on its current port.
HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_067B&PID_2303]
Under this key is a subkey that will duplicate every time the device gets a new port, I have not figure out quite yet how to determin what that subkey will be named, but it has the "FriendlyName" key and also the Device parameters\PortName key

Both of those are unfortunately SYSTEM level permission so that makes scripting a change even harder.

Last is HKLM\SYSTEM\CurrentControlSet\Control\COM Name Arbiter
This contains the record of what comm ports are occupied or vacant via binary to hex.  I would have to find a way to read its current value and change it to show port 25 taken and if possible free up the port taken by the device before the change.  Not sure how to write something like that.

Has anybody done something like this before?  If so what was your best approach?

Share this post


Link to post
Share on other sites

Here is what I have so far working on the ComDB portion.

#Include <Array.au3>

$sComDB = RegRead("HKLM\SYSTEM\CurrentControlSet\Control\COM Name Arbiter", "ComDB")
$sComDB = StringTrimLeft($sComDB, 2)
$aComDB =  _StringChop($sComDB, 2) ;Array of ComDB
 _ArrayDisplay($aComDB) ;Debug Show Array
$sOldPort = RegRead("HKLM\HARDWARE\DEVICEMAP\SERIALCOMM", "\Device\ProlificSerial0")
$sOldPort = StringTrimLeft($sOldPort, 3) ; Port Device is on Currently
$sArraySection =Ceiling($sOldPort / 8) ;Returns the Array Row This Com Port Falls Into

MsgBox(0, "", $sOldPort)
 
MsgBox(0, "", $sArraySection)
MsgBox(0, "", Dec($aComDB[$sArraySection]))




Func _StringChop($string, $size)
$count = Ceiling(StringLen($string)/$size)
Dim $array[$count+1], $start = 1
For $i = 1 To $count
    $array[$i] = StringMid($string, $start, $size)
    $start += $size
Next
$array[0] = $count
Return $array
EndFunc

I have it so that I will known what octet I am working with in the array so that I can modify that portion of the DB and write it back to the registry.  Stuck on the fact that I need to find a way to know what binary position the Com port is using so that I can subtract it out of the decimal value and change it back to hex.

For the registry permissions issue probably will use SetACL.  I have a BAT file working so need to convert that to Autoit (Surprised I never found a "finished" ObjectCom version of SetACL for autoit)

Share this post


Link to post
Share on other sites

Reporting back with all my findings.

Contains all the free/taken Com Ports via Binary to Hex

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\COM Name Arbiter]

 

Parent Key for Devices GPS is under USB

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\

 

FriendlyName: What shows up in Device Manager

DeviceParameters\PortName: The Actual Port Assignment If you Change in Device Manager

 

Record of Ports

[HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM]

 

All Ports Registered and there settings

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports

 

Seems the FriendlyName is key only effects the name you see in Device Manager, this one is system level permissions, but the Deviceparameters\PortName is the actual port asignment and its not locked to system.  I can change it as admin without any permission changes.  So of all the keys above I can change exclusively that one and it works!  However its sloppy because I am not blocking/relieving any ports for autodetect, I am not changing the display name, I found the CurrentVersion\Ports key this must be where port settings are stored.  

 

Definitely close on this project.  

What would be awesome is a script to take all ports listed in HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports and do the conversion needed to block/unblock those ports in HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\COM Name Arbiter then you can just delete/add as needed and have the script/function do the update.

 

Edit:

I have a working script its sort of a "shotgun" approach.  I am taking any/all instances of this device and making them Port25 I think this is safe because you can only have one device plugged in so its not like 2 of them will be attached causing a conflict.  I do have Port25 being blocked out here in the ComDatabase but I am not relieving the old port, but that is not a major issue and ok for now until I can figure out how to make that work cleanly.

This script runs as system so I did not need to use the SetACL to give permissions to change the FriendlyName.

 

#RequireAdmin
#Include <Array.au3>

Local $sTestKey  ="HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_067B&PID_2303"
Local $sNewReg

RegWrite("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports", "COM25:", "REG_SZ", "9600,n,8,1")
RegWrite("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM", "\Device\ProlificSerial0", "REG_SZ", "COM25")

For $i = 1 to 30
$sCurKey = RegEnumKey($sTestKey, $i)
If @Error Then ExitLoop
RegWrite($sTestKey & "\" & $sCurKey & "\Device Parameters", "PortName", "REG_SZ", "COM25")
RegWrite($sTestKey & "\" & $sCurKey, "FriendlyName", "REG_SZ", "Prolific USB-to-Serial Comm Port (COM25)")
Next

$sComDB = RegRead("HKLM\SYSTEM\CurrentControlSet\Control\COM Name Arbiter", "ComDB")
$sComDB = StringTrimLeft($sComDB, 2)
$aComDB =  _StringChop($sComDB, 2) ;Array of ComDB
 ;_ArrayDisplay($aComDB) ;Debug Show Array
$aComDB[4] = "01" ;Change Port 25 to Occupied

For $i = 1 to $aComDB[0]
    $sNewReg &= $aComDB[$i]
Next
;Write Port25 Taken to Registry
RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\COM Name Arbiter", "ComDB", "REG_BINARY", Binary("0x" & $sNewReg))

;Functions
Func _StringChop($string, $size)
$count = Ceiling(StringLen($string)/$size)
Dim $array[$count+1], $start = 1
For $i = 1 To $count
    $array[$i] = StringMid($string, $start, $size)
    $start += $size
Next
$array[0] = $count
Return $array
EndFunc

 

Edited by ViciousXUSMC

Share this post


Link to post
Share on other sites

hi there,

I have a PC with one physical serial port and the connected device use com2, however, under device manager, there are 3 ports available com1, com2, com3. so we need each time to go into device manager to ensure com 2 is the default one, as per below

1. if it's on com1  we will need to default on com2

2. if it's on com3  we will need to default on com 2

3. if it's on com2  we do nothing

I know the thread is 2 years old please no flame, perhaps there is a solution out there, thanks ahead for anyone that can help

#RequireAdmin
#Include <Array.au3>

Local $sTestKey  ="HKLM\SYSTEM\CurrentControlSet\Enum\USB\VID_067B&PID_2303"
Local $sNewReg

RegWrite("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Ports", "COM2:", "REG_SZ", "9600,n,8,1")
RegWrite("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM", "\Device\ProlificSerial0", "REG_SZ", "COM2")

For $i = 1 to 3
$sCurKey = RegEnumKey($sTestKey, $i)
If @Error Then ExitLoop
RegWrite($sTestKey & "\" & $sCurKey & "\Device Parameters", "PortName", "REG_SZ", "COM2")
RegWrite($sTestKey & "\" & $sCurKey, "FriendlyName", "REG_SZ", "Prolific USB-to-Serial Comm Port (COM2)")
Next


;*****************************************************explain this part not sure if need this*********************************
$sComDB = RegRead("HKLM\SYSTEM\CurrentControlSet\Control\COM Name Arbiter", "ComDB")
$sComDB = StringTrimLeft($sComDB, 2)
$aComDB =  _StringChop($sComDB, 2) ;Array of ComDB
 ;_ArrayDisplay($aComDB) ;Debug Show Array
$aComDB[4] = "01" ;Change Port 25 to Occupied   




For $i = 1 to $aComDB[0]
    $sNewReg &= $aComDB[$i]
Next
;Write Port25 Taken to Registry
RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\COM Name Arbiter", "ComDB", "REG_BINARY", Binary("0x" & $sNewReg))

;Functions
Func _StringChop($string, $size)
$count = Ceiling(StringLen($string)/$size)
Dim $array[$count+1], $start = 1
For $i = 1 To $count
    $array[$i] = StringMid($string, $start, $size)
    $start += $size
Next
$array[0] = $count
Return $array
EndFunc
 ;*****************************************************explain this part not sure if need this*********************************

 

 

 

Share this post


Link to post
Share on other sites
#RequireAdmin
#Include <Array.au3>
#include <CommMG.au3>



local $sportSetError


_commswitch(1) ;go  to COM1
_CommSetPort("2",$sportSetError,9600, 8,  0,  1,  0, 0,  0);channel 1 is COM2

_commswitch(3);go  to COM3
_CommSetPort("2",$sportSetError,9600, 8,  0,  1,  0, 0,  0);channel 3 is COM2




 $chan = IsConnected("COM2")
if $chan <> 0 then MsgBox(0,'COM2', "Is connected on channel " & $chan)

Func IsComConnected($someCOM)
    Local $n
    For $n = 1 To 3
        _CommSwitch ($n)
        If _CommPortConnection () = $someCOM Then Return $n  ; connected
    Next
    Return 0;not connected
    
    ;~ If _CommPortConnection() = "COM2" Then MsgBox(0,"","connected")

using the udf CommMG.AU3

Edited by antonioj84

Share this post


Link to post
Share on other sites
#RequireAdmin
#Include <Array.au3>
#include <CommMG.au3>



local $sportSetError

$aListPortNames = _ComGetPortNames(0)
 _ArrayDisplay($aListPortNames) ; list available port



_commswitch(1) ;go  to COM1
_CommSetPort("2",$sportSetError,9600, 8,  0,  1,  0, 0,  0);channel 1 is COM2

_commswitch(3);go  to COM3
_CommSetPort("2",$sportSetError,9600, 8,  0,  1,  0, 0,  0);channel 3 is COM2




 $chan = IscomConnected("COM2")
if $chan <> 0 then MsgBox(0,'COM2', "Is connected on channel " & $chan)
_CommCloseport("2")



Func IsComConnected($someCOM)
    Local $n
    For $n = 1 To 3
        _CommSwitch ($n)
        If _CommPortConnection () = $someCOM Then Return $n  ; connected
    Next
    Return 0;not connected

EndFunc  ;==>IsComConnected

HI there thank you for your interest,  you need to add 2 files commg.dll and the commMg.au3 is the UDF

CommMG.au3

commg.dll

Edited by antonioj84
error

Share this post


Link to post
Share on other sites

Looks so much better than what I hand rolled a while back for my project.  I did not find any UDF for Com Ports so I learned how it all worked and came up with my own solution.

The knowledge of how the registry keys work and what not was very good to learn however.

 

Share this post


Link to post
Share on other sites

Hi
i did ask a Question like this few months back and only got bit help with Reg way and did not like that and ended up going with controlclick commands and using
Run('rundll32.exe devmgr.dll,DeviceProperties_RunDLL /DeviceID acpi\pnp0501\1')
Run('rundll32.exe devmgr.dll,DeviceProperties_RunDLL /DeviceID acpi\pnp0501\2')

some times after a reinstall of windows the ports reassignment is wrong... the port i want as 1 is now port 2 and what i want as port 2 is port 1

Port1 should be rs232 and i think com2 might be a dvi or something else on the acer pc either way i wanted the rs232 on 1
So i have to Switch
port 1 To port 3  (Cant switch To 2 as there is already a 2)
port 2 To port 1
port 3 To port 1

 

#RequireAdmin
#Include <Array.au3>
#include <CommMG.au3>

ocal $sportSetError
$aListPortNames = _ComGetPortNames(0)
 _ArrayDisplay($aListPortNames) ; list available port

_commswitch(1) ;go  to COM1
_CommSetPort("2",$sportSetError,9600, 8,  0,  1,  0, 0,  0);channel 1 is COM2
 $chan = IscomConnected("COM2")
if $chan <> 0 then MsgBox(0,'COM2', "Is connected on channel " & $chan)
_CommCloseport("2")

Run('rundll32.exe devmgr.dll,DeviceProperties_RunDLL /DeviceID acpi\pnp0501\1')

The msgbox Says COM2 is on channel 1

but the ports have not switched

Share this post


Link to post
Share on other sites

here is the info below,  can perhaps add some clarification

;===============================================================================
;
; Function Name:  _CommSwitch($channel)
;switches functions to operate on channel 1, 2, 3 to 50
;returns  on succes the channel switched to ie 1 or 2
;         on failure -1
;Remarks  on start up of script channel 1 is selected, so if you only need one COM port
;         you don't need to use _CommSwitch
;         each channel needs to be set up with _CommSetPort
;         The same COM port cannot be used on more than one channel.
;When switch is used the first time on a channel number that port will be inactive
; and the port name will be '' (an empty string) until it is set with _CommSetport.
;The exception is that on creation channel 1 is always created and used as the
;port so switch is not needed unless more than one port is used.
;         The channel number is not related to the COM port number, so channel 1 can
;         be set to use COM2 and channel 4 can be set to use COM1 or any available port.
;Any channel number in the range 1 - 50 can be used, so it is possible to use
; the same channel number as the port number, ie switch(21) switches to COM21

 

Share this post


Link to post
Share on other sites

you should do

#RequireAdmin



RegWrite("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM", "\Device\Serial1", "REG_SZ", "COM2")
RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\ACPI\PNP0501\1\Device Parameters", "\PortName", "REG_SZ", "COM2")
RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Enum\ACPI\PNP0501\1\", "FriendlyName", "REG_SZ","Communications Port(COM2)")

You can do it that way instead, this show how to port assignment serial1 (com1) to com2, . The only problem unless you have full admin rights on regedit you can not modify the friendlyName .  however you can use tool like regini.exe

Edited by antonioj84
ok

Share this post


Link to post
Share on other sites

thank you for the reply... sorry i only just logged in to notice

no issue with admin rights..... i run from a USB plugged into the computer..
i will have to wait until Tuesday + Wed to test that.. no com ports at home to test :)

i guess that also needs a restart as its registry to work... where the control clicking horrible as it is does not.. but i will still play test and see only a restart lol :)

Edited by larksp

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Similar Content

    • By nacerbaaziz
      good morning everybody.
      today i liked to share an small example with you
      which it an function to read the registry values as an array
      the result array is 2d array witch
      $a_array[n][0] = value name
      $a_array[n][1] = value Data
      $a_array[0][0] = values count
      here's the function

      #include <Array.au3> #include <WinAPIReg.au3> #include <APIRegConstants.au3> Local $a_array = _RegReadToArray("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run") If @error Then     MsgBox(16, "error", @error)     Exit EndIf _ArrayDisplay($a_array) Func _RegReadToArray($s_RegKey)     Local $a_KeySplitInfo = StringSplit($s_RegKey, "\\", 2)     If UBound($a_KeySplitInfo) <= 1 Then         $a_KeySplitInfo = StringSplit($s_RegKey, "\", 2)         If UBound($a_KeySplitInfo) <= 1 Then Return (1, 1, 0)     EndIf     Local $H_KeyInfo = "", $s_RegKeyInfo = ""     Switch $a_KeySplitInfo[0]         Case "hklm", "HKEY_LOCAL_MACHINE", "hklm64", "HKEY_LOCAL_MACHINE64"             $H_KeyInfo = $HKEY_LOCAL_MACHINE         Case "hkCu", "HKEY_CURRENT_USER", "hkCU64", "HKEY_CURRENT_USER64"             $H_KeyInfo = $HKEY_CURRENT_USER         Case "hkCr", "HKEY_CLASSES_ROOT", "HKCR64", "HKEY_CLASSES_ROOT64"             $H_KeyInfo = $HKEY_CLASSES_ROOT         Case "HKU", "HKEY_USERS", "HKU64", "HKEY_USERS64"             $H_KeyInfo = $HKEY_USERS         Case Else             Return SetError(2, 2, 0)     EndSwitch     _ArrayDelete($a_KeySplitInfo, 0)     $s_RegKeyInfo = _ArrayToString($a_KeySplitInfo, "\")     Local $H_KeyInfoOpen = _WinAPI_RegOpenKey($H_KeyInfo, $s_RegKeyInfo, $KEY_READ)     Local $A_KeyInfo = _WinAPI_RegQueryInfoKey($H_KeyInfoOpen)     If @error Then Return SetError(1, 1, 0)     _WinAPI_RegCloseKey($H_KeyInfoOpen)     Local $A_RegVal[$A_KeyInfo[2] + 1][2]     Local $iV = 1, $s_RegRead = ""     While 1         $s_RegVal = RegEnumVal($s_RegKey, $iV)         If @error <> 0 Then ExitLoop         $s_RegRead = RegRead($s_RegKey, $s_RegVal)         If Not (@error) Then             $A_RegVal[$iV][0] = $s_RegVal             $A_RegVal[$iV][1] = $s_RegRead         EndIf         $iV += 1     WEnd     $A_RegVal[0][0] = UBound($A_RegVal) - 1     If $A_RegVal[0][0] >= 1 Then         Return $A_RegVal     Else         Return SetError(3, 3, 0)     EndIf EndFunc   ;==>_RegReadToArray
      i hope you benefit from it
      with my greetings
    • By Simpel
      Hi,
      I wondered why negative integers I wrote into registry (e.g. negative x-coordinates of a gui if using two monitors and the right one is the main one) wouldn't return right when reading. Now I know: it is saved as an unsigned integer (without algebraic sign). So here is a snippet that is changing unsigned to signed integer:
      Global Const $g_sRegKey = "HKEY_CURRENT_USER\Software\" & @ScriptName ; path to registry RegWrite($g_sRegKey, "Value", "REG_DWORD", -2147483647) ; write some negative integer into registry; -2147483647 highest possible negative integer , 2147483648 highest possible positive integer if talking of 32bit Local $sValue = RegRead($g_sRegKey, "Value") ; read out registry ConsoleWrite("Value: " & $sValue & @CRLF) ; show real value in console Local $sResult = _SignedInteger($sValue) ; change to signed value ConsoleWrite("Result: " & $sResult & @CRLF) ; and show it in console Func _SignedInteger($iUnsignedInteger) Local $iSignedInteger If $iUnsignedInteger > (2^31) Then ; then it means a negative integer $iSignedInteger = $iUnsignedInteger - (2^32) Else $iSignedInteger = $iUnsignedInteger EndIf Return $iSignedInteger EndFunc It took me some time to find out the problem and so I hope I can help somebody with this.
      Regards, Conrad
    • By copyleft
      I've looked at a bunch of SetACL examples on this site and none seem to be able to convert this batch script into a working AutoIt script.
      BATCH
      @echo off "%~dp0setacl.exe" -on "HKEY_CLASSES_ROOT\CLSID\{9C60DE1E-E5FC-40f4-A487-460851A8D915}\DefaultIcon" -ot reg -actn setowner -ownr n:administrators >nul 2>nul "%~dp0setacl.exe" -on "HKEY_CLASSES_ROOT\CLSID\{9C60DE1E-E5FC-40f4-A487-460851A8D915}\DefaultIcon" -ot reg -actn ace -ace "n:administrators;p:full" >nul 2>nul Reg.exe add "HKCR\CLSID\{9C60DE1E-E5FC-40f4-A487-460851A8D915}\DefaultIcon" /ve /t REG_EXPAND_SZ /d "C:\My.ico" /f NON-WORKING AUTOIT
      RunWait('setacl.exe "HKCR64\CLSID\{9C60DE1E-E5FC-40f4-A487-460851A8D915}\DefaultIcon" -ot reg -actn setowner -ownr "n:administrators"') RunWait('setacl.exe "HKCR64\CLSID\{9C60DE1E-E5FC-40f4-A487-460851A8D915}\DefaultIcon" -ot reg -actn setowner -ownr "n:administrators;p:full"') RegWrite('HKCR64\CLSID\{9C60DE1E-E5FC-40f4-A487-460851A8D915}\DefaultIcon', '','REG_EXPAND_SZ','C:\Windows\My.ico') Any ideas on what I'm doing wrong?
       
    • By GeorgeB
      I'm writing a little applet that basically tells you when Windows was installed.  There is a REG_DWORD in Windows that gives you this. It's basically a value that is the # of seconds from 1970.
      The location is:  "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\InstallDate"
      So if I run this in AutoIT, I should get the value displayed within the msgbox:
      MsgBox($MB_SYSTEMMODAL, "InstallDate Test", RegRead("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", "InstallDate"))
      However, what happens is it always returns a value of "0"  I tried this on several machines (Windows 8, Windows 8.1 and Windows 10). 
      Am I missing something?  If I manually view this REG_DWORD with RegEdit, it shows me the HEX value, or I can view the Decimal value. I don't care which value AutoIT reads, as I can always convert back and forth, but I just don't see why it can't read a value from this REG_DWORD.  As a test, I've read other REG_DWORD values, and with most it doesn't return any value, not even a 0.
      Please, even if you guys have some other (perhaps better) way to read the Windows install date, I would still like to find a resolution to this problem, because I want to understand why I am having so much difficulty with reading REG_DWORD values from the Windows Registry with AutoIT.
      Thanks for any help!
       
       
       
       
       
    • By cahsobo
      Hello,

      I need help inserting these keys and values to registry
       
      [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\DirectDraw\Compatibility\WK.1.exe]
      "Flags"=hex:00,08,00,00
      "ID"=hex:ec,33,74,3b
       
      Still can't figure out how...
×
×
  • Create New...