mlNavigationHelper.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. #include "main.h"
  2. #include "./mlNavigationHelper.h"
  3. #include "./ifc_omcachemanager.h"
  4. #include "./ifc_omcachegroup.h"
  5. #include "./ifc_omcacherecord.h"
  6. #include "./ifc_mlnavigationcallback.h"
  7. #include "./resource.h"
  8. #include "./ifc_wasabihelper.h"
  9. #include "../Plugins/General/gen_ml/ml.h"
  10. #define _ML_HEADER_IMPMLEMENT
  11. #include "../Plugins/General/gen_ml/ml_ipc_0313.h"
  12. #undef _ML_HEADER_IMPMLEMENT
  13. #include <shlwapi.h>
  14. #include <strsafe.h>
  15. #include <algorithm>
  16. MlNavigationHelper::MlNavigationHelper(HWND hLibraryWnd, ifc_omcachegroup *cacheGroup)
  17. : ref(1), hLibrary(hLibraryWnd), defaultIndex(-1), cacheGroup(cacheGroup), lastCookie(0)
  18. {
  19. InitializeCriticalSection(&lock);
  20. if (NULL != cacheGroup)
  21. cacheGroup->AddRef();
  22. }
  23. MlNavigationHelper::~MlNavigationHelper()
  24. {
  25. EnterCriticalSection(&lock);
  26. for(CallbackMap::iterator iter = callbackMap.begin(); iter != callbackMap.end(); iter++)
  27. {
  28. ifc_mlnavigationcallback *callback = iter->second;
  29. if (NULL != callback) callback->Release();
  30. }
  31. size_t index = recordList.size();
  32. while(index--)
  33. {
  34. Plugin_FreeString(recordList[index].name);
  35. }
  36. if (NULL != cacheGroup)
  37. cacheGroup->Release();
  38. LeaveCriticalSection(&lock);
  39. DeleteCriticalSection(&lock);
  40. }
  41. HRESULT MlNavigationHelper::CreateInstance(HWND hLibrary, ifc_omcachemanager *cacheManager, MlNavigationHelper **instance)
  42. {
  43. if (NULL == instance) return E_POINTER;
  44. if (NULL == hLibrary || FALSE == IsWindow(hLibrary) || NULL == cacheManager)
  45. {
  46. *instance = NULL;
  47. return E_INVALIDARG;
  48. }
  49. ifc_omcachegroup *group = NULL;
  50. HRESULT hr = cacheManager->Find(L"icons", TRUE, &group, NULL);
  51. if (SUCCEEDED(hr) && group != NULL)
  52. {
  53. *instance = new MlNavigationHelper(hLibrary, group);
  54. if (NULL == *instance)
  55. {
  56. hr = E_OUTOFMEMORY;
  57. }
  58. group->Release();
  59. }
  60. return hr;
  61. }
  62. size_t MlNavigationHelper::AddRef()
  63. {
  64. return InterlockedIncrement((LONG*)&ref);
  65. }
  66. size_t MlNavigationHelper::Release()
  67. {
  68. if (0 == ref)
  69. return ref;
  70. LONG r = InterlockedDecrement((LONG*)&ref);
  71. if (0 == r)
  72. delete(this);
  73. return r;
  74. }
  75. int MlNavigationHelper::QueryInterface(GUID interface_guid, void **object)
  76. {
  77. if (NULL == object) return E_POINTER;
  78. if (IsEqualIID(interface_guid, IFC_MlNavigationHelper))
  79. *object = static_cast<ifc_mlnavigationhelper*>(this);
  80. else
  81. {
  82. *object = NULL;
  83. return E_NOINTERFACE;
  84. }
  85. if (NULL == *object)
  86. return E_UNEXPECTED;
  87. AddRef();
  88. return S_OK;
  89. }
  90. HRESULT MlNavigationHelper::GetDefaultIndex(int *index)
  91. {
  92. if (NULL == index)
  93. return E_POINTER;
  94. EnterCriticalSection(&lock);
  95. if (-1 == defaultIndex)
  96. {
  97. WCHAR szPath[MAX_PATH] = {0};
  98. HRESULT hr = Plugin_MakeResourcePath(szPath, ARRAYSIZE(szPath), Plugin_GetInstance(),
  99. RT_RCDATA, MAKEINTRESOURCE(IDR_SERVICEICON_IMAGE), RESPATH_COMPACT);
  100. if (SUCCEEDED(hr))
  101. {
  102. UpdateImageList(szPath, &defaultIndex);
  103. }
  104. }
  105. *index = defaultIndex;
  106. LeaveCriticalSection(&lock);
  107. return S_OK;
  108. }
  109. HRESULT MlNavigationHelper::UpdateImageList(LPCWSTR address, INT *index)
  110. {
  111. if (NULL == address || NULL == index)
  112. {
  113. if (NULL != index) *index = -1;
  114. return E_INVALIDARG;
  115. }
  116. HMLIMGLST hmlil = MLNavCtrl_GetImageList(hLibrary);
  117. if (NULL == hmlil) return E_UNEXPECTED;
  118. MLIMAGESOURCE source = {0};
  119. source.cbSize = sizeof(source);
  120. source.lpszName = address;
  121. source.type = SRC_TYPE_PNG;
  122. source.bpp = 24;
  123. source.flags = ISF_LOADFROMFILE | ISF_FORCE_BPP | ISF_PREMULTIPLY;
  124. MLIMAGELISTIMAGESIZE imageSize = {0};
  125. imageSize.hmlil = hmlil;
  126. if (FALSE != MLImageList_GetImageSize(hLibrary, &imageSize))
  127. {
  128. source.cxDst = imageSize.cx;
  129. source.cyDst = imageSize.cy;
  130. source.flags |= ISF_FORCE_SIZE | ISF_SCALE;
  131. }
  132. if (FALSE == MLImageLoader_CheckExist(hLibrary, &source))
  133. return HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
  134. MLIMAGELISTITEM item = {0};
  135. item.cbSize = sizeof(item);
  136. item.hmlil = hmlil;
  137. item.filterUID = MLIF_FILTER3_UID;
  138. item.pmlImgSource = &source;
  139. HRESULT hr = S_OK;
  140. INT iImage = *index;
  141. if (iImage >= 0)
  142. {
  143. INT imageCount = MLImageList_GetImageCount(hLibrary, item.hmlil);
  144. if (iImage >= imageCount) iImage = -1;
  145. }
  146. if (iImage >=0)
  147. {
  148. item.mlilIndex = iImage;
  149. if (FALSE == MLImageList_Replace(hLibrary, &item))
  150. hr = E_FAIL;
  151. }
  152. else
  153. {
  154. iImage = MLImageList_Add(hLibrary, &item);
  155. if (-1 == iImage) hr = E_FAIL;
  156. }
  157. *index = iImage;
  158. return hr;
  159. }
  160. HRESULT MlNavigationHelper::ImageLocator(LPCWSTR address, INT *index)
  161. {
  162. if (NULL == address) return E_INVALIDARG;
  163. HRESULT hr;
  164. BOOL runtimeOnly = TRUE;
  165. if (PathIsURL(address) &&
  166. CSTR_EQUAL != CompareString(CSTR_INVARIANT, NORM_IGNORECASE, address, 6, L"res://", -1))
  167. {
  168. runtimeOnly = FALSE;
  169. }
  170. else
  171. {
  172. hr = UpdateImageList(address, index);
  173. if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) != hr)
  174. return hr;
  175. }
  176. BOOL fCreated = FALSE;
  177. ifc_omcacherecord *cacheRecord = NULL;
  178. hr = cacheGroup->Find(address, TRUE, &cacheRecord, &fCreated);
  179. if (SUCCEEDED(hr) && cacheRecord != NULL)
  180. {
  181. cacheRecord->RegisterCallback(this);
  182. if (FALSE != fCreated && FALSE != runtimeOnly)
  183. {
  184. cacheRecord->SetFlags(ifc_omcacherecord::flagNoStore, ifc_omcacherecord::flagNoStore);
  185. }
  186. WCHAR szBuffer[MAX_PATH * 2] = {0};
  187. if (SUCCEEDED(cacheRecord->GetPath(szBuffer, ARRAYSIZE(szBuffer))))
  188. {
  189. hr = UpdateImageList(szBuffer, index);
  190. }
  191. cacheRecord->Release();
  192. }
  193. return hr;
  194. }
  195. HRESULT MlNavigationHelper::QueryIndex(LPCWSTR pszName, INT *index, BOOL *defaultUsed)
  196. {
  197. if (NULL == cacheGroup)
  198. return E_UNEXPECTED;
  199. if (NULL == index)
  200. {
  201. if (NULL != defaultUsed) *defaultUsed = FALSE;
  202. return E_POINTER;
  203. }
  204. if (NULL == pszName || L'\0' == *pszName)
  205. {
  206. if (SUCCEEDED(GetDefaultIndex(index)))
  207. {
  208. if (NULL != defaultUsed) *defaultUsed = TRUE;
  209. }
  210. else
  211. {
  212. *index = 0;
  213. if (NULL != defaultUsed) *defaultUsed = FALSE;
  214. }
  215. return S_OK;
  216. }
  217. EnterCriticalSection(&lock);
  218. RECORD *record = Find(pszName);
  219. if (NULL == record)
  220. { // create new
  221. RECORD rec = {0};
  222. size_t index = recycledList.size();
  223. if (0 != index)
  224. {
  225. rec.index = recycledList[index - 1];
  226. recycledList.pop_back();
  227. }
  228. else
  229. {
  230. rec.index = -1;
  231. }
  232. rec.ref = 1;
  233. rec.name = Plugin_CopyString(pszName);
  234. recordList.push_back(rec);
  235. Sort();
  236. // locate record again;
  237. record = Find(pszName);
  238. ImageLocator(record->name, &record->index);
  239. }
  240. else
  241. {
  242. record->ref++;
  243. if (-1 == record->index)
  244. ImageLocator(record->name, &record->index);
  245. }
  246. if(-1 == record->index)
  247. {
  248. if (SUCCEEDED(GetDefaultIndex(index)) && NULL != defaultUsed)
  249. *defaultUsed = TRUE;
  250. }
  251. else
  252. {
  253. *index = record->index;
  254. }
  255. LeaveCriticalSection(&lock);
  256. return S_OK;
  257. }
  258. HRESULT MlNavigationHelper::ReleaseIndex(LPCWSTR pszName)
  259. {
  260. if(NULL == pszName || L'\0' == *pszName)
  261. return E_INVALIDARG;
  262. EnterCriticalSection(&lock);
  263. RECORD *record = Find(pszName);
  264. if (NULL != record)
  265. {
  266. record->ref--;
  267. if (0 == record->ref)
  268. {
  269. if (-1 != record->index)
  270. recycledList.push_back(record->index);
  271. Plugin_FreeString(record->name);
  272. record->name = NULL;
  273. size_t index = recordList.size();
  274. while(index--)
  275. {
  276. if (&recordList[index] == record)
  277. {
  278. recordList.erase(recordList.begin() + index);
  279. break;
  280. }
  281. }
  282. }
  283. }
  284. LeaveCriticalSection(&lock);
  285. return (NULL != record) ? S_OK : S_FALSE;
  286. }
  287. HRESULT MlNavigationHelper::RegisterAlias(LPCWSTR pszName, LPCWSTR pszAddress)
  288. {
  289. if (NULL == cacheGroup)
  290. return E_UNEXPECTED;
  291. BOOL fCreated = FALSE;
  292. ifc_omcacherecord *record = NULL;
  293. HRESULT hr = cacheGroup->Find(pszName, TRUE, &record, &fCreated);
  294. if (SUCCEEDED(hr) && record != NULL)
  295. {
  296. if (FALSE != fCreated)
  297. {
  298. record->SetFlags(ifc_omcacherecord::flagNoStore, ifc_omcacherecord::flagNoStore);
  299. }
  300. hr = record->SetPath(pszAddress);
  301. record->RegisterCallback(this);
  302. record->Release();
  303. }
  304. return hr;
  305. }
  306. HWND MlNavigationHelper::GetLibrary()
  307. {
  308. return hLibrary;
  309. }
  310. typedef struct __CACHECHANGEAPCPARAM
  311. {
  312. MlNavigationHelper *instance;
  313. ifc_omcacherecord *record;
  314. } CACHECHANGEAPCPARAM;
  315. static void CALLBACK MlNavigationHelper_CacheRecordPathChangedApc(ULONG_PTR data)
  316. {
  317. CACHECHANGEAPCPARAM *param = (CACHECHANGEAPCPARAM*)data;
  318. if (NULL != param)
  319. {
  320. if (NULL != param->instance)
  321. {
  322. param->instance->CacheRecordPathChangedApc(param->record);
  323. param->instance->Release();
  324. }
  325. if (NULL != param->record)
  326. param->record->Release();
  327. free(param);
  328. }
  329. }
  330. void MlNavigationHelper::CacheRecordPathChanged(ifc_omcacherecord *cacheRecord)
  331. {
  332. if (NULL == cacheRecord)
  333. return;
  334. HRESULT hr = E_FAIL;
  335. CACHECHANGEAPCPARAM *param = (CACHECHANGEAPCPARAM*)calloc(1, sizeof(CACHECHANGEAPCPARAM));
  336. if (NULL != param)
  337. {
  338. param->instance = this;
  339. param->instance->AddRef();
  340. param->record = cacheRecord;
  341. param->record->AddRef();
  342. ifc_wasabihelper *wasabi = NULL;
  343. if (SUCCEEDED(Plugin_GetWasabiHelper(&wasabi)) && wasabi != NULL)
  344. {
  345. api_application *application = NULL;
  346. if (SUCCEEDED(wasabi->GetApplicationApi(&application)) && application != NULL)
  347. {
  348. HANDLE hThread = application->main_getMainThreadHandle();
  349. if (NULL != hThread &&
  350. 0 != QueueUserAPC(MlNavigationHelper_CacheRecordPathChangedApc, hThread, (ULONG_PTR)param))
  351. {
  352. hr = S_OK;
  353. }
  354. application->Release();
  355. }
  356. wasabi->Release();
  357. }
  358. if (FAILED(hr))
  359. {
  360. if (NULL != param->instance) param->instance->Release();
  361. if (NULL != param->record) param->record->Release();
  362. free(param);
  363. param = NULL;
  364. }
  365. }
  366. }
  367. void MlNavigationHelper::CacheRecordPathChangedApc(ifc_omcacherecord *cacheRecord)
  368. {
  369. if (NULL == cacheRecord)
  370. return;
  371. WCHAR szBuffer[1024] = {0};
  372. if (FAILED(cacheRecord->GetName(szBuffer, ARRAYSIZE(szBuffer))))
  373. return;
  374. EnterCriticalSection(&lock);
  375. RECORD *record = Find(szBuffer);
  376. if (NULL != record &&
  377. SUCCEEDED(cacheRecord->GetPath(szBuffer, ARRAYSIZE(szBuffer))) &&
  378. SUCCEEDED(UpdateImageList(szBuffer, &record->index)))
  379. {
  380. for(CallbackMap::iterator iter = callbackMap.begin(); iter != callbackMap.end(); iter++)
  381. {
  382. ifc_mlnavigationcallback *callback = iter->second;
  383. callback->ImageChanged(record->name, record->index);
  384. }
  385. }
  386. LeaveCriticalSection(&lock);
  387. }
  388. HRESULT MlNavigationHelper::RegisterCallback(ifc_mlnavigationcallback *callback, UINT *cookie)
  389. {
  390. if (NULL == cookie) return E_POINTER;
  391. *cookie = 0;
  392. if (NULL == callback)
  393. return E_INVALIDARG;
  394. EnterCriticalSection(&lock);
  395. *cookie = ++lastCookie;
  396. callbackMap.insert({ *cookie, callback });
  397. callback->AddRef();
  398. LeaveCriticalSection(&lock);
  399. return S_OK;
  400. }
  401. HRESULT MlNavigationHelper::UnregisterCallback(UINT cookie)
  402. {
  403. if (0 == cookie) return E_INVALIDARG;
  404. ifc_mlnavigationcallback *callback = NULL;
  405. EnterCriticalSection(&lock);
  406. for(CallbackMap::iterator iter = callbackMap.begin(); iter != callbackMap.end(); iter++)
  407. {
  408. if (cookie == iter->first)
  409. {
  410. callback = iter->second;
  411. callbackMap.erase(iter);
  412. break;
  413. }
  414. }
  415. LeaveCriticalSection(&lock);
  416. if (NULL != callback)
  417. {
  418. callback->Release();
  419. return S_OK;
  420. }
  421. return S_FALSE;
  422. }
  423. __inline static int __cdecl MlNavigationHelper_RecordComparer(const void *elem1, const void *elem2)
  424. {
  425. LPCWSTR s1 = ((MlNavigationHelper::RECORD*)elem1)->name;
  426. LPCWSTR s2 = ((MlNavigationHelper::RECORD*)elem2)->name;
  427. if (NULL == s1 || NULL == s2) return ((INT)(ULONG_PTR)(s1 - s2));
  428. return CompareString(CSTR_INVARIANT, NORM_IGNORECASE, s1, -1, s2, -1) - 2;
  429. }
  430. __inline static int __cdecl MlNavigationHelper_RecordComparer_V2(const void* elem1, const void* elem2)
  431. {
  432. return MlNavigationHelper_RecordComparer(elem1, elem2) < 0;
  433. }
  434. __inline static int __cdecl MlNavigationHelper_RecordSearch(const void *elem1, const void *elem2)
  435. {
  436. LPCWSTR s1 = (LPCWSTR)elem1;
  437. LPCWSTR s2 = ((MlNavigationHelper::RECORD*)elem2)->name;
  438. if (NULL == s1 || NULL == s2) return ((INT)(ULONG_PTR)(s1 - s2));
  439. INT r = CompareString(CSTR_INVARIANT, NORM_IGNORECASE, s1, -1, s2, -1) - 2;
  440. return r;
  441. }
  442. HRESULT MlNavigationHelper::Sort()
  443. {
  444. HRESULT hr;
  445. if (recordList.size() < 2)
  446. {
  447. hr = S_FALSE;
  448. }
  449. else
  450. {
  451. //qsort(recordList.begin(), recordList.size(), sizeof(RECORD), MlNavigationHelper_RecordComparer);
  452. std::sort(recordList.begin(), recordList.end(),
  453. [&](RECORD lhs, RECORD rhs) -> bool
  454. {
  455. return MlNavigationHelper_RecordComparer_V2(&lhs, &rhs);
  456. }
  457. );
  458. hr = S_OK;
  459. }
  460. return hr;
  461. }
  462. MlNavigationHelper::RECORD *MlNavigationHelper::Find(LPCWSTR pszName)
  463. {
  464. if (0 == recordList.size())
  465. return NULL;
  466. //return (RECORD*)bsearch(pszName, recordList.begin(), recordList.size(),
  467. // sizeof(RECORD), MlNavigationHelper_RecordSearch);
  468. auto it = std::find_if(recordList.begin(), recordList.end(),
  469. [&](RECORD upT) -> bool
  470. {
  471. return MlNavigationHelper_RecordSearch(pszName, &upT) == 0;
  472. }
  473. );
  474. if (it == recordList.end())
  475. {
  476. return NULL;
  477. }
  478. return &(* it);
  479. }
  480. #define CBCLASS MlNavigationHelper
  481. START_MULTIPATCH;
  482. START_PATCH(MPIID_MLNAVIGATIONHELPER)
  483. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, ADDREF, AddRef);
  484. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, RELEASE, Release);
  485. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, QUERYINTERFACE, QueryInterface);
  486. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_GETDEFAULTINDEX, GetDefaultIndex);
  487. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_QUERYINDEX, QueryIndex);
  488. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_RELEASEINDEX, ReleaseIndex);
  489. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_REGISTERALIAS, RegisterAlias);
  490. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_REGISTERCALLBACK, RegisterCallback);
  491. M_CB(MPIID_MLNAVIGATIONHELPER, ifc_mlnavigationhelper, API_UNREGISTERCALLBACK, UnregisterCallback);
  492. NEXT_PATCH(MPIID_OMCACHECALLBACK)
  493. M_CB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, ADDREF, AddRef);
  494. M_CB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, RELEASE, Release);
  495. M_CB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, QUERYINTERFACE, QueryInterface);
  496. M_VCB(MPIID_OMCACHECALLBACK, ifc_omcachecallback, API_PATHCHANGED, CacheRecordPathChanged);
  497. END_PATCH
  498. END_MULTIPATCH;
  499. #undef CBCLASS