Home

PB's Visual Studio Utilities

posted Feb 3, 2013, 6:40 AM by Pieter-Bas IJdens   [ updated Feb 3, 2013, 6:42 AM ]

Before the release of Visual Studio 2012, I used to have a small set of macro functions ready that would make my life easier during software development. With the introduction of Visual Studio 2012, the macro functionality has been removed because it was rarely used.

The macros that I used to use a lot, I have now replaced with a visual studio package called PB's Visual Studio Utilities. For now, this package contains two functions that I use a lot when creating Wix installers.
  • Insert a GUID into the text editor.
    At the current caret position, inserts a GUID. If text is selected, replaces the text instead.
    When multiple spans are selected (hold the alt- key when selecting), each line of the selection is replaced with a different GUID.

  • Insert a random ID into the text editor.
    At the current caret position, inserts a random ID. If text is selected, replaces the text instead.
    When multiple spans are selected each span is replaced with a different ID.
These options are available via the tools menu, or via the keyboard shortcuts Alt-I , I (for an ID) and Alt-I + G (for a GUID).

How to convert an mp3 file to an m4b audiobook

posted Nov 4, 2012, 1:53 PM by Pieter-Bas IJdens   [ updated Nov 4, 2012, 1:54 PM ]

Converting an mp3 file to an audiobook is not hard per se. All that's really needed is to convert the file to AAC (m4a) format, after which it should be renamed to have a .m4b extension. As I could not find any decent software to do this, I created a little program of my own. The sources and the binaries can be found in the downloads section of this site. See http://www.ijdens.com/downloads/Setup_ConvertToAudiobook.zip for the installer, and http://www.ijdens.com/downloads/ConvertToAudiobook.zip for the sources.

Drop down button in Office Ribbon XML

posted Oct 11, 2012, 1:11 AM by Pieter-Bas IJdens   [ updated Oct 11, 2012, 1:13 AM ]

For a customer we were asked to create a split-button with a drop-down menu in an office ribbon. Documented here is how we did this:

Result:

Sample


The Ribbon XML:
<?xml version="1.0" encoding="utf-8" ?>
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="OnLoad">
  <ribbon>
    <tabs>
      <tab id="TabTEST" label="Test 123" visible="true">
        <group id="MyGroup" label="My Group" visible="true">
          <box id="Box0" boxStyle="horizontal">
            <labelControl id="Label0" label="Click or drop down to select: " />
            <splitButton id="SplitButton0" size="normal">
              <button id="Button0" label="Action" onAction="RibbonControlButtonAction" showImage="false" />
              <menu id="Menu0">
                <toggleButton id="Option1" label="Option one for action" onAction="RibbonControlToggleButtonAction" getPressed="GetIsPressed"/>
                <toggleButton id="Option2" label="Option two for action" onAction="RibbonControlToggleButtonAction" getPressed="GetIsPressed"/>
              </menu>
            </splitButton>
          </box>
        </group>
      </tab>
    </tabs>
  </ribbon>
</customUI>
The code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Office.Core;
using System.Reflection;
using System.IO;
using System.Runtime.InteropServices;
 
namespace WordAddInDropDownMenu
{
    [ComVisible(true)]
    public class Ribbon1 : IRibbonExtensibility
    {
        public string GetCustomUI(string ribbonID)
        {
            return GetResourceText("WordAddInDropDownMenu.Ribbon1.xml");
        }         public void OnLoad(IRibbonUI ribbon)         {             _ribbon = ribbon;         }         private static string GetResourceText(string resourceName)         {             Assembly asm = Assembly.GetExecutingAssembly();             using (StreamReader resourceReader = new StreamReader(asm.GetManifestResourceStream(resourceName)))             {                 return resourceReader.ReadToEnd();             }         }         public void RibbonControlButtonAction(IRibbonControl control)         {             if ("Button0" == control.Id)             {                 DoAction();             }         }         public void RibbonControlToggleButtonAction(IRibbonControl control, bool pressed)         {             _lastPressed = control.Id;             // Make sure all buttons are updated, could also search for specific buttons and invalidate those.             _ribbon.Invalidate();             DoAction();         }         private void DoAction()         {             System.Windows.Forms.MessageBox.Show("Selected " + _lastPressed, "Message");         }         private String _lastPressed = "Option1";         public bool GetIsPressed(IRibbonControl control)
        {
            return control.Id.Equals(_lastPressed);
        }
 
        private IRibbonUI _ribbon; 
    }
}
Attached all code an a VSTO Add-In.

Which/whereis for windows

posted Jul 28, 2011, 12:33 AM by Pieter-Bas IJdens

As someone who grew up using Unix systems, one of the tiny little tools I really miss in Windows is the which/whereis command. I am sure there are tens of ways that I am not aware of for doing the exact same thing, but I still would like to share my quick-hack solution:

Create a batch file called 'which.bat' in C:\Windows\System32 with the following contents:
@echo off
SET FOUND_IN=%~dp$PATH:1
SET FILENAME=%~n1%~x1

set RESULT=%FOUND_IN%%FILENAME%
if not exist "%FOUND_IN%\%FILENAME%" set RESULT=Not found: %FILENAME%

echo %RESULT%

Proof that it works:
C:\>which foo.bar
Not found: foo.bar

C:\>which ildasm.exe
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\ildasm.exe

Adding strong-name key to unsigned 3rd party DLLs

posted Jul 28, 2011, 12:21 AM by Pieter-Bas IJdens

Recently I gave myself the task to make sure that all software we release at Sevensteps is propertly strong-name signed, and properly code-signed with our own signing certificates. Obviously, when doing this I encountered some issues where it would be impossible for me to strong-name sign an assembly because one of assemblies it depended on did not have any strong-name information.

The only correct way around this is obviously to acquire a version of the DLL that's properly signed. Alternatively once could acquire the sources to such DLL, review them and create a signed build.

Taking above steps solved 99% of the issues that I was having. However, I still was stuck with a few DLLs for which neither solution was an option.

In order to be able to continue building, I decided to strong-name sign these assemblies myself with a 'special' key that we use for this purpose. By far the easiest way I found of doing this is to disassemble the DLL (provided the license allows this), and re-assemble it, using ildasm and ilasm.

I created the following little batch file to help me with this:

@echo off

set SIGNTEMP=%TEMP%\SigningTempDir
set WORKING_FOLDER=%~dp1
set BASENAMEANDPATH=%~dpn1
set FILENAME=%~n1
set LOGFILE=%BASENAMEANDPATH%-Convert.log
set ERRFILE=%BASENAMEANDPATH%-Convert.err

del %LOGFILE%
del %ERRFILE%

rmdir /q/s %SIGNTEMP% >> "%LOGFILE%" 2>> "%ERRFILE%"
mkdir %SIGNTEMP% >> "%LOGFILE%" 2>> "%ERRFILE%"

"%ProgramFiles(x86)%\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\ildasm.exe" %BASENAMEANDPATH%.dll /OUT:%SIGNTEMP%\%FILENAME%.il >> "%LOGFILE%" 2>> "%ERRFILE%"

pushd %SIGNTEMP% >> "%LOGFILE%" 2>> "%ERRFILE%"
copy \\ourfileserver.example.com\SharedBuildTasks\OurSpecialKey.snk . >> "%LOGFILE%" 2>> "%ERRFILE%"
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\ilasm.exe" %FILENAME%.il /DLL /RESOURCE=%FILENAME%.res /KEY=OurSpecialKey.snk >> "%LOGFILE%" 2>> "%ERRFILE%"
copy %FILENAME%.dll "%WORKING_FOLDER%\SIGNED-%FILENAME%.dll" >> "%LOGFILE%" 2>> "%ERRFILE%"
popd >> "%LOGFILE%" 2>> "%ERRFILE%"

echo Created signed copy of the DLL in %WORKING_FOLDER%\SIGNED-%FILENAME%.dll

p.s. The %~... parameters are explained at http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/percent.mspx?mfr=true. Very useful when writing batch files.

BitmapButton for BlackBerry 5

posted Mar 28, 2011, 5:57 AM by Pieter-Bas IJdens   [ updated Jul 28, 2011, 12:44 AM ]

Two days ago I posted a Blackberry API (6.0) class for creating image buttons. Obviously in the BB5 API there was no support yet for disabling buttons. Included below is an updated version for BB5, which also has a nice feature: It allows the creator to specify a provider for the images, rather than the images themselves. I use this feature to change the meaning, and the image, of a button on-the-fly depending on the state of the underlying object (getting a bit tired of having to fix scrolling issues and virtual keyboard issues all the time when swapping-out controls)

package nl.sevensteps.blackberry.widgets;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.XYEdges;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.decor.BorderFactory;

/**
 * Button field with a bitmap as its label.
 */
public class BitmapButtonField extends ButtonField implements DrawStyle {
    public static final int STATE_FOCUS = 0;
    public static final int STATE_NORMAL = 1;
    public static final int STATE_DISABLED = 2;

    private Bitmap _bitmap;
    private boolean _isEnabled = true;

    private IBitmapProvider _provider = null;

    BitmapButtonField(final IBitmapProvider provider) {
        super(Field.FOCUSABLE | ButtonField.CONSUME_CLICK);

        _provider = provider;

        setLabel("");
        _bitmap = getNormalBitmap();

        // I don't care which visual state we're in, we do NOT want to draw a
        // border, ever.
        setBorder(VISUAL_STATE_FOCUS,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_NORMAL,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_ACTIVE,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_DISABLED,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_DISABLED_FOCUS,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));

    }

    BitmapButtonField(final Bitmap noFocus, final Bitmap focus,
            final Bitmap disabled) {
        this(new IBitmapProvider() {

            public Bitmap getBitmap(final int state) {
                switch (state) {
                case STATE_DISABLED:
                    return disabled;
                case STATE_FOCUS:
                    return focus;
                default:
                    return noFocus;
                }
            }
        });
    }

    protected void onFocus(final int direction) {
        if (_isEnabled) {
            _bitmap = getFocusedBitmap();
        } else {
            _bitmap = getDisabledBitmap();
        }
        invalidate();
    }

    protected void onUnfocus() {
        if (_isEnabled) {
            _bitmap = getNormalBitmap();
        } else {
            _bitmap = getDisabledBitmap();
        }
        invalidate();
    }

    public void setEnabled(final boolean enabled) {
        _isEnabled = enabled;
        if (!enabled) {
            setVisualState(Field.VISUAL_STATE_DISABLED);
            _bitmap = getDisabledBitmap();
        } else if (isFocus()) {
            setVisualState(Field.VISUAL_STATE_FOCUS);
            _bitmap = getFocusedBitmap();
        } else {
            setVisualState(Field.VISUAL_STATE_NORMAL);
            _bitmap = getNormalBitmap();
        }
        invalidate();
    }

    public int getPreferredWidth() {
        return getNormalBitmap().getWidth();
    }

    public int getPreferredHeight() {
        return getNormalBitmap().getHeight();
    }

    protected void layout(final int width, final int height) {
        setExtent(getPreferredWidth(), getPreferredHeight());
    }

    protected void paint(final Graphics graphics) {
        graphics.drawBitmap(0, 0, _bitmap.getWidth(), _bitmap.getHeight(),
                _bitmap, 0, 0);
    }

    protected void applyTheme() {
        // Not applying the theme as this will cause the border to be rendered,
        // which we do not want to see, ever.
    }

    protected void applyTheme(final Graphics arg0, final boolean arg1) {
        // Not applying the theme as this will cause the border to be rendered,
        // which we do not want to see, ever.
    }

    private Bitmap getFocusedBitmap() {
        return _provider.getBitmap(STATE_FOCUS);
    }

    private Bitmap getDisabledBitmap() {
        return _provider.getBitmap(STATE_DISABLED);
    }

    private Bitmap getNormalBitmap() {
        return _provider.getBitmap(STATE_NORMAL);
    }

    /**
     * When changing the contents of the bitmaps on the fly (e.g. as part of a
     * button press), it's nice to force redraw.
     * 
     * @param hasFocus
     *            Especially when a result of button-clicks, the proper focus
     *            state is temporarily lost. This happens to coincide with this
     *            call. Therefore, as the caller usually is properly aware of
     *            the state, the caller can also just tell s what the focus
     *            state should be.
     */
    public void redraw(final boolean hasFocus) {
        setDirty(true);
        // Cheap trick to make sure the bitmaps are set properly without having
        // to re-implement all logic.
        if (hasFocus) {
            setFocus();
            onFocus(0);
        } else {
            onUnfocus();
        }
        invalidate();
    }

    /**
     * Redraw (e.g. after a button image change)
     */
    public void redraw() {
        // Unfortunately, this has a habit of returning false when we all know
        // the value is true.
        redraw(isFocus());
    }

    /**
     * Override to implement _isEnabled.
     */
    public boolean isFocusable() {
        return _isEnabled && super.isFocusable();
    }

    /**
     * Interface that is to be implemented by classes.
     */
    public interface IBitmapProvider {
        public Bitmap getBitmap(int state);
    }
}

No borders: Blackberry Bitmap ButtonField

posted Mar 26, 2011, 5:20 AM by Pieter-Bas IJdens   [ updated Mar 29, 2011, 11:06 PM ]

Lately I have been working on a Blackberry application with lots of UI customizations. One of the small hurdles I had to take was creating a button that merely display a bitmap. This is simple enough, but there are some tricky things having to do with rendering the borders. To save others the trouble of having to figure all this out themselves, here is the class:

Edit: Note that this class is for version 6 only. For v5, see http://www.ijdens.com/home/bitmapbuttonforblackberry5
 

package nl.sevensteps.blackberry.widgets;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.DrawStyle;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.XYEdges;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.decor.BorderFactory;

public class BitmapButtonField extends ButtonField implements DrawStyle {
    private final Bitmap _noFocusBitmap;
    private final Bitmap _focusBitmap;
    private final Bitmap _disabledBitmap;
    private Bitmap _bitmap;

    BitmapButtonField(final Bitmap noFocus, final Bitmap focus,
            final Bitmap disabled) {
        super(Field.FOCUSABLE | ButtonField.CONSUME_CLICK);

        setLabel("");
        _noFocusBitmap = noFocus;
        _focusBitmap = focus;
        _bitmap = _noFocusBitmap;
        _disabledBitmap = disabled;

        // I don't care which visual state we're in, we do NOT want to draw a
        // border, ever.
        setBorder(VISUAL_STATE_FOCUS,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_NORMAL,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_ACTIVE,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_DISABLED,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
        setBorder(VISUAL_STATE_DISABLED_FOCUS,
                BorderFactory.createSimpleBorder(new XYEdges(0, 0, 0, 0)));
    }

    protected void onFocus(final int direction) {
        if (isEnabled()) {
            _bitmap = _focusBitmap;
        } else {
            _bitmap = _disabledBitmap;
        }
        invalidate();
    }

    protected void onUnfocus() {
        if (isEnabled()) {
            _bitmap = _noFocusBitmap;
        } else {
            _bitmap = _disabledBitmap;
        }
        invalidate();
    }

    public void setEnabled(final boolean enabled) {
        if (!enabled) {
            _bitmap = _disabledBitmap;
        }
        invalidate();
        super.setEnabled(enabled);
    }

    public int getPreferredWidth() {
        return _noFocusBitmap.getWidth();
    }

    public int getPreferredHeight() {
        return _noFocusBitmap.getHeight();
    }

    protected void layout(final int width, final int height) {
        setExtent(getPreferredWidth(), getPreferredHeight());

    }

    protected void paint(final Graphics graphics) {
        graphics.drawBitmap(0, 0, _bitmap.getWidth(),
                _bitmap.getHeight(), _bitmap, 0, 0);
    }

    protected void applyTheme() {
        // Not applying the theme as this will cause the border to be rendered,
        // which we do not want to see, ever.
    }

    protected void applyTheme(final Graphics arg0, final boolean arg1) {
        // Not applying the theme as this will cause the border to be rendered,
        // which we do not want to see, ever.
    }
}


Enjoy!

Using robocopy to backup all files from a disk

posted Mar 14, 2011, 2:51 AM by Pieter-Bas IJdens

Recently I got handed a Windows Vista laptop that would not boot properly into Windows anymore. After careful examination, I decided that cleanly re-installing the laptop was the best option. However, the laptop not being mine I decided to first check with the owner if they had a recent backup. Obviously I ended up backing it up myself. Not being familiar with the places that the owner stored their data, I decided to run a full filesystem backup to a spare disk.

Having heard 'robocopy' is an excellent tool for this, I decided to give it a try.

Naturally, due to inexperience with it, I ran into all kinds of problems with the tool. Here is a quick summary for those of you who are also new to robocopy and need to solve a similar problem.
  1. While working, robocopy creates a hidden directory on the target disk. If you abort the process, this hidden directory will not be deleted, and will keep consuming space until you delete it yourself.

  2. You'll find that deleting the directory will be very difficult from Explorer, especially if you also copy permissions. Also rmdir /s/q will fail to delete the directory. Instead:

    Suppose your hidden target directory is P:\BACKUP:

    Create an empty directory named P:\EMPTY and then run:

    robocopy P:\EMPTY P:\BACKUP /PURGE

    This will cause robocopy to remove all files it created, even if they violate normal filesystem rules.

  3. On Windows Vista and Windows 7 there is a concept of Junctions. These are special folders that are created by the operating system, and act as redirection points to other directories. The idea is that software written for previous versions of Windows, and that expects directories like C:\Documents and Settings, and "My Documents" in the user directory will still work on the new structure (that uses C:\Users, and uses a Documents directory instead of "My Documents").

    These junctions are protected on the FS with special permissions causing normal user access to them to be denied. However, robocopy run in administrator mode will be able to open them, iterate over them, and copy them.

    This is NOT what I wanted in this scenario, as it causes robocopy to endlessly loop over "Application Data", as the junctions cause an endless loop in the file system.

    The /XJ option for robocopy causes it to exclude junctions from the copy process.

  4. Robocopy can resume an interrupted copy if you use the /Z option, or the /ZB option.

  5. Using /TEE and /LOG: options are useful. /LOG allows robocopy to create a logfile. This causes it to stop outputting anything. However, specifying /TEE will cause it to also log to the console.

  6. /E will cause robocopy to also copy empty subdirectories
    /ETA will cause it to log a remaining time on the console for each file
    /R:<n> can be used to limit the number of retries when copy fails (default is 1000000)
    /W:<n> can be used to limit the amount of time waited before retrying.

I ended up creating a batch file with the following content:

robocopy G:\ P:\BACKUP *.* /XJ /SL /E /ETA /TEE /R:5 /W:5 /ZB /LOG:D:\mariska.all\c-disk-copylog.txt

G:\ was the source disk, and P:\BACKUP the target, ending up with a nice copy of all data. 

I intentionally didn't include /COPYALL in this case becuase the purpose was to backup the files, not the permissions and security attributes. These are not relevant to the user in this case.

Lessons learned: Robocopy is a really powerful tool for copying lots of data. The resume method is really useful. However, when anything goes wrong while copying the data some google skills are needed to clean up after it.
 

Useful MTG sites

posted Feb 27, 2011, 6:02 AM by Pieter-Bas IJdens

Word Document Template for MTG Proxies

posted Feb 27, 2011, 5:56 AM by Pieter-Bas IJdens

For the past weeks, while working on an EDH deck, I found I was printing quite a few proxies. Getting all proxies the correct size to fit the sleeves can be quite a bit of work. As any good and lazy IT person would do, I automated the process and created a document template to aid in this.

You can find the template on the Downloads page.

It's quite simple to use. Just create a document based on the template (should work in office 2007 and 2010). Copy-paste all images form the web into the document. Hit the resize all images button, and print. In order to use the document, you must configure Word to allow macro's for this document. 

1-10 of 11

Comments