Jump to content
Sign in to follow this  
Ascend4nt

I/O Port Functions - x64 Parallel Port IO, Keyboard, etc + Restore PC Speaker Beep

Recommended Posts

Ascend4nt

I/O Port Functions UDF

Windows 7 and x64-compatible!

This is a simple I/O (Input/Output) UDF for interacting with ports. The ability to write to ports was stripped from Windows with Vista+. While many do not miss this ability, there are some uses still in existence:

  • Re-enable the internal PC Speaker - the good ol' "Beep" function can be restored using basic I/O
  • PS/2 Keyboard Functions - Turn it off/on, mess with LED's, inject keys into the keyboard output stream - even thwart UAC prompts! =O
  • Also work with Parallel, Serial (COM), PS/2 mouse, and miscellaneous ports that devices interface through using I/O operations

For some good lists of ports and programming, see:

Chapters from 'The Art of Assembly Language':

On the PS/2 Keyboard and PS/2 Mouse, some more links:

The I/O DLL's, which will install the I/O drivers (they are embedded as a resource in the DLL), come from Phillip Gibbons. His webpage, where more information, and extra downloads are, is available here: InpOut32 and InpOutx64. Note: everything you need is already included in my UDF.

IOInstallx86 and IOInstallx64 are included to help with the install. Run these once to install the DLL's and drivers. (Administrator rights are required!)

In addition to the base _IOFunctions UDF, I've included _IOBeep, which is based on trancexx's _Beep function, and _IOKeyboardFunctions [PS/2 only*]. There are now three examples of the UDF usage included: IOBeepExample, IOCMOSReadExample (based on trancexx's CMOS code), and IOKeyboardExamples. If anyone else has more code suggestions, feel free to add to the thread.

*Update: Some BIOS's allow Legacy USB Port 64/60 Emulation, which may allow the _IOKeyboardFunctions to work for USB (non-PS/2) Keyboards, though this is untested thus far.

While I bundle the binaries with my code, remember they are not my own. However, they are released as freeware. To ensure proper credit goes where it belongs, I've included the Readme files from the download (linked above), as well as a link to the original page.

Ascend4nt's AutoIT Code License agreement

Screw silly licenses.  Just make sure you remember the people you get free stuff from!

 

IOFunctions.zip

UPDATES:

07/11/2013:

- Updated to use (and install) v1.5.0.0 of InpOut32 & InpOutx64

- Version check & compare before install

- Fixed links

- Tiny bug fixes

InpOut32 and InpOutx64 ChangeLog:

v1.5.0.0 New Build (20-Jan-2011):

- Added _stdcall to DlPortReadPortUshort, DlPortWritePortUshort, DlPortReadPortUlong, DlPortWritePortUlong to maintain compatibility with old DLPortIO driver.

v1.4.0.0 New Build (13-Jan-2011):

- Removed references to WinRing0 which was discontinued.

- Fixed uninitialized buffers & return from Inp32 > byte value!

v1.3.0.0 New Build (15-Aug-2010):

- Removed bool's from header (replaced with BOOL). This is to maintain compatibility with other DLL’s (DLPortIO etc.).

 

10/22/2010:

  • Added _IOKeyboardFunctions UDF
  • Added IOKeyboardExamples and IOCMOSReadExample (based on trancexx's CMOS code)
Edited by Ascend4nt

Share this post


Link to post
Share on other sites
taietel

Thank you Ascend4nt! I have used InpOut32 for a VB project few years ago and still have it on an old laptop. Unfortunately new ones don't have LPT port... ;)( Do you know if ImpOut32.dll works with USB to LPT port adapter?

M.I.

Share this post


Link to post
Share on other sites
trancexx

Great! Made it really simple.

Thanks.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
Ascend4nt

@trancexx: and thank you for the Beep function;) By the way, do you think it makes a difference doing Ceiling vs. Floor for that function?

@taietel: According to the readme:

The InpOut32 device driver support writing to "old fashioned" hardware port addresses.

It does NOT support USB devices such as USB Parallel ports or even PCI parallel ports (as I am lead to believe)

However, I've read that someone had success with a 'true' PCI parallel port.. though I'm not quite clear on the details of that.. sorry

*edit: here's a link for ya:

INPOUT32/X64 Works with SOME PCI cards

Edited by Ascend4nt

Share this post


Link to post
Share on other sites
trancexx

@trancexx: and thank you for the Beep function;) By the way, do you think it makes a difference doing Ceiling vs. Floor for that function?

It's about rounding, so they are probably as wrong.

Have you tried keyboard?

I asked 400 bucks for the script.

400 dollars I asked, they wouldn't pay ;)

edit: wrong link lol

Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
Ascend4nt

Hrmm.. I just dug through my old DOS Hardware/programming books, and saw some info on port x60, but I can't seem to force basic keys into the keyboard output buffer.

I was able to screw with the keyboard LED's though:

Anyway.. what's your $400 secret? ;)

*edit: Ahh, nevermind - the $400 secret was actually really simple. See this post.

Y'know.. looking through these books, I sorta miss the old DOS 'interrupt' days.. ok, only a little..

*edit: added input buffer check to function.

--------------------------------------------------------------

Also, on the PS/2 keyboard programming/ports, I found these links:

Chapter Twenty: The PC Keyboard and 8042

Another good link (might as well put them all in one place):

Keyboard scancodes (and commands)

Edited by Ascend4nt

Share this post


Link to post
Share on other sites
Andreik

I have a little question. I played with On-Board Keyboard Controller Commands to disable and enable my keyboard. My question it's how can I clear the keyboard buffer so after I enable my keyboard to not send keys that I pressed while my keyboard was disable?

My test script to disable/enable keyboards:

#include "_IOFunctions.au3"
_IO_Initialize()
_IO_OutB(0x64,0xAD) ;Disable keyboard
Sleep(5000)
_IO_OutB(0x64,0xAE) ;Enable keyboard
_IO_UnInitialize()
Edited by Andreik

When the words fail... music speaks

Share this post


Link to post
Share on other sites
Ascend4nt

Andreik, try adding the 'Enable and Clear' command:

_IO_OutB(0x64,0xAD) ; Disable keyboard
Sleep(5000)
_IO_OutB(0x64,0xAE) ; Enable keyboard
_IO_OutB(0x60,0xF4) ; Enable & Clear output buffer

btw, USB keyboards won't be affected at all by these I/O calls

Share this post


Link to post
Share on other sites
Ascend4nt

So I figured out the 'magic' output sequence to insert keys into the keyboard stream. Be VERY careful about use of this, as it only appears to work with 1-byte sequences (no extended keys seem to work [ones requiring 'e0'], and I don't know how to get them to work. Also, 'break' codes aren't necessary for most keys as no repeat-sequence is initiated. Perhaps trancexx knows how to do extended keys..). Anyway, stick to 1-byte sequences for now, and check this list of scancodes.

Basically, the programmer must make sure the input buffer is clear, send 0xD2 'Write Keyboard Output Register' to port 0x64, and then write the 1-byte sequence to port 0x60.

For example, this will write 'e' 10 times to the keyboard buffer. This, as trancexx pointed out, is the ultimate 'send' - it should work with any program or game that tries to block any tricky input methods:

For $i=1 To 10
    ; Wait for input buffer to clear
    While BitAND(_IO_InB(0x64),2)
        Sleep(5)
    WEnd
    _IO_OutB(0x64,0xD2) ; send 'Write Keyboard Output Register'
    _IO_OutB(0x60,0x12) ; send key scancode (in this case, 'e')
    Sleep(50)   ; Putting in sleeps between key-sends decreases the chance that the O/S will miss certain keypresses
Next

*edit: 2-byte scancodes don't work and the above just sends the standard key (10 times) and doesn't initiate a repeat-sequence for the key.

*Edit again: 'break' codes ARE necessary for keys like 'ctrl', 'alt' and 'shift', so be sure to send the 'break' (release) code for those. Just add 0x80 to the code and you have your break code.

P.S. Its actually possible to thwart Window's UAC prompts using the sendkey code ;)

Edited by Ascend4nt

Share this post


Link to post
Share on other sites
Ascend4nt

Updated! I took most everything I've learned from keyboard I/O and created a small UDF with it, as well as some examples. Also added trancexx's CMOS code:

UPDATES: 10/22/2010:

  • Added _IOKeyboardFunctions UDF
  • Added IOKeyboardExamples and IOCMOSReadExample (based on trancexx's CMOS code)

Share this post


Link to post
Share on other sites
trancexx

Anyway.. what's your $400 secret? ;)

*edit: Ahh, nevermind - the $400 secret was actually really simple. See this post.

I just saw the edit :)

No, that's not $400. That's $100.

It likely wouldn't work without ps/2 keyboard plugged in (when starting the system). Try it. For example I use USB keyboard and your solution is not working for me.

That means it wouldn't work on any laptops almost for sure.

You could have added reboot function _IO_OutB(0x64, 0xFE) for the sake of completeness. Maybe disabling/enabling keyboard too.

USB <- that. Do you dare?

Edited by trancexx

♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
Ascend4nt

Yeah, I already mentioned in a few posts about the I/O functions working only on PS/2 keyboards. USB keyboards aren't affected. Perhaps I should have made it more clear, but I did mention it and it is mentioned in the UDFs.

And speaking of, Enabling/Disabling the keyboard is already part of those UDF's. As far as resetting the CPU.. mmmm, no. lol

Just for reference, here's part of the header from my _IOKeyboardFunctions UDF:

;
; Uses _IOFuntions to manipulate PS/2 keyboards.
;   NOTE: Functions are specific to PS/2 Keyboards and will not affect USB keyboards!
;
; Functions:
;   _IO_KeyboardLED()       ; Simply turns on or off keyboard LED's
;   _IO_KeyboardLEDRestore() ; Restores the LED's to what the O/S currently 'sees' them as
;   _IO_KeyboardDisable()   ; Disables the keyboard (be careful with this!)
;   _IO_KeyboardReEnable()  ; Re-enables the keyboard after a _IO_KeyboardDisable() call
;   _IO_KeySend()           ; Injects a key into the PS/2 keyboard output buffer

As far as USB devices go.. do they even work with I/O functions? I have no idea how they interact with the hardware, but I don't see anything indicating input/output functions work on them..

Share this post


Link to post
Share on other sites
Thornhunt

WOW Nice . better than i could EVER do

shame i abonded the project i was making wich needed this :)

gratz anyway :)

It's about rounding, so they are probably as wrong.

Have you tried keyboard?

I asked 400 bucks for the script.

400 dollars I asked, they wouldn't pay ;)

edit: wrong link lol

ahhh ahaha you remembered ;) and heyy i have the money just no plane ticket to get it to you :shocked:

Budweiser + room = warm beerwarm beer + fridge = too long!warm beer + CO2 fire extinguisher = Perfect![quote]Protect the easly offended ... BAN EVERYTHING[/quote]^^ hmm works for me :D

Share this post


Link to post
Share on other sites
Ascend4nt

Does anyone have a PS/2 mouse? I only have a PS/2 keyboard, so haven't been able to try some I/O functions specific to a PS/2 mouse.

For example, the following should disable the PS/2 mouse for 8 seconds:

MsgBox(0,"Disabling mouse","Disabling mouse..")
_IO_MouseDisable()
Sleep(8000)
_IO_MouseReEnable()

Func _IO_MouseDisable()
    ; Wait for input buffer to clear
    While BitAND(_IO_InB(0x64),2)
        Sleep(5)
    WEnd
    _IO_OutB(0x64,0xA7) ; disable mouse port
EndFunc

Func _IO_MouseReEnable()
    ; Wait for input buffer to clear
    While BitAND(_IO_InB(0x64),2)
        Sleep(5)
    WEnd
    _IO_OutB(0x64,0xA8) ; enable mouse port
EndFunc

If anyone can confirm this is working, I could add a UDF for it. I also have a MouseSend() command, but without a PS/2 mouse installed it sends data incorrectly.. I'm also looking for ways to detect if a PS/2 mouse is even installed..

Share this post


Link to post
Share on other sites
Jummi

I am trying to configure the serial port. Do not do it.

Someone tell me how to configure the port to send data by rs232.

Share this post


Link to post
Share on other sites
trancexx

geez some idiot rated this 3.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites
hench

all this stuff sounds really really interesting

remembers me of asm classes!! and stuff like adding mouse feature to a pure DOS command prompt :graduated:

@trancexx: got these hundred bucks clog so far? hehe :(

Edited by hench

Share this post


Link to post
Share on other sites
Ascend4nt

I totally forgot about this.  I had updated the DLLs and drivers on my machine a while back but didn't update this package.  So, for anyone that is still interested, the first post now has the newest version as well as the installer.

Here's what's changed:

UPDATES:

07/11/2013:

- Updated to use (and install) v1.5.0.0 of InpOut32 & InpOutx64

- Version check & compare before install

- Fixed links

- Tiny bug fixes

InpOut32 and InpOutx64 ChangeLog:
v1.5.0.0 New Build (20-Jan-2011):
- Added _stdcall to DlPortReadPortUshort, DlPortWritePortUshort, DlPortReadPortUlong, DlPortWritePortUlong to maintain compatibility with old DLPortIO driver.
v1.4.0.0 New Build (13-Jan-2011):
- Removed references to WinRing0 which was discontinued.
- Fixed uninitialized buffers & return from Inp32 > byte value!
v1.3.0.0 New Build (15-Aug-2010):
- Removed bool's from header (replaced with BOOL). This is to maintain compatibility with other DLL’s (DLPortIO etc.).

 

license agreement? what license agreement?

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
Sign in to follow this  

  • Similar Content

    • MMedina
      By MMedina
      Hello all, 
      Been looking for and playing around with a script that would prompt me for a UserName and Password then Map a network drive.
      I have included the code: 
      #AutoIt3Wrapper_icon=your_icon.ico #AutoIt3Wrapper_Run_Obfuscator=y #obfuscator_parameters=/striponly #NoTrayIcon #include <ButtonConstants.au3> #include <EditConstants.au3> #include <GUIConstantsEx.au3> #include <StaticConstants.au3> #include <WindowsConstants.au3> $Form1 = GUICreate("Connect To Your Drive", 265, 135) $username_id = GUICtrlCreateInput("", 88, 16, 153, 21) $password_id = GUICtrlCreateInput("", 87, 44, 153, 21, $ES_PASSWORD) GUICtrlCreateLabel("&Username", 24, 16, 52, 17) GUICtrlCreateLabel("&Password", 26, 46, 50, 17) $connect = GUICtrlCreateButton("&Connect", 24, 80, 217, 33, BitOr($GUI_SS_DEFAULT_BUTTON, $BS_DEFPUSHBUTTON)) GUISetState(@SW_SHOW) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit Case $connect $username = GUICtrlRead($username_id) $password = GUICtrlRead($password_id) If $username = '' Or $password = '' Then MsgBox(16, 'Error', 'Empty username or password') ContinueLoop EndIf If DriveMapGet("X:") <> '' Then ; very fast MsgBox(16, 'Error', 'The device is already assigned') ContinueLoop EndIf GUISetCursor(15,1) DriveMapAdd("X:", "\\Server\share\filestore\" & $username, 0, $username, $password) ; slow If @error Then Switch @error Case 1 $err_message = 'Undefined / Other error. Windows API return code: ' & @extended Case 2 $err_message = 'Access to the remote share was denied' Case 3 $err_message = 'The device is already assigned' Case 4 $err_message = 'Invalid device name' Case 5 $err_message = 'Invalid remote share' Case 6 $err_message = 'Invalid password' EndSwitch GUISetCursor(2) MsgBox(16, 'Error', $err_message) Else ; everything OK Exit EndIf EndSwitch WEnd  
      When I attempt the build I get the following:
       Obfuscator support has been discontinued and is replaced by Au3Stripper using "#Au3Stripper_" directives.
      ! The directive to run Au3Stripper is: #AutoIt3Wrapper_Run_Au3Stripper=y  ; Default is n
      ! #Au3Stripper_Parameters options are: 
      /pe  : Replace and reference to a Global Const variable with its actual value.
      /tl  : Create Au3Stripper.Log with a trace of all actions.
      /debug: add Debug information to Au3Stripper.Log.
      /so : This is the default when no parameters are provided. same as /sf + /sv
      /sf : Strip all unused Func's
      /sv : Strip all unused Global var records.
      /mo : Just merges the Include files into the source and strips the Comments.
            This is similar to aut2exe and helps finding the errorline.
      /mi : Sets the maximum Iterations Au3Stripper will perform. Default is 5.
      /rm : Rename Variables and Functions to a shorter name.
      /rsln: Replace @ScriptLineNumber with the actual line number.
      /Beta: Use Beta Includes.
      - Icon not found:  your_icon.ico ==> Changing to default ICON.
      >Running AU3Check (3.3.14.5)  from:C:\Program Files (x86)\AutoIt3  input:C:\Users\Migue\Documents\Sync\Batches and Scripts\WDW-Scripts\SMS.au3
      +>18:28:11 AU3Check ended.rc:0
      >Running Au3Stripper (18.708.1148.0)  from:C:\Program Files (x86)\AutoIt3\SciTE\Au3Stripper cmdline:
      - 0.22 Iteration 1 Strip Functions result: Output  1050 lines, stripped 0 Func lines and 234 Commentlines
      - 0.61 Iteration 2 Strip Variables result: Output  88 lines and stripped 962 lines
      - 0.63 Iteration 3 Strip Variables result: Output  58 lines and stripped 30 lines
      - 0.64 Iteration 4 Strip Variables result: Output  52 lines and stripped 6 lines
      - 0.66 Iteration 5 Strip Variables result: Output  51 lines and stripped 1 lines
      +> Source    1285 lines 48435 Characters.
      +> Stripped  999 Func/Var lines and  234 comment lines, Total 46893 Characters.
      +> Saved     95% lines 96% Characters.
      +> Au3Stripper v18.708.1148.0 finished created:C:\Users\Migue\Documents\Sync\Batches and Scripts\WDW-Scripts\SMS_stripped.au3
      +>18:28:12 Au3Stripper ended.rc:0
      >Running AU3Check (3.3.14.5)  from:C:\Program Files (x86)\AutoIt3  input:C:\Users\Migue\Documents\Sync\Batches and Scripts\WDW-Scripts\SMS_stripped.au3
      +>18:28:12 AU3Check ended.rc:0
      >Running:(3.3.14.5):C:\Program Files (x86)\AutoIt3\aut2exe\aut2exe.exe  /in "C:\Users\Migue\Documents\Sync\Batches and Scripts\WDW-Scripts\SMS_stripped.au3" /out "C:\Users\Migue\AppData\Local\AutoIt v3\Aut2exe\~AU495C.tmp.exe" /nopack /comp 2
      +>18:28:13 Aut2exe.exe ended.C:\Users\Migue\AppData\Local\AutoIt v3\Aut2exe\~AU495C.tmp.exe. rc:0
      !>18:28:13 Problem copying file from: C:\Users\Migue\AppData\Local\AutoIt v3\Aut2exe\~AU495C.tmp.exe To :C:\Users\Migue\Documents\Sync\Batches and Scripts\WDW-Scripts\SMS.exe
      +>18:28:14 AutoIt3Wrapper Finished.
      >Exit code: 0    Time: 3.046
       
      When I attempt to run the executable I get the following:

      Many thanks in advance
    • ammaul
      By ammaul
      Hi folks, I'm having problems with a screenshot capture script.
      Let me explain.
      Everyday I (and my colleagues at work) need to take some screenshots from a web-page. These screenshots are used to compile a report. Normally, I (and others) used to log in into the website and took screenshots of desired graphics and tables. This is tediuos and time consuming. To easy this task I made a script using autoit that basically logs into the website (user and password) and using some clicks, stroke send, coordinates, it is able to generate the graphics and save them to some folders into our network (this script saves arouund 50 pics. It works like a sharm.
      In order to make things easier, I tried to schedule this script (compiled to a Screnpics.exe file) using task scheduler from windows. We already use this (task scheduler) to run some vbs scripts, some vba excel scripts and so on. The computer used for this tasks is a windows 7 desktop computer. Due to security policies, the computer locks after some time. All this tasks run in the locked computer.
      My script screenpics.exe runs also from this locked computer. When the computer is unlocked, it does everything as expected. But, when it is locked, all the "pics" are BLACK. As I understand, it runs ok, but, as the "windows" are innactive, it prints what it "sees": a black rectangular.
      Some details: The web-page with hold the information I need, it only works in Firefox and, because of this it couldn`t be managed by vba or some "getobject" like commands. In fact, it has some flash things that make it impossible to control programatically. So my script is based on mouse move to coordinates, mouse click, screen capture and so one.
       
      So, I read many posts trying to figure out a way to overcome this, but... nothing came to mind. My first idea was try to unlock windows. Theses lead me to some posts with no solution. This is worse because I'm not a computer admin, so procedures that need to replace/change the register are not an option.
       
      If someone has any idea, I'll be gratefull.
    • therks
      By therks
      Has anybody else noticed that Windows 7 reacts ignorantly when you use the Windows key + arrow key shortcuts on a GUI with GUIEventMode set to 1? I discovered this recently when I was working on an app where I wanted complete control over the maximize/minimize buttons.
      Just give it a spin:
      #include <GUIConstants.au3> Opt('GUIEventOptions', 1) $hGUI = GUICreate('', 300, 200, Default, Default, BitOR($GUI_SS_DEFAULT_GUI, $WS_MAXIMIZEBOX)) GUISetState() While 1 Switch GUIGetMsg() Case $GUI_EVENT_MAXIMIZE ToolTip('Maximized') Case $GUI_EVENT_MINIMIZE ToolTip('Minimized') Case $GUI_EVENT_RESTORE ToolTip('Restored') Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Run that, then hit Win+Up or Win+Down. None of those events get triggered, and it still maximizes/minimizes. Although I can't get it to restore down from a maximize unless the window is also resizable ($WS_THICKFRAME in the style).
      Is there a way to stop Windows from doing what it wants or is the only option to check with WinGetState() and then change it back?
       
      Windows 10 seems to respect my settings, and I don't have any other versions to test on.
    • WoodGrain
      By WoodGrain
      Hi All,
      I've bought a Ergodox EZ programmable keyboard with layers of key maps (eg, layer 0 = dvorak, layer 1 = numberpad & nav, layer 2 = qwerty, etc), I've also got a small USB screen that can pull information from the registry. What I would like to determine is a way to pull the value of each current key from the keyboard, I can then write the values to the registry and pull them into my USB screen so I can see the keyboard key layout.
      I'm stuck with retrieving the key values, I've looked at _IsPressed(), _WinAPI_GetKeyState(), _WinAPI_GetKeyboardState(), _WinAPI_GetKeyboardType(), _WinAPI_GetKeyNameText(), but none of them appear to be able to pull the keyboard key values without user interaction.
      The idea being, in pseudo code, bear in mind no error checking etc in this code, "GetKeyValue()" is what I'm needing help with and is a made up function, as is "HardwareKey1" etc:
      $keyOnePrev = "" $numOfKeys = 76 While 1 $keyOneCurrent = GetKeyValue(HardwareKey1) If $keyOneCurrent <> $keyOnePrev Then For $key = 1 To $numOfKeys $keyToWrite = GetKeyValue(HardwareKey & $key) RegWrite("HKEY_CURRENT_USER\Software\myKeyboard", "key" & $key, "REG_SZ", $keyToWrite) Next EndIf $keyOnePrev = $keyOneCurrent Sleep(5000) WEnd Thanks guys!
    • msd1994
      By msd1994
      I have a script that just adds some keyboard shortcuts for things like displaying the current song and artist, moving the window to the side so it won't pop up in my way, and play/pause, next song, previous song (these are the only 3 to still work since they don't need the window handle.)
      In some update recently, Spotify's window class swapped from "[CLASS:SpotifyMainWindow]" to "[CLASS:Chrome_WidgetWin_0]". Using the new class in my controls doesn't seem to work, I've tried getting the window handle from the process handle (_GetHwndFromPID($PID)) but that seems to fail as well.
      Does anybody have some idea of a way I could get this script working again?
       
      edit: seems like discord has the same window class name, so could be some issue with this? Still not sure of a way to solve the issue though, I added a function to get the handle of the active window and can just use that now, but it was able to find it on its own before on spotify startup or script startup which would be preferred.
       
      Thanks!
×