Search code examples
delphiwinapinlm

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


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.


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.