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.
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 (GetStatus(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_Date(/*[out, retval]*/VARIANT
* date
)
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
));
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
)
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());
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
)
286 *result
= (value
== 0) ? VARIANT_FALSE
: VARIANT_TRUE
;
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
))
300 if (_localtime64_s(&newtime
, &ttime
))
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
);
307 HRESULT
GitWCRev::LoadTypeInfo(ITypeInfo
** pptinfo
, const CLSID
& libid
, const CLSID
& iid
, LCID lcid
)
313 // Load type library.
314 CComPtr
<ITypeLib
> ptlib
;
315 HRESULT hr
= LoadRegTypeLib(libid
, 1, 0, lcid
, &ptlib
);
319 // Get type information for interface of the object.
320 LPTYPEINFO ptinfo
= nullptr;
321 hr
= ptlib
->GetTypeInfoOfGuid(iid
, &ptinfo
);
329 HRESULT __stdcall
GitWCRev::GetTypeInfoCount(UINT
* pctinfo
)
338 HRESULT __stdcall
GitWCRev::GetTypeInfo(UINT itinfo
, LCID
/*lcid*/, ITypeInfo
** pptinfo
)
346 return ResultFromScode(DISP_E_BADINDEX
);
351 m_ptinfo
->AddRef(); // AddRef and return pointer to cached
352 // typeinfo for this object.
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
)
376 if (IsEqualIID(iid
, IID_IUnknown
) || IsEqualIID(iid
, IID_IClassFactory
))
377 *ppv
= static_cast<IClassFactory
*>(this) ;
381 return E_NOINTERFACE
;
387 ULONG __stdcall
CFactory::AddRef()
389 // No true reference counting in global object
393 ULONG __stdcall
CFactory::Release()
395 // No true reference counting in global object
400 // IClassFactory implementation
402 HRESULT __stdcall
CFactory::CreateInstance(IUnknown
* pUnknownOuter
, const IID
& iid
, void** ppv
)
409 return CLASS_E_NOAGGREGATION
;
412 ATL::CComPtr
<GitWCRev
> pA
;
413 pA
.Attach(new (std::nothrow
) GitWCRev()); // refcount set to 1 in constructor
415 return E_OUTOFMEMORY
;
417 return pA
->QueryInterface(iid
, ppv
);
421 HRESULT __stdcall
CFactory::LockServer(BOOL bLock
)
423 ::LockServer(bLock
!= 0);
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
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.
443 // (loop ends if WM_QUIT message is received)
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
,
465 L
"GitWCRev Server Object",
467 L
"GitWCRev.object.1",
470 RegisterTypeLib(hModule
, nullptr);
475 // Server unregistration
477 STDAPI
DllUnregisterServer()
479 const HMODULE hModule
= ::GetModuleHandle(nullptr);
481 HRESULT hr
= UnregisterServer(CLSID_GitWCRev
,
483 L
"GitWCRev.object.1",
486 UnRegisterTypeLib(hModule
, nullptr);