Drop unused declarations from gitdll.h
[TortoiseGit.git] / src / GitWCRev / GitWCRevCOM.cpp
blob6c9cbb301957944b4da426d448093be1aeb75da9
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2017 - 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 (GetStatus(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_Date(/*[out, retval]*/VARIANT* date)
204 if (!date)
205 return E_POINTER;
207 date->vt = VT_BSTR;
209 WCHAR destbuf[32] = { 0 };
210 HRESULT result = CopyDateToString(destbuf, _countof(destbuf), GitStat.HeadTime) ? S_OK : S_FALSE;
211 if (S_FALSE == result)
212 swprintf_s(destbuf, L"");
214 date->bstrVal = SysAllocStringLen(destbuf, (UINT)wcslen(destbuf));
215 return result;
218 HRESULT __stdcall GitWCRev::get_Author(/*[out, retval]*/VARIANT* author)
220 return Utf8StringToVariant(GitStat.HeadAuthor.c_str(), author);
223 HRESULT GitWCRev::Utf8StringToVariant(const char* string, VARIANT* result )
225 if (!result)
226 return E_POINTER;
228 result->vt = VT_BSTR;
229 const size_t len = strlen(string);
230 auto buf = std::make_unique<WCHAR[]>(len * 4 + 1);
231 SecureZeroMemory(buf.get(), (len * 4 + 1) * sizeof(WCHAR));
232 MultiByteToWideChar(CP_UTF8, 0, string, -1, buf.get(), (int)len * 4);
233 result->bstrVal = SysAllocString(buf.get());
234 return S_OK;
237 HRESULT __stdcall GitWCRev::get_HasModifications(VARIANT_BOOL* modifications)
239 return BoolToVariantBool(GitStat.HasMods, modifications);
242 HRESULT __stdcall GitWCRev::get_HasUnversioned(VARIANT_BOOL* modifications)
244 return BoolToVariantBool(GitStat.HasUnversioned, modifications);
247 HRESULT __stdcall GitWCRev::get_IsWcTagged(VARIANT_BOOL* modifications)
249 return BoolToVariantBool(GitStat.bIsTagged, modifications);
252 HRESULT __stdcall GitWCRev::get_IsGitItem(/*[out, retval]*/VARIANT_BOOL* item)
254 return BoolToVariantBool(GitStat.bIsGitItem, item);
257 HRESULT __stdcall GitWCRev::get_IsUnborn(/*[out, retval]*/VARIANT_BOOL* unborn)
259 return BoolToVariantBool(GitStat.bIsUnborn, unborn);
262 HRESULT __stdcall GitWCRev::get_HasSubmodule(/*[out, retval]*/VARIANT_BOOL* has_submodule)
264 return BoolToVariantBool(GitStat.bHasSubmodule, has_submodule);
267 HRESULT __stdcall GitWCRev::get_HasSubmoduleModifications(/*[out, retval]*/VARIANT_BOOL* modifications)
269 return BoolToVariantBool(GitStat.bHasSubmoduleMods, modifications);
272 HRESULT __stdcall GitWCRev::get_HasSubmoduleUnversioned(/*[out, retval]*/VARIANT_BOOL* unversioned)
274 return BoolToVariantBool(GitStat.bHasSubmoduleUnversioned, unversioned);
277 HRESULT __stdcall GitWCRev::get_IsSubmoduleUp2Date(/*[out, retval]*/VARIANT_BOOL* up2date)
279 return BoolToVariantBool(GitStat.bHasSubmoduleNewCommits, up2date);
282 HRESULT GitWCRev::BoolToVariantBool(BOOL value, VARIANT_BOOL* result)
284 if (!result)
285 return E_POINTER;
286 *result = (value == 0) ? VARIANT_FALSE : VARIANT_TRUE;
287 return S_OK;
291 // Get a readable string from a __time64_t date
293 BOOL GitWCRev::CopyDateToString(WCHAR* destbuf, int buflen, __time64_t ttime)
295 const int min_buflen = 32;
296 if (!destbuf || (min_buflen > buflen))
297 return FALSE;
299 struct tm newtime;
300 if (_localtime64_s(&newtime, &ttime))
301 return FALSE;
302 // Format the date/time in international format as yyyy/mm/dd hh:mm:ss
303 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);
304 return TRUE;
307 HRESULT GitWCRev::LoadTypeInfo(ITypeInfo** pptinfo, const CLSID& libid, const CLSID& iid, LCID lcid)
309 if (!pptinfo)
310 return E_POINTER;
311 *pptinfo = nullptr;
313 // Load type library.
314 CComPtr<ITypeLib> ptlib;
315 HRESULT hr = LoadRegTypeLib(libid, 1, 0, lcid, &ptlib);
316 if (FAILED(hr))
317 return hr;
319 // Get type information for interface of the object.
320 LPTYPEINFO ptinfo = nullptr;
321 hr = ptlib->GetTypeInfoOfGuid(iid, &ptinfo);
322 if (FAILED(hr))
323 return hr;
325 *pptinfo = ptinfo;
326 return S_OK;
329 HRESULT __stdcall GitWCRev::GetTypeInfoCount(UINT* pctinfo)
331 if (!pctinfo)
332 return E_POINTER;
334 *pctinfo = 1;
335 return S_OK;
338 HRESULT __stdcall GitWCRev::GetTypeInfo(UINT itinfo, LCID /*lcid*/, ITypeInfo** pptinfo)
340 if (!pptinfo)
341 return E_POINTER;
343 *pptinfo = nullptr;
345 if (itinfo)
346 return ResultFromScode(DISP_E_BADINDEX);
348 if (!m_ptinfo)
349 return E_UNEXPECTED;
351 m_ptinfo->AddRef(); // AddRef and return pointer to cached
352 // typeinfo for this object.
353 *pptinfo = m_ptinfo;
355 return S_OK;
358 HRESULT __stdcall GitWCRev::GetIDsOfNames(REFIID /*riid*/, LPOLESTR* rgszNames, UINT cNames, LCID /*lcid*/, DISPID* rgdispid)
360 return DispGetIDsOfNames(m_ptinfo, rgszNames, cNames, rgdispid);
363 HRESULT __stdcall GitWCRev::Invoke(DISPID dispidMember, REFIID /*riid*/, LCID /*lcid*/, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexcepinfo, UINT* puArgErr)
365 return DispInvoke(this, m_ptinfo, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
369 // Class factory IUnknown implementation
371 HRESULT __stdcall CFactory::QueryInterface(const IID& iid, void** ppv)
373 if (!ppv)
374 return E_POINTER;
376 if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IClassFactory))
377 *ppv = static_cast<IClassFactory*>(this) ;
378 else
380 *ppv = nullptr;
381 return E_NOINTERFACE ;
383 AddRef();
384 return S_OK ;
387 ULONG __stdcall CFactory::AddRef()
389 // No true reference counting in global object
390 return 1;
393 ULONG __stdcall CFactory::Release()
395 // No true reference counting in global object
396 return 1;
400 // IClassFactory implementation
402 HRESULT __stdcall CFactory::CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv)
404 if (!ppv)
405 return E_POINTER;
407 // Cannot aggregate.
408 if (pUnknownOuter)
409 return CLASS_E_NOAGGREGATION ;
411 // Create component.
412 ATL::CComPtr<GitWCRev> pA;
413 pA.Attach(new (std::nothrow) GitWCRev()); // refcount set to 1 in constructor
414 if (!pA)
415 return E_OUTOFMEMORY ;
417 return pA->QueryInterface(iid, ppv);
420 // LockServer
421 HRESULT __stdcall CFactory::LockServer(BOOL bLock)
423 ::LockServer(bLock != 0);
424 return S_OK ;
427 // Refcount set to 1 in constructor.
428 CFactory gClassFactory;
430 static void RunOutprocServer()
432 // register ourself as a class object against the internal COM table
433 DWORD nToken = 0;
434 HRESULT hr = ::CoRegisterClassObject(CLSID_GitWCRev, &gClassFactory, CLSCTX_SERVER, REGCLS_MULTIPLEUSE, &nToken);
436 // If the class factory is not registered no object will ever be instantiated,
437 // hence no object will ever be released, hence WM_QUIT never appears in the
438 // message queue and the message loop below will run forever.
439 if (FAILED(hr))
440 return;
443 // (loop ends if WM_QUIT message is received)
445 MSG msg;
446 while (GetMessage(&msg, 0, 0, 0) > 0)
448 TranslateMessage(&msg);
449 DispatchMessage(&msg);
452 // unregister from the known table of class objects
453 ::CoRevokeClassObject(nToken);
457 // Server registration
459 STDAPI DllRegisterServer()
461 const HMODULE hModule = ::GetModuleHandle(nullptr);
463 HRESULT hr = RegisterServer(hModule,
464 CLSID_GitWCRev,
465 L"GitWCRev Server Object",
466 L"GitWCRev.object",
467 L"GitWCRev.object.1",
468 LIBID_LibGitWCRev) ;
469 if (SUCCEEDED(hr))
470 RegisterTypeLib(hModule, nullptr);
471 return hr;
475 // Server unregistration
477 STDAPI DllUnregisterServer()
479 const HMODULE hModule = ::GetModuleHandle(nullptr);
481 HRESULT hr = UnregisterServer(CLSID_GitWCRev,
482 L"GitWCRev.object",
483 L"GitWCRev.object.1",
484 LIBID_LibGitWCRev) ;
485 if (SUCCEEDED(hr))
486 UnRegisterTypeLib(hModule, nullptr);
487 return hr;