• IS THIS SITE UGLY? Click "RG3" at the very bottom-left of this page to change it. To dismiss this notice, click the X --->

Guide - HOW TO POWERSHELL: A treatise on the post modern nouveau riche of Microsoft

G

gse7en

#1
If you're running a recent version of Windows, you probably have Microsoft PowerShell at your disposal. When you press WinKey+X (or right click the start menu icon), you'll probably see two options for running Windows Powershell (one regular, one admin). Some of you heathens and blasphemers might have replaced it with Cmd prompt option, in which case shame on you and your generations of offspring and ancestors for many generations; go undo your filthy sin and make penance by learning that powershell does everything Cmd does except it looks better doing it.

So WTF is PowerShell anyway? It's a command line environment similar to Bash or whatever your favourite intelligent command line environment is. It's basically Cmd on crack. And getting to it is easy. There several ways to open PowerShell. The fastest is to open the start menu and simply type "PowerShell". Alternatively, you can do as I said above with WinKey+X. If you have a Windows Explorer window open, in the address bar, you can simply type "Powershell" to open a prompt at that directory. For now, however, let's open an even more powerful version of PowerShell by opening start and typing "Powershell ISE", which will open the Integrated Scripting Environment.

With the ISE open, you will see the powershell prompt in the bottom portion of the window. On the right pane, you will see a help index. In the upper bit of the window, you can see the scripting environment. At any time, you may enter input at the prompt as if it were a dedicated powershell console. Many of your familiar DOS commands will work as well. For example, you may do a "cd C:\EverQuest" to take you to that directory. You can do a "dir *.ini" to display all ini files (in a very low level way, as you'll see later.) Additionally, if you're like me, you will occasionally type Linux console commands at the prompt, such as "ls *.ini". Unlike previous eras in Windows, the console will no longer give you dirty looks and refuse to execute the command, it will just do the command.

That's well and good, gSe7eN, but what's the big fucking deal? Well, I don't appreciate your vulgarities, but the big fucking deal is, powershell is a big fucking deal. And even you can use it to do mindless menial tasks for EverQuest maintenance. For example, I use it to launch all my shit when I restart the PC. I have no friends (especially not @kaen01). I hate people, people hate me, but I like to raid. The practical solution is to not get over myself and learn to work with people, but rather 60 box (54 with a bench). As such, my challenges are greater than your challenges. So let's learn how to launch shit using powershell.

Powershell uses commands called CmdLets to do all the work. The first CmdLet this guide is going to teach you is the "Start-Process" CmdLet. This CmdLet…..starts a process. So "how the fuck does it work", you ask. Well, I'd tell you if you would stop swearing and interrupting me. The first thing we're going to open is MacroQuest2.exe. For me, that's located at D:\MQ2RG\Macroquest2.exe. So in powershell:

Code:
Start-Process -FilePath 'd:\MQ2RG\macroquest2.exe' -Verb RunAs
The first bit, -FilePath, tells the CmdLet which process to start. There's a second bit, -Verb. In this case, I'm telling this process to run as administer (as required by MQ2.exe). Assuming your path is the same as mine, you can copy and paste that directly into the PS console and execute it, macroquest2.exe will run immediately as administrating (with UAC prompt, if you have that enabled). Swell. I use InnerSpace for my window management so let's go ahead and start that.

Code:
Start-Process -FilePath 'D:\Program Files\isBoxer\InnerSpace.exe' -WindowStyle Minimized
You'll notice a new parameter, "-WindowStyle". -WindowStyle accepts "Normal", "Minimized", "Maximized", and "Hidden" (as we'll see in a few). I don't want to see InnerSpace, but I might later, so we'll start it minimized. Easy enough. I also start the MySEQ server, GINA, GamParse, and EQBCI. So....

Code:
Start-Process -FilePath 'd:\MQ2RG\MySEQ\server.exe'-WindowStyle Minimized -Verb RunAs
Start-Process -FilePath 'd:\MQ2RG\eqbci.exe' -WindowStyle Minimized
Start-Process -FilePath 'C:\Users\gSe7eN The Best\AppData\Local\Apps\2.0\X9ENEG3A.7AT\8DG44H21.L1E\gina..tion_d03ba0016bff7a04_0001.0000_01c7bcb8e29f5572\gina.exe' -WindowStyle Minimized
Start-Process -FilePath 'd:\MQ2RG\Utilities\GamParse-1.5.2.20.exe'-WindowStyle Minimized
Nothing new there. I also start FIVE processes of the EQBC server (this allows me to be dynamic if I need certain groups in or out of certain server.) Five is probably over kill, but RAM is cheap, why not put it to work. I also don't want to run EQBCS as a service because I don't want it running unless I'm playing EQ. Plus I run five of them. Like such:

Code:
Start-Process -WindowStyle hidden -FilePath D:\MQ2RG\eqbcs2.exe -ArgumentList "-p 2111"
Start-Process -WindowStyle hidden -FilePath D:\MQ2RG\eqbcs2.exe -ArgumentList "-p 2112"
Start-Process -WindowStyle hidden -FilePath D:\MQ2RG\eqbcs2.exe -ArgumentList "-p 2113"
Start-Process -WindowStyle hidden -FilePath D:\MQ2RG\eqbcs2.exe -ArgumentList "-p 2114"
Start-Process -WindowStyle hidden -FilePath D:\MQ2RG\eqbcs2.exe -ArgumentList "-p 2115"
I've hidden the windows completely as you can see. I don't need to see input/output ever, so they're removed from view. Out of sight, out of mind. You'll notice a new parameter, "ArgumentList". In dos, you'd simply run 'eqbcs2.exe -p 2111" to specify the port. In the object oriented world of C# and PowerShell, we use -ArgumentList ""

(InnerSpace stuff warning!) And hell, we might as well launch some EverQuest boxes. Let's do 18 of them, eh? My team is called "Doctor Death and Friends". Let's do it.

Code:
Start-Process -FilePath 'D:\Program Files\isBoxer\InnerSpace.exe' -Verb RunAs -ArgumentList "run isboxer -launchslot `"Doctor Death and Friends`" 1"
Start-Sleep -s 1
Start-Process -FilePath 'D:\Program Files\isBoxer\InnerSpace.exe' -Verb RunAs -ArgumentList "run isboxer -launchslot `"Doctor Death and Friends`" 2"
Start-Sleep -s 1
Start-Process -FilePath 'D:\Program Files\isBoxer\InnerSpace.exe' -Verb RunAs -ArgumentList "run isboxer -launchslot `"Doctor Death and Friends`" 3"
Start-Sleep -s 1
//snip//
Start-Process -FilePath 'D:\Program Files\isBoxer\InnerSpace.exe' -Verb RunAs -ArgumentList "run isboxer -launchslot `"Doctor Death and Friends`" 17"
Start-Sleep -s 1
Start-Process -FilePath 'D:\Program Files\isBoxer\InnerSpace.exe' -Verb RunAs -ArgumentList "run isboxer -launchslot `"Doctor Death and Friends`" 18"
Start-Sleep -s 1
Couple things to notice here. -ArgumentList is wrapped in inverted commas (or quotation marks, for the yanks). BUT the InnerSpace argument requires the same. We can escape these with a backtick/grave character. Also, you've learnt your second CmdLet, "Start-Sleep". You might notice that CmdLets use a Verb-Noun relationship (If you haven't figured that out yet, spoiler alert) so you can sort of tell what the command does without the need to memorise 9,000 different commands. Start-Sleep pauses 1 second between execution, otherwise InnerSpace, being the big jock-like idiot software that it is, goes thick headed and stupid, so you need to slow down your instruction so it understands.

Note: if you're using a Microsoft network (and if you are, you already know this stuff, and if you don't know this stuff, then you stole Windows Server from TPB, in which case I spit on your father and piss on your mother), you can send these commands to other PC's on the network to launch any apps you need there.

The next thing to learn is how to shut all this shit down. That's pretty easy, and we're going to learn how to pipe information from one CmdLet to another. So let's learn the Get part of the CmdLets. In this case, we're going to Get-Process. This fetches information about a process, like so:

Code:
Get-Process -Name macroquest2.exe
Handles  NPM(K)    PM(K)      WS(K)     CPU(s)     Id  SI ProcessName                                                                         
-------  ------    -----      -----     ------     --  -- -----------                                                                         
    210      14     3144      12052       0.31    884   1 MacroQuest2
We're going to take that information and give it to the Stop-Process command.

Code:
 Get-Process -Name Macroquest2 | Stop-Process
And just like that, RIP MQ2. You'll notice the Pipe character separating those two CmdLets. It is indeed a pipe and not some strange, illogical division between two unrelated ideas in a bloated and complicated ini file. It's a pipe, like a bridge, that takes information from the first bit and feeds it to the second bit.

But why stop at murder one when we can go serial:

Code:
 Get-Process | Where-Object {$_.Path -like "D:\mq2rg\eqbcs2.exe"} | Stop-Process
Repeat until you're content.

Everything is well and good, but all of a sudden, @kaen01 breaks his restraining order and finally locates my crew to personally sexually harass each of them. #MeToo. It's time to transfer servers and change our names OR perhaps time to roll a new team on a new server until he starts perving on the new ones. That leaves a lot of file editing we have to do for our new characters. In the second case, we want to copy settings and what-not, but keep the old ones. Regex and powershell to the rescue!

Code:
Get-ChildItem -Filter "*Hugglebear*" | Where-Object Name -Match '(.*)Hugglebear(.*)' | Copy-Item -Destination { $Ma
tches.1 + 'Softteddybearodeathnlove' + $Matches.2}
My previous character was named Hugglebear. I'm going to roll a new character named Softteddybearodeathnlove. We're going to go to our EverQuest directory, and give that command. It's going to find all Hugglebear's files, it's then going to analyse Hugglebear's files with Regex (-Match 'regex'), then it's going to copy those files renaming the old to the new, giving you two sets of files, Hugglebear's and Softteddybearodeathnlove's. I'll probably do this same thing within my MQ2 directory. (Note well, adding -WhatIf to the end of the line will do a practice run, showing you what will be done.)

But, I decide it's time to move Hugglebear to a new server (or transfer to test). Unfortunately, some bellend prick already has the name Hugglebear on the new server. That's a lot of new setup in MacroQuest2 that I'd rather avoid. So, powershell to the rescue, in conjunction with everything above, let's change all the old reference in MQ2 to the new ones. So, in our MQ2 directory:

Code:
$configFiles = Get-ChildItem . *.ini -rec
foreach ($file in $configFiles)
{
    (Get-Content $file.PSPath) |
    Foreach-Object { $_ -replace "Hugglebear", "Hugglebearexexe" } |
    Set-Content $file.PSPath
}
So that's my UI, my autologin, my autogroup, my EQBC, etc etc etc, sorted. MIght as well do file names, as well.

Code:
Get-ChildItem -Filter "*Hugglebear*" -Recurse -Path 'D:\MQ2RG\' | Rename-Item -NewName {$_.name -repla
ce 'Hugglebear','Hugglebearexexe' }
And assuming we did transfer to test rather than another live server, and we're on an m.2 drive and storage space is a premium where we don't want two different explicit EQ installs, we can keep the files for test and live in folders called "Test" and "Live" and switch between the two of them with a simple powershell script.

Code:
$EQDir = "C:\EverQuest"
$live = "C:\EverQuest\Live"
$test = "C:\EverQuest\Test"
$mq2l = "D:\MQ2RG\Macroquest2.exe"
$mq2t = "D:\MQ2RG - Copy\Macroquest2.exe"
Get-ChildItem -Path $EQDir -Filter EQGraphicsDX9.dll | Remove-Item
Get-ChildItem -Path $EQDir -Filter eqhost.txt | Remove-Item
Get-ChildItem -Path $EQDir -Filter eqmain.dll | Remove-Item
Get-ChildItem -Path $EQDir -Filter eqgame.exe | Remove-Item
Get-ChildItem -Path $EQDir -Filter OptionsEditor.exe | Remove-Item
Get-ChildItem -Path $test -Filter EQGraphicsDX9.dll | Copy-Item -Destination $EQDir
Get-ChildItem -Path $test -Filter eqhost.txt | Copy-Item -Destination $EQDir
Get-ChildItem -Path $test -Filter eqmain.dll | Copy-Item -Destination $EQDir
Get-ChildItem -Path $test -Filter eqgame.exe | Copy-Item -Destination $EQDir
Get-ChildItem -Path $test -Filter OptionsEditor.exe | Copy-Item -Destination $EQDir
Get-Process -Name macroquest2 | Stop-Process
Start-Process -FilePath $mq2t -verb runAs
You can figure out how to reverse from test to live pretty easily but re-arranging the above.

So that's a basic introducing to both Microsoft Windows PowerShell and Kaen01 brand sexual harassment.

For reference on the latter, you should message Kaen01 directly, his rates are reasonable.

For Windows PowerShell reference, either consult The (Quite Good In Fact) Fucking Manual: HERE.. Any questions about PowerShell, Regex, Object Oriented Programming, or Programming in general should contact resident staff member and God's Gift's to Code, Chats.
 

Redbot

lvl 69 Rouge
Moderator
Joined
Oct 15, 2004
Likes
588
RedCents
39,572¢
#3
Broke: clicking everything
Woke: EQ without a mouse
Bespoke: Windows without a mouse
 
Joined
Feb 16, 2018
Likes
16
RedCents
411¢
#5
Good write-up. While powershell > traditional windows CLI from the DOS days (which has gotten better with w10) it still seems like MS tries to make some of the commands way more complicated then they need to be. There's also the subsystem you can install to windows if you're more of a *nix guy now.
 
Joined
Mar 23, 2018
Likes
13
RedCents
147¢
#6
Good write-up. While powershell > traditional windows CLI from the DOS days (which has gotten better with w10) it still seems like MS tries to make some of the commands way more complicated then they need to be. There's also the subsystem you can install to windows if you're more of a *nix guy now.
Dear god, how am I now just finding out about this?! Even though the guide is awesome, I kept thinking, "Why can't the commands be more line *nix?"

Although, I've know become more used to the PowerShell stuff thanks to gse7ven!
 

gSe7eN

gSe7eN makes porn for a living & is RG staff
Moderator
Joined
Nov 10, 2018
Likes
133
RedCents
71,821¢
#8
WorkHorses.ps1:
$IS = "$env:InnerSpace\InnerSpace.exe"
$MQ2 = "$env:MQ2\MacroQuest2.exe"
$Run1 = Get-Process -Name InnerSpace -ErrorAction SilentlyContinue
$Run2 = Get-Process -Name MacroQuest2 -ErrorAction SilentlyContinue

if($Run1 -eq $null) {Start-Process -FilePath $IS -WindowStyle Minimized}
if($Run2 -eq $null) {Start-Process -FilePath $MQ2 -verb RunAs -WorkingDirectory $env:MQ2}
Start-Sleep -s 3

ForEach ($n in 1..32) {
    Start-Process -FilePath $IS -Verb RunAs -ArgumentList "run isboxer -launchslot `"WorkHorses`" $n"
    Start-Sleep -s 1
}
 

gSe7eN

gSe7eN makes porn for a living & is RG staff
Moderator
Joined
Nov 10, 2018
Likes
133
RedCents
71,821¢
#9
MakeTest.ps1:
$EQDir = "C:\EverQuest"
$live = "C:\EverQuest\Live"
$test = "C:\EverQuest\Test"
$mq2l = "$env:MQ2\MacroQuest2.exe"
$mq2t = "$env:MQ2\MacroQuest2.exe"
$MQ2ZipDir = "$env:USERPROFILE\My Cloud\MQ2RG"
$ZipFilter = "Very Vanilla TEST*"
$MQ2Test = Get-ChildItem -Path $MQ2ZipDir -Filter $ZipFilter | Sort-Object -Property Name -Descending | Select-Object -First 1
$FileList = "$MQ2ZipDir\FileList.txt"
Get-Process -Name macroquest2 -ErrorAction SilentlyContinue | Stop-Process -ErrorAction SilentlyContinue
Expand-Archive "$MQ2ZipDir\$MQ2Test" -DestinationPath "$env:MQ2" -Force
Get-ChildItem -Path "$env:MQ2\Release"  | Copy-Item -Force -Destination $env:MQ2 -Recurse
Get-ChildItem -Path "$env:MQ2\Release"  | Remove-Item -Force -Recurse
Remove-Item "$env:MQ2\Release"  -Force -Recurse
Get-ChildItem -Path $EQDir -Filter "*-201*-*" | Remove-Item
Get-ChildItem -Path $env:MyCloud -Filter "*-201*-*" | Remove-Item
Get-Content -Path $FileList | ForEach-Object {
    Get-ChildItem -Path $EQDir -Filter $_ | Move-Item -Force -Destination $live -ErrorAction SilentlyContinue
    Get-ChildItem -Path $test -Filter $_ | Copy-Item -Destination $EQDir -ErrorAction SilentlyContinue
}
Start-Process -FilePath $mq2t -verb runAs -WorkingDirectory $env:MQ2
FileList.txt:
EQGraphicsDX9.dll
eqhost.txt
eqmain.dll
eqgame.exe
OptionsEditor.exe
Here's an example of a script that patches a live install of EQ to a test install without using the patcher. What it does: Kills MQ2.exe. It moves the files in the FileList.txt to EQ\Live and moves the same files from EQ\Test too .\EQ. It also unzips the test version of VV, puts it in my MQ2 directory, and cleans itself up. The next challenge, of course, is to check to see if there's a newer RedGuides VV zip and download it if there is. But we're not there yet.
 

gSe7eN

gSe7eN makes porn for a living & is RG staff
Moderator
Joined
Nov 10, 2018
Likes
133
RedCents
71,821¢
#10
Here's a great one by Alynel. Knightly wrote the Credentials part and I made it work within Alynel's original script. This depends on Credential Manager from the powershell marketplace. Just remove that bit if you don't feel like adding it. What this does simply grabs the newest RedGuides Very Vanilla zip. Change cd $env:MyCloud\MQ2RG to where ever you want the zip downloaded.

Get-LiveZip.ps1:
$MyCredentials = Get-StoredCredential -Target "RedGuides"
        if($MyCredentials -eq $null) {
            $MyUsername = Read-Host -Prompt 'Input your user ID'
            $MyPassword = Read-Host -Prompt 'Input your password' -AsSecureString
            New-StoredCredential -Target "RedGuides" -UserName $MyUsername -SecurePassword $MyPassword -Persist Enterprise -Type Generic | Out-Null
            $MyCredentials = Get-StoredCredential -Target "RedGuides"
        }
# Add extra entries to download more stuff
$downloads = @{
#    "MQ2EQWire.zip" = @{
#        url = "https://www.redguides.com/community/resources/mq2eqwire.295/download";
#        regex = 'a href="(/community/resources/mq2eqwire\.295/version/\d+/download\?file=\d+)'
#    }
    "VeryVanillaLive.zip" = @{
        url = "https://www.redguides.com/community/threads/very-vanilla-mq2-live-servers.66784/";
        regex = 'a href="(/community/attachments/very_vanilla_compile\d+-zip\.\d+/)'
        #https://www.redguides.com/community/attachments/very_vanilla_compile20190510-zip.16735/
    }
}

cd $env:MyCloud\MQ2RG

function New-RGSession {
    param([string]$Username,
          [string]$Password)

    $preLogin = Invoke-WebRequest -Uri "https://www.redguides.com/community/login" -SessionVariable session

    $token = ($preLogin.Forms | ? { $_.Action -like "*login" }).Fields["_xfToken"]

    $fields = @{_xfToken = $token; login = $Username; password = $Password }

    $login = Invoke-WebRequest -Uri "https://www.redguides.com/community/login/login" -WebSession $session -Method POST -Body $fields

    return $session
}

$username = $MyCredentials.UserName
$password = $MyCredentials.Password
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($password))
# Or hardcode them

$session = New-RGSession -Username $username -Password $password
# Or persist session

foreach ($kvp in $downloads.GetEnumerator()) {
    $filename = $kvp.Key
    $url = $kvp.Value["url"]
    $pattern = $kvp.Value["regex"]
    

    $req = iwr -Uri $url -WebSession $session

    $match = [regex]::Match($req.Content, $pattern)

    if ($match.Success) {
        Write-Host "Downloading $filename"
        $dl = Invoke-WebRequest -Uri "https://www.redguides.com$($match.Captures.Groups[1])" -WebSession $session -OutFile $filename -PassThru
        
        if ($dl.StatusCode -ne 200) {
            Write-Error "Failed to download $filename"
        }
    } else {
        Write-Error "Failed to download $filename"
    }

}
I use this in conjunction with something like this.

Do-TestPatch.ps1:
$MyCredentials = Get-StoredCredential -Target "Super Secret Credentials for MQ2"
        if($MyCredentials -eq $null) {
            $MyUsername = Read-Host -Prompt 'Input your user ID'
            $MyPassword = Read-Host -Prompt 'Input your password' -AsSecureString
            New-StoredCredential -Target "Super Secret Credentials for MQ2" -UserName $MyUsername -SecurePassword $MyPassword -Persist Enterprise -Type Generic | Out-Null
            $MyCredentials = Get-StoredCredential -Target "Super Secret Credentials for MQ2"
        }

$PCNames = "localhost","BOB","MONSTER", "Plexi"
if($sess1 -eq $null) { New-PSSession -ComputerName $PCNames -Credential $MyCredentials }
$sess1, $sess2, $sess3, $sess4 = Get-PSSession

#Define paths
$MQ2ZipDir = "$env:USERPROFILE\My Cloud\MQ2RG"
$ZipFilter = "VeryVanillaTEST*"
$MCMQ = "$env:MyCloud\MQ Stuff"

#Define Data
$MQ2Test = Get-ChildItem -Path $MQ2ZipDir -Filter $ZipFilter | Sort-Object -Property Name -Descending | Select-Object -First 1
$CoreBeta = "$env:MyCloud\MQ Stuff\Beta\*"
$MacFiles = "$env:MyCloud\MQ Stuff\*.mac"
$MQ2Files = "$env:MQ2\Release\*"
$TempZip = Get-ChildItem -Path "$env:MQ2\Release"


# Expand the newest test zip.
Expand-Archive "$MQ2ZipDir\$MQ2Test" -DestinationPath "$env:MQ2" -Force

# Update the yits.
cd "$MCMQ\Beta"
git stash
git pull
git stash pop
cd "$env:USERPROFILE\.vscode\extensions\MQ2Macro"
git pull


# Close EQ, Copy the stuff around.
$PCNames[0], $PCNames[1], $PCNames[2], $PCNames[3] | ForEach-Object {
    Invoke-Command -ComputerName $_ -FilePath "$env:MyCloud\KillEQ.ps1" -Credential $MyCredentials
    Copy-Item -Path "$CoreBeta" -Destination "\\$_\MQ2\Macros\TCB" -Force
    Copy-Item -Path "$MQ2Files" -Destination "\\$_\MQ2\" -Force -Recurse
    Copy-Item -Path "$MacFiles" -Destination "\\$_\MQ2\Macros" -Force -Recurse -Exclude *-20*-*
}
 
Top