Initial revision
[git-cheetah.git] / git_shell_ext.c
blobcb4796517090c7ef952e034398294b3253a1d426
1 #include <stdarg.h>
2 #include <tchar.h>
3 #include "git_shell_ext.h"
5 static DWORD object_count = 0, lock_count = 0;
7 static void debug(char *format, ...)
9 va_list params;
10 char buffer[1024];
12 va_start(params, format);
13 vsprintf(buffer, format, params);
14 va_end(params);
15 MessageBox(0, buffer, "Hello", MB_OK|MB_ICONEXCLAMATION);
19 * Standard methods for the IUnknown interface:
20 * add_ref, release, query_interface and initialize.
22 * Both of our COM objects contain pointers to the git_data object.
25 static inline ULONG STDMETHODCALLTYPE add_ref_git_data(struct git_data *this_)
27 return ++(this_->count);
30 static inline ULONG STDMETHODCALLTYPE release_git_data(struct git_data *this_)
32 if (--(this_->count) == 0) {
33 GlobalFree(this_);
34 InterlockedDecrement(&object_count);
35 return 0;
37 return this_->count;
40 static inline STDMETHODIMP query_interface_git_data(struct git_data *this_,
41 REFIID iid, LPVOID FAR *pointer)
43 if (IsEqualIID(iid, &IID_git_shell_ext) ||
44 IsEqualIID(iid, &IID_IShellExtInit) ||
45 IsEqualIID(iid, &IID_IUnknown)) {
46 *pointer = this_;
47 } else if (IsEqualIID(iid, &IID_git_menu) ||
48 IsEqualIID(iid, &IID_IContextMenu)) {
49 *pointer = (void *)(((void **)this_) + 1);
50 } else
51 return E_NOINTERFACE;
53 add_ref_git_data(this_);
54 return NOERROR;
57 static inline STDMETHODIMP initialize_git_data(struct git_data *this_,
58 LPCITEMIDLIST folder, LPDATAOBJECT data, HKEY id)
60 FORMATETC format
61 = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
62 STGMEDIUM stg = {TYMED_HGLOBAL};
63 HDROP drop;
64 UINT count;
65 HRESULT result = S_OK;
67 if (FAILED(data->lpVtbl->GetData(data, &format, &stg)))
68 return E_INVALIDARG;
70 drop = (HDROP)GlobalLock(stg.hGlobal);
72 if (!drop)
73 return E_INVALIDARG;
75 count = DragQueryFile(drop, 0xFFFFFFFF, NULL, 0);
77 if (count == 0)
78 result = E_INVALIDARG;
79 else if (!DragQueryFile(drop, 0, this_->name, sizeof(this_->name)))
80 result = E_INVALIDARG;
82 GlobalUnlock(stg.hGlobal);
83 ReleaseStgMedium(&stg);
85 return result;
88 #define DEFINE_STANDARD_METHODS(name) \
89 static ULONG STDMETHODCALLTYPE add_ref_##name(void *p) { \
90 struct name *this_ = p; \
91 return add_ref_git_data(this_->git_data); \
92 } \
94 static ULONG STDMETHODCALLTYPE \
95 release_##name(void *p) { \
96 struct name *this_ = p; \
97 return release_git_data(this_->git_data); \
98 } \
100 static STDMETHODIMP query_interface_##name(void *p, \
101 REFIID iid, LPVOID FAR *pointer) { \
102 struct name *this_ = p; \
103 return query_interface_git_data(this_->git_data, \
104 iid, pointer); \
107 static STDMETHODIMP initialize_##name(void *p, \
108 LPCITEMIDLIST folder, LPDATAOBJECT data, HKEY id) { \
109 struct name *this_ = p; \
110 return initialize_git_data(this_->git_data, folder, data, id); \
114 * Define the shell extension.
116 * Its sole purpose is to be able to query_interface() the context menu.
118 DEFINE_STANDARD_METHODS(git_shell_ext)
120 struct git_shell_ext_virtual_table git_shell_ext_virtual_table = {
121 query_interface_git_shell_ext,
122 add_ref_git_shell_ext,
123 release_git_shell_ext,
124 initialize_git_shell_ext
128 * These are the functions for handling the context menu.
131 static STDMETHODIMP query_context_menu(void *p, HMENU menu,
132 UINT index, UINT first_command, UINT last_command, UINT flags)
134 struct git_menu *this_menu = p;
135 struct git_data *this_ = this_menu->git_data;
136 if (flags & CMF_DEFAULTONLY)
137 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
139 InsertMenu(menu, index, MF_STRING | MF_BYPOSITION,
140 first_command, _T("SimpleShlExt Test Item"));
142 return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
145 static STDMETHODIMP invoke_command(void *p,
146 LPCMINVOKECOMMANDINFO info)
148 struct git_menu *this_menu = p;
149 struct git_data *this_ = this_menu->git_data;
150 int command = LOWORD(info->lpVerb);
151 if (HIWORD(info->lpVerb) != 0)
152 return E_INVALIDARG;
154 if (command == 0) {
155 TCHAR msg[MAX_PATH + 32];
157 wsprintf(msg, _T("The selected file was:\n\n%s"), this_->name);
158 MessageBox(info->hwnd, msg, _T("SimpleShlExt"),
159 MB_ICONINFORMATION);
161 return S_OK;
164 return E_INVALIDARG;
167 static STDMETHODIMP get_command_string(void *p, UINT id,
168 UINT flags, UINT *reserved, LPSTR name, UINT size)
170 struct git_menu *this_menu = p;
171 struct git_data *this_ = this_menu->git_data;
172 if (id == 0)
173 return E_INVALIDARG;
175 #if 0
176 if (flags & GCS_HELPTEXT) {
177 LPCTSTR text = _T("This is the simple shell extension's help");
179 if (flags & GCS_UNICODE)
180 lstrcpynW((LPWSTR)name, mbrtowc(text), size);
181 else
182 lstrcpynA(name, text, size);
184 return S_OK;
186 #endif
188 return E_INVALIDARG;
191 DEFINE_STANDARD_METHODS(git_menu)
193 struct git_menu_virtual_table git_menu_virtual_table = {
194 query_interface_git_menu,
195 add_ref_git_menu,
196 release_git_menu,
197 query_context_menu,
198 invoke_command,
199 get_command_string
203 * Since COM objects cannot be constructed like your traditional object (i.e.
204 * with a proper constructor), they have to be constructed by another object,
205 * the class factory.
207 * The class factory is an object which exists exactly once, and it cannot
208 * be constructed or destroyed. Its sole purpose is to construct objects
209 * given an interface.
212 static STDMETHODIMP class_factory_query_interface(IClassFactory *this,
213 REFIID guid, void **pointer)
215 if (!IsEqualIID(guid, &IID_IUnknown) &&
216 !IsEqualIID(guid, &IID_IClassFactory)) {
217 *pointer = 0;
218 return E_NOINTERFACE;
221 *pointer = this;
222 return NOERROR;
225 static ULONG STDMETHODCALLTYPE return_one(IClassFactory *this)
227 return(1);
230 static STDMETHODIMP create_instance(IClassFactory *this_,
231 IUnknown *outer, REFIID guid, void **pointer)
233 HRESULT result;
234 struct git_data *data;
236 *pointer = 0;
238 if (outer)
239 return CLASS_E_NOAGGREGATION;
241 if (!(data = GlobalAlloc(GMEM_FIXED, sizeof(struct git_data))))
242 return E_OUTOFMEMORY;
243 memset(data, 0, sizeof(struct git_data));
245 data->shell_ext.virtual_table = &git_shell_ext_virtual_table;
246 data->menu.virtual_table = &git_menu_virtual_table;
247 data->shell_ext.git_data = data->menu.git_data = data;
249 result = query_interface_git_data(data, guid, pointer);
250 if (!result)
251 InterlockedIncrement(&object_count);
252 return result;
255 static STDMETHODIMP lock_server(IClassFactory *this, BOOL lock)
257 if (lock)
258 InterlockedIncrement(&lock_count);
259 else
260 InterlockedDecrement(&lock_count);
262 return NOERROR;
265 static IClassFactoryVtbl factory_virtual_table = {
266 class_factory_query_interface,
267 return_one,
268 return_one,
269 create_instance,
270 lock_server
273 static IClassFactory factory = {
274 &factory_virtual_table
278 * The following is just the necessary infrastructure for having a .dll
279 * which can be registered as a COM object.
282 HRESULT PASCAL DllGetClassObject(REFCLSID obj_guid, REFIID factory_guid,
283 void **factory_handle)
285 if (IsEqualCLSID(obj_guid, &CLSID_git_shell_ext) ||
286 IsEqualCLSID(obj_guid, &CLSID_git_menu))
287 return class_factory_query_interface(&factory,
288 factory_guid, factory_handle);
290 *factory_handle = 0;
291 return CLASS_E_CLASSNOTAVAILABLE;
294 HRESULT PASCAL DllCanUnloadNow(void)
296 return (object_count || lock_count) ? S_FALSE : S_OK;
299 const char *program_name = "Hare GIT";
300 const char *program_version = "HareGIT.Application.1";
301 const char *program_id = "HareGIT.Application";
303 HRESULT PASCAL DllRegisterServer(void)
305 char module[MAX_PATH];
306 wchar_t module_name[MAX_PATH];
307 ITypeLib *typelib = NULL;
309 GetModuleFileName(NULL, module, MAX_PATH);
310 MultiByteToWideChar(CP_ACP, 0, module, -1, module_name, MAX_PATH);
311 if (LoadTypeLib(module_name, &typelib) == S_OK) {
312 HRESULT result = RegisterTypeLib(typelib, module_name, NULL);
313 typelib->lpVtbl->Release(typelib);
314 return result;
316 return 1;
319 HRESULT PASCAL DllUnregisterServer(void)
321 return S_OK;
324 BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
326 if (reason == DLL_PROCESS_ATTACH) {
327 object_count = lock_count = 0;
328 DisableThreadLibraryCalls(instance);
331 return 1;