Downloading Outlook contact photos / profile pictures

Sample Outlook Profile imageI wanted to grab the photos (“ContactPicture”) that people put in their Outlook Contact Details.

Not sure if I grabbed this code from somewhere, or wrote it myself.  Just leaving it here for the next time I need it.

Note to self:
* you need a local copy of the address book to run this code against.

Sub SaveContactPhoto()
Dim itemContact As ContactItem
Dim fdrContacts As MAPIFolder
Dim colAttachments As Outlook.Items
Dim colItems As Outlook.Items
Dim fname As String
'Default Contacts folder
'Set fdrContacts = Session.GetDefaultFolder(olFolderContacts)
' Selected folder
' Ensure you select the GAL folder ....
Set fdrContacts = Application.ActiveExplorer.CurrentFolder
On Error Resume Next
For itemCounter = 1 To fdrContacts.Items.Count
    Set itemContact = fdrContacts.Items(itemCounter)
    Set collAttachments = itemContact.Attachments
    For Each attach In collAttachments
      If attach.FileName = "ContactPicture.jpg" Then
         fname = (itemContact.FirstName & " " & itemContact.LastName & ".jpg")
        If fname <> "" Then
            attach.SaveAsFile ("C:\data\Contact Photos\" & fname)
        End If
      End If
End Sub

Bonus fact: Microsoft call the Outlook Contact Details “Profile Cards”

.Net WiFi Programming

free wifiSome time ago I was asked to write some code to

  1. detect if a user had saved a connection to an Open/Public Wifi point, and
  2. delete it if it exists.

The code had to work on Windows 7, so using the Wi-Fi Direct feature wasn’t available to me.

I could do this using the Native WiFi API, except that doing that from .Net isn’t easy.

So I used the managedwifi wrapper which Monfort Engineering developed and made open source.

Everything worked well except …

The code would occasionally fail.  Much reading of documentation, and lots of head scratching happened.  This was the answer:

“All wireless LAN functions require an interface GUID for the wireless interface when performing profile operations. When a wireless interface is removed, its state is cleared from Wireless LAN Service (WLANSVC) and no profile operations are possible.”
WlanSetProfile function

What that means in practice:

  1. if the Wireless LAN Service is not running, then there is no WiFi running
  2. if the user has disabled the WiFi Adapter, then there is no WiFi to query.

So if 1 or 2 occurred, the code I wrote would fail.

The code to fix that was as simple as this:

Private Sub DeleteOpenWifi()
If wifi.Interfaces.Length = 0 Then
     Exit Sub
End If

How to Avoid Public WiFi Security Risks
About the Native Wifi API
Managed WiFi API (Codeplex)
Is there anyway of detecting what wireless security is being used i.e WPA2, WEP, open etc?

Today I learnt about FIPS and SHA1

Sha-familySystem.InvalidOperationException: This implementation is not part of the Windows Platform FIPS validated cryptographic algorithms. at System.Security.Cryptography.SHA1Managed..ctor() at ....

When I say “learnt”, it was more about reading documents to determine what happened to cause the above error.

I suspect either of these:

  1. Microsoft have released a new schannel.dll which removes and/or breaks SHA1 functionally.
  2. The Group Policy setting “System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing” has been enabled.

My quick “fix” was to change the application to use a different hashing algorithm.


How to delay in a batch file

by using the CHOICE command.  Which is a hack, but it works.

To delay for 5 seconds:
choice /T 5 /D Y

An explanation follows:
/T is the number of seconds before the default choice is made.  In this case, 5 seconds.
/D the default choice.  In this case, Y

The default list of choices for the CHOICE command is YN.

Saturday Link Roundup

Programming Suckscodewithoutlimitsshameitsnoteventswithoutlimits_thumb.jpg
”There will always be darkness
I spent a few years growing up with a closet in my bedroom. The closet had an odd design. It looked normal at first, then you walked in to do closet things, and discovered that the wall on your right gave way to an alcove, making for a handy little shelf. Then you looked up, and the wall at the back of the alcove gave way again, into a crawlspace of utter nothingness, where no light could fall and which you immediately identified as the daytime retreat for every ravenous monster you kept at bay with flashlights and stuffed animals each night.
This is what it is to learn programming. You get to know your useful tools, then you look around, and there are some handy new tools nearby and those tools show you the bottomless horror that was always right next to your bed.”

Internet Explorer (IE) version detection in JavaScript
Minification-safe JavaScript detection of version of Internet Explorer (IE) browser up to version 10 inclusive.

Falsehoods programmers believe about time
”I have repeatedly been confounded to discover just how many mistakes in both test and application code stem from misunderstandings or misconceptions about time. By this I mean both the interesting way in which computers handle time, and the fundamental gotchas inherent in how we humans have constructed our calendar – daylight savings being just the tip of the iceberg.”

Getting a list of printers published in an Active Directory domain

So I need to get a list of print servers and printers in the domain.

Using Powershell.

Looking around the interwebs, I found a PowerShell commandline here which formed the basis of this commandline:
Get-ADObject -LDAPFilter "(objectCategory=printQueue)" -Properties cn, drivername, location, printername, portname, servername | select portname, cn, drivername, location, printername, servername | Format-Table -Property * -AutoSize | Out-String -Width 4096 | Out-File C:\wisefaq\printerlist.txt

Which outputs to a text file, like this:
portname cn drivername location printername servername
-------- -- ---------- -------- ----------- ----------
{} PRT001-LZR960-2 Dataproducts LZR 960 PS US/UT/Boort/99 Anytown St LZR960-2
{} PRT001-LZR960-1 Dataproducts LZR 960 PS US/UT/Boort/99 Anytown St LZR960-1
{} PRT001-LZR960-3 Dataproducts LZR 960 PCL US/UT/Boort/99 Anytown St LZR960-3
{} PRT001-LZR960-4 Dataproducts LZR 960 PS US/UT/Boort/99 Anytown St LZR960-4
{} PRT001-LZR960-5 Dataproducts LZR 960 PCL US/UT/Boort/99 Anytown St LZR960-5

So why did I use Out-File instead of Export-CSV?
Export-CSV is refusing to output the {ip.addresses}. I don’t know why, and I’ve wasted an hour trying to work around the issue.

Update: December 2015
Adrian suggests that I could use Powershell Custom Objects to fix the issue of ip.addresses not outputting.

PowerShell Quick Tip: Creating wide tables with PowerShell

Searching for Specific Printers in a Domain (Attributes for the printQueue Object)

Print-Queue class

PowerShell print server inventory script (looks very useful, but you need admin access to each of the printers)

Using VBScript to read though a set of registry keys

I’m currently doing another deployment of Acrobat Reader, this time with VBScript.  I wrote about using Visual Basic to do that here.

Function CheckForPro
    ' returns TRUE if Acrobat Pro found OR we're not able to read registry key

    Dim strHost,sAddRemTmp,iRegRC,objFCReg,strAddRemValue,arrSubKeys,strSubKey
    Const strBaseKey = "Software\Microsoft\Windows\CurrentVersion\Uninstall\"   
    Const HKEY_LOCAL_MACHINE = &H80000002   

    strHost = "."
    sAddRemTmp = ""
    iRegRC = 0
    CheckForPro = False
    Set objFCReg = GetObject("winmgmts://" & strHost & "/root/default:StdRegProv")

    iRegRC = objFCReg.EnumKey (HKEY_LOCAL_MACHINE, strBaseKey, arrSubKeys)
    If iRegRC <> 0 Then
        CheckForPro = True
        Exit Function
    End If
    For Each strSubKey In arrSubKeys
           intAddRemRet = objFCReg.GetStringValue(HKEY_LOCAL_MACHINE, strBaseKey & strSubKey, "DisplayName", strAddRemValue)
           If intAddRemRet = 0 Then
            If InStr(lcase(strAddRemValue),"acrobat") <> 0 Then
                If InStr(lcase(strAddRemValue),"pro") <> 0 Then
                    CheckForPro = True
                    Exit Function
                End If
            End If
           End If

    CheckForPro = False

End Function

Update 11 DEC 2013:
A clever co-worker also points out that if you have a “acrobat.exe” somewhere in Acrobat Program Files directory, then this means you have a Acrobat Pro installation.

VS2010 & .Net 2 Framework Selection

.Net 2 is not the default framework option …  No, you get .Net 4.  To change it, you select Project / <your project name> Properties / Compile / Advanced Compile Options, and select the Target Framework you want.
WinXP SP3 WS2010-2012-03-07-12-50-14

I prefer to use the .Net 2 Framework as it’s the lowest common denominator on the systems I write code for.  And yes, I documented this, as I can’t remember where the option is, and Google Search wasn’t my friend at the time.

Using Visual Basic to read though a set of registry keys.

The problem we have with a corporate deployment of Acrobat Reader, was that we need to uninstall various older versions of Acrobat Reader.  Normally to do this, you need to know the uninstall command string, which changes with each Acrobat Reader release.  One of our senior application packagers said

Why don’t just loop though SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, detect Acrobat Reader in the DisplayName key, then grab the UninstallString.

By jove, what a clever idea.

Doing that in Visual Basic .Net proved me with an afternoon’s programming exercise.  Here is the simplified result:

Dim SoftwareRegistry = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, ".")
Dim SoftwareUninstall = SoftwareRegistry.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", False)
Dim sDisplayName As String = vbEmpty
Dim sReaderUninstallString As String = vbEmpty
Dim sMSIUninstallGUID As String = vbEmpty

For Each item In SoftwareUninstall.GetSubKeyNames

    Dim SoftwareUninstallItemKey = SoftwareRegistry.OpenSubKey("SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" + "\" + item, False)

    sDisplayName = SoftwareUninstallItemKey.GetValue("DisplayName")

    If InStr(LCase(sDisplayName), "adobe reader", CompareMethod.Text) Then
sReaderUninstallString = SoftwareUninstallItemKey.GetValue("UninstallString")
If sReaderUninstallString <> "" Then
End If
End If