The Network List Manager (NLM) API (introduced on Windows Vista) allows you to retrieve a list of available connections and properties of each network .Also the NLM API support notifications (events) about the availability of new network connections or changes to existing network connections. This API is useful for filter networks, based on his attributes and signatures. Also you can use this API in your own Application to adjust their logic depending on: which network they are connected to; or what the network properties are.
Ok, So the first step to use this API from Delphi is import the Network List Manager Type Library (C:\WINDOWS\system32\netprofm.dll) from the option Component-> Import Component as is show below.
After of this the NETWORKLIST_TLB unit will be created containing all the Interfaces, Types and Enumerations of the NLM API.
Note: Depending of your Windows version the content of the imported type library can change, because some additional Interfaces and types was added in Windows 8.
INetworkListManager
The INetworkListManager is the main interface to access the methods to perform NLM API functions Like enumerate Networks, Connections and check the overall connectivity state of the current machine.
This is the definition of the INetworkListManager interface.
INetworkListManager = interface(IDispatch) ['{DCB00000-570F-4A9B-8D69-199FDBA5723B}'] function GetNetworks(Flags: NLM_ENUM_NETWORK): IEnumNetworks; safecall; function GetNetwork(gdNetworkId: TGUID): INetwork; safecall; function GetNetworkConnections: IEnumNetworkConnections; safecall; function GetNetworkConnection(gdNetworkConnectionId: TGUID): INetworkConnection; safecall; function Get_IsConnectedToInternet: WordBool; safecall; function Get_IsConnected: WordBool; safecall; function GetConnectivity: NLM_CONNECTIVITY; safecall; procedure SetSimulatedProfileInfo(var pSimulatedInfo: NLM_SIMULATED_PROFILE_INFO); safecall; //Introduced on Windows 8.1 procedure ClearSimulatedProfileInfo; safecall; //Introduced on Windows 8.1 property IsConnectedToInternet: WordBool read Get_IsConnectedToInternet; property IsConnected: WordBool read Get_IsConnected; end;
In order to access to this interface you must declare a INetworkListManager variable and call the CoNetworkListManager.Create; method to get an instance to such Interface.
Check the next sample console application which shows the current connectivity of the machine and if the internet connection is available.
{$APPTYPE CONSOLE} uses SysUtils, ActiveX, ComObj, Windows, NETWORKLIST_TLB in 'NETWORKLIST_TLB.pas'; //https://msdn.microsoft.com/en-us/library/windows/desktop/aa370795(v=vs.85).aspx function GetNetworkConnectivity(Connectivity : NLM_CONNECTIVITY) : string; begin Result:=''; if NLM_CONNECTIVITY_DISCONNECTED and Connectivity <> 0 then Result := Result+ 'Disconnected, '; if NLM_CONNECTIVITY_IPV4_NOTRAFFIC and Connectivity <> 0 then Result := Result+ 'Connected but not ipv4 traffic, '; if NLM_CONNECTIVITY_IPV6_NOTRAFFIC and Connectivity <> 0 then Result := Result+ 'Connected but not ipv6 traffic, '; if NLM_CONNECTIVITY_IPV4_SUBNET and Connectivity <> 0 then Result := Result+ 'Subnet ipv4, '; if NLM_CONNECTIVITY_IPV4_LOCALNETWORK and Connectivity <> 0 then Result := Result+ 'LocalNetwork ipv4, '; if NLM_CONNECTIVITY_IPV4_INTERNET and Connectivity <> 0 then Result := Result+ 'Internet ipv4, '; if NLM_CONNECTIVITY_IPV6_SUBNET and Connectivity <> 0 then Result := Result+ 'Subnet ipv6, '; if NLM_CONNECTIVITY_IPV6_LOCALNETWORK and Connectivity <> 0 then Result := Result+ 'LocalNetwork ipv6, '; if NLM_CONNECTIVITY_IPV6_INTERNET and Connectivity <> 0 then Result := Result+'Internet ipv6, '; Result:= StringReplace('['+Result+']', ', ]', ']', [rfReplaceAll]); end; procedure GetNetworkListManagerInfo; var NetworkListManager: INetworkListManager; begin NetworkListManager := CoNetworkListManager.Create; Writeln(Format('Connected : %s', [boolToStr(NetworkListManager.IsConnected, True)])); Writeln(Format('Internet : %s', [boolToStr(NetworkListManager.IsConnectedToInternet, True)])); Writeln(Format('Connectivity : %s', [GetNetworkConnectivity(NetworkListManager.GetConnectivity)])); end; begin try //Check is Windows Vista at least if TOSVersion.Check(6) then begin CoInitialize(nil); try GetNetworkListManagerInfo; finally CoUninitialize; end; end else Writeln('This windows version doesn''t support the NLM API'); except on E:EOleException do Writeln(Format('EOleException %s %x', [E.Message, E.ErrorCode])); on E:Exception do Writeln(E.Classname, ':', E.Message); end; Writeln('Press Enter to exit'); Readln; end.
Enumerating Connections
The NLM API allow you to enumerate network connections using the INetworkListManager.GetNetworkConnections method, this function returns an instance to the IEnumNetworkConnections interface that enumerates all network connections on the machine, each element returned by the IEnumNetworkConnections.Next method represents a INetworkConnection object.
INetworkConnection = interface(IDispatch) ['{DCB00005-570F-4A9B-8D69-199FDBA5723B}'] function GetNetwork: INetwork; safecall; function Get_IsConnectedToInternet: WordBool; safecall; function Get_IsConnected: WordBool; safecall; function GetConnectivity: NLM_CONNECTIVITY; safecall; function GetConnectionId: TGUID; safecall; function GetAdapterId: TGUID; safecall; function GetDomainType: NLM_DOMAIN_TYPE; safecall; property IsConnectedToInternet: WordBool read Get_IsConnectedToInternet; property IsConnected: WordBool read Get_IsConnected; end;
Check the next sample console application which shows how enumerate the connections with his connectivity, domain type, network adapters associated and so on.
{$APPTYPE CONSOLE} uses SysUtils, ActiveX, ComObj, Windows, NETWORKLIST_TLB in 'NETWORKLIST_TLB.pas'; //https://msdn.microsoft.com/en-us/library/windows/desktop/aa370796(v=vs.85).aspx function GetNetworkDomainType(DomainType : NLM_DOMAIN_TYPE) : string; begin Result:=''; case DomainType of NLM_DOMAIN_TYPE_NON_DOMAIN_NETWORK : Result := 'Non Domain Network'; //The Network is not an Active Directory Network NLM_DOMAIN_TYPE_DOMAIN_NETWORK : Result := 'Domain Network';//The Network is an Active Directory Network, but this machine is not authenticated against it. NLM_DOMAIN_TYPE_DOMAIN_AUTHENTICATED : Result := 'Domain Network Authenticated';//The Network is an Active Directory Network, and this machine is authenticated against it. end; end; //https://msdn.microsoft.com/en-us/library/windows/desktop/aa370795(v=vs.85).aspx function GetNetworkConnectivity(Connectivity : NLM_CONNECTIVITY) : string; begin Result:=''; if NLM_CONNECTIVITY_DISCONNECTED and Connectivity <> 0 then Result := Result+ 'Disconnected, '; if NLM_CONNECTIVITY_IPV4_NOTRAFFIC and Connectivity <> 0 then Result := Result+ 'Connected but not ipv4 traffic, '; if NLM_CONNECTIVITY_IPV6_NOTRAFFIC and Connectivity <> 0 then Result := Result+ 'Connected but not ipv6 traffic, '; if NLM_CONNECTIVITY_IPV4_SUBNET and Connectivity <> 0 then Result := Result+ 'Subnet ipv4, '; if NLM_CONNECTIVITY_IPV4_LOCALNETWORK and Connectivity <> 0 then Result := Result+ 'LocalNetwork ipv4, '; if NLM_CONNECTIVITY_IPV4_INTERNET and Connectivity <> 0 then Result := Result+ 'Internet ipv4, '; if NLM_CONNECTIVITY_IPV6_SUBNET and Connectivity <> 0 then Result := Result+ 'Subnet ipv6, '; if NLM_CONNECTIVITY_IPV6_LOCALNETWORK and Connectivity <> 0 then Result := Result+ 'LocalNetwork ipv6, '; if NLM_CONNECTIVITY_IPV6_INTERNET and Connectivity <> 0 then Result := Result+'Internet ipv6, '; Result:= StringReplace('['+Result+']', ', ]', ']', [rfReplaceAll]); end; procedure GetConnections; var NetworkListManager: INetworkListManager; EnumNetworkConnections: IEnumNetworkConnections; NetworkConnection : INetworkConnection; pceltFetched: ULONG; begin NetworkListManager := CoNetworkListManager.Create; EnumNetworkConnections := NetworkListManager.GetNetworkConnections(); while true do begin EnumNetworkConnections.Next(1, NetworkConnection, pceltFetched); if (pceltFetched>0) then begin Writeln(Format('Adapter Id : %s', [GuidToString(NetworkConnection.GetAdapterId)])); Writeln(Format('Connection Id : %s', [GuidToString(NetworkConnection.GetConnectionId)])); Writeln(Format('Domain Type : %s', [GetNetworkDomainType(NetworkConnection.GetDomainType)])); Writeln(Format('Connected : %s', [boolToStr(NetworkConnection.IsConnected, True)])); Writeln(Format('Internet : %s', [boolToStr(NetworkConnection.IsConnectedToInternet, True)])); Writeln(Format('Network : %s', [NetworkConnection.GetNetwork.GetName])); Writeln(Format('Connectivity : %s', [GetNetworkConnectivity(NetworkConnection.GetConnectivity)])); end else Break; end; end; begin try //Check is Windows Vista at least if TOSVersion.Check(6) then begin CoInitialize(nil); try GetConnections; finally CoUninitialize; end; end else Writeln('This windows version doesn''t support the NLM API'); except on E:EOleException do Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode])); on E:Exception do Writeln(E.Classname, ':', E.Message); end; Writeln('Press Enter to exit'); Readln; end.
Enumerating Networks
For enumerate the networks available on the local machine you must use the INetworkListManager.GetNetworks method passing a valid NLM_ENUM_NETWORK value.
- NLM_ENUM_NETWORK_CONNECTED Returns connected networks
- NLM_ENUM_NETWORK_DISCONNECTED Returns disconnected networks
- NLM_ENUM_NETWORK_ALL Returns connected and disconnected networks
from here you can gain access to several elements related to the network like Name, Description, Connectivity, Network Category and the local date and time when the network was created and connected.
Sample code to enumerate the networks using the NLM API
{$APPTYPE CONSOLE} uses SysUtils, ActiveX, ComObj, Windows, NETWORKLIST_TLB in 'NETWORKLIST_TLB.pas'; //https://msdn.microsoft.com/en-us/library/windows/desktop/aa370796(v=vs.85).aspx function GetNetworkDomainType(DomainType : NLM_CONNECTIVITY) : string; begin Result:=''; case DomainType of NLM_DOMAIN_TYPE_NON_DOMAIN_NETWORK : Result := 'Non Domain Network'; //The Network is not an Active Directory Network NLM_DOMAIN_TYPE_DOMAIN_NETWORK : Result := 'Domain Network';//The Network is an Active Directory Network, but this machine is not authenticated against it. NLM_DOMAIN_TYPE_DOMAIN_AUTHENTICATED : Result := 'Domain Network Authenticated';//The Network is an Active Directory Network, and this machine is authenticated against it. end; end; function GetNetworkCategory(Category : NLM_NETWORK_CATEGORY) : string; begin Result:=''; case Category of NLM_NETWORK_CATEGORY_PUBLIC : Result := 'Public'; NLM_NETWORK_CATEGORY_PRIVATE : Result := 'Private'; NLM_NETWORK_CATEGORY_DOMAIN_AUTHENTICATED : Result := 'Authenticated'; end; end; procedure GetNetworks; var NetworkListManager: INetworkListManager; EnumNetworks: IEnumNetworks; EnumNetworksConnections: IEnumNetworkConnections; NetworkConnection : INetworkConnection; Network: INetwork; fetched, pceltFetched: ULONG; pdwLowDateTimeCreated: LongWord; pdwHighDateTimeCreated: LongWord; pdwLowDateTimeConnected: LongWord; pdwHighDateTimeConnected: LongWord; lpFileTime : TFileTime; lpSystemTime: TSystemTime; LDateTime : TDateTime; begin NetworkListManager := CoNetworkListManager.Create; EnumNetworks := NetworkListManager.GetNetworks(NLM_ENUM_NETWORK_CONNECTED); while true do begin EnumNetworks.Next(1, Network, fetched); if (fetched>0) then begin Writeln(Format('%s - %s', [Network.GetName, Network.GetDescription])); Writeln(Format('Network Id : %s', [GuidToString(Network.GetNetworkId)])); Writeln(Format('Domain Type : %s', [GetNetworkDomainType(Network.GetDomainType)])); Writeln(Format('Category : %s', [GetNetworkCategory(Network.GetCategory)])); //https://msdn.microsoft.com/en-us/library/windows/desktop/aa370787(v=vs.85).aspx Network.GetTimeCreatedAndConnected(pdwLowDateTimeCreated, pdwHighDateTimeCreated, pdwLowDateTimeConnected, pdwHighDateTimeConnected); lpFileTime.dwLowDateTime := pdwLowDateTimeCreated; lpFileTime.dwHighDateTime := pdwHighDateTimeCreated; if FileTimeToSystemTime(lpFileTime, lpSystemTime) then begin LDateTime := SystemTimeToDateTime(lpSystemTime); Writeln('Created : '+FormatDateTime('dd/mm/yyyy hh:nn', LDateTime)); end; lpFileTime.dwLowDateTime := pdwLowDateTimeConnected; lpFileTime.dwHighDateTime := pdwHighDateTimeConnected; if FileTimeToSystemTime(lpFileTime, lpSystemTime) then begin LDateTime := SystemTimeToDateTime(lpSystemTime); Writeln('Last Connection : '+FormatDateTime('dd/mm/yyyy hh:nn', LDateTime)); end; Writeln(Format('Connected : %s', [boolToStr(Network.IsConnected, True)])); Writeln(Format('Internet : %s', [boolToStr(Network.IsConnectedToInternet, True)])); EnumNetworksConnections := Network.GetNetworkConnections(); Writeln; Writeln('Connections'); Writeln('-----------'); while true do begin EnumNetworksConnections.Next(1, NetworkConnection, pceltFetched); if (pceltFetched>0) then begin Writeln(Format(' Adapter Id : %s', [GuidToString(NetworkConnection.GetAdapterId)])); Writeln(Format(' Connection Id : %s', [GuidToString(NetworkConnection.GetConnectionId)])); end else break; end; Writeln; end else Break; end; end; begin try //Check is Windows Vista at least if TOSVersion.Check(6) then begin CoInitialize(nil); try GetNetworks; finally CoUninitialize; end; end else Writeln('This windows version doesn''t support the NLM API'); except on E:EOleException do Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode])); on E:Exception do Writeln(E.Classname, ':', E.Message); end; Writeln('Press Enter to exit'); Readln; end.
Events
The NML API exposes several events (notifications) that you Application must implements to get network related events. These callback functions that are called automatically when the respective events are raised.
These are the notifications interfaces available on the NLM API
- INetworkConnectionEvents Get network connection-related events.
- INetworkEvents Get network related events.
- INetworkListManagerEvents Get overall machine state related events.
- INetworkCostManagerEvents Get machine-wide cost and data plan related events.
- INetworkConnectionCostEvents Get cost and data plan status change events for a connection.
Note : The next sample shows how subscribe to the INetworkEvents only. I will leave it as reader’s exercise the implementation of the another events.
The INetworkEvents interface can inform when a Network was added, deleted or modified. To receive the notifications related to these events you must inherit a class from this interface and implement the next methods.
INetworkEvents = interface(IUnknown) ['{DCB00004-570F-4A9B-8D69-199FDBA5723B}'] function NetworkAdded(networkId: TGUID): HResult; stdcall; function NetworkDeleted(networkId: TGUID): HResult; stdcall; function NetworkConnectivityChanged(networkId: TGUID; newConnectivity: NLM_CONNECTIVITY): HResult; stdcall; function NetworkPropertyChanged(networkId: TGUID; Flags: NLM_NETWORK_PROPERTY_CHANGE): HResult; stdcall; end;
then you need get an instance to the IConnectionPointContainer interface, after using the IConnectionPointContainer.FindConnectionPoint return a connection point for the interface and finally call the IConnectionPoint.Advise method to establish the connection with the event.
NetworkListManager := CoNetworkListManager.Create; if Succeeded(NetworkListManager.QueryInterface(IID_IConnectionPointContainer, LConnectionPointContainer)) then if Succeeded(LConnectionPointContainer.FindConnectionPoint(IID_INetworkEvents, LConnectionPoint)) then LConnectionPoint.Advise(Self as IUnknown, dwCookie);
When you not longer need to use the event, you must to terminate the advisory connection previously established with the connection point using the IConnectionPoint.Unadvise method.
if Succeeded(NetworkListManager.QueryInterface(IID_IConnectionPointContainer, LConnectionPointContainer)) then if Succeeded(LConnectionPointContainer.FindConnectionPoint(IID_INetworkEvents, LConnectionPoint)) then begin LConnectionPoint.Unadvise(dwCookie); LConnectionPoint := nil; end;
Check the next sample console application which implements the INetworkEvents interface.
{$APPTYPE CONSOLE} uses Winapi.Windows, {$IF CompilerVersion > 18.5} Vcl.Forms, {$IFEND } System.SysUtils, Winapi.ActiveX, System.Win.ComObj, NETWORKLIST_TLB in 'NETWORKLIST_TLB.pas'; type TNetworkEvents = class(TInterfacedObject, INetworkEvents) private NetworkListManager : INetworkListManager; dwCookie : Integer; public function NetworkAdded(networkId: TGUID): HResult; stdcall; function NetworkDeleted(networkId: TGUID): HResult; stdcall; function NetworkConnectivityChanged(networkId: TGUID; newConnectivity: NLM_CONNECTIVITY): HResult; stdcall; function NetworkPropertyChanged(networkId: TGUID; Flags: NLM_NETWORK_PROPERTY_CHANGE): HResult; stdcall; constructor Create; procedure Start; procedure Stop; end; //Detect when a key was pressed in the console window function KeyPressed:Boolean; var lpNumberOfEvents : DWORD; lpBuffer : TInputRecord; lpNumberOfEventsRead : DWORD; nStdHandle : THandle; begin Result:=false; nStdHandle := GetStdHandle(STD_INPUT_HANDLE); lpNumberOfEvents:=0; GetNumberOfConsoleInputEvents(nStdHandle, lpNumberOfEvents); if (lpNumberOfEvents<> 0) then begin PeekConsoleInput(nStdHandle, lpBuffer, 1, lpNumberOfEventsRead); if lpNumberOfEventsRead <> 0 then begin if lpBuffer.EventType = KEY_EVENT then begin if lpBuffer.Event.KeyEvent.bKeyDown then Result:=true else FlushConsoleInputBuffer(nStdHandle); end else FlushConsoleInputBuffer(nStdHandle); end; end; end; //https://msdn.microsoft.com/en-us/library/windows/desktop/aa370795(v=vs.85).aspx function GetNetworkConnectivity(Connectivity : NLM_CONNECTIVITY) : string; begin Result:=''; if NLM_CONNECTIVITY_DISCONNECTED and Connectivity <> 0 then Result := Result+ 'Disconnected, '; if NLM_CONNECTIVITY_IPV4_NOTRAFFIC and Connectivity <> 0 then Result := Result+ 'Connected but not ipv4 traffic, '; if NLM_CONNECTIVITY_IPV6_NOTRAFFIC and Connectivity <> 0 then Result := Result+ 'Connected but not ipv6 traffic, '; if NLM_CONNECTIVITY_IPV4_SUBNET and Connectivity <> 0 then Result := Result+ 'Subnet ipv4, '; if NLM_CONNECTIVITY_IPV4_LOCALNETWORK and Connectivity <> 0 then Result := Result+ 'LocalNetwork ipv4, '; if NLM_CONNECTIVITY_IPV4_INTERNET and Connectivity <> 0 then Result := Result+ 'Internet ipv4, '; if NLM_CONNECTIVITY_IPV6_SUBNET and Connectivity <> 0 then Result := Result+ 'Subnet ipv6, '; if NLM_CONNECTIVITY_IPV6_LOCALNETWORK and Connectivity <> 0 then Result := Result+ 'LocalNetwork ipv6, '; if NLM_CONNECTIVITY_IPV6_INTERNET and Connectivity <> 0 then Result := Result+'Internet ipv6, '; Result:= StringReplace('['+Result+']', ', ]', ']', [rfReplaceAll]); end; //https://msdn.microsoft.com/en-us/library/windows/desktop/aa370801(v=vs.85).aspx function GetNetworkPropertyChange(PropertyChange : NLM_NETWORK_PROPERTY_CHANGE) : string; begin Result:=''; if NLM_NETWORK_PROPERTY_CHANGE_CONNECTION and PropertyChange <> 0 then Result := Result+ 'Connection, '; if NLM_NETWORK_PROPERTY_CHANGE_DESCRIPTION and PropertyChange <> 0 then Result := Result+ 'Description, '; if NLM_NETWORK_PROPERTY_CHANGE_NAME and PropertyChange <> 0 then Result := Result+ 'Name, '; if NLM_NETWORK_PROPERTY_CHANGE_ICON and PropertyChange <> 0 then Result := Result+ 'Icon, '; if NLM_NETWORK_PROPERTY_CHANGE_CATEGORY_VALUE and PropertyChange <> 0 then Result := Result+ 'Category value, '; Result:= StringReplace('['+Result+']', ', ]', ']', [rfReplaceAll]); end; { TNetworkEvents } const IID_IConnectionPointContainer: TGUID = ( D1:$B196B284;D2:$BAB4;D3:$101A;D4:($B6,$9C,$00,$AA,$00,$34,$1D,$07)); constructor TNetworkEvents.Create; begin dwCookie := 0; end; function TNetworkEvents.NetworkAdded(networkId: TGUID): HResult; begin Writeln(Format('Network Added : %s', [GuidToString(networkId)])); Result := S_OK; end; function TNetworkEvents.NetworkConnectivityChanged(networkId: TGUID; NewConnectivity: NLM_CONNECTIVITY): HResult; begin Writeln(Format('Network Connectivity Changed : %s - %s', [GuidToString(networkId), GetNetworkConnectivity(NewConnectivity)])); Result := S_OK; end; function TNetworkEvents.NetworkDeleted(networkId: TGUID): HResult; begin Writeln(Format('Network Deleted : %s', [GuidToString(networkId)])); Result := S_OK; end; function TNetworkEvents.NetworkPropertyChanged(networkId: TGUID; Flags: NLM_NETWORK_PROPERTY_CHANGE): HResult; begin Writeln(Format('Network Property Changed : %s - %s', [GuidToString(networkId), GetNetworkPropertyChange(Flags)])); Result := S_OK; end; procedure TNetworkEvents.Start; var LConnectionPointContainer: IConnectionPointContainer; LConnectionPoint: IConnectionPoint; begin if dwCookie > 0 then exit; NetworkListManager := CoNetworkListManager.Create; if Succeeded(NetworkListManager.QueryInterface(IID_IConnectionPointContainer, LConnectionPointContainer)) then begin if Succeeded(LConnectionPointContainer.FindConnectionPoint(IID_INetworkEvents, LConnectionPoint)) then begin LConnectionPoint.Advise(Self as IUnknown, dwCookie); LConnectionPoint := nil; end; end; end; procedure TNetworkEvents.Stop; var LConnectionPointContainer: IConnectionPointContainer; LConnectionPoint: IConnectionPoint; begin if dwCookie = 0 then exit; if Succeeded(NetworkListManager.QueryInterface(IID_IConnectionPointContainer, LConnectionPointContainer)) then if Succeeded(LConnectionPointContainer.FindConnectionPoint(IID_INetworkEvents, LConnectionPoint)) then begin LConnectionPoint.Unadvise(dwCookie); LConnectionPoint := nil; end; end; var NLMEvents : TNetworkEvents; begin try //Check is Windows Vista at least if TOSVersion.Check(6) then begin NLMEvents:=TNetworkEvents.Create; try NLMEvents.Start; Writeln('Listening NLM events - press any key to stop'); //The next loop is only necessary on this sample console App //In VCL forms Apps you don't need use a loop while not KeyPressed do begin Sleep(100); Application.ProcessMessages; end; finally NLMEvents.Stop; Writeln('NLM events - Done'); end; end else Writeln('This windows version doesn''t support the NLM API'); except on E:EOleException do Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode])); on E:Exception do Writeln(E.Classname, ':', E.Message); end; Readln; end.
All the source code posted on this article is available on Github.
Rodrigo.