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.
22 #include "GitWCRevCOM_h.h"
23 #include "GitWCRevCOM_i.c"
24 #include "GitWCRevCOM.h"
30 #include "UnicodeUtils.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*/,
48 static void ImplWinMain()
51 LPWSTR
* argv
= CommandLineToArgvW(GetCommandLine(), &argc
);
55 if ((argc
>= 2) && (argc
<= 5))
57 if (wcscmp(argv
[1], L
"/automation") == 0)
59 else if (wcscmp(argv
[1], L
"unregserver") == 0)
60 DllUnregisterServer();
61 else if (wcscmp(argv
[1], L
"regserver") == 0)
67 static void AutomationMain()
69 // initialize the COM library
70 HRESULT hr
= ::CoInitialize(nullptr);
78 git_libgit2_shutdown();
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
)
92 InterlockedIncrement(&g_cServerLocks
);
95 long newLockCount
= InterlockedDecrement(&g_cServerLocks
);
96 if (newLockCount
== 0)
97 ::PostMessage(nullptr, WM_QUIT
, 0, 0);
109 LoadTypeInfo(&m_ptinfo
, LIBID_LibGitWCRev
, IID_IGitWCRev
, 0);
115 GitWCRev::~GitWCRev()
123 // IUnknown implementation
125 HRESULT __stdcall
GitWCRev::QueryInterface(const IID
& iid
, void** ppv
)
130 if (IsEqualIID(iid
, IID_IUnknown
) || IsEqualIID(iid
, IID_IGitWCRev
) || IsEqualIID(iid
, IID_IDispatch
))
131 *ppv
= static_cast<IGitWCRev
*>(this) ;
135 return E_NOINTERFACE
;
141 ULONG __stdcall
GitWCRev::AddRef()
143 return InterlockedIncrement(&m_cRef
) ;
146 ULONG __stdcall
GitWCRev::Release()
148 const LONG refCount
= InterlockedDecrement(&m_cRef
);
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')
177 if (!PathFileExists(wcPath
))
180 if (GetStatusUnCleanPath(wcPath
, GitStat
) == 0)
187 // IGitWCRev implementation
189 HRESULT __stdcall
GitWCRev::GetWCInfo(/*[in]*/ BSTR wcPath
, /*[in]*/VARIANT_BOOL include_submodules
)
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
)
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
));
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
)
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());
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
)
296 *result
= (value
== 0) ? VARIANT_FALSE
: VARIANT_TRUE
;
300 HRESULT
GitWCRev::LongToVariant(LONG value
, VARIANT
* result
)
306 result
->lVal
= value
;
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
))
320 if (_localtime64_s(&newtime
, &ttime
))
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
);
327 HRESULT
GitWCRev::LoadTypeInfo(ITypeInfo
** pptinfo
, const CLSID
& libid
, const CLSID
& iid
, LCID lcid
)
333 // Load type library.
334 CComPtr
<ITypeLib
> ptlib
;
335 HRESULT hr
= LoadRegTypeLib(libid
, 1, 0, lcid
, &ptlib
);
339 // Get type information for interface of the object.
340 LPTYPEINFO ptinfo
= nullptr;
341 hr
= ptlib
->GetTypeInfoOfGuid(iid
, &ptinfo
);
349 HRESULT __stdcall
GitWCRev::GetTypeInfoCount(UINT
* pctinfo
)
358 HRESULT __stdcall
GitWCRev::GetTypeInfo(UINT itinfo
, LCID
/*lcid*/, ITypeInfo
** pptinfo
)
366 return ResultFromScode(DISP_E_BADINDEX
);
371 m_ptinfo
->AddRef(); // AddRef and return pointer to cached
372 // typeinfo for this object.
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
)
396 if (IsEqualIID(iid
, IID_IUnknown
) || IsEqualIID(iid
, IID_IClassFactory
))
397 *ppv
= static_cast<IClassFactory
*>(this) ;
401 return E_NOINTERFACE
;
407 ULONG __stdcall
CFactory::AddRef()
409 // No true reference counting in global object
413 ULONG __stdcall
CFactory::Release()
415 // No true reference counting in global object
420 // IClassFactory implementation
422 HRESULT __stdcall
CFactory::CreateInstance(IUnknown
* pUnknownOuter
, const IID
& iid
, void** ppv
)
429 return CLASS_E_NOAGGREGATION
;
432 ATL::CComPtr
<GitWCRev
> pA
;
433 pA
.Attach(new (std::nothrow
) GitWCRev()); // refcount set to 1 in constructor
435 return E_OUTOFMEMORY
;
437 return pA
->QueryInterface(iid
, ppv
);
441 HRESULT __stdcall
CFactory::LockServer(BOOL bLock
)
443 ::LockServer(bLock
!= 0);
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
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.
463 // (loop ends if WM_QUIT message is received)
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
,
485 L
"GitWCRev Server Object",
487 L
"GitWCRev.object.1",
490 RegisterTypeLib(hModule
, nullptr);
495 // Server unregistration
497 STDAPI
DllUnregisterServer()
499 const HMODULE hModule
= ::GetModuleHandle(nullptr);
501 HRESULT hr
= UnregisterServer(CLSID_GitWCRev
,
503 L
"GitWCRev.object.1",
506 UnRegisterTypeLib(hModule
, nullptr);