Monday, 28 January 2013

Fire your most irreplaceable team members now

Although the analysis may be a bit simplistic as there it makes no mention of skill level, I found this really interesting article here:
  1. An employee that voluntarily shares his knowledge with his coworkers, keeps nothing solely in his head, but rather strives to document publicly the knowledge and know-how he acquires on the job, is the most precious employee the company has. He knows that if he quits tomorrow, he is totally replaceable, in the sense that there's nothing he would need to teach his replacement - it's all there in the open. No knowledge is permanently lost. This is the guy you never actually want to replace. 
  2. An employee that hoards knowledge and positions himself as the omniscient guru, to whom all must come for answers, should be the first one fired. Sometimes people do this unintentionally. They don't see a problem with having all the answers. That's why managers need to instill into them the following mantra: the stuff you keep in your head should only be a copy.
I used to be think of myself as a number 1 type of worker and then I quickly morphed into to a number 2, why? Well, unfortunately management tend to value more a type 2 employee than a type 1, because they are not looking at the medium term, never mind the long term, they just care about the here and now and if somebody can pick up your work, then they can get rid of you. Short sighted? absolutely, but unfortunately this is the world we inhabit.

I'm trying to go back to my good ways of documenting everything and taking an attitude of I cannot afford NOT to document things. I'm not there yet, but I'm getting there. My advise is to try to work for a company where a type 1 worker will not get the sack when push comes to shove.

Wednesday, 23 January 2013

Use metadata service in MS Dynamics CRM 2011 to list Entities' Display Names and logical names

I normally try to keep the entity's Display Name as close to the Logical Name as possible, but more often than not, even with the best of intentions, the names go out of sync. This can be a problem for many reasons, so I created a quick console app to write the Display Names and corresponding Logical Names to a file, for quick reference.

The code will only write to file the custom and customizable entities, ignoring system entities and intersect entities, those created by N:N relationships (oh, yes, behind the scenes it's similar to two 1:N relationships).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk;
using System.IO;

namespace XRM.Metadata
{
    public class Metadata
    {
      public List<EntityMetadata> RetrieveAllEntities(IOrganizationService service)
        {
            RetrieveAllEntitiesRequest request = new RetrieveAllEntitiesRequest()
            {
                EntityFilters = EntityFilters.Entity,
                RetrieveAsIfPublished = true
            };

            RetrieveAllEntitiesResponse resp = (RetrieveAllEntitiesResponse)service.Execute(request);

            return resp.EntityMetadata.OrderBy(x => x.LogicalName).ToList();          
        }

        public void WriteDetailsToFile(string fileName, List<EntityMetadata> entitiesMeta)
        {
            using (StreamWriter sw = new StreamWriter(fileName))
            {
                foreach (var entmeta in entitiesMeta)
                {
                    if (((bool)entmeta.IsCustomEntity || entmeta.IsCustomizable.Value) && !(bool)entmeta.IsIntersect)
                    {
                        if (entmeta.DisplayName.UserLocalizedLabel != null)
                        {
                            sw.WriteLine(string.Format("Display Name: {0} - Logical Name: {1}.", entmeta.DisplayName.UserLocalizedLabel.Label, entmeta.LogicalName));
                        }
                        else if (entmeta.Description.UserLocalizedLabel != null)
                        {
                            sw.WriteLine(string.Format("Display Name: {0} - Logical Name: {1} - Description: {2}", "Unknown", entmeta.LogicalName, entmeta.Description.UserLocalizedLabel.Label));

                        }

                        else
                        {
                            sw.WriteLine(string.Format("Display Name: {0} - Logical Name: {1} - Description: {2}", "Unknown", entmeta.LogicalName, "Unknown"));
                        }
                    }
                }
            }
        }
    }
}

Sunday, 20 January 2013

Blogger Comment Spam Detection - part 2

I thought that this was pretty funny (I changed the title after I took the screenshot).


The only comment on a post about comment spam detection is, you guessed it, a spam comment. I'll leave it up so that it's there for posterity.

Thursday, 17 January 2013

Blogger Comment Spam Detection

I noticed something strange going through the comments on my blog.

This comment, which was a reply to a comment, got directed to the spam comments:
Weell the information about the Joomla is very imformative because you tell all the important things which we can creat just watching your blog and it is also very easy to read and understand. I change many things just watching your blog. Thank you for the imformation you provide. 
Yet this comment was displayed:
Well the information about the Joomla is very imformative because you tell all the important things which we can creat just watching your blog and it is also very easy to latest news on information technology read and understand. I change many things just watching your blog.
They were both on the same post by the same blogger user.

The displayed comment had two fewer typos and was not a reply to a comment, other than that they are almost identical. I guess the spam filter was triggered for the former as it had too many red flags:
  1. One typo per line (average)
  2. A blog profile that's actually an html link to a page unrelated to the blog's theme.
  3. Reply to a comment?
Or was it simply the mention of Information technology on the latter comment, that got it through the spam filter?

Monday, 14 January 2013

Search MS Dynamics CRM 2011 workflow definition (xaml) in PowerShell

Sometimes it's necessary to search the workflow definition quickly for an action, for instance: creating a task or creating a custom entity. If you have a few workflows in the system then you can probably get away with looking at them manually from the GUI, if you have many then it makes sense to search the xaml definition file.

The simplest option is described here:

  1. Export the solution containing your workflows.
  2. Extract the solution to a directory.
  3. Start PowerShell and navigate to the directory above.
  4. Search the definition with the following command, which will provide you with the filename:
 ls -Include *.xaml -Recurse | Select-String "YourSearchString" | Group-Object -Property filename | Format-List -Property name
Note that by default YourSearchString will be read as regular expression.

If you want to do negative matching you could try this:
 ls -Include *.xaml -Recurse | Select-String "YourSearchString" -NotMatch | Group-Object -Property filename | Format-List -Property name
This command can be very slow as the notmatches are going to be a lot more than the matches in the xaml, so use it at your peril.

Wednesday, 9 January 2013

MS Dynamics CRM 2011 for Outlook issues looking at sub folders.

A few weeks one our customers was complaining that they were having issues with Outlook when accessing the Dynamics CRM sub folders. In essence, any view would display the error below and then Outlook and Dynamics would continue on their merry way, working fine.


The error traces weren't being terrible helpful:
Exception generated at: 18/12/2012 14:09:31Error Type: System.NullReferenceExceptionError Message: Object reference not set to an instance of an object.Error Stack Trace: at CommandBarOnDemandPopupWrapper..ctor(XmlNode controlXml, ExplorerWrapper explorer, PrepareMenuCallback callback)  ilOffset = 0x42at MenuState.PrepareMenu(CommandBarPopupWrapper parentMenu)  ilOffset = 0xF9at MenuState..ctor(IClientOrganizationContext clientOrgContext, String context, ExplorerWrapper explorer, Int32 index)  ilOffset = 0x1E2at <>c__DisplayClass2f.<SetupMenus>b__2a()  ilOffset = 0x3Cat <>c__DisplayClass31.<SetupMenus>b__2c(Object unused)  ilOffset = 0x0at <>c__DisplayClass1a.<QueueUserWorkItemSilent>b__15()  ilOffset = 0x0at ExceptionFilter.TryFilterAllCatch(Action body, Action`1 filter)  ilOffset = 0xF

Stack Frame: 
at OutlookUtility.HandleExceptionInternal(Exception exception, Boolean showMessageBox, IWin32Window messageBoxOwner, String errorMessage)  ilOffset = 0x107at <>c__DisplayClass17.<QueueUserWorkItemSilent>b__16(Exception ex)  ilOffset = 0x6at <>c__DisplayClassa.<TryFilterAllCatch>b__8(Exception ex)  ilOffset = 0xCat ExceptionFilter.TryFilterAllCatch(Action body, Action`1 filter)  ilOffset = 0x34at CommandBarOnDemandPopupWrapper..ctor(XmlNode controlXml, ExplorerWrapper explorer, PrepareMenuCallback callback)  ilOffset = 0x42at MenuState.PrepareMenu(CommandBarPopupWrapper parentMenu)  ilOffset = 0xF9at MenuState..ctor(IClientOrganizationContext clientOrgContext, String context, ExplorerWrapper explorer, Int32 index)  ilOffset = 0x1EAat <>c__DisplayClass2f.<SetupMenus>b__2a()  ilOffset = 0x65at <>c__DisplayClass31.<SetupMenus>b__2c(Object unused)  ilOffset = 0xBat <>c__DisplayClass1a.<QueueUserWorkItemSilent>b__15()  ilOffset = 0x16at ExceptionFilter.TryFilterAllCatch(Action body, Action`1 filter)  ilOffset = 0xFat <>c__DisplayClass17.<QueueUserWorkItemSilent>b__14(Object innerState)  ilOffset = 0x42at QueueUserWorkItemCallback.WaitCallback_Context(Object state)  ilOffset = 0x1Aat ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)  ilOffset = 0x8Eat QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()  ilOffset = 0x35at ThreadPoolWorkQueue.Dispatch()  ilOffset = 0x81at _ThreadPoolWaitCallback.PerformWaitCallback()  ilOffset = 0x51

The first thing we noticed was that there was a mismatch  in the update rollups installed, but then we found another workstation without the mismatch and the problem occurred.

We then applied a few patches to the workstation, including an upgrade from Outlook 2007 SP2 to SP3 but to no avail.

In a last ditch attempt to solve the issue, we upgraded to Outlook 2010 and it solved the issue. Not sure the customer will be happy with our proposed solution.

Friday, 4 January 2013

What's the Powershell equivalent of back tick on bash?

I was wondering this myself today and it's one of them questions, where you say, well of course.

In Powershell, one uses $( ) to evaluate sub-expressions, so say that you wanted to pass your username to a PowerShell script.

You could do this:
$user = whoami
myscript.ps1 $user
But this is more elegant
myscript.ps1 $(whoami)
From here.

Thursday, 3 January 2013

Find domain controller used for logon

I managed to lock out an account today so I was trying to login to the DC, but I couldn't get access to our SharePoint server which meant that I didn't have the environment information at hand, and because this is a new environment, I don't know the name of the servers off the top of my head, which meant that I needed a way to find out a domain controller.

It turns out that this is really easy. Logonserver is an environment variable that contains the domain controller that was used for logging on.

From PowerShell (again with elevated privileges):
Get-ChildItem ENV: | where {$_.Name -eq "logonserver"}
or
[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().FindDomainController()  

From a Command Prompt (with elevated privileges):
set logonserver
or 
set L

If you want to get all domain controllers, not just the one you used to logon:
[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().DomainControllers

Monday, 31 December 2012

Programmatically change the default sound device in Windows 7

I got a new monitor last week and it came with integrated speakers, which I didn't think I would use, but it occurred to me that it would be handy to use these speakers rather than my Logitech 5.1 system when browsing the internet. I normally have the speakers off at the sub woofer for one main reason, the system uses 30 Watts in stand by, which roughly equates to £30 a year in wasted electricity and since I normally use my desktop only to watch movies or TV series this is not a major inconvenience, but sometimes it's annoying. At any rate, I was looking for a way of changing the default  sound device using PowerShell, but it looks like it's not possible, however, it is possible to do it programmatically.

I'm reproducing the code here because I thought it was interesting. This is not my code, the original article can be found here.

PolicyConfig.h:
// ----------------------------------------------------------------------------
// 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
};
Console App Code:

#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;
}
As mentioned above this is not my code, copied from here.