initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include <OleAuto.h>
|
||||
|
||||
struct BStrHolder
|
||||
{
|
||||
BStrHolder() :
|
||||
m_Str(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
BStrHolder(const wchar_t* str) :
|
||||
m_Str(SysAllocString(str))
|
||||
{
|
||||
}
|
||||
|
||||
~BStrHolder()
|
||||
{
|
||||
if (m_Str != NULL)
|
||||
SysFreeString(m_Str);
|
||||
}
|
||||
|
||||
operator BSTR() const
|
||||
{
|
||||
return m_Str;
|
||||
}
|
||||
|
||||
BSTR* operator&()
|
||||
{
|
||||
if (m_Str != NULL)
|
||||
{
|
||||
SysFreeString(m_Str);
|
||||
m_Str = NULL;
|
||||
}
|
||||
|
||||
return &m_Str;
|
||||
}
|
||||
|
||||
private:
|
||||
BSTR m_Str;
|
||||
};
|
|
@ -0,0 +1,14 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(com)
|
||||
set(SOURCES
|
||||
COMIntegration.cpp
|
||||
BStrHolder.h
|
||||
ComPtr.h
|
||||
dte80a.tlh
|
||||
)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -Wall")
|
||||
add_executable(COMIntegration ${SOURCES})
|
||||
set_property(TARGET COMIntegration PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
|
||||
target_link_libraries(COMIntegration Shlwapi.lib)
|
|
@ -0,0 +1,477 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Unity Technologies.
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <windows.h>
|
||||
#include <shlwapi.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
|
||||
#include "BStrHolder.h"
|
||||
#include "ComPtr.h"
|
||||
#include "dte80a.tlh"
|
||||
|
||||
constexpr int RETRY_INTERVAL_MS = 150;
|
||||
constexpr int TIMEOUT_MS = 10000;
|
||||
|
||||
// Often a DTE call made to Visual Studio can fail after Visual Studio has just started. Usually the
|
||||
// return value will be RPC_E_CALL_REJECTED, meaning that Visual Studio is probably busy on another
|
||||
// thread. This types filter the RPC messages and retries to send the message until VS accepts it.
|
||||
class CRetryMessageFilter : public IMessageFilter
|
||||
{
|
||||
private:
|
||||
static bool ShouldRetryCall(DWORD dwTickCount, DWORD dwRejectType)
|
||||
{
|
||||
if (dwRejectType == SERVERCALL_RETRYLATER || dwRejectType == SERVERCALL_REJECTED) {
|
||||
return dwTickCount < TIMEOUT_MS;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
win::ComPtr<IMessageFilter> currentFilter;
|
||||
|
||||
public:
|
||||
CRetryMessageFilter()
|
||||
{
|
||||
HRESULT hr = CoRegisterMessageFilter(this, ¤tFilter);
|
||||
_ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
~CRetryMessageFilter()
|
||||
{
|
||||
win::ComPtr<IMessageFilter> messageFilter;
|
||||
HRESULT hr = CoRegisterMessageFilter(currentFilter, &messageFilter);
|
||||
_ASSERT(SUCCEEDED(hr));
|
||||
}
|
||||
|
||||
// IUnknown methods
|
||||
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
|
||||
{
|
||||
static const QITAB qit[] =
|
||||
{
|
||||
QITABENT(CRetryMessageFilter, IMessageFilter),
|
||||
{ 0 },
|
||||
};
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) AddRef()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) Release()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE HandleInComingCall(DWORD dwCallType, HTASK htaskCaller, DWORD dwTickCount, LPINTERFACEINFO lpInterfaceInfo)
|
||||
{
|
||||
if (currentFilter)
|
||||
return currentFilter->HandleInComingCall(dwCallType, htaskCaller, dwTickCount, lpInterfaceInfo);
|
||||
|
||||
return SERVERCALL_ISHANDLED;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE RetryRejectedCall(HTASK htaskCallee, DWORD dwTickCount, DWORD dwRejectType)
|
||||
{
|
||||
if (ShouldRetryCall(dwTickCount, dwRejectType))
|
||||
return RETRY_INTERVAL_MS;
|
||||
|
||||
if (currentFilter)
|
||||
return currentFilter->RetryRejectedCall(htaskCallee, dwTickCount, dwRejectType);
|
||||
|
||||
return (DWORD)-1;
|
||||
}
|
||||
|
||||
DWORD STDMETHODCALLTYPE MessagePending(HTASK htaskCallee, DWORD dwTickCount, DWORD dwPendingType)
|
||||
{
|
||||
if (currentFilter)
|
||||
return currentFilter->MessagePending(htaskCallee, dwTickCount, dwPendingType);
|
||||
|
||||
return PENDINGMSG_WAITDEFPROCESS;
|
||||
}
|
||||
};
|
||||
|
||||
static void DisplayProgressbar() {
|
||||
std::wcout << "displayProgressBar" << std::endl;
|
||||
}
|
||||
|
||||
static void ClearProgressbar() {
|
||||
std::wcout << "clearprogressbar" << std::endl;
|
||||
}
|
||||
|
||||
inline const std::wstring QuoteString(const std::wstring& str)
|
||||
{
|
||||
return L"\"" + str + L"\"";
|
||||
}
|
||||
|
||||
static std::wstring ErrorCodeToMsg(DWORD code)
|
||||
{
|
||||
LPWSTR msgBuf = nullptr;
|
||||
if (!FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, nullptr))
|
||||
{
|
||||
return L"Unknown error";
|
||||
}
|
||||
else
|
||||
{
|
||||
return msgBuf;
|
||||
}
|
||||
}
|
||||
|
||||
// Get an environment variable
|
||||
static std::wstring GetEnvironmentVariableValue(const std::wstring& variableName) {
|
||||
DWORD currentBufferSize = MAX_PATH;
|
||||
std::wstring variableValue;
|
||||
variableValue.resize(currentBufferSize);
|
||||
|
||||
DWORD requiredBufferSize = GetEnvironmentVariableW(variableName.c_str(), variableValue.data(), currentBufferSize);
|
||||
if (requiredBufferSize == 0) {
|
||||
// Environment variable probably does not exist.
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
if (currentBufferSize < requiredBufferSize) {
|
||||
variableValue.resize(requiredBufferSize);
|
||||
if (GetEnvironmentVariableW(variableName.c_str(), variableValue.data(), currentBufferSize) == 0)
|
||||
return std::wstring();
|
||||
}
|
||||
|
||||
variableValue.resize(requiredBufferSize);
|
||||
return variableValue;
|
||||
}
|
||||
|
||||
static bool StartVisualStudioProcess(
|
||||
const std::filesystem::path &visualStudioExecutablePath,
|
||||
const std::filesystem::path &solutionPath,
|
||||
DWORD *dwProcessId) {
|
||||
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
BOOL result;
|
||||
|
||||
ZeroMemory(&si, sizeof(si));
|
||||
si.cb = sizeof(si);
|
||||
ZeroMemory(&pi, sizeof(pi));
|
||||
|
||||
std::wstring startingDirectory = visualStudioExecutablePath.parent_path();
|
||||
|
||||
// Build the command line that is passed as the argv of the VS process
|
||||
// argv[0] must be the quoted full path to the VS exe
|
||||
std::wstringstream commandLineStream;
|
||||
commandLineStream << QuoteString(visualStudioExecutablePath) << L" ";
|
||||
|
||||
std::wstring vsArgsWide = GetEnvironmentVariableValue(L"UNITY_VS_ARGS");
|
||||
if (!vsArgsWide.empty())
|
||||
commandLineStream << vsArgsWide << L" ";
|
||||
|
||||
commandLineStream << QuoteString(solutionPath);
|
||||
|
||||
std::wstring commandLine = commandLineStream.str();
|
||||
|
||||
std::wcout << "Starting Visual Studio process with: " << commandLine << std::endl;
|
||||
|
||||
result = CreateProcessW(
|
||||
visualStudioExecutablePath.c_str(), // Full path to VS, must not be quoted
|
||||
commandLine.data(), // Command line, as passed as argv, separate arguments must be quoted if they contain spaces
|
||||
nullptr, // Process handle not inheritable
|
||||
nullptr, // Thread handle not inheritable
|
||||
false, // Set handle inheritance to FALSE
|
||||
0, // No creation flags
|
||||
nullptr, // Use parent's environment block
|
||||
startingDirectory.c_str(), // starting directory set to the VS directory
|
||||
&si,
|
||||
&pi);
|
||||
|
||||
if (!result) {
|
||||
DWORD error = GetLastError();
|
||||
std::wcout << "Starting Visual Studio process failed: " << ErrorCodeToMsg(error) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
*dwProcessId = pi.dwProcessId;
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static win::ComPtr<EnvDTE::_DTE> FindRunningVisualStudioWithSolution(
|
||||
const std::filesystem::path &visualStudioExecutablePath,
|
||||
const std::filesystem::path &solutionPath)
|
||||
{
|
||||
win::ComPtr<IUnknown> punk = nullptr;
|
||||
win::ComPtr<EnvDTE::_DTE> dte = nullptr;
|
||||
|
||||
CRetryMessageFilter retryMessageFilter;
|
||||
|
||||
// Search through the Running Object Table for an instance of Visual Studio
|
||||
// to use that either has the correct solution already open or does not have
|
||||
// any solution open.
|
||||
win::ComPtr<IRunningObjectTable> ROT;
|
||||
if (FAILED(GetRunningObjectTable(0, &ROT)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IBindCtx> bindCtx;
|
||||
if (FAILED(CreateBindCtx(0, &bindCtx)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IEnumMoniker> enumMoniker;
|
||||
if (FAILED(ROT->EnumRunning(&enumMoniker)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IMoniker> moniker;
|
||||
ULONG monikersFetched = 0;
|
||||
while (SUCCEEDED(enumMoniker->Next(1, &moniker, &monikersFetched)) && monikersFetched) {
|
||||
if (FAILED(ROT->GetObject(moniker, &punk)))
|
||||
continue;
|
||||
|
||||
punk.As(&dte);
|
||||
if (!dte)
|
||||
continue;
|
||||
|
||||
// Okay, so we found an actual running instance of Visual Studio.
|
||||
|
||||
// Get the executable path of this running instance.
|
||||
BStrHolder visualStudioFullName;
|
||||
if (FAILED(dte->get_FullName(&visualStudioFullName)))
|
||||
continue;
|
||||
|
||||
std::filesystem::path currentVisualStudioExecutablePath = std::wstring(visualStudioFullName);
|
||||
|
||||
// Ask for its current solution.
|
||||
win::ComPtr<EnvDTE::_Solution> solution;
|
||||
if (FAILED(dte->get_Solution(&solution)))
|
||||
continue;
|
||||
|
||||
// Get the name of that solution.
|
||||
BStrHolder solutionFullName;
|
||||
if (FAILED(solution->get_FullName(&solutionFullName)))
|
||||
continue;
|
||||
|
||||
std::filesystem::path currentSolutionPath = std::wstring(solutionFullName);
|
||||
if (currentSolutionPath.empty())
|
||||
continue;
|
||||
|
||||
std::wcout << "Visual Studio opened on " << currentSolutionPath.wstring() << std::endl;
|
||||
|
||||
// If the name matches the solution we want to open and we have a Visual Studio installation path to use and this one matches that path, then use it.
|
||||
// If we don't have a Visual Studio installation path to use, just use this solution.
|
||||
if (std::filesystem::equivalent(currentSolutionPath, solutionPath)) {
|
||||
std::wcout << "We found a running Visual Studio session with the solution open." << std::endl;
|
||||
if (!visualStudioExecutablePath.empty()) {
|
||||
if (std::filesystem::equivalent(currentVisualStudioExecutablePath, visualStudioExecutablePath)) {
|
||||
return dte;
|
||||
}
|
||||
else {
|
||||
std::wcout << "This running Visual Studio session does not seem to be the version requested in the user preferences. We will keep looking." << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::wcout << "We're not sure which version of Visual Studio was requested in the user preferences. We will use this running session." << std::endl;
|
||||
return dte;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
MonikerIsVisualStudioProcess(const win::ComPtr<IMoniker> &moniker, const win::ComPtr<IBindCtx> &bindCtx, const DWORD dwProcessId) {
|
||||
LPOLESTR oleMonikerName;
|
||||
if (FAILED(moniker->GetDisplayName(bindCtx, nullptr, &oleMonikerName)))
|
||||
return false;
|
||||
|
||||
std::wstring monikerName(oleMonikerName);
|
||||
|
||||
// VisualStudio Moniker is "!VisualStudio.DTE.$Version:$PID"
|
||||
// Example "!VisualStudio.DTE.14.0:1234"
|
||||
|
||||
if (monikerName.find(L"!VisualStudio.DTE") != 0)
|
||||
return false;
|
||||
|
||||
std::wstringstream suffixStream;
|
||||
suffixStream << ":";
|
||||
suffixStream << dwProcessId;
|
||||
|
||||
std::wstring suffix(suffixStream.str());
|
||||
|
||||
return monikerName.length() - suffix.length() == monikerName.find(suffix);
|
||||
}
|
||||
|
||||
static win::ComPtr<EnvDTE::_DTE> FindRunningVisualStudioWithPID(const DWORD dwProcessId) {
|
||||
win::ComPtr<IUnknown> punk = nullptr;
|
||||
win::ComPtr<EnvDTE::_DTE> dte = nullptr;
|
||||
|
||||
// Search through the Running Object Table for a Visual Studio
|
||||
// process with the process ID specified
|
||||
win::ComPtr<IRunningObjectTable> ROT;
|
||||
if (FAILED(GetRunningObjectTable(0, &ROT)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IBindCtx> bindCtx;
|
||||
if (FAILED(CreateBindCtx(0, &bindCtx)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IEnumMoniker> enumMoniker;
|
||||
if (FAILED(ROT->EnumRunning(&enumMoniker)))
|
||||
return nullptr;
|
||||
|
||||
win::ComPtr<IMoniker> moniker;
|
||||
ULONG monikersFetched = 0;
|
||||
while (SUCCEEDED(enumMoniker->Next(1, &moniker, &monikersFetched)) && monikersFetched) {
|
||||
if (FAILED(ROT->GetObject(moniker, &punk)))
|
||||
continue;
|
||||
|
||||
if (!MonikerIsVisualStudioProcess(moniker, bindCtx, dwProcessId))
|
||||
continue;
|
||||
|
||||
punk.As(&dte);
|
||||
if (dte)
|
||||
return dte;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool HaveRunningVisualStudioOpenFile(const win::ComPtr<EnvDTE::_DTE> &dte, const std::filesystem::path &filename, int line) {
|
||||
BStrHolder bstrFileName(filename.c_str());
|
||||
BStrHolder bstrKind(L"{00000000-0000-0000-0000-000000000000}"); // EnvDTE::vsViewKindPrimary
|
||||
win::ComPtr<EnvDTE::Window> window = nullptr;
|
||||
|
||||
CRetryMessageFilter retryMessageFilter;
|
||||
|
||||
if (!filename.empty()) {
|
||||
std::wcout << "Getting operations API from the Visual Studio session." << std::endl;
|
||||
|
||||
win::ComPtr<EnvDTE::ItemOperations> item_ops;
|
||||
if (FAILED(dte->get_ItemOperations(&item_ops)))
|
||||
return false;
|
||||
|
||||
std::wcout << "Waiting for the Visual Studio session to open the file: " << filename.wstring() << "." << std::endl;
|
||||
|
||||
if (FAILED(item_ops->OpenFile(bstrFileName, bstrKind, &window)))
|
||||
return false;
|
||||
|
||||
if (line > 0) {
|
||||
win::ComPtr<IDispatch> selection_dispatch;
|
||||
if (window && SUCCEEDED(window->get_Selection(&selection_dispatch))) {
|
||||
win::ComPtr<EnvDTE::TextSelection> selection;
|
||||
if (selection_dispatch &&
|
||||
SUCCEEDED(selection_dispatch->QueryInterface(__uuidof(EnvDTE::TextSelection), &selection)) &&
|
||||
selection) {
|
||||
selection->GotoLine(line, false);
|
||||
selection->EndOfLine(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window = nullptr;
|
||||
if (SUCCEEDED(dte->get_MainWindow(&window))) {
|
||||
// Allow the DTE to make its main window the foreground
|
||||
HWND hWnd;
|
||||
window->get_HWnd((LONG *)&hWnd);
|
||||
|
||||
DWORD processID;
|
||||
if (SUCCEEDED(GetWindowThreadProcessId(hWnd, &processID)))
|
||||
AllowSetForegroundWindow(processID);
|
||||
|
||||
// Activate() set the window to visible and active (blinks in taskbar)
|
||||
window->Activate();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool VisualStudioOpenFile(
|
||||
const std::filesystem::path &visualStudioExecutablePath,
|
||||
const std::filesystem::path &solutionPath,
|
||||
const std::filesystem::path &filename,
|
||||
int line)
|
||||
{
|
||||
win::ComPtr<EnvDTE::_DTE> dte = nullptr;
|
||||
|
||||
std::wcout << "Looking for a running Visual Studio session." << std::endl;
|
||||
|
||||
// TODO: If path does not exist pass empty, which will just try to match all windows with solution
|
||||
dte = FindRunningVisualStudioWithSolution(visualStudioExecutablePath, solutionPath);
|
||||
|
||||
if (!dte) {
|
||||
std::wcout << "No appropriate running Visual Studio session not found, creating a new one." << std::endl;
|
||||
|
||||
DisplayProgressbar();
|
||||
|
||||
DWORD dwProcessId;
|
||||
if (!StartVisualStudioProcess(visualStudioExecutablePath, solutionPath, &dwProcessId)) {
|
||||
ClearProgressbar();
|
||||
return false;
|
||||
}
|
||||
|
||||
int timeWaited = 0;
|
||||
|
||||
while (timeWaited < TIMEOUT_MS) {
|
||||
dte = FindRunningVisualStudioWithPID(dwProcessId);
|
||||
|
||||
if (dte)
|
||||
break;
|
||||
|
||||
std::wcout << "Retrying to acquire DTE" << std::endl;
|
||||
|
||||
Sleep(RETRY_INTERVAL_MS);
|
||||
timeWaited += RETRY_INTERVAL_MS;
|
||||
}
|
||||
|
||||
ClearProgressbar();
|
||||
|
||||
if (!dte)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
std::wcout << "Using the existing Visual Studio session." << std::endl;
|
||||
}
|
||||
|
||||
return HaveRunningVisualStudioOpenFile(dte, filename, line);
|
||||
}
|
||||
|
||||
int wmain(int argc, wchar_t* argv[]) {
|
||||
|
||||
// We need this to properly display UTF16 text on the console
|
||||
_setmode(_fileno(stdout), _O_U16TEXT);
|
||||
|
||||
if (argc != 3 && argc != 5) {
|
||||
std::wcerr << argc << ": wrong number of arguments\n" << "Usage: com.exe installationPath solutionPath [fileName lineNumber]" << std::endl;
|
||||
for (int i = 0; i < argc; i++) {
|
||||
std::wcerr << argv[i] << std::endl;
|
||||
}
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (FAILED(CoInitialize(nullptr))) {
|
||||
std::wcerr << "CoInitialize failed." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::filesystem::path visualStudioExecutablePath = std::filesystem::absolute(argv[1]);
|
||||
std::filesystem::path solutionPath = std::filesystem::absolute(argv[2]);
|
||||
|
||||
if (argc == 3) {
|
||||
VisualStudioOpenFile(visualStudioExecutablePath, solutionPath, L"", -1);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::filesystem::path fileName = std::filesystem::absolute(argv[3]);
|
||||
int lineNumber = std::stoi(argv[4]);
|
||||
|
||||
VisualStudioOpenFile(visualStudioExecutablePath, solutionPath, fileName, lineNumber);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
#pragma once
|
||||
|
||||
namespace win
|
||||
{
|
||||
template<typename T>
|
||||
class ComPtr;
|
||||
|
||||
template<typename T>
|
||||
class ComPtrRef
|
||||
{
|
||||
private:
|
||||
ComPtr<T>& m_ComPtr;
|
||||
|
||||
ComPtrRef(ComPtr<T>& comPtr) :
|
||||
m_ComPtr(comPtr)
|
||||
{
|
||||
}
|
||||
|
||||
friend class ComPtr<T>;
|
||||
|
||||
public:
|
||||
inline operator T**()
|
||||
{
|
||||
return m_ComPtr.ReleaseAndGetAddressOf();
|
||||
}
|
||||
|
||||
inline operator void**()
|
||||
{
|
||||
return reinterpret_cast<void**>(m_ComPtr.ReleaseAndGetAddressOf());
|
||||
}
|
||||
|
||||
inline T* operator*() throw ()
|
||||
{
|
||||
return m_ComPtr;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class ComPtr
|
||||
{
|
||||
private:
|
||||
T *ptr;
|
||||
|
||||
public:
|
||||
inline ComPtr(void) : ptr(NULL) {}
|
||||
inline ~ComPtr(void) { this->Free(); }
|
||||
|
||||
ComPtr(T *ptr)
|
||||
{
|
||||
if (NULL != (this->ptr = ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr(const ComPtr &ptr)
|
||||
{
|
||||
if (NULL != (this->ptr = ptr.ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
inline bool operator!() const
|
||||
{
|
||||
return (NULL == this->ptr);
|
||||
}
|
||||
|
||||
inline operator T*() const { return this->ptr; }
|
||||
|
||||
inline T *operator->() const
|
||||
{
|
||||
//_assert(NULL != this->ptr);
|
||||
return this->ptr;
|
||||
}
|
||||
|
||||
inline T &operator*()
|
||||
{
|
||||
//_assert(NULL != this->ptr);
|
||||
return *this->ptr;
|
||||
}
|
||||
|
||||
inline ComPtrRef<T> operator&()
|
||||
{
|
||||
return ComPtrRef<T>(*this);
|
||||
}
|
||||
|
||||
const ComPtr &operator=(T *ptr)
|
||||
{
|
||||
if (this->ptr != ptr)
|
||||
{
|
||||
this->Free();
|
||||
|
||||
if (NULL != (this->ptr = ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
const ComPtr &operator=(const ComPtr &ptr)
|
||||
{
|
||||
if (this->ptr != ptr.ptr)
|
||||
{
|
||||
this->Free();
|
||||
|
||||
if (NULL != (this->ptr = ptr.ptr))
|
||||
{
|
||||
this->ptr->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Free(void)
|
||||
{
|
||||
if (NULL != this->ptr)
|
||||
{
|
||||
this->ptr->Release();
|
||||
this->ptr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline T** ReleaseAndGetAddressOf()
|
||||
{
|
||||
Free();
|
||||
return &ptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline HRESULT As(ComPtrRef<U> p) const throw ()
|
||||
{
|
||||
return ptr->QueryInterface(__uuidof(U), p);
|
||||
}
|
||||
|
||||
inline bool operator==(std::nullptr_t) const
|
||||
{
|
||||
return this->ptr == nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator==(U* other)
|
||||
{
|
||||
if (ptr == nullptr || other == nullptr)
|
||||
return ptr == other;
|
||||
|
||||
ComPtr<IUnknown> meUnknown;
|
||||
ComPtr<IUnknown> otherUnknown;
|
||||
|
||||
if (FAILED(this->ptr->QueryInterface(__uuidof(IUnknown), &meUnknown)))
|
||||
return false;
|
||||
|
||||
if (FAILED(other->QueryInterface(__uuidof(IUnknown), &otherUnknown)))
|
||||
return false;
|
||||
|
||||
return static_cast<IUnknown*>(meUnknown) == static_cast<IUnknown*>(otherUnknown);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator==(ComPtr<U>& other)
|
||||
{
|
||||
return *this == static_cast<U*>(other);
|
||||
}
|
||||
|
||||
inline bool operator!=(std::nullptr_t) const
|
||||
{
|
||||
return this->ptr != nullptr;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator!=(U* other)
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
inline bool operator!=(ComPtr<U>& other)
|
||||
{
|
||||
return *this != static_cast<U*>(other);
|
||||
}
|
||||
};
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,6 @@
|
|||
Direct style:
|
||||
cl /EHsc /std:c++17 COMIntegration.cpp /link Shlwapi.lib /out:"..\Release\COMIntegration.exe"
|
||||
|
||||
CMake style:
|
||||
cmake ../COMIntegration~ -B ./build
|
||||
cmake --build ./build --config=release -- /p:OutDir=..
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 41b2d972bdac29e4a89ef08b3b52c248
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Binary file not shown.
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cb67edc1800c2ec4ba8dfb1cf0dfc84a
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue