3 #include "git_shell_ext.h"
5 static DWORD object_count
= 0, lock_count
= 0;
7 static void debug(char *format
, ...)
12 va_start(params
, format
);
13 vsprintf(buffer
, format
, 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) {
34 InterlockedDecrement(&object_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
)) {
47 } else if (IsEqualIID(iid
, &IID_git_menu
) ||
48 IsEqualIID(iid
, &IID_IContextMenu
)) {
49 *pointer
= (void *)(((void **)this_
) + 1);
53 add_ref_git_data(this_
);
57 static inline STDMETHODIMP
initialize_git_data(struct git_data
*this_
,
58 LPCITEMIDLIST folder
, LPDATAOBJECT data
, HKEY id
)
61 = {CF_HDROP
, NULL
, DVASPECT_CONTENT
, -1, TYMED_HGLOBAL
};
62 STGMEDIUM stg
= {TYMED_HGLOBAL
};
65 HRESULT result
= S_OK
;
67 if (FAILED(data
->lpVtbl
->GetData(data
, &format
, &stg
)))
70 drop
= (HDROP
)GlobalLock(stg
.hGlobal
);
75 count
= DragQueryFile(drop
, 0xFFFFFFFF, NULL
, 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
);
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); \
94 static ULONG STDMETHODCALLTYPE \
95 release_##name(void *p) { \
96 struct name *this_ = p; \
97 return release_git_data(this_->git_data); \
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, \
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)
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"),
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
;
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
);
182 lstrcpynA(name
, text
, size
);
191 DEFINE_STANDARD_METHODS(git_menu
)
193 struct git_menu_virtual_table git_menu_virtual_table
= {
194 query_interface_git_menu
,
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,
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
)) {
218 return E_NOINTERFACE
;
225 static ULONG STDMETHODCALLTYPE
return_one(IClassFactory
*this)
230 static STDMETHODIMP
create_instance(IClassFactory
*this_
,
231 IUnknown
*outer
, REFIID guid
, void **pointer
)
234 struct git_data
*data
;
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
);
251 InterlockedIncrement(&object_count
);
255 static STDMETHODIMP
lock_server(IClassFactory
*this, BOOL lock
)
258 InterlockedIncrement(&lock_count
);
260 InterlockedDecrement(&lock_count
);
265 static IClassFactoryVtbl factory_virtual_table
= {
266 class_factory_query_interface
,
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
);
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
);
319 HRESULT PASCAL
DllUnregisterServer(void)
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
);