Embed PowerShell inside a batch file

As far as I’m concerned, the only thing batch files are useful for is bootstrapping PowerShell scripts.  Below are two different ways to launch a PowerShell script from a batch file.

First, we use a small batch file to check that the PowerShell execution policy is suitable for our script (i.e. either RemoteSigned or Unrestricted).  If the policy is suitable, we continue to the script.  If it isn’t, we prompt to elevate, change the policy, then continue on to execute the script.

@echo off
:CheckPowerShellExecutionPolicy
FOR /F "tokens=*" %%i IN ('powershell -noprofile -command Get-ExecutionPolicy') DO Set PSExecMode=%%i
if /I "%PSExecMode%"=="unrestricted" goto :RunPowerShellScript
if /I "%PSExecMode%"=="remotesigned" goto :RunPowerShellScript
 
NET FILE 1>NUL 2>NUL
if not "%ERRORLEVEL%"=="0" (
	echo Elevation required to change PowerShell execution policy from [%PSExecMode%] to RemoteSigned
	powershell -NoProfile -Command "start-process -Wait -Verb 'RunAs' -FilePath 'powershell.exe' -ArgumentList '-NoProfile Set-ExecutionPolicy RemoteSigned'"
) else (
	powershell -NoProfile Set-ExecutionPolicy RemoteSigned
)
 
:RunPowerShellScript
powershell -noprofile "%~dp0MY_SCRIPT_IN_THE_SAME_DIRECTORY.ps1 %1"

Second, we take it a step further and create a polyglot script to embed the entire ps1 file into the batch file.  This is great for distributing a single file and not having to worry about the launcher (the .cmd file) being separated from the ps1 file, or someone trying to run the ps1 file directly, or opening it in the ISE.

@@echo off
:CheckPowerShellExecutionPolicy
@@FOR /F "tokens=*" %%i IN ('powershell -noprofile -command Get-ExecutionPolicy') DO Set PSExecMode=%%i
@@if /I "%PSExecMode%"=="unrestricted" goto :RunPowerShellScript
@@if /I "%PSExecMode%"=="remotesigned" goto :RunPowerShellScript
 
@@NET FILE 1>NUL 2>NUL
@@if not "%ERRORLEVEL%"=="0" (
@@echo Elevation required to change PowerShell execution policy from [%PSExecMode%] to RemoteSigned
@@powershell -NoProfile -Command "start-process -Wait -Verb 'RunAs' -FilePath 'powershell.exe' -ArgumentList '-NoProfile Set-ExecutionPolicy RemoteSigned'"
@@) else (
@@powershell -NoProfile Set-ExecutionPolicy RemoteSigned
@@)
 
:RunPowerShellScript
@@set POWERSHELL_BAT_ARGS=%*
@@if defined POWERSHELL_BAT_ARGS set POWERSHELL_BAT_ARGS=%POWERSHELL_BAT_ARGS:"=\"%
@@PowerShell -Command Invoke-Expression $('$args=@(^&{$args} %POWERSHELL_BAT_ARGS%);'+[String]::Join([Environment]::NewLine,$((Get-Content '%~f0') -notmatch '^^@@^|^^:'))) & goto :EOF
 
{ 
	# Start PowerShell
	write-host -ForegroundColor Yellow "hello"
	# End PowerShell
}.Invoke($args)

In order for the polyglot script to work, we exclude all lines in the file which begin with @@ (two at symbols) or : (a semi-colon).  Neither @@ or : are normally used in PowerShell on the beginning of the line (whitespace counts), so it’s generally (but not always) safe to do this.  Anything else in the file will be treated as PowerShell.  A script block is used to invoke the script such that param() can remain valid, and accept parameters passed into the batch file.

March 17, 2013

Using RemoteApps on Surface RT (Windows RT)

First check out: How to make the Windows RT desktop touch friendly.

RemoteApps aren’t a new feature in Windows 8, they’ve actually been around since Windows Vista.  RemoteApps are similar to Remote Desktop sessions, but instead of the entire remote experience in a single full-screen window, individual application windows are displayed on the remote machine.  RemoteApp windows live on the same desktop and can be used alongside of local desktop apps.  RemoteApps even have separate taskbar entries for each window, and a small icon indicating they are running remotely.  For the most part, RemoteApps act very similarly to local apps, even touch and pen input is redirected as appropriate, thanks to the many improvements in RDP 8.0 for Windows 8.

 

Setting up RemoteApps for a single user (e.g. personal use) requires the following:

A Windows Server installation.  While Windows Server 2008/2008 R2 may work, I’m using Server 2012 Standard which will have a better touch experience.

A RemoteApp client.  Windows XP and higher can access RemoteApps, so you aren’t limited to Windows 8 or Windows RT.

I recommend installing Windows Server 2012 in a Virtual Machine, potentially with RemoteFX enabled on the VM server.  Neither are requirements though, so any Server 2012 installation should work just fine.

Configure the server

First make sure that you do the following on the server:

Enable Remote Desktop on the server and forward the appropriate port in order to provide access inside and outside the network.

Create or designate a user which will be running apps remotely.

Since RemoteApp uses remote desktop, confirm that you are able to access the machine remotely with the regular remote desktop client.

Enable RemoteApp

Create a registry file (text file with .reg extension) with the following contents:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Terminal Server\TSAppAllowList]
"fDisabledAllowList"=dword:00000001

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
"PromptOnSecureDesktop"=dword:0

Name the file Enable-RemoteApp.reg and run it on the server.  This will disable the allowed app list completely, letting us run any application we’d like.  Additionally, Secure Desktop is turned off to make the elevation experience cleaner and more touch friendly.

Note: This is not the configuration you would use for a large-scale RemoteApp deployment, but this makes sense for an administrator granting themselves access to any allowed resource)

Warning: Only disable secure desktop if the implications are understood and accepted.

Now that the server is ready to remote apps onto another client, install the software you’d like to use remotely.  I installed Visual Studio 2012, Office 2013 and a handful of apps from Ninite.com.  Windows Store apps can’t be run remotely through RemoteApp.

Connecting to RemoteApps

One of the easiest ways to deploy a RemoteApp is to craft a RDP file (text file) which contains the configuration necessary to access the remote machine, and which program to start.  I’m using the following template:

screen mode id:i:1
use multimon:i:1
session bpp:i:32
winposstr:s:0,1,2244,149,3044,749
compression:i:1
keyboardhook:i:2
audiocapturemode:i:1
videoplaybackmode:i:1
connection type:i:7
networkautodetect:i:1
bandwidthautodetect:i:1
displayconnectionbar:i:1
enableworkspacereconnect:i:0
disable wallpaper:i:0
allow font smoothing:i:1
allow desktop composition:i:1
disable full window drag:i:0
disable menu anims:i:0
disable themes:i:0
disable cursor setting:i:0
bitmapcachepersistenable:i:1
full address:s:[[SERVER_ADDRESS]]
audiomode:i:0
redirectprinters:i:1
redirectcomports:i:1
redirectsmartcards:i:1
redirectclipboard:i:1
redirectposdevices:i:0
autoreconnection enabled:i:1
authentication level:i:2
prompt for credentials:i:0
negotiate security layer:i:1
remoteapplicationmode:i:0
alternate shell:s:
shell working directory:s:
gatewayhostname:s:
gatewayusagemethod:i:4
gatewaycredentialssource:i:4
gatewayprofileusagemethod:i:0
promptcredentialonce:i:0
use redirection server name:i:0
rdgiskdcproxy:i:0
kdcproxyname:s:
devicestoredirect:s:*
drivestoredirect:s:*

// RemoteApp
remoteapplicationmode:i:1
RemoteApplicationName:s:[[APP_NAME]]
RemoteApplicationProgram:s:[[APP_PATH]]
DisableRemoteAppCheck:i:1
Prompt for Credentials on Client:i:0
Alternate Shell:s:rdpinit.exe

Copy the template into a text file called Command Prompt.rdp and replace the following:

[[SERVER_ADDRESS]] -> your server address in the form of host or host:port.  E.g. remote.example.com or example.com:3390

[[APP_NAME]] -> Command Prompt

[[APP_PATH]] -> %windir%\system32\cmd.exe

The template enables most visual enhancements, shares disks, audio and Smart Cards with the host session.  Lines above // RemoteApp can be replaced with the contents of your Default.rdp (in the Documents library), or any other saved RDP connection.

Now copy Command Prompt.rdp onto the Surface RT and double-click it.  If there isn’t an existing RemoteApp connection, one will be established before the window appears.

PowerShell RemoteApp running on Windows RT

 

Clicking the same .rdp file again will open another Command Prompt (without the cost of setting up another session), and running a command which creates new windows (e.g. notepad) will properly remote them over.  The beauty of RemoteApp is that many apps can be running remotely at the same time, and apps from different machines can share the same desktop on the client.

Creating more RemoteApp shortcuts

I wrote a little PowerShell script to generate shortcut files:

function Create-RemoteAppEntryPoints($ServerAddress)
{
    $Apps = @(
        @('Command Prompt','%windir%\system32\cmd.exe'),
        @('Explorer','%windir%\explorer.exe'),
        @('mIRC','E:\mIRC\mIRC.exe'),
        @('Firefox','C:\Program Files (x86)\Mozilla Firefox\firefox.exe'),
        @('Spotify','C:\Users\%username%\AppData\Roaming\Spotify\spotify.exe'),
        @('Outlook','C:\Program Files (x86)\Microsoft Office\Office15\OUTLOOK.EXE')
    )

    $FSSafeServerAddress = $ServerAddress.Replace(":","")

    new-item -ItemType Directory -Path ".\$FSSafeServerAddress" -ErrorAction SilentlyContinue | Out-Null

    $Apps | % {
        Create-RemoteAppEntryPoint $_[0] $_[1] $ServerAddress ".\$FSSafeServerAddress"
    }
}

function Create-RemoteAppEntryPoint($AppName, $AppPath, $ServerAddress, $OutputFolder)
{
    $contents = Get-Template -TemplateFileName "RemoteApp.rdp" @{
        'SERVER_ADDRESS'=$ServerAddress
        'APP_NAME'=$AppName
        'APP_PATH'=$AppPath
        'APP_GUID'=[Guid]::NewGuid()
    }

    SC "$OutputFolder\$AppName.rdp" -Value $contents
}

function Get-Template($TemplateFileName, $Items)
{
    $str = [string]::Join([System.Environment]::NewLine, (Get-Content "$ProfileRoot\Templates\$TemplateFileName"))
    $Items.Keys |% { $str = $str.Replace("[[" + $_.ToUpper() + "]]", $Items[$_]) }
    return $str
}

It’s great to have shortcuts to apps which I know beforehand I’ll want, but what about just getting a list of apps on the system?  Windows 8 replaces the Start Menu with the Start Screen, a full-screen experience for finding and launching apps.  While we can’t remote over this UI, we can do something similar in spirit.  The list of apps shown on Start is backed by what Windows knows as “Apps Folder”, a special shell folder which provides items for Start and All Programs View. This folder isn’t normally displayed anywhere except on Start, but it’s simple to create an entry point.  Simply create a new folder anywhere on the system (e.g. on the desktop) called:

Applications.{4234d49b-0245-4df3-b780-3893943456e1}

And for good measure, a Settings folder can also be useful:

Settings.{ED7BA470-8E54-465E-825C-99712043E01C}

The text before the . Is the display name for the folder, it can be anything, the GUID is the part which references the location it should point to.  I like to pin these folders to the left sidebar in File Explorer so they’re always easy to get to.

 

Opening explorer as a RemoteApp will yield our new Applications folder, with quick access to any programs installed on the system.  Simply double-tap to run the program.

 

Building Windows Store apps

If we put everything together, some really cool stuff becomes possible.  Firing up Visual Studio 2012 over RemoteApp works great, now I’ve got the horsepower of a fast machine running the compiler and doing the heavy lifting, and Windows RT is simply displaying the UI.  Developing desktop apps this way works great, just hit F5 to run and the workflow hasn’t changed.  Windows Store apps can’t be deployed and remotely displayed though.  Fortunately Visual Studio 2012 includes remote debugging functionality.  Simply download the Remote Tools for Visual Studio 2012 ARM (English) and install them on Windows RT.  Tap on the Remote Debugger tile on Start to begin.

Remote Debugger running on Windows RT

 

Switch back to Visual Studio and click the arrow next to Local Machine, and select Remote Machine.  Select your device or manually enter the address in the resulting dialog.

Remote Debugger setup in Visual Studio 2012

 

Tap Play or press F5 to launch the app under the debugger.  The app deploys and runs natively on Windows RT.  Unfortunately, setting a breakpoint causes some component to stop processing window messages while the debugger is broken, so this functionality is unfortunately reduced.

Windows Store app launching on Windows RT

And switching back to the desktop on the Surface…

 

Many apps run well in RemoteApp, even over 3G connections

Spotify RemoteApp on Windows RT

 

It’s awesome to get some work done on the road or connect up to a bigger screen to use the Surface RT as a workstation.

 

RemoteApps drastically increase the functionality of the Windows RT desktop, giving you long battery life and access to all of your productivity apps.  Access a touch-friendly Outlook or even Silverlight content through IE10.

Silverlight running in Internet Explorer 10 RemoteApp on Windows RT

 

Notes:

Office won’t install on Windows Server unless it is a Volume License.

Since there isn’t a taskbar in the RemoteApp session, windows like to snap up to the full screen size, without reserving space for the taskbar. Perhaps forcing an exact screen resolution which doesn’t contain the taskbar area could resolve this.

If you are using High-DPI in the remote session, apps which aren’t DPI-Aware (like Spotify) need to have their compatibility settings changed such that High-DPI is disabled, otherwise the coordinates do not translate properly and the UI isn’t responsive to touch or mouse input. (Ensure the DPI is the same on the Surface and the Server, otherwise window coordinates do not match up, and the taskbar isn’t excepted from the working area.)

IM apps like Digsby work well, but the taskbar “needy state” (flashing) does not translate to the remote session. Jumplists and taskbar thumbnail controls also don’t translate over.

November 4, 2012

Make the desktop more touch-friendly on Surface RT (Windows RT)

The desktop is a valuable part of Windows RT, hosting the built-in Office apps as well as system tools, desktop Internet Explorer and more.  While Windows RT can’t run x86/amd64 software, it does have the same RDP 8.0 shipped with Windows 8.  Pair the Windows RT desktop with a RemoteApp-capable server and you’ll get long battery life while still accessing your Windows apps.  I’ll cover using RemoteApps on Surface RT in my next post.

 

First, let’s walk through making the desktop just a little bit more finger-friendly.  There are 3 options which can be used independently or together: Increase the system DPI, change the default text size, enable touch mode in Office apps.

Change the system DPI

Screen Resolution control panel

Visit Control Panel\Appearance and Personalization\Display\Screen Resolution (right-click on the desktop, Screen Resolution) and choose Make text and other items larger.  Select Medium – 125% to make hit targets larger across the system.  Some desktop apps will be too large to fit on the screen in landscape mode, so this option won’t work for every app.

Change text size

Screen Resolution control panel

Visit Control Panel\Appearance and Personalization\Display\Screen Resolution (right-click on the desktop, Screen Resolution) and choose Make text and other items larger.  Under Change only the text size, select the types of content and the associated font size.  Menus and Icons work nicely at 14.  Enlarging the menus and icons will also make Office and all right-click menus more touch friendly!

Enable Touch Mode in Office.

Office 2013 - Quick Access toolbar - Touch/Mouse mode

Open Word and select the down-arrow on the Quick Access toolbar, just above the File menu in Office 2013.  Ensure that “Touch/Mouse mode” is checked so the item appears on the Quick Access toolbar, and then select the hand icon to toggle between touch and mouse spacing.  Office is a little easier to use with touch-spacing, but the font size changes in the section above are actually much more helpful than the option built-in to Office 2013.

Office 2013 - Quick Access toolbar - Touch/Mouse mode

With just a few tweaks, the desktop on Windows RT can be highly touchable while still remaining functional with a mouse and keyboard.

 

Next time: See how to use RemoteApps on Windows RT.

November 4, 2012

Fix “orphaned” AppX apps from Windows 8 and Windows RT

It might be possible to get into a state where you can’t search for an app on Start, yet the Store insists that “You own this app.” Since the app isn’t shown on Start, there isn’t an entry point to uninstall it within the UI. We can use PowerShell to locate the package and remove it, then reinstall from the Store.

Box.net search results in Windows 8 - the app is not found

Box product page in the Windows Store - You own this appHere’s a PowerShell script to fix an app that gets into this state:

function Get-OrphanedAppxPackage
{
    # Ignore frameworks, WinStore and control panel
    Get-AppxPackage | ? IsFramework -eq $false | ? InstallLocation -notlike "*:\Windows\*" | % { 
        $AppShortcutsAppFolder = "$env:LOCALAPPDATA\Microsoft\Windows\Application Shortcuts\$($_.PackageFamilyName)"
        if ((-Not (Test-Path $AppShortcutsAppFolder)) -or ((ls $AppShortcutsAppFolder).Count -eq 0)) 
        {
            $_
        }
    }
}

Simply run Get-OrphanedAppxPackage:

Get-OrphanedAppxPackage | Remove-AppxPackageNow when visiting the Store, the app can be reinstalled:

Box can not be installed from the Windows Store

October 31, 2012

Programmatically (or Command Line) change the default sound playback device in Windows 7

This post sums up the information found here.

Microsoft kept the API which changes the default sound playback device closed, for good reason. Drivers and control applet applications from different manufactures would mostly certainly end up ‘fighting’ over the default device, which would be terribly confusing and effectively makes the hardware incompatible. There are legitimate reasons for one to wish to change the default playback device from the command line or programmatically, but the needs are generally isolated to a single user or a group of users looking for something highly specialized. A friend of mine needed just this kind of solution for switching from headphones to speakers (without clicking a few times and without opening new windows).

I’ve simply wrapped the information in the aforementioned MSDN thread into a console application. The application uses undocumented API; while it works on Windows Vista and Windows 7, is it very likely to become broken with the next version of Windows. This isn’t something you can rely on.

Here is the undocumented and private COM interface which is used by mmsys.cpl to interact with the Audio service (credit goes to EreTIk obviously):

// ----------------------------------------------------------------------------
// PolicyConfig.h
// Undocumented COM-interface IPolicyConfig.
// Use for set default audio render endpoint
// @author EreTIk
// ----------------------------------------------------------------------------
 
#pragma once
 
interface DECLSPEC_UUID("f8679f50-850a-41cf-9c72-430f290290c8")
IPolicyConfig;
class DECLSPEC_UUID("870af99c-171d-4f9e-af0d-e63df40c2bc9")
CPolicyConfigClient;
// ----------------------------------------------------------------------------
// class CPolicyConfigClient
// {870af99c-171d-4f9e-af0d-e63df40c2bc9}
//
// interface IPolicyConfig
// {f8679f50-850a-41cf-9c72-430f290290c8}
//
// Query interface:
// CComPtr[IPolicyConfig] PolicyConfig;
// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigClient));
//
// @compatible: Windows 7 and Later
// ----------------------------------------------------------------------------
interface IPolicyConfig : public IUnknown
{
public:
 
    virtual HRESULT GetMixFormat(
        PCWSTR,
        WAVEFORMATEX **
    );
 
    virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
        PCWSTR,
        INT,
        WAVEFORMATEX **
    );
 
    virtual HRESULT STDMETHODCALLTYPE ResetDeviceFormat(
        PCWSTR
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
        PCWSTR,
        WAVEFORMATEX *,
        WAVEFORMATEX *
    );
 
    virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
        PCWSTR,
        INT,
        PINT64,
        PINT64
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
        PCWSTR,
        PINT64
    );
 
    virtual HRESULT STDMETHODCALLTYPE GetShareMode(
        PCWSTR,
        struct DeviceShareMode *
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetShareMode(
        PCWSTR,
        struct DeviceShareMode *
    );
 
    virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
        PCWSTR,
        const PROPERTYKEY &,
        PROPVARIANT *
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
        PCWSTR,
        const PROPERTYKEY &,
        PROPVARIANT *
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
        __in PCWSTR wszDeviceId,
        __in ERole eRole
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
        PCWSTR,
        INT
    );
};
 
interface DECLSPEC_UUID("568b9108-44bf-40b4-9006-86afe5b5a620")
IPolicyConfigVista;
class DECLSPEC_UUID("294935CE-F637-4E7C-A41B-AB255460B862")
CPolicyConfigVistaClient;
// ----------------------------------------------------------------------------
// class CPolicyConfigVistaClient
// {294935CE-F637-4E7C-A41B-AB255460B862}
//
// interface IPolicyConfigVista
// {568b9108-44bf-40b4-9006-86afe5b5a620}
//
// Query interface:
// CComPtr[IPolicyConfigVista] PolicyConfig;
// PolicyConfig.CoCreateInstance(__uuidof(CPolicyConfigVistaClient));
//
// @compatible: Windows Vista and Later
// ----------------------------------------------------------------------------
interface IPolicyConfigVista : public IUnknown
{
public:
 
    virtual HRESULT GetMixFormat(
        PCWSTR,
        WAVEFORMATEX **
    );  // not available on Windows 7, use method from IPolicyConfig
 
    virtual HRESULT STDMETHODCALLTYPE GetDeviceFormat(
        PCWSTR,
        INT,
        WAVEFORMATEX **
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetDeviceFormat(
        PCWSTR,
        WAVEFORMATEX *,
        WAVEFORMATEX *
    );
 
    virtual HRESULT STDMETHODCALLTYPE GetProcessingPeriod(
        PCWSTR,
        INT,
        PINT64,
        PINT64
    );  // not available on Windows 7, use method from IPolicyConfig
 
    virtual HRESULT STDMETHODCALLTYPE SetProcessingPeriod(
        PCWSTR,
        PINT64
    );  // not available on Windows 7, use method from IPolicyConfig
 
    virtual HRESULT STDMETHODCALLTYPE GetShareMode(
        PCWSTR,
        struct DeviceShareMode *
    );  // not available on Windows 7, use method from IPolicyConfig
 
    virtual HRESULT STDMETHODCALLTYPE SetShareMode(
        PCWSTR,
        struct DeviceShareMode *
    );  // not available on Windows 7, use method from IPolicyConfig
 
    virtual HRESULT STDMETHODCALLTYPE GetPropertyValue(
        PCWSTR,
        const PROPERTYKEY &,
        PROPVARIANT *
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetPropertyValue(
        PCWSTR,
        const PROPERTYKEY &,
        PROPVARIANT *
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetDefaultEndpoint(
        __in PCWSTR wszDeviceId,
        __in ERole eRole
    );
 
    virtual HRESULT STDMETHODCALLTYPE SetEndpointVisibility(
        PCWSTR,
        INT
    );  // not available on Windows 7, use method from IPolicyConfig
};

We simply enumerate the devices and choose one via the command line:

#include "stdio.h"
#include "wchar.h"
#include "tchar.h"
#include "windows.h"
#include "Mmdeviceapi.h"
#include "PolicyConfig.h"
#include "Propidl.h"
#include "Functiondiscoverykeys_devpkey.h"
 
HRESULT SetDefaultAudioPlaybackDevice(LPCWSTR devID)
{
	IPolicyConfigVista *pPolicyConfig;
	ERole reserved = eConsole;
 
    HRESULT hr = CoCreateInstance(__uuidof(CPolicyConfigVistaClient),
		NULL, CLSCTX_ALL, __uuidof(IPolicyConfigVista), (LPVOID *)&pPolicyConfig);
	if (SUCCEEDED(hr))
	{
		hr = pPolicyConfig->SetDefaultEndpoint(devID, reserved);
		pPolicyConfig->Release();
	}
	return hr;
}
 
// EndPointController.exe [NewDefaultDeviceID]
int _tmain(int argc, _TCHAR* argv[])
{
	// read the command line option, -1 indicates list devices.
	int option = -1;
	if (argc == 2) option = atoi((char*)argv[1]);
 
	HRESULT hr = CoInitialize(NULL);
	if (SUCCEEDED(hr))
	{
		IMMDeviceEnumerator *pEnum = NULL;
		// Create a multimedia device enumerator.
		hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL,
			CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pEnum);
		if (SUCCEEDED(hr))
		{
			IMMDeviceCollection *pDevices;
			// Enumerate the output devices.
			hr = pEnum->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, &pDevices);
			if (SUCCEEDED(hr))
			{
				UINT count;
				pDevices->GetCount(&count);
				if (SUCCEEDED(hr))
				{
					for (int i = 0; i < count; i++)
					{
						IMMDevice *pDevice;
						hr = pDevices->Item(i, &pDevice);
						if (SUCCEEDED(hr))
						{
							LPWSTR wstrID = NULL;
							hr = pDevice->GetId(&wstrID);
							if (SUCCEEDED(hr))
							{
								IPropertyStore *pStore;
								hr = pDevice->OpenPropertyStore(STGM_READ, &pStore);
								if (SUCCEEDED(hr))
								{
									PROPVARIANT friendlyName;
									PropVariantInit(&friendlyName);
									hr = pStore->GetValue(PKEY_Device_FriendlyName, &friendlyName);
									if (SUCCEEDED(hr))
									{
										// if no options, print the device
										// otherwise, find the selected device and set it to be default
										if (option == -1) printf("Audio Device %d: %ws\n",i, friendlyName.pwszVal);
										if (i == option) SetDefaultAudioPlaybackDevice(wstrID);
										PropVariantClear(&friendlyName);
									}
									pStore->Release();
								}
							}
							pDevice->Release();
						}
					}
				}
				pDevices->Release();
			}
			pEnum->Release();
		}
	}
	return hr;
}

You can download the application and source project. (MIT License)

My friend followed up with some .NET code to wrap it.

May 31, 2011

Compass app for Windows Phone 7

As per the request of an XDA user, I’ve put together a Compass application for Windows Phone 7 that will work (for now) on Samsung phones.  Windows Phone 7 device guidelines dictate that each phone must have Compass hardware, but there is not a common API through which developers can access the hardware.  Samsung includes support in their platform drivers, so I’ve taken advantage of that.

As always, you can download the XAP and source code.  A developer-unlocked Windows Phone is required.  This app will not work on Dell, HTC or LG phones as of this posting.

I have tested so far on the Samsung Focus, I suspect it will work on all Samsung Windows Phones.

January 17, 2011

Change Accent colors on Windows Phone 7 Samsung, LG, HTC devices

After seeing this thread at XDA, I’ve put together a quick accent color changer for Samsung (and now HTC and LG) devices running Windows Phone 7.

The app simply lets you edit the name and color value for all 10 of the installed accent colors. Simply edit the color, and then switch your theme in the Settings app for the changes to take effect. There’s a button to reset the theme to default.

You can download the XAP which will work on any Samsung, LG or HTC Windows Phone 7 that is developer unlocked.  The source code may also be downloaded.

Update: I’ve added the ability to restore default colors.
Update: I’ve added support for LG and HTC!
Update: Some people are reporting that HTC doesn’t work, but I haven’t made changes since it was confirmed working, and I don’t have a device to test.

January 15, 2011

LED Flashlight for Windows Phone 7

Windows Phone 7 is a fantastic platform, but for me there was one feature I just could not live without – the LED flashlight that I loved so much from iPhone 4.  The API in the Windows Phone 7 SDK does not allow one to interface with the LED, nor does it allow you to interface with the Camera.  I was able, however, to enable a functional flashlight by utilizing the APIs inside Microsoft.Phone.Media.Extended.

This app simply turns on the video camera, and the flash, but does not show the video, or save it to your Zune library.  The unfortunate limitation of this is that you’ll hear a chirp sound when you turn the LED on.  I can live with this, since the functionality gained outweighs the quirks.

I posted about this on XDA, but this version does NOT leak videos.  Upon starting up, all videos in the IsolatedStorage for this application are removed, your phone won’t fill up with leaked videos, but the versions on XDA will.

Download LED Flashlight for Windows Phone 7.  A developer-unlocked or otherwise “Homebrew enabled” Windows Phone is required.  The app is very simple, but here’s the source code anyway.

January 12, 2011

Send to WP7 Desktop & Send to WP7 Opera Extension

I’ve been working on two projects related to Send to WP7:

Opera Extension

Send to WP7 Desktop


Update: The Opera extension is ready, you can pick it up on the product page.

Neither are ready for prime-time, but both are functional.  The desktop client will check for updates automatically, the Opera extension will be featured on the product page when it is configured with the ability to update.  I’ve put both of these on twitter, so thought I’d mention them here.

January 8, 2011

Chrome to WP7 is now Send to WP7

Just an FYI – Chrome to WP7 is now Send to WP7.  This was just a logical name change.  Clearly I shot far too low in limiting to just Chrome.  I’ve updated the app in the Windows Phone Marketplace – but unfortunately the old logo is still being shown in the store.  Later this week I will update Send to WP7 for local cache and deleting links.  Setup Send to WP7.

December 13, 2010