Wednesday, July 05, 2017

Using Powershell as a Command / Alias Runner

I've been using an odd little utility(1) for many years to have quick access to commonly run commands, utilities (with parameters), etc.  I finally decided to retire it.  My first thought for a replacement was Unix aliases - I can use the bash shell on windows now.  That went straight to nowheresville; I don't think bash for windows is intended for these kinds of things.  I'm glad it did, however, because I learned how useful powershell is as a command runner.
In order to use it as such, follow these simple steps:
  • At the powershell command prompt, run Test-Path $Profile.  If the result is true, then you have created a profile.  To see the path to your profile, just type $Profile.
  • If the result is false, run this command to create a profile: New-Item -Path $Profile -Type File -Force.  I've no idea how to create it an a location you desire, but this will create a file like this:  "C:\Users\[you]\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"
  • Open your profile in the Powershell ISE.  Create functions to your heart's desire.  Of course you have access to all of powershell's built in commands.  You can then run your functions from the Powershell command line.  Below are the functions I created.  See how I have a bunch beginning with "csm"?  On the command line I type "csm" followed by ctrl-space and I get a list of all available functions (global functions and my custom functions) beginning with those letters.  Then I can tab through the list to the one I want and hit enter to execute it.  So nice!


# the "." is there for "dot raising" or something.  Doesn't work without it.  I guess we wouldn't know; it's a powershell thing.
function set-config-false {. "C:\Program Files\ConfigUpdater\ConfigUpdater.exe" C:\redacted\Web.config}

# Pass the paths to the files you want to compare.
function compare-files([string]$s1, [string]$s2) {. "C:\Program Files (x86)\WinMerge\WinMergeU.exe" $s1 $s2}

function gulp-compile {. "C:\Data\BatchFiles\gulp.bat"}

function open-git 
{ 
    Set-Location C:\Dev\Trebuchet #cd
    . "C:\Program Files\Git\bin\bash.exe"
}

# Our utilities
function csm {. "C:\redacted.exe"}
function csm-admin {. "C:\redacted.exe" -hiddensettings}
function csm-app-server {. "C:\redacted.exe" -protocol:tcp -port:7999 -connection:"[Common]Demo8"}
function csm-def-editor {. "C:\redacted.exe"}
function csm-restore {. "C:\redacted.exe"}
function csm-server-manager {. "C:\redacted.exe" -a}
function csm-upgrade {. "C:\redacted.exe"}
function reddis-server {. "C:\redacted\redis-server.exe"}

function find-color-under-cursor {. "C:\Program Files\pixie.exe"}
function pixel-ruler {. "C:\Program Files\JRuler.exe"}
function remote {. "C:\WINDOWS\system32\mstsc.exe"}
function show-locked-files {. "C:\Program Files\OpenedFilesView\OpenedFilesView.exe"}
function cleanup-vs {. "C:\Data\BatchFiles\CleanVS.bat"}
function backup-data {. "C:\Data\BatchFiles\backupData.bat"}

<# 
    When invoking powershell, type "power" at the windows run box, then ctrl+shift+enter to launch elevated.
    Then, vs will run elevated.
#>
function vs {. "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe"}

1. In fact, I still can't get away from using SlickRun for 2 purposes; an unobtrusive always-on-top clock and a quick-to-invoke paste pad that I'm constantly using, often to simply clean formatting for text.

Monday, February 06, 2017

Will Web Applications be Fixed Soon?

I read recently that in-browser apps are extremely favored over native apps (people use their favorite social media app and maybe one other and don't bother with anything else). This makes sense; users are taking the path of least resistance - don't bother installing something unless you have to.
But yet, in-browser applications are so frequently busted. They even seem to get worse as time goes on in some cases. I used to listen to slacker and pandora in the browser. Slacker stopped working some time ago (in any browser), and pandora stopped working recently. I mean not working as in not at all - can't make it through more than one song without hanging the browser. So I started using spotify which seems to default to having you install a client. It works. You can even pause your song and it resumes at the exact place the next time you open the app. The UI is terrible IMO, but it works. Isn't this the goal, to actually work?
Web as a platform for applications still seems like a hack after all this time. I remember getting excited about Silverlight back in the day: yay, we can write an application and it will work like an application! It was a workaround for web as platform. Then Silverlight (and Flash) died.
Now Service Workers / WebAssembly seem to be the next candidate to overcome the web as platform hack. Will they enable applications to actually work in browsers, or will they go the way of flash? Both are starting out as actual standards, so they seem promising.
If they do catch on, won't that render most of the native app work obsolete?  Maybe these technologies can't help whatever broke slacker/pandora?  What other capabilities will remain un-achievable at that point?

Tuesday, December 27, 2016

Simplest F# Asynchronous Workflow Sample


Can't nobody tell me F# async is easy; it seems hard to me. I wanted to put together the simplest sample (but different from all the samples I've seen) so I could get a better understanding. Maybe thistle help someone else. I'm searching a file line by line to see if I can find a match. In this case there are only 4 lines in the file so I'm splitting the file into 2 chunks.  Each... async... thing (after watching a long video on C# async lately and ending up hopelessly confused about when actual new threads are created I've given up guessing) gets 2 lines. In this sample, it's pretty handy how it stops looking after finding a match.

open System.IO
open System

// So we don't just print "null" to console when not found.
let OptionToStr o =
  match o with
  | None -> "not found"
  | Some(o) -> o

// Because our line gets wrapped into an option twice (once in Seq.tryPick and again in Seq.tryFind).
let UnwrapOption o =
  match o with
  | None -> None
  | Some(o) -> o

// Some(matching line) if the current chunk contains our id
let ChunkContains (id:string) (chunk:string seq) =
  chunk
  |> Seq.tryPick ( fun line -> 
                   // Notice how it doesn't process all lines when it finds a match.
                   printfn "Current: %s" line
                   if line.Contains id then Some(line) else None )

// Skip n lines and call our search method for the current chunk
let ProcessFileChunkAsync (lines:string seq) skip (func: string seq -> string option) =
  async {         // Note: async is cranky.  You have to format the curly braces a certain way or compiler will complain with strange error.
    return lines  // return keyword required.
    |> Seq.skip skip
    |> Seq.truncate 2
    |> func       // 2nd parameter applied to previous partial application of ChunkContains below.
   }

[]
let main argv = 
  let lines = File.ReadAllLines @"c:\temp\4LinesOfTrash.txt"  // one of the lines is "hi mom"
  seq { 0 .. 1 }
  |> Seq.map ( fun i -> let chunkFun = ChunkContains "hi"   // find the line with "hi"; partial func application
                        ProcessFileChunkAsync lines (i * 2) chunkFun
             )
  |> Async.Parallel          // Fork
  |> Async.RunSynchronously  // Join: Take the async results and wait for them to complete.
  |> Seq.tryFind ( fun item -> Option.isSome item )
  |> UnwrapOption
  |> OptionToStr
  |> printfn "Result: %A"
  |> Console.ReadLine
  |> ignore
  0

Thursday, October 13, 2016

URL Redirecting in an OWIN Self-Hosted Web Application

Perhaps this will get old quick; I don't know. Had a small research task for this. In this case I'm using a couple WebAPI controllers along with OWIN to demo.

  1. Create new console application.
  2. Install nuget packages Microsoft.Owin.SelfHost, Microsoft.AspNet.WebApi.Owin
  3. Create RedirectMiddleware.cs like below (source)
  4. Create Startup.cs like below
  5. Make Program.cs (self-host starter) like below.
  6. Add a couple controller classes, I added AController and BController.

 public class RedirectMiddleware : OwinMiddleware
 {

  public RedirectMiddleware(OwinMiddleware next) : base(next)
  {
  }

  public async override Task Invoke(IOwinContext context)
  {
   var url = context.Request.Uri;
   var shouldRedirect = ShouldRedirect(url.AbsoluteUri);
   if (shouldRedirect.Item1)
   {
    // permanent redirect
    context.Response.StatusCode = 301;
    context.Response.Headers.Set("Location", shouldRedirect.Item2);
   }
   else
   {
    await Next.Invoke(context);
   }
  }

  private Tuple<bool, string> ShouldRedirect(string uri)
  {
   if (uri.EndsWith("A"))
   {
    return Tuple.Create(true, "/B");
   }
   else
   {
    return Tuple.Create(false, "");
   }
  }

 }

 class Startup
 {

  public void Configuration(IAppBuilder appBuilder)
  {
   // Setup WebAPI configuration
   var configuration = new HttpConfiguration();

   configuration.Routes.Add("API Default", new HttpRoute("{Controller}"));

   /// Register our middleware
   /// Order of registration is the order the middleware will execute in.
   appBuilder.Use();

   // Register the WebAPI to the pipeline
   appBuilder.UseWebApi(configuration);

   appBuilder.Run((owinContext) =>
   {
    owinContext.Response.ContentType = "text/plain";
    return owinContext.Response.WriteAsync("Controllers are /A and /B.  http://localhost:5000/A will take you to B");
   });
  }

 }

 class Program
 {
  static void Main(string[] args)
  {
   const string baseUrl = "http://localhost:5000/";
   using (WebApp.Start<Startup>(baseUrl))
   {
    Console.WriteLine("Hit me up @ http://localhost:5000.  Press Enter to shut down.");
    Console.ReadKey();
   }
  }
 }

Thursday, October 06, 2016

Powershell Tray Icon Sample

The powershell samples for popping a tray icon weren't complete.  Here is a simple example (pops for 5 seconds) that I'm using to regularly remind me to stand/stretch/focus my eyes on distant objects. I've wrapped it in a scheduled task that calls powershell passing this script as the arg starting every weekday at about the time I start work repeating every 20 minutes.

[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

$m = (Get-date).Minute

<# This is multi-line comment syntax #>
# Console.Write equivalent:
#Write-Host $m

$objNotifyIcon = New-Object System.Windows.Forms.NotifyIcon
$objNotifyIcon.Icon = "C:\Someplace\YourTrayIcon.ico"
$objNotifyIcon.BalloonTipIcon = "Warning"
$objNotifyIcon.BalloonTipTitle = "Hocus Focus"

if ($m -lt 30)
{
$objNotifyIcon.BalloonTipText = "Focus your eyes distant and blink frequently"
}
else
{
$objNotifyIcon.BalloonTipText = "Focus eyes & stretch too"
}

$objNotifyIcon.Visible = $True
$objNotifyIcon.ShowBalloonTip(1) #Not sure what the heck this millisecond param is even for; it stays until you hide it.

Start-Sleep -Seconds 5
$objNotifyIcon.Visible = $False

Thursday, December 04, 2014

Programatically Reordering Excel Columns

My task is to reorder the columns in an Excel spreadsheet.  The files are generally quite large (75-100K rows, up to 200 columns).

My first inclination was, as usual, EPPlus.  It took a bit of doing, but I got it working.  Only it was slow.  Really slow.  It will stream properly and not overload memory, but too slow.

After reading this post that mentioned the benefits of using Excel automation, I thought I'd give that a try.  I was amazed.  Even though I'm not actually saving the resulting file until after I've copy/pasted all columns of data in, I never see the memory spike.  There's some really good memory management in there.  Yes, having a dependency on a COM object isn't optimal, but when we're talking Excel, I think it's acceptable.  It freakin rocks, and the code is very simple.

Anyway, I'm including both here (if you have a need for this and your files aren't large, you might want to use EPPlus).

In either case the call is like:
EPPlus version:

Helpers:
Main Class:
Excel Automation version:

Wednesday, January 08, 2014

MP3 Player Hack (one way Windows 8 on a tablet is so useful)

I have a few requirements for an MP3 player on my tablet:
  • I need playlists.  Pretty basic.
  • I need a large area that, when tapped, will pause play.
  • Another fairly large area that will rewind 15-30 seconds.
  • I need the app to remember where I left off and resume there if I close/reopen.
  • I need the dang thing not to shut off or for the screen to go black disallowing input.
The reasons for these is because I listen to lectures while driving (with the tablet connected to the car audio).  When I want to pause, I need to be able to simply reach over and tap darn near anywhere to pause.  I need to rewind 30 for when I didn't quite catch part of the lecture.

After quite a search, I couldn't find anything in the windows store that worked (one app came close, had those features, but simply wigged out whenever resuming play after turning the tablet back on).  I probably could find something in the google or apple stores that worked, but for myriad other reasons, I love my windows tablet.

Legacy windows to the rescue.  I can run legacy windows applications, so surely the ever-loved, classic, most fully featured MP3 player in history; WinAmp will fill my needs, right?  Well, it came really close.  Of course, it installed and worked fine on the legacy side of Windows 8, which right there is awesome.  The other vendor's tablets just aren't going to run a 15 year old (or however old WinAmp is) application.

Close.  Prefs allowed me to zoom the interface (using the "modern" skin) so the buttons became large.  Pause is pretty easy to find and push now.  Amazingly, WinAmp doesn't remember where you left off (unless it's a podcast) so I needed a plug-in for that.  Now I'm close.  I need it to keep the screen on (not sure there's a plug-in for that), and I still can't rewind.  The closest way is to invoke the on-screen keyboard and hit the back arrow.

Simple.  I want a controller for WinAmp.  I want something that starts WinAmp (allowing it to simply resume where it left off), issues 2 keystroke commands to it (pause, rewind), and prevents the tablet from going to sleep.  So I created that in very little time.  I threw together an old-school WinForms (damn that technology was useful in many situations, wasn't it?) app that starts WinAmp when it starts, shows itself full-screen with ~ 75% of it's surface for pause, the rest for rewind, and sends a mouse move command every 2 minutes to prevent screen shutdown.  Ye olde Win32 API is ugly, but having the ability to still use it is pretty cool.
If someone out there actually wants the executable, contact me and I'll send it.

The Win32 Code.

  1. class Win32Trash
  2. {
  3.     [DllImport( "User32" )]
  4.     public static extern int SetForegroundWindow( IntPtr hwnd );
  5.  
  6.     /// <summary>
  7.     /// Synthesizes keystrokes, mouse motions, and button clicks.
  8.     /// </summary>
  9.     [DllImport( "User32.dll" )]
  10.     public static extern uint SendInput( uint numberOfInputs, [MarshalAs( UnmanagedType.LPArray, SizeConst = 1 )] INPUT[] input, int structSize );
  11.  
  12.     public enum InputType
  13.     {
  14.         INPUT_MOUSE = 0,
  15.         INPUT_KEYBOARD = 1,
  16.         INPUT_HARDWARE = 2
  17.     };
  18.  
  19.     public enum MouseEventFlags
  20.     {
  21.         MOVE = 0x00000001,
  22.         LEFTDOWN = 0x00000002,
  23.         LEFTUP = 0x00000004,
  24.         RIGHTDOWN = 0x00000008,
  25.         MIDDLEDOWN = 0x00000020,
  26.         MIDDLEUP = 0x00000040,
  27.         ABSOLUTE = 0x00008000,
  28.         RIGHTUP = 0x00000010
  29.     };
  30.  
  31.     public struct MOUSEINPUT
  32.     {
  33.         public int dx;
  34.         public int dy;
  35.         public int mouseData;
  36.         public MouseEventFlags dwFlags;
  37.         public int time;
  38.         public IntPtr dwExtraInfo;
  39.     };
  40.  
  41.     public struct KEYBDINPUT
  42.     {
  43.         public short wVk;
  44.         public short wScan;
  45.         public int dwFlags;
  46.         public int time;
  47.         public IntPtr dwExtraInfo;
  48.     };
  49.  
  50.     public struct HARDWAREINPUT
  51.     {
  52.         public int uMsg;
  53.         public short wParamL;
  54.         public short wParamH;
  55.     };
  56.  
  57.     [StructLayout(LayoutKind.Explicit)]
  58.     public struct MOUSEKEYBDHARDWAREINPUT
  59.     {
  60.         [FieldOffset(0)]
  61.         public MOUSEINPUT mi;
  62.         [FieldOffset( 0 )]
  63.         public KEYBDINPUT ki;
  64.         [FieldOffset( 0 )]
  65.         public HARDWAREINPUT hi;
  66.     };
  67.  
  68.     public struct INPUT
  69.     {
  70.         public InputType dwType;
  71.         public MOUSEKEYBDHARDWAREINPUT mkhi;
  72.     };
  73.  
  74. }

The WinFormCode.  Set the form state to maximized.  There is a timer hard coded to fire every 2 minutes (to issue the mouse move command to prevent tablet sleep), and the path to WinAmp is hard coded to where it's installed by default.

  1. public partial class WinAmpController : Form
  2. {
  3.  
  4.     private static Win32Trash.INPUT[] _junkForMouseInput = CreateMouseInput();
  5.     private static Process _winampProcess;
  6.  
  7.     public WinAmpController()
  8.     {
  9.         InitializeComponent();
  10.     }
  11.  
  12.     public void launchWinAmp()
  13.     {
  14.         try
  15.         {
  16.             // We don't want to attach to an existing process if one exists
  17.             var si = new ProcessStartInfo( @"C:\Program Files\Winamp\winamp.exe" ) { UseShellExecute = false, WindowStyle = ProcessWindowStyle.Normal };
  18.             _winampProcess = Process.Start( si );
  19.             _winampProcess.WaitForInputIdle();
  20.         }
  21.         catch( Exception e )
  22.         {
  23.             MessageBox.Show( "Error launching Winamp: " + e.Message );
  24.         }
  25.  
  26.     }
  27.  
  28.     private void WinAmpController_Load( object sender, EventArgs e )
  29.     {
  30.         launchWinAmp();
  31.         /// Expected that the plug-in will begin playing where left off.
  32.         this.TopMost = true;
  33.     }
  34.  
  35.     private void WinAmpController_FormClosing( object sender, FormClosingEventArgs e )
  36.     {
  37.         if ( _winampProcess != null )
  38.         {
  39.             _winampProcess.CloseMainWindow();
  40.             _winampProcess.Close();
  41.         }
  42.     }
  43.  
  44.     private static void SendKey( string key )
  45.     {
  46.         IntPtr windowHandle = _winampProcess.MainWindowHandle;
  47.         //ShowWindow( windowHandle, 1 );  // ain't shit
  48.         Win32Trash.SetForegroundWindow( windowHandle );
  49.         SendKeys.SendWait( key );
  50.     }
  51.  
  52.     private void btnPause_Click( object sender, EventArgs e )
  53.     {
  54.         SendKey("c");
  55.     }
  56.  
  57.     /// <summary>
  58.     /// Each left command is only 5 secs back.  This results in about 19 secs back.  Putting multiple calls to SendWait inside the SendKey method didn't work.
  59.     /// </summary>
  60.     /// <param name="sender"></param>
  61.     /// <param name="e"></param>
  62.     private void btnBack_Click( object sender, EventArgs e )
  63.     {
  64.         SendKey( "{LEFT}" );
  65.         SendKey( "{LEFT}" );
  66.         SendKey( "{LEFT}" );
  67.     }
  68.  
  69.     /// <summary>
  70.     /// Set for every 2 min.
  71.     /// </summary>
  72.     /// <param name="sender"></param>
  73.     /// <param name="e"></param>
  74.     private void timer1_Tick( object sender, EventArgs e )
  75.     {
  76.         this.Text = "timer clicked";
  77.         MoveMouse();
  78.     }
  79.  
  80.     private static Win32Trash.INPUT[] CreateMouseInput()
  81.     {
  82.         var i = new Win32Trash.INPUT()
  83.         {
  84.             dwType = Win32Trash.InputType.INPUT_MOUSE,
  85.             mkhi = new Win32Trash.MOUSEKEYBDHARDWAREINPUT()
  86.             {
  87.                 mi = new Win32Trash.MOUSEINPUT()
  88.                 {
  89.                     dx = 0,
  90.                     dy = 0,
  91.                     mouseData = 0,
  92.                     dwFlags = Win32Trash.MouseEventFlags.MOVE,
  93.                     time = 0,
  94.                     dwExtraInfo = IntPtr.Zero
  95.                 }
  96.             }
  97.         };
  98.         return new Win32Trash.INPUT[] { i };
  99.     }
  100.  
  101.     private void MoveMouse()
  102.     {
  103.         Win32Trash.SendInput( 1, _junkForMouseInput, Marshal.SizeOf( _junkForMouseInput[0] ) );
  104.     }
  105.  
  106. }