Use reference instead of copy
[TortoiseGit.git] / src / GitWCRev / GitWCRevCOM.cpp
blob9badfe09e5e4bbae6645215b0bc5a0961af9beb4
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2017-2018 - TortoiseGit
4 // Copyright (C) 2007-2015 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include <objbase.h>
22 #include "GitWCRevCOM_h.h"
23 #include "GitWCRevCOM_i.c"
24 #include "GitWCRevCOM.h"
26 #include <comutil.h>
27 #include <atlbase.h>
29 #include "Register.h"
30 #include "UnicodeUtils.h"
31 #include "status.h"
33 STDAPI DllRegisterServer();
34 STDAPI DllUnregisterServer();
35 static void AutomationMain();
36 static void RunOutprocServer();
37 static void ImplWinMain();
39 int APIENTRY _tWinMain(HINSTANCE /*hInstance*/,
40 HINSTANCE /*hPrevInstance*/,
41 LPTSTR /*lpCmdLine*/,
42 int /*nCmdShow*/)
44 ImplWinMain();
45 return 0;
48 static void ImplWinMain()
50 int argc = 0;
51 LPWSTR* argv = CommandLineToArgvW(GetCommandLine(), &argc);
52 if (!argv)
53 return;
55 if ((argc >= 2) && (argc <= 5))
57 if (wcscmp(argv[1], L"/automation") == 0)
58 AutomationMain();
59 else if (wcscmp(argv[1], L"unregserver") == 0)
60 DllUnregisterServer();
61 else if (wcscmp(argv[1], L"regserver") == 0)
62 DllRegisterServer();
64 LocalFree(argv);
67 static void AutomationMain()
69 // initialize the COM library
70 HRESULT hr = ::CoInitialize(nullptr);
71 if (FAILED(hr))
72 return;
74 git_libgit2_init();
76 RunOutprocServer();
78 git_libgit2_shutdown();
80 ::CoUninitialize();
83 // Count of locks to the server - one for each non-factory live object
84 // plus one for each call of IClassFactory::LockServer(true) not yet
85 // followed by IClassFactory::LockServer(false)
86 static long g_cServerLocks = 0;
88 static void LockServer(bool doLock)
90 if (doLock)
92 InterlockedIncrement(&g_cServerLocks);
93 return;
95 long newLockCount = InterlockedDecrement(&g_cServerLocks);
96 if (newLockCount == 0)
97 ::PostMessage(nullptr, WM_QUIT, 0, 0);
101 // Constructor
103 GitWCRev::GitWCRev()
104 : m_cRef(1)
105 , GitStat()
106 , m_ptinfo(nullptr)
108 LockServer(true);
109 LoadTypeInfo(&m_ptinfo, LIBID_LibGitWCRev, IID_IGitWCRev, 0);
113 // Destructor
115 GitWCRev::~GitWCRev()
117 LockServer(false);
118 if (m_ptinfo)
119 m_ptinfo->Release();
123 // IUnknown implementation
125 HRESULT __stdcall GitWCRev::QueryInterface(const IID& iid, void** ppv)
127 if (!ppv)
128 return E_POINTER;
130 if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IGitWCRev) || IsEqualIID(iid, IID_IDispatch))
131 *ppv = static_cast<IGitWCRev*>(this) ;
132 else
134 *ppv = nullptr;
135 return E_NOINTERFACE ;
137 AddRef();
138 return S_OK ;
141 ULONG __stdcall GitWCRev::AddRef()
143 return InterlockedIncrement(&m_cRef) ;
146 ULONG __stdcall GitWCRev::Release()
148 const LONG refCount = InterlockedDecrement(&m_cRef);
149 if (refCount == 0)
150 delete this;
151 return refCount;
154 HRESULT GitWCRev::GetWCInfoInternal(/*[in]*/ BSTR wcPath, /*[in]*/VARIANT_BOOL ignore_submodules)
156 GitStat.bNoSubmodules = ignore_submodules;
158 // clear all possible previous data
159 GitStat.bHasSubmodule = FALSE;
160 GitStat.bHasSubmoduleMods = FALSE;
161 GitStat.bHasSubmoduleNewCommits = FALSE;
162 GitStat.bHasSubmoduleUnversioned = FALSE;
163 GitStat.HeadHash[0] = '\0';
164 GitStat.HeadHashReadable[0] = '\0';
165 GitStat.HasMods = FALSE;
166 GitStat.HasUnversioned = FALSE;
167 GitStat.bIsGitItem = FALSE;
168 GitStat.bIsTagged = FALSE;
169 GitStat.bIsUnborn = FALSE;
170 GitStat.HeadTime = 0;
171 GitStat.HeadAuthor.clear();
172 GitStat.HeadEmail.clear();
173 GitStat.ignorepatterns.clear();
175 if (!wcPath || wcPath[0] == L'\0')
176 return S_FALSE;
177 if (!PathFileExists(wcPath))
178 return S_FALSE;
180 if (GetStatusUnCleanPath(wcPath, GitStat) == 0)
181 return S_OK;
183 return S_FALSE;
187 // IGitWCRev implementation
189 HRESULT __stdcall GitWCRev::GetWCInfo(/*[in]*/ BSTR wcPath, /*[in]*/VARIANT_BOOL include_submodules)
191 if (!wcPath)
192 return E_INVALIDARG;
194 return GetWCInfoInternal(wcPath, include_submodules);
197 HRESULT __stdcall GitWCRev::get_Revision(/*[out, retval]*/VARIANT* rev)
199 return Utf8StringToVariant(GitStat.HeadHashReadable, rev);
202 HRESULT __stdcall GitWCRev::get_Branch(/*[out, retval]*/VARIANT* branch)
204 return Utf8StringToVariant(GitStat.CurrentBranch.c_str(), branch);
207 HRESULT __stdcall GitWCRev::get_Date(/*[out, retval]*/VARIANT* date)
209 if (!date)
210 return E_POINTER;
212 date->vt = VT_BSTR;
214 WCHAR destbuf[32] = { 0 };
215 HRESULT result = CopyDateToString(destbuf, _countof(destbuf), GitStat.HeadTime) ? S_OK : S_FALSE;
216 if (S_FALSE == result)
217 swprintf_s(destbuf, L"");
219 date->bstrVal = SysAllocStringLen(destbuf, (UINT)wcslen(destbuf));
220 return result;
223 HRESULT __stdcall GitWCRev::get_Author(/*[out, retval]*/VARIANT* author)
225 return Utf8StringToVariant(GitStat.HeadAuthor.c_str(), author);
228 HRESULT GitWCRev::Utf8StringToVariant(const char* string, VARIANT* result )
230 if (!result)
231 return E_POINTER;
233 result->vt = VT_BSTR;
234 const size_t len = strlen(string);
235 auto buf = std::make_unique<WCHAR[]>(len * 4 + 1);
236 SecureZeroMemory(buf.get(), (len * 4 + 1) * sizeof(WCHAR));
237 MultiByteToWideChar(CP_UTF8, 0, string, -1, buf.get(), (int)len * 4);
238 result->bstrVal = SysAllocString(buf.get());
239 return S_OK;
242 HRESULT __stdcall GitWCRev::get_HasModifications(VARIANT_BOOL* modifications)
244 return BoolToVariantBool(GitStat.HasMods, modifications);
247 HRESULT __stdcall GitWCRev::get_HasUnversioned(VARIANT_BOOL* modifications)
249 return BoolToVariantBool(GitStat.HasUnversioned, modifications);
252 HRESULT __stdcall GitWCRev::get_IsWcTagged(VARIANT_BOOL* modifications)
254 return BoolToVariantBool(GitStat.bIsTagged, modifications);
257 HRESULT __stdcall GitWCRev::get_IsGitItem(/*[out, retval]*/VARIANT_BOOL* item)
259 return BoolToVariantBool(GitStat.bIsGitItem, item);
262 HRESULT __stdcall GitWCRev::get_IsUnborn(/*[out, retval]*/VARIANT_BOOL* unborn)
264 return BoolToVariantBool(GitStat.bIsUnborn, unborn);
267 HRESULT __stdcall GitWCRev::get_HasSubmodule(/*[out, retval]*/VARIANT_BOOL* has_submodule)
269 return BoolToVariantBool(GitStat.bHasSubmodule, has_submodule);
272 HRESULT __stdcall GitWCRev::get_HasSubmoduleModifications(/*[out, retval]*/VARIANT_BOOL* modifications)
274 return BoolToVariantBool(GitStat.bHasSubmoduleMods, modifications);
277 HRESULT __stdcall GitWCRev::get_HasSubmoduleUnversioned(/*[out, retval]*/VARIANT_BOOL* unversioned)
279 return BoolToVariantBool(GitStat.bHasSubmoduleUnversioned, unversioned);
282 HRESULT __stdcall GitWCRev::get_IsSubmoduleUp2Date(/*[out, retval]*/VARIANT_BOOL* up2date)
284 return BoolToVariantBool(GitStat.bHasSubmoduleNewCommits, up2date);
287 HRESULT __stdcall GitWCRev::get_CommitCount(/*[out, retval]*/VARIANT* rev)
289 return LongToVariant((LONG)GitStat.NumCommits, rev);
292 HRESULT GitWCRev::BoolToVariantBool(BOOL value, VARIANT_BOOL* result)
294 if (!result)
295 return E_POINTER;
296 *result = (value == 0) ? VARIANT_FALSE : VARIANT_TRUE;
297 return S_OK;
300 HRESULT GitWCRev::LongToVariant(LONG value, VARIANT* result)
302 if (!result)
303 return E_POINTER;
305 result->vt = VT_I4;
306 result->lVal = value;
307 return S_OK;
311 // Get a readable string from a __time64_t date
313 BOOL GitWCRev::CopyDateToString(WCHAR* destbuf, int buflen, __time64_t ttime)
315 const int min_buflen = 32;
316 if (!destbuf || (min_buflen > buflen))
317 return FALSE;
319 struct tm newtime;
320 if (_localtime64_s(&newtime, &ttime))
321 return FALSE;
322 // Format the date/time in international format as yyyy/mm/dd hh:mm:ss
323 swprintf_s(destbuf, min_buflen, L"%04d/%02d/%02d %02d:%02d:%02d", newtime.tm_year + 1900, newtime.tm_mon + 1, newtime.tm_mday, newtime.tm_hour, newtime.tm_min, newtime.tm_sec);
324 return TRUE;
327 HRESULT GitWCRev::LoadTypeInfo(ITypeInfo** pptinfo, const CLSID& libid, const CLSID& iid, LCID lcid)
329 if (!pptinfo)
330 return E_POINTER;
331 *pptinfo = nullptr;
333 // Load type library.
334 CComPtr<ITypeLib> ptlib;
335 HRESULT hr = LoadRegTypeLib(libid, 1, 0, lcid, &ptlib);
336 if (FAILED(hr))
337 return hr;
339 // Get type information for interface of the object.
340 LPTYPEINFO ptinfo = nullptr;
341 hr = ptlib->GetTypeInfoOfGuid(iid, &ptinfo);
342 if (FAILED(hr))
343 return hr;
345 *pptinfo = ptinfo;
346 return S_OK;
349 HRESULT __stdcall GitWCRev::GetTypeInfoCount(UINT* pctinfo)
351 if (!pctinfo)
352 return E_POINTER;
354 *pctinfo = 1;
355 return S_OK;
358 HRESULT __stdcall GitWCRev::GetTypeInfo(UINT itinfo, LCID /*lcid*/, ITypeInfo** pptinfo)
360 if (!pptinfo)
361 return E_POINTER;
363 *pptinfo = nullptr;
365 if (itinfo)
366 return ResultFromScode(DISP_E_BADINDEX);
368 if (!m_ptinfo)
369 return E_UNEXPECTED;
371 m_ptinfo->AddRef(); // AddRef and return pointer to cached
372 // typeinfo for this object.
373 *pptinfo = m_ptinfo;
375 return S_OK;
378 HRESULT __stdcall GitWCRev::GetIDsOfNames(REFIID /*riid*/, LPOLESTR* rgszNames, UINT cNames, LCID /*lcid*/, DISPID* rgdispid)
380 return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);
383 HRESULT __stdcall GitWCRev::Invoke(DISPID dispidMember, REFIID /*riid*/, LCID /*lcid*/, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
385 return DispInvoke(this, m_ptinfo, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
389 // Class factory IUnknown implementation
391 HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
393 if (!ppv)
394 return E_POINTER;
396 if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IClassFactory))
397 *ppv = static_cast<IClassFactory*>(this) ;
398 else
400 *ppv = nullptr;
401 return E_NOINTERFACE ;
403 AddRef();
404 return S_OK ;
407 ULONG __stdcall CFactory::AddRef()
409 // No true reference counting in global object
410 return 1;
413 ULONG __stdcall CFactory::Release()
415 // No true reference counting in global object
416 return 1;
420 // IClassFactory implementation
422 HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv)
424 if (!ppv)
425 return E_POINTER;
427 // Cannot aggregate.
428 if (pUnknownOuter)
429 return CLASS_E_NOAGGREGATION ;
431 // Create component.
432 ATL::CComPtr<GitWCRev> pA;
433 pA.Attach(new (std::nothrow) GitWCRev()); // refcount set to 1 in constructor
434 if (!pA)
435 return E_OUTOFMEMORY ;
437 return pA->QueryInterface(iid, ppv);
440 // LockServer
441 HRESULT __stdcall CFactory::LockServer(BOOL bLock)
443 ::LockServer(bLock != 0);
444 return S_OK ;
447 // Refcount set to 1 in constructor.
448 CFactory gClassFactory;
450 static void RunOutprocServer()
452 // register ourself as a class object against the internal COM table
453 DWORD nToken = 0;
454 HRESULT hr = ::CoRegisterClassObject(CLSID_GitWCRev, &gClassFactory, CLSCTX_SERVER, REGCLS_MULTIPLEUSE, &nToken);
456 // If the class factory is not registered no object will ever be instantiated,
457 // hence no object will ever be released, hence WM_QUIT never appears in the
458 // message queue and the message loop below will run forever.
459 if (FAILED(hr))
460 return;
463 // (loop ends if WM_QUIT message is received)
465 MSG msg;
466 while (GetMessage(&msg, 0, 0, 0) > 0)
468 TranslateMessage(&msg);
469 DispatchMessage(&msg);
472 // unregister from the known table of class objects
473 ::CoRevokeClassObject(nToken);
477 // Server registration
479 STDAPI DllRegisterServer()
481 const HMODULE hModule = ::GetModuleHandle(nullptr);
483 HRESULT hr = RegisterServer(hModule,
484 CLSID_GitWCRev,
485 L"GitWCRev Server Object",
486 L"GitWCRev.object",
487 L"GitWCRev.object.1",
488 LIBID_LibGitWCRev) ;
489 if (SUCCEEDED(hr))
490 RegisterTypeLib(hModule, nullptr);
491 return hr;
495 // Server unregistration
497 STDAPI DllUnregisterServer()
499 const HMODULE hModule = ::GetModuleHandle(nullptr);
501 HRESULT hr = UnregisterServer(CLSID_GitWCRev,
502 L"GitWCRev.object",
503 L"GitWCRev.object.1",
504 LIBID_LibGitWCRev) ;
505 if (SUCCEEDED(hr))
506 UnRegisterTypeLib(hModule, nullptr);
507 return hr;