Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AtlFreeServer/AtlFreeServer.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IListener_i.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="..\Build\Output\Include\Interfaces\IListener_p.c">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">NotUsing</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="GuardDog.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
Expand Down
14 changes: 14 additions & 0 deletions Interfaces/IListener.idl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import "oaidl.idl";
import "ocidl.idl";

[
oleautomation,
object,
uuid(2E2944D8-CD27-45ED-ADAB-9644C87CE6B8),
pointer_default(unique)
]
interface IListener : IUnknown
{
HRESULT Start([in] IUnknown* toListen);
HRESULT Stop();
};
14 changes: 13 additions & 1 deletion Interfaces/IPetShop.idl
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,16 @@ interface IPetShop : IUnknown
{
HRESULT BuyDog([out, retval] IDog** dog);
HRESULT GetAddress([out, retval] Address* address);
};
HRESULT Open();
};

[
uuid(A8545F26-9028-46DB-9D89-A7B395B3260F)
]
dispinterface _DPetShopEvents
{
properties:
methods:
[id(1)]
void Opened();
};
3 changes: 3 additions & 0 deletions Interfaces/Interfaces.idl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "IHen.idl";
import "IDog.idl";
import "IPostman.idl";
import "IPetShop.idl";
import "IListener.idl";

[
uuid(8edbf75d-ecf2-4a1f-affc-07fcb8dee00a),
Expand All @@ -18,5 +19,7 @@ library Interfaces
interface IDog;
interface IPostman;
interface IPetShop;
interface IListener;
dispinterface _DPetShopEvents;
};

1 change: 1 addition & 0 deletions Interfaces/interfaces.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<Midl Include="IListener.idl" />
<Midl Include="IRoyalPython.idl" />
<Midl Include="IHen.idl" />
<Midl Include="Interfaces.idl" />
Expand Down
1 change: 1 addition & 0 deletions Interfaces/interfaces.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<Midl Include="IPostman.idl" />
<Midl Include="IPetShop.idl" />
<Midl Include="IRoyalPython.idl" />
<Midl Include="IListener.idl" />
</ItemGroup>
<ItemGroup>
<None Include="readme.md" />
Expand Down
10 changes: 10 additions & 0 deletions ManagedServer/PetShop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@ namespace ManagedServer
{
[Guid("5011c315-994d-49b4-b737-03a846f590a0")]
[ProgId("ManagedServer.PetShop.1")]
[ComSourceInterfaces(typeof(_DPetShopEvents))]
[ComVisible(true)]
public class PetShop : IPetShop
{
public delegate void Opened_Delegate();

private event Opened_Delegate Opened;

public PetShop()
{

Expand All @@ -27,5 +32,10 @@ public Address GetAddress()
address.City = "Oslo";
return address;
}

public void Open()
{
Opened?.Invoke();
}
}
}
103 changes: 102 additions & 1 deletion TutorialsAndTests/Tests/ManagedServerTests.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#include "../pch.h"
#include <gtest/gtest.h>
#include <Interfaces/Interfaces.h>
#include <Interfaces/IListener.h>
#include <Interfaces/IDog.h>
#include <Interfaces/IPetShop.h>
#include <ComUtility/Utility.h>
#include <wrl.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlcomcli.h>

using Microsoft::WRL::ComPtr;

Expand Down Expand Up @@ -57,4 +62,100 @@ TEST(ManagedServerTests,
SysFreeString(address.Street);
SysFreeString(address.PostalCode);
SysFreeString(address.City);
}
}

// Base class to listen events from COM event source
template<unsigned Id, typename Class, typename Events>
struct ListenerImpl :
IDispEventSimpleImpl<Id, Class, &__uuidof(Events)>,
IListener
{
// Connect to toListen event source
HRESULT Start(IUnknown* toListen) override
{
m_toListen = toListen;
// Setup the connection with the event source
DispEventAdvise(m_toListen);

return S_OK;
}

// Disconnect from event source
HRESULT Stop() override
{
// Break the connection with the event source
DispEventUnadvise(m_toListen);

// Release the application
m_toListen.Release();

return S_OK;
}

protected:
CComPtr<IUnknown> m_toListen;
};

// Interface to add call expectations
struct IEventsHandler
{
virtual ~IEventsHandler() = default;
virtual void OnOpenedImpl() const = 0;
};

_ATL_FUNC_INFO OnOpenedInfo = { CC_STDCALL, VT_EMPTY, 0 };

// Real PetShopListener, inspired from :
// https://github.com/microsoft/VCSamples/blob/9e1d4475555b76a17a3568369867f1d7b6cc6126/VC2008Samples/ATL/General/ATLEventHandling/Simple.h
struct PetShopListener :
CComObjectRootEx<CComMultiThreadModel>,
CComCoClass<PetShopListener>,
ListenerImpl<1, PetShopListener, _DPetShopEvents>,
IEventsHandler
{
BEGIN_COM_MAP(PetShopListener)
COM_INTERFACE_ENTRY(IListener)
END_COM_MAP()

void __stdcall OnOpened()
{
OnOpenedImpl();
}

MOCK_METHOD(void, OnOpenedImpl, (), (const override));

BEGIN_SINK_MAP(PetShopListener)
SINK_ENTRY_INFO(/*nID =*/ 1, __uuidof(_DPetShopEvents), /*dispid =*/ 1, OnOpened, &OnOpenedInfo)
END_SINK_MAP()
};

TEST(ManagedServerTests,
RequireThat_Open_TriggersOpenedEvent_WhenCalled)
{
CLSID petShopClsid{};
HR(CLSIDFromString(L"{5011c315-994d-49b4-b737-03a846f590a0}", &petShopClsid));

// Create managed PetShop what can Raise events
ATL::CComPtr<IPetShop> petShop;
HR(CoCreateInstance(petShopClsid, nullptr, CLSCTX_INPROC_SERVER, __uuidof(IPetShop), reinterpret_cast<void**>(&petShop)));
EXPECT_NE(petShop, nullptr);

// Create local instance for PetShopListener
auto petShopListener = make_self<PetShopListener>();

// Subscribe to the petShop events
petShopListener->Start(petShop);

// We expect that OnOpenedImpl called while we subscribed
EXPECT_CALL(*petShopListener, OnOpenedImpl())
.Times(1);

// Raise Opened event, this should call PetShopListener::OnOpened
petShop->Open();

// Unsubscribe from the petShop events
petShopListener->Stop();

// Raise Opened event, but no Listeners and PetShopListener::OnOpened is not called
petShop->Open();
}