In a C++ test app it performs as expected, code:
Code:
#include <Windows.h>
#include <ComDef.h>
#include <iostream>
#include <string>
#include <atomic>
#include <chrono>
using namespace std;
// {864C3D72-B6FD-4014-8AB4-55B664FAEF2C}
static const GUID CLSID_BasicServer =
{ 0x864c3d72, 0xb6fd, 0x4014, { 0x8a, 0xb4, 0x55, 0xb6, 0x64, 0xfa, 0xef, 0x2c } };
// {177D3798-3D6D-4217-B355-D2355884CFDF}
static const GUID IID_IBasicServer =
{ 0x177d3798, 0x3d6d, 0x4217, { 0xb3, 0x55, 0xd2, 0x35, 0x58, 0x84, 0xcf, 0xdf } };
struct IBasicServer : IUnknown
{
virtual int __stdcall GetFive() = 0;
};
class BasicServer : IBasicServer
{
private:
std::atomic<int> m_refs = 0;
public:
// IUnknown
HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
// IBasicServer
int __stdcall GetFive();
};
// IUnknown
STDMETHODIMP BasicServer::QueryInterface(const IID& iid, void** ppv)
{
if (!ppv)
return E_POINTER;
if (iid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
}
else if (iid == IID_IBasicServer)
{
*ppv = (IBasicServer*)this;
}
else
{
*ppv = nullptr;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) BasicServer::AddRef()
{
return ++m_refs;
}
STDMETHODIMP_(ULONG) BasicServer::Release() {
int refs = --m_refs;
if (!refs)
delete this;
return refs;
}
// IBasicServer
int __stdcall BasicServer::GetFive()
{
return 5;
}
// Extern
extern "C" {
__declspec(dllexport) LPVOID __stdcall
CreateBasicServer()
{
BasicServer* pServer = new BasicServer();
LPVOID pIBasicServer;
if (FAILED(pServer->QueryInterface(IID_IBasicServer, &pIBasicServer)))
return nullptr;
return pIBasicServer;
}
__declspec(dllexport) LPVOID __stdcall
CreateBasicServerSlow()
{
return (IBasicServer*) new BasicServer();
}
}
int main()
{
// measure fast CreateBasicServer()
auto start = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < 1'000'000; i++)
((IUnknown*)CreateBasicServer())->Release();
auto end = std::chrono::high_resolution_clock::now();
cout << "fast: " << (end - start).count() << endl;
// measure slow CreateBasicServerSlow()
auto start2 = std::chrono::high_resolution_clock::now();
for (size_t i = 0; i < 1'000'000; i++)
((IUnknown*)CreateBasicServerSlow())->Release();
auto end2 = std::chrono::high_resolution_clock::now();
cout << "slow: " << (end2 - start2).count() << endl;
cout << '\n' << "Press key to exit" << endl;
cin.get();
}
If I create a C++ DLL and test it in a C# app it's 350 times slower!
Code:
#include "pch.h"
#include <ComDef.h>
#include <atomic>
// {864C3D72-B6FD-4014-8AB4-55B664FAEF2C}
static const GUID CLSID_BasicServer =
{ 0x864c3d72, 0xb6fd, 0x4014, { 0x8a, 0xb4, 0x55, 0xb6, 0x64, 0xfa, 0xef, 0x2c } };
// {177D3798-3D6D-4217-B355-D2355884CFDF}
static const GUID IID_IBasicServer =
{ 0x177d3798, 0x3d6d, 0x4217, { 0xb3, 0x55, 0xd2, 0x35, 0x58, 0x84, 0xcf, 0xdf } };
struct IBasicServer : IUnknown
{
virtual int __stdcall GetFive() = 0;
};
class BasicServer : IBasicServer
{
private:
std::atomic<int> m_refs = 0;
public:
// IUnknown
HRESULT __stdcall QueryInterface(const IID& iid, void** ppv);
ULONG __stdcall AddRef();
ULONG __stdcall Release();
// IBasicServer
int __stdcall GetFive();
};
// IUnknown
STDMETHODIMP BasicServer::QueryInterface(const IID& iid, void** ppv)
{
if (!ppv)
return E_POINTER;
if (iid == IID_IUnknown)
{
*ppv = (IUnknown*)this;
}
else if (iid == IID_IBasicServer)
{
*ppv = (IBasicServer*)this;
}
else
{
*ppv = nullptr;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) BasicServer::AddRef()
{
return ++m_refs;
}
STDMETHODIMP_(ULONG) BasicServer::Release() {
int refs = --m_refs;
if (!refs)
delete this;
return refs;
}
// IBasicServer
int __stdcall BasicServer::GetFive()
{
return 5;
}
// Extern
extern "C" {
__declspec(dllexport) LPVOID __stdcall
CreateBasicServer()
{
BasicServer* pServer = new BasicServer();
LPVOID pIBasicServer;
pServer->QueryInterface(IID_IBasicServer, &pIBasicServer);
return pIBasicServer;
}
__declspec(dllexport) LPVOID __stdcall
CreateBasicServerSlow()
{
return (IBasicServer*) new BasicServer();
}
}
Code:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
// test fast
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 1000; i++)
Marshal.ReleaseComObject(CreateBasicServer());
sw.Stop();
Console.WriteLine("fast: " + sw.ElapsedMilliseconds);
// test slow
sw.Reset();
sw.Start();
for (int i = 0; i < 1000; i++)
Marshal.ReleaseComObject(CreateBasicServerSlow());
sw.Stop();
Console.WriteLine("slow: " + sw.ElapsedMilliseconds);
Console.ReadKey();
}
[DllImport(@"D:\Projekte\CPP\FrameServer\x64\Debug\FrameServer.dll")]
static extern IBasicServer CreateBasicServer();
[DllImport(@"D:\Projekte\CPP\FrameServer\x64\Debug\FrameServer.dll")]
static extern IBasicServer CreateBasicServerSlow();
}
[Guid("177D3798-3D6D-4217-B355-D2355884CFDF")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IBasicServer
{
[PreserveSig]
int GetFive();
}
result:
fast: 8
slow: 2815