Register for NLM events (INetworkListManager, Advise, Sink, etc.)

delphi, nlm, winapi

Solution

You are passing the wrong object to `Advise()`. You are passing the `IConnectionPointContainer` object. You need to instead write your own class that implements the `INetworkEvents` interface, then create an instance of the class and pass that object to `Advise()`. That is how the NLM (or any other COM object that uses Connection Points) is able to send events to your code.

Update: You need to change your `NLMEvents` unit to keep the NLM object alive after `StartNLMEventListener()` exits, then the events will work correctly. The way you have it coded, the NLM object is local to `StartNLMEventListener()` and thus is released when `StartNLMEventListener()` exits, which unregisters your event sink.

Try this instead:

unit NLMEvents;

interface

function StartNLMEventListener: HResult;
function StopNLMEventListener: HResult;

implementation

uses
  Windows, ActiveX, NETWORKLIST_TLB, SysUtils, Unit1;

type
  TMyNetworkEvents = class(TInterfacedObject, INetworkEvents, INetworkConnectionEvents, INetworkListManagerEvents)
  public
    function NetworkAdded(networkId: TGUID): HResult; stdcall;
    function NetworkConnectivityChanged(networkId: TGUID; NewConnectivity: NLM_CONNECTIVITY): HResult; stdcall;
    function NetworkDeleted(networkId: TGUID): HResult; stdcall;
    function NetworkPropertyChanged(networkId: TGUID; fFlags: NLM_NETWORK_PROPERTY_CHANGE): HResult; stdcall;
    function ConnectivityChanged(NewConnectivity: NLM_CONNECTIVITY): HResult; stdcall;
    function NetworkConnectionConnectivityChanged(networkId: TGUID; NewConnectivity: NLM_CONNECTIVITY): HResult; stdcall;
    function NetworkConnectionPropertyChanged(networkId: TGUID; fFlags: NLM_NETWORK_PROPERTY_CHANGE): HResult; stdcall;
  end;

var
  pNLM: INetworkListManager = nil;
  dwCookie1: LongInt = -1;
  dwCookie2: LongInt = -1;
  dwCookie3: LongInt = -1;

const
  IID_IConnectionPointContainer: TGUID = '{B196B284-BAB4-101A-B69C-00AA00341D07}';
  //IID_IUnknown: TGUID = '{00000000-0000-0000-C000-000000000046}';
  //CLSID_NetworkListManager: TGUID = '{DCB00C01-570F-4A9B-8D69-199FDBA5723B}';

function TMyNetworkEvents.ConnectivityChanged(NewConnectivity: NLM_CONNECTIVITY): HResult; stdcall;
begin
  Form2.Memo1.Lines.Add('ConnectivityChanged');
  Result := S_OK;
end;

function TMyNetworkEvents.NetworkConnectionConnectivityChanged(networkId: TGUID; NewConnectivity: NLM_CONNECTIVITY): HResult; stdcall;
begin
  Form2.Memo1.Lines.Add('NetworkConnectionConnectivityChanged');
  Result := S_OK;
end;

function TMyNetworkEvents.NetworkConnectionPropertyChanged(networkId: TGUID; fFlags: NLM_NETWORK_PROPERTY_CHANGE): HResult; stdcall;
begin
  Form2.Memo1.Lines.Add('NetworkConnectionPropertyChange');
  Result := S_OK;
end;

function TMyNetworkEvents.NetworkAdded(networkId: TGUID): HResult; stdcall;
begin
  Form2.Memo1.Lines.Add('NetworkAdded');
  Result := S_OK;
end;

function TMyNetworkEvents.NetworkConnectivityChanged(networkId: TGUID; NewConnectivity: NLM_CONNECTIVITY): HResult; stdcall;
begin
  Form2.Memo1.Lines.Add('NetworkConnectivityChanged');
  Result := S_OK;
end;

function TMyNetworkEvents.NetworkDeleted(networkId: TGUID): HResult; stdcall;
begin
  Form2.Memo1.Lines.Add('NetworkDeleted');
  Result := S_OK;
end;

function TMyNetworkEvents.NetworkPropertyChanged(networkId: TGUID; fFlags: NLM_NETWORK_PROPERTY_CHANGE): HResult; stdcall;
begin
  Form2.Memo1.Lines.Add('NetworkPropertyChanged');
  Result := S_OK;
end;

function StartNLMEventListener: HResult;
var
  pCpc: IConnectionPointContainer;
  pConnectionPoint: IConnectionPoint;
  pSink: INetworkEvents;
begin
  if pNLM = nil then
  begin
    Result := CoCreateInstance(CLASS_NetworkListManager, nil, CLSCTX_ALL, IID_INetworkListManager, pNLM);
    if Failed(Result) then
      Exit;
  end else
  begin
    Result := S_OK;
  end;

  if Succeeded(pNLM.QueryInterface(IID_IConnectionPointContainer, pCpc)) then
  begin
    pSink := TMyNetworkEvents.Create as INetworkEvents;

    if dwCookie1 = -1 then
    begin
      if Succeeded(pCpc.FindConnectionPoint(IID_INetworkEvents, pConnectionPoint)) then
      begin
        pConnectionPoint.Advise(pSink, dwCookie1);
        pConnectionPoint := nil;
      end;
    end;

    if dwCookie2 = -1 then
    begin
      if Succeeded(pCpc.FindConnectionPoint(IID_INetworkConnectionEvents, pConnectionPoint)) then
      begin
        pConnectionPoint.Advise(pSink, dwCookie2);
        pConnectionPoint := nil;
      end;
    end;

    if dwCookie3 = -1 then
    begin
      if Succeeded(pCpc.FindConnectionPoint(IID_INetworkListManagerEvents, pConnectionPoint)) then
      begin
        pConnectionPoint.Advise(pSink, dwCookie3);
        pConnectionPoint := nil;
      end;
    end;
  end;
end;

function StopNLMEventListener: HResult;
var
  pCpc: IConnectionPointContainer;
  pConnectionPoint: IConnectionPoint;
begin
  if pNLM <> nil then
  begin
    if Succeeded(pNLM.QueryInterface(IID_IConnectionPointContainer, pCpc)) then
    begin
      if dwCookie1 <> -1 then
      begin
        if Succeeded(pCpc.FindConnectionPoint(IID_INetworkEvents, pConnectionPoint)) then
        begin
          pConnectionPoint.Unadvise(dwCookie1);
          pConnectionPoint := nil;
        end;
      end;

      if dwCookie2 <> -1 then
      begin
        if Succeeded(pCpc.FindConnectionPoint(IID_INetworkConnectionEvents, pConnectionPoint)) then
        begin
          pConnectionPoint.Unadvise(dwCookie2);
          pConnectionPoint := nil;
        end;
      end;

      if dwCookie3 <> -1 then
      begin
        if Succeeded(pCpc.FindConnectionPoint(IID_INetworkListManagerEvents, pConnectionPoint)) then
        begin
          pConnectionPoint.Unadvise(dwCookie3);
          pConnectionPoint := nil;
        end;
      end;
    end;

    pNLM := nil;
  end;

  dwCookie1 := -1;
  dwCookie2 := -1;
  dwCookie3 := -1;

  Result := S_OK;
end;

end.

Problem

In my Delphi application I'd like to get informed about network changes using the Microsoft Windows Network List Manager API (NLM): http://msdn.microsoft.com/library/ee264321 I've looked at the linked "How to register for NLM events" example and translated it to Delphi. However, I have no idea how to continue with this. ``` var pNLM: INetworkListManager; pCpc: IConnectionPointContainer; pConnectionPoint: IConnectionPoint; pSink: IUnknown; dwCookie: LongInt; const IID_IConnectionPointContainer: TGUID = '{B196B284-BAB4-101A-B69C-00AA00341D07}'; IID_IUnknown: TGUID = '{00000000-0000-0000-C000-000000000046}'; begin if Succeeded(CoCreateInstance(CLASS_NetworkListManager, nil, CLSCTX_ALL, IID_INetworkListManager, pNLM)) then begin if Succeeded(pNLM.QueryInterface(IID_IConnectionPointContainer, pCpc)) then begin if Succeeded(pCpc.FindConnectionPoint(IID_INetworkEvents, pConnectionPoint)) then begin if Succeeded(pCpc.QueryInterface(IID_IUnknown, pSink)) then begin pConnectionPoint.Advise(pSink, dwCookie); end; end; end; end; end; ``` The article sais: "After you have created the INetworkListManager object above you will receive INetworkEvents notifications from that point forward. pSink implements the INetworkEvent interface including those event processing methods such as NetworkAdded, NetworkDeleted, NetworkConnectivityChanged, and NetworkPropertyChanged." Unfortunately I have no idea how to do that. There's no further instructions and so I hope someone here could instruct me / provide an example what else to do to call custom procedures for those events. Thanks.

Original source