1 /* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved gvimext by Tianmiao Hu 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 */ 8 9 /* 10 * gvimext is a DLL which is used for the "Edit with Vim" context menu 11 * extension. It implements a MS defined interface with the Shell. 12 * 13 * If you have any questions or any suggestions concerning gvimext, please 14 * contact Tianmiao Hu: [email protected]. 15 */ 16 17 #include "gvimext.h" 18 19 static char *searchpath(char *name); 20 21 // Always get an error while putting the following stuff to the 22 // gvimext.h file as class protected variables, give up and 23 // declare them as global stuff 24 FORMATETC fmte = {CF_HDROP, 25 (DVTARGETDEVICE FAR *)NULL, 26 DVASPECT_CONTENT, 27 -1, 28 TYMED_HGLOBAL 29 }; 30 STGMEDIUM medium; 31 HRESULT hres = 0; 32 UINT cbFiles = 0; 33 34 /* The buffers size used to be MAX_PATH (260 bytes), but that's not always 35 * enough */ 36 #define BUFSIZE 1100 37 38 // 39 // Get the name of the Gvim executable to use, with the path. 40 // When "runtime" is non-zero, we were called to find the runtime directory. 41 // Returns the path in name[BUFSIZE]. It's empty when it fails. 42 // 43 static void 44 getGvimName(char *name, int runtime) 45 { 46 HKEY keyhandle; 47 DWORD hlen; 48 49 // Get the location of gvim from the registry. 50 name[0] = 0; 51 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, 52 KEY_READ, &keyhandle) == ERROR_SUCCESS) 53 { 54 hlen = BUFSIZE; 55 if (RegQueryValueEx(keyhandle, "path", 0, NULL, (BYTE *)name, &hlen) 56 != ERROR_SUCCESS) 57 name[0] = 0; 58 else 59 name[hlen] = 0; 60 RegCloseKey(keyhandle); 61 } 62 63 // Registry didn't work, use the search path. 64 if (name[0] == 0) 65 strcpy(name, searchpath((char *)"gvim.exe")); 66 67 if (!runtime) 68 { 69 // Only when looking for the executable, not the runtime dir, we can 70 // search for the batch file or a name without a path. 71 if (name[0] == 0) 72 strcpy(name, searchpath((char *)"gvim.bat")); 73 if (name[0] == 0) 74 strcpy(name, "gvim"); // finds gvim.bat or gvim.exe 75 } 76 } 77 78 static void 79 getGvimInvocation(char *name, int runtime) 80 { 81 getGvimName(name, runtime); 82 // avoid that Vim tries to expand wildcards in the file names 83 strcat(name, " --literal"); 84 } 85 86 static void 87 getGvimInvocationW(wchar_t *nameW) 88 { 89 char *name; 90 91 name = (char *)malloc(BUFSIZE); 92 getGvimInvocation(name, 0); 93 mbstowcs(nameW, name, BUFSIZE); 94 free(name); 95 } 96 97 // 98 // Get the Vim runtime directory into buf[BUFSIZE]. 99 // The result is empty when it failed. 100 // When it works, the path ends in a slash or backslash. 101 // 102 static void 103 getRuntimeDir(char *buf) 104 { 105 int idx; 106 107 getGvimName(buf, 1); 108 if (buf[0] != 0) 109 { 110 // When no path found, use the search path to expand it. 111 if (strchr(buf, '/') == NULL && strchr(buf, '\\') == NULL) 112 strcpy(buf, searchpath(buf)); 113 114 // remove "gvim.exe" from the end 115 for (idx = (int)strlen(buf) - 1; idx >= 0; idx--) 116 if (buf[idx] == '\\' || buf[idx] == '/') 117 { 118 buf[idx + 1] = 0; 119 break; 120 } 121 } 122 } 123 124 HBITMAP IconToBitmap(HICON hIcon, HBRUSH hBackground, int width, int height) 125 { 126 HDC hDC = GetDC(NULL); 127 HDC hMemDC = CreateCompatibleDC(hDC); 128 HBITMAP hMemBmp = CreateCompatibleBitmap(hDC, width, height); 129 HBITMAP hResultBmp = NULL; 130 HGDIOBJ hOrgBMP = SelectObject(hMemDC, hMemBmp); 131 132 DrawIconEx(hMemDC, 0, 0, hIcon, width, height, 0, hBackground, DI_NORMAL); 133 134 hResultBmp = hMemBmp; 135 hMemBmp = NULL; 136 137 SelectObject(hMemDC, hOrgBMP); 138 DeleteDC(hMemDC); 139 ReleaseDC(NULL, hDC); 140 DestroyIcon(hIcon); 141 return hResultBmp; 142 } 143 144 // 145 // GETTEXT: translated messages and menu entries 146 // 147 #ifndef FEAT_GETTEXT 148 # define _(x) x 149 #else 150 # define _(x) (*dyn_libintl_gettext)(x) 151 # define VIMPACKAGE "vim" 152 # ifndef GETTEXT_DLL 153 # define GETTEXT_DLL "libintl.dll" 154 # define GETTEXT_DLL_ALT "libintl-8.dll" 155 # endif 156 157 // Dummy functions 158 static char *null_libintl_gettext(const char *); 159 static char *null_libintl_textdomain(const char *); 160 static char *null_libintl_bindtextdomain(const char *, const char *); 161 static int dyn_libintl_init(char *dir); 162 static void dyn_libintl_end(void); 163 164 static HINSTANCE hLibintlDLL = 0; 165 static char *(*dyn_libintl_gettext)(const char *) = null_libintl_gettext; 166 static char *(*dyn_libintl_textdomain)(const char *) = null_libintl_textdomain; 167 static char *(*dyn_libintl_bindtextdomain)(const char *, const char *) 168 = null_libintl_bindtextdomain; 169 170 // 171 // Attempt to load libintl.dll. If it doesn't work, use dummy functions. 172 // "dir" is the directory where the libintl.dll might be. 173 // Return 1 for success, 0 for failure. 174 // 175 static int 176 dyn_libintl_init(char *dir) 177 { 178 int i; 179 static struct 180 { 181 char *name; 182 FARPROC *ptr; 183 } libintl_entry[] = 184 { 185 {(char *)"gettext", (FARPROC*)&dyn_libintl_gettext}, 186 {(char *)"textdomain", (FARPROC*)&dyn_libintl_textdomain}, 187 {(char *)"bindtextdomain", (FARPROC*)&dyn_libintl_bindtextdomain}, 188 {NULL, NULL} 189 }; 190 DWORD len, len2; 191 LPWSTR buf = NULL; 192 LPWSTR buf2 = NULL; 193 194 // No need to initialize twice. 195 if (hLibintlDLL) 196 return 1; 197 198 // Load gettext library from $VIMRUNTIME\GvimExt{64,32} directory. 199 // Add the directory to $PATH temporarily. 200 len = GetEnvironmentVariableW(L"PATH", NULL, 0); 201 len2 = MAX_PATH + 1 + len; 202 buf = (LPWSTR)malloc(len * sizeof(WCHAR)); 203 buf2 = (LPWSTR)malloc(len2 * sizeof(WCHAR)); 204 if (buf != NULL && buf2 != NULL) 205 { 206 GetEnvironmentVariableW(L"PATH", buf, len); 207 # ifdef _WIN64 208 _snwprintf(buf2, len2, L"%S\\GvimExt64;%s", dir, buf); 209 # else 210 _snwprintf(buf2, len2, L"%S\\GvimExt32;%s", dir, buf); 211 # endif 212 SetEnvironmentVariableW(L"PATH", buf2); 213 hLibintlDLL = LoadLibrary(GETTEXT_DLL); 214 # ifdef GETTEXT_DLL_ALT 215 if (!hLibintlDLL) 216 hLibintlDLL = LoadLibrary(GETTEXT_DLL_ALT); 217 # endif 218 SetEnvironmentVariableW(L"PATH", buf); 219 } 220 free(buf); 221 free(buf2); 222 if (!hLibintlDLL) 223 return 0; 224 225 // Get the addresses of the functions we need. 226 for (i = 0; libintl_entry[i].name != NULL 227 && libintl_entry[i].ptr != NULL; ++i) 228 { 229 if ((*libintl_entry[i].ptr = GetProcAddress(hLibintlDLL, 230 libintl_entry[i].name)) == NULL) 231 { 232 dyn_libintl_end(); 233 return 0; 234 } 235 } 236 return 1; 237 } 238 239 static void 240 dyn_libintl_end(void) 241 { 242 if (hLibintlDLL) 243 FreeLibrary(hLibintlDLL); 244 hLibintlDLL = NULL; 245 dyn_libintl_gettext = null_libintl_gettext; 246 dyn_libintl_textdomain = null_libintl_textdomain; 247 dyn_libintl_bindtextdomain = null_libintl_bindtextdomain; 248 } 249 250 static char * 251 null_libintl_gettext(const char *msgid) 252 { 253 return (char *)msgid; 254 } 255 256 static char * 257 null_libintl_bindtextdomain(const char * /* domainname */, const char * /* dirname */) 258 { 259 return NULL; 260 } 261 262 static char * 263 null_libintl_textdomain(const char* /* domainname */) 264 { 265 return NULL; 266 } 267 268 // 269 // Setup for translating strings. 270 // 271 static void 272 dyn_gettext_load(void) 273 { 274 char szBuff[BUFSIZE]; 275 DWORD len; 276 277 // Try to locate the runtime files. The path is used to find libintl.dll 278 // and the vim.mo files. 279 getRuntimeDir(szBuff); 280 if (szBuff[0] != 0) 281 { 282 len = (DWORD)strlen(szBuff); 283 if (dyn_libintl_init(szBuff)) 284 { 285 strcpy(szBuff + len, "lang"); 286 287 (*dyn_libintl_bindtextdomain)(VIMPACKAGE, szBuff); 288 (*dyn_libintl_textdomain)(VIMPACKAGE); 289 } 290 } 291 } 292 293 static void 294 dyn_gettext_free(void) 295 { 296 dyn_libintl_end(); 297 } 298 #endif // FEAT_GETTEXT 299 300 // 301 // Global variables 302 // 303 UINT g_cRefThisDll = 0; // Reference count of this DLL. 304 HINSTANCE g_hmodThisDll = NULL; // Handle to this DLL itself. 305 306 307 //--------------------------------------------------------------------------- 308 // DllMain 309 //--------------------------------------------------------------------------- 310 extern "C" int APIENTRY 311 DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /* lpReserved */) 312 { 313 switch (dwReason) 314 { 315 case DLL_PROCESS_ATTACH: 316 // Extension DLL one-time initialization 317 g_hmodThisDll = hInstance; 318 break; 319 320 case DLL_PROCESS_DETACH: 321 break; 322 } 323 324 return 1; // ok 325 } 326 327 static void 328 inc_cRefThisDLL() 329 { 330 #ifdef FEAT_GETTEXT 331 if (g_cRefThisDll == 0) 332 dyn_gettext_load(); 333 #endif 334 InterlockedIncrement((LPLONG)&g_cRefThisDll); 335 } 336 337 static void 338 dec_cRefThisDLL() 339 { 340 #ifdef FEAT_GETTEXT 341 if (InterlockedDecrement((LPLONG)&g_cRefThisDll) == 0) 342 dyn_gettext_free(); 343 #else 344 InterlockedDecrement((LPLONG)&g_cRefThisDll); 345 #endif 346 } 347 348 //--------------------------------------------------------------------------- 349 // DllCanUnloadNow 350 //--------------------------------------------------------------------------- 351 352 STDAPI DllCanUnloadNow(void) 353 { 354 return (g_cRefThisDll == 0 ? S_OK : S_FALSE); 355 } 356 357 STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut) 358 { 359 *ppvOut = NULL; 360 361 if (IsEqualIID(rclsid, CLSID_ShellExtension)) 362 { 363 CShellExtClassFactory *pcf = new CShellExtClassFactory; 364 365 return pcf->QueryInterface(riid, ppvOut); 366 } 367 368 return CLASS_E_CLASSNOTAVAILABLE; 369 } 370 371 CShellExtClassFactory::CShellExtClassFactory() 372 { 373 m_cRef = 0L; 374 375 inc_cRefThisDLL(); 376 } 377 378 CShellExtClassFactory::~CShellExtClassFactory() 379 { 380 dec_cRefThisDLL(); 381 } 382 383 STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID riid, 384 LPVOID FAR *ppv) 385 { 386 *ppv = NULL; 387 388 // any interface on this object is the object pointer 389 390 if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory)) 391 { 392 *ppv = (LPCLASSFACTORY)this; 393 394 AddRef(); 395 396 return NOERROR; 397 } 398 399 return E_NOINTERFACE; 400 } 401 402 STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef() 403 { 404 return InterlockedIncrement((LPLONG)&m_cRef); 405 } 406 407 STDMETHODIMP_(ULONG) CShellExtClassFactory::Release() 408 { 409 if (InterlockedDecrement((LPLONG)&m_cRef)) 410 return m_cRef; 411 412 delete this; 413 414 return 0L; 415 } 416 417 STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, 418 REFIID riid, 419 LPVOID *ppvObj) 420 { 421 *ppvObj = NULL; 422 423 // Shell extensions typically don't support aggregation (inheritance) 424 425 if (pUnkOuter) 426 return CLASS_E_NOAGGREGATION; 427 428 // Create the main shell extension object. The shell will then call 429 // QueryInterface with IID_IShellExtInit--this is how shell extensions are 430 // initialized. 431 432 LPCSHELLEXT pShellExt = new CShellExt(); // create the CShellExt object 433 434 if (NULL == pShellExt) 435 return E_OUTOFMEMORY; 436 437 return pShellExt->QueryInterface(riid, ppvObj); 438 } 439 440 441 STDMETHODIMP CShellExtClassFactory::LockServer(BOOL /* fLock */) 442 { 443 return NOERROR; 444 } 445 446 // *********************** CShellExt ************************* 447 CShellExt::CShellExt() 448 { 449 m_cRef = 0L; 450 m_pDataObj = NULL; 451 452 inc_cRefThisDLL(); 453 454 LoadMenuIcon(); 455 } 456 457 CShellExt::~CShellExt() 458 { 459 if (m_pDataObj) 460 m_pDataObj->Release(); 461 462 dec_cRefThisDLL(); 463 464 if (m_hVimIconBitmap) 465 DeleteObject(m_hVimIconBitmap); 466 } 467 468 STDMETHODIMP CShellExt::QueryInterface(REFIID riid, LPVOID FAR *ppv) 469 { 470 *ppv = NULL; 471 472 if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown)) 473 { 474 *ppv = (LPSHELLEXTINIT)this; 475 } 476 else if (IsEqualIID(riid, IID_IContextMenu)) 477 { 478 *ppv = (LPCONTEXTMENU)this; 479 } 480 481 if (*ppv) 482 { 483 AddRef(); 484 485 return NOERROR; 486 } 487 488 return E_NOINTERFACE; 489 } 490 491 STDMETHODIMP_(ULONG) CShellExt::AddRef() 492 { 493 return InterlockedIncrement((LPLONG)&m_cRef); 494 } 495 496 STDMETHODIMP_(ULONG) CShellExt::Release() 497 { 498 499 if (InterlockedDecrement((LPLONG)&m_cRef)) 500 return m_cRef; 501 502 delete this; 503 504 return 0L; 505 } 506 507 508 // 509 // FUNCTION: CShellExt::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) 510 // 511 // PURPOSE: Called by the shell when initializing a context menu or property 512 // sheet extension. 513 // 514 // PARAMETERS: 515 // pIDFolder - Specifies the parent folder 516 // pDataObj - Specifies the set of items selected in that folder. 517 // hRegKey - Specifies the type of the focused item in the selection. 518 // 519 // RETURN VALUE: 520 // 521 // NOERROR in all cases. 522 // 523 // COMMENTS: Note that at the time this function is called, we don't know 524 // (or care) what type of shell extension is being initialized. 525 // It could be a context menu or a property sheet. 526 // 527 528 STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST /* pIDFolder */, 529 LPDATAOBJECT pDataObj, 530 HKEY /* hRegKey */) 531 { 532 // Initialize can be called more than once 533 if (m_pDataObj) 534 m_pDataObj->Release(); 535 536 // duplicate the object pointer and registry handle 537 538 if (pDataObj) 539 { 540 m_pDataObj = pDataObj; 541 pDataObj->AddRef(); 542 } 543 544 return NOERROR; 545 } 546 547 548 // 549 // FUNCTION: CShellExt::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT) 550 // 551 // PURPOSE: Called by the shell just before the context menu is displayed. 552 // This is where you add your specific menu items. 553 // 554 // PARAMETERS: 555 // hMenu - Handle to the context menu 556 // indexMenu - Index of where to begin inserting menu items 557 // idCmdFirst - Lowest value for new menu ID's 558 // idCmtLast - Highest value for new menu ID's 559 // uFlags - Specifies the context of the menu event 560 // 561 // RETURN VALUE: 562 // 563 // 564 // COMMENTS: 565 // 566 567 STDMETHODIMP CShellExt::QueryContextMenu(HMENU hMenu, 568 UINT indexMenu, 569 UINT idCmdFirst, 570 UINT /* idCmdLast */, 571 UINT /* uFlags */) 572 { 573 UINT idCmd = idCmdFirst; 574 575 hres = m_pDataObj->GetData(&fmte, &medium); 576 if (medium.hGlobal) 577 cbFiles = DragQueryFile((HDROP)medium.hGlobal, (UINT)-1, 0, 0); 578 579 // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); 580 581 // Initialize m_cntOfHWnd to 0 582 m_cntOfHWnd = 0; 583 584 HKEY keyhandle; 585 bool showExisting = true; 586 bool showIcons = true; 587 588 // Check whether "Edit with existing Vim" entries are disabled. 589 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Vim\\Gvim", 0, 590 KEY_READ, &keyhandle) == ERROR_SUCCESS) 591 { 592 if (RegQueryValueEx(keyhandle, "DisableEditWithExisting", 0, NULL, 593 NULL, NULL) == ERROR_SUCCESS) 594 showExisting = false; 595 if (RegQueryValueEx(keyhandle, "DisableContextMenuIcons", 0, NULL, 596 NULL, NULL) == ERROR_SUCCESS) 597 showIcons = false; 598 RegCloseKey(keyhandle); 599 } 600 601 // Retrieve all the vim instances, unless disabled. 602 if (showExisting) 603 EnumWindows(EnumWindowsProc, (LPARAM)this); 604 605 MENUITEMINFO mii = { sizeof(MENUITEMINFO) }; 606 mii.fMask = MIIM_STRING | MIIM_ID; 607 if (showIcons) 608 { 609 mii.fMask |= MIIM_BITMAP; 610 mii.hbmpItem = m_hVimIconBitmap; 611 } 612 613 if (cbFiles > 1) 614 { 615 mii.wID = idCmd++; 616 mii.dwTypeData = _("Edit with &multiple Vims"); 617 mii.cch = lstrlen(mii.dwTypeData); 618 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); 619 620 mii.wID = idCmd++; 621 mii.dwTypeData = _("Edit with single &Vim"); 622 mii.cch = lstrlen(mii.dwTypeData); 623 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); 624 625 if (cbFiles <= 4) 626 { 627 // Can edit up to 4 files in diff mode 628 mii.wID = idCmd++; 629 mii.dwTypeData = _("Diff with Vim"); 630 mii.cch = lstrlen(mii.dwTypeData); 631 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); 632 m_edit_existing_off = 3; 633 } 634 else 635 m_edit_existing_off = 2; 636 637 } 638 else 639 { 640 mii.wID = idCmd++; 641 mii.dwTypeData = _("Edit with &Vim"); 642 mii.cch = lstrlen(mii.dwTypeData); 643 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); 644 m_edit_existing_off = 1; 645 } 646 647 HMENU hSubMenu = NULL; 648 if (m_cntOfHWnd > 1) 649 { 650 hSubMenu = CreatePopupMenu(); 651 mii.fMask |= MIIM_SUBMENU; 652 mii.wID = idCmd; 653 mii.dwTypeData = _("Edit with existing Vim"); 654 mii.cch = lstrlen(mii.dwTypeData); 655 mii.hSubMenu = hSubMenu; 656 InsertMenuItem(hMenu, indexMenu++, TRUE, &mii); 657 mii.fMask = mii.fMask & ~MIIM_SUBMENU; 658 mii.hSubMenu = NULL; 659 } 660 // Now display all the vim instances 661 for (int i = 0; i < m_cntOfHWnd; i++) 662 { 663 char title[BUFSIZE]; 664 char temp[BUFSIZE]; 665 int index; 666 HMENU hmenu; 667 668 // Obtain window title, continue if can not 669 if (GetWindowText(m_hWnd[i], title, BUFSIZE - 1) == 0) 670 continue; 671 // Truncate the title before the path, keep the file name 672 char *pos = strchr(title, '('); 673 if (pos != NULL) 674 { 675 if (pos > title && pos[-1] == ' ') 676 --pos; 677 *pos = 0; 678 } 679 // Now concatenate 680 if (m_cntOfHWnd > 1) 681 temp[0] = '\0'; 682 else 683 { 684 strncpy(temp, _("Edit with existing Vim - "), BUFSIZE - 1); 685 temp[BUFSIZE - 1] = '\0'; 686 } 687 strncat(temp, title, BUFSIZE - 1 - strlen(temp)); 688 temp[BUFSIZE - 1] = '\0'; 689 690 mii.wID = idCmd++; 691 mii.dwTypeData = temp; 692 mii.cch = lstrlen(mii.dwTypeData); 693 if (m_cntOfHWnd > 1) 694 { 695 hmenu = hSubMenu; 696 index = i; 697 } 698 else 699 { 700 hmenu = hMenu; 701 index = indexMenu++; 702 } 703 InsertMenuItem(hmenu, index, TRUE, &mii); 704 } 705 // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); 706 707 // Must return number of menu items we added. 708 return ResultFromShort(idCmd-idCmdFirst); 709 } 710 711 // 712 // FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO) 713 // 714 // PURPOSE: Called by the shell after the user has selected on of the 715 // menu items that was added in QueryContextMenu(). 716 // 717 // PARAMETERS: 718 // lpcmi - Pointer to an CMINVOKECOMMANDINFO structure 719 // 720 // RETURN VALUE: 721 // 722 // 723 // COMMENTS: 724 // 725 726 STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 727 { 728 HRESULT hr = E_INVALIDARG; 729 730 // If HIWORD(lpcmi->lpVerb) then we have been called programmatically 731 // and lpVerb is a command that should be invoked. Otherwise, the shell 732 // has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has 733 // selected. Actually, it's (menu ID - idCmdFirst) from QueryContextMenu(). 734 if (!HIWORD(lpcmi->lpVerb)) 735 { 736 UINT idCmd = LOWORD(lpcmi->lpVerb); 737 738 if (idCmd >= m_edit_existing_off) 739 { 740 // Existing with vim instance 741 hr = PushToWindow(lpcmi->hwnd, 742 lpcmi->lpDirectory, 743 lpcmi->lpVerb, 744 lpcmi->lpParameters, 745 lpcmi->nShow, 746 idCmd - m_edit_existing_off); 747 } 748 else 749 { 750 switch (idCmd) 751 { 752 case 0: 753 hr = InvokeGvim(lpcmi->hwnd, 754 lpcmi->lpDirectory, 755 lpcmi->lpVerb, 756 lpcmi->lpParameters, 757 lpcmi->nShow); 758 break; 759 case 1: 760 hr = InvokeSingleGvim(lpcmi->hwnd, 761 lpcmi->lpDirectory, 762 lpcmi->lpVerb, 763 lpcmi->lpParameters, 764 lpcmi->nShow, 765 0); 766 break; 767 case 2: 768 hr = InvokeSingleGvim(lpcmi->hwnd, 769 lpcmi->lpDirectory, 770 lpcmi->lpVerb, 771 lpcmi->lpParameters, 772 lpcmi->nShow, 773 1); 774 break; 775 } 776 } 777 } 778 return hr; 779 } 780 781 STDMETHODIMP CShellExt::PushToWindow(HWND /* hParent */, 782 LPCSTR /* pszWorkingDir */, 783 LPCSTR /* pszCmd */, 784 LPCSTR /* pszParam */, 785 int /* iShowCmd */, 786 int idHWnd) 787 { 788 HWND hWnd = m_hWnd[idHWnd]; 789 790 // Show and bring vim instance to foreground 791 if (IsIconic(hWnd) != 0) 792 ShowWindow(hWnd, SW_RESTORE); 793 else 794 ShowWindow(hWnd, SW_SHOW); 795 SetForegroundWindow(hWnd); 796 797 // Post the selected files to the vim instance 798 PostMessage(hWnd, WM_DROPFILES, (WPARAM)medium.hGlobal, 0); 799 800 return NOERROR; 801 } 802 803 STDMETHODIMP CShellExt::GetCommandString(UINT_PTR /* idCmd */, 804 UINT uFlags, 805 UINT FAR * /* reserved */, 806 LPSTR pszName, 807 UINT cchMax) 808 { 809 if (uFlags == GCS_HELPTEXT && cchMax > 35) 810 lstrcpy(pszName, _("Edits the selected file(s) with Vim")); 811 812 return NOERROR; 813 } 814 815 BOOL CALLBACK CShellExt::EnumWindowsProc(HWND hWnd, LPARAM lParam) 816 { 817 char temp[BUFSIZE]; 818 819 // First do a bunch of check 820 // No invisible window 821 if (!IsWindowVisible(hWnd)) return TRUE; 822 // No child window ??? 823 // if (GetParent(hWnd)) return TRUE; 824 // Class name should be Vim, if failed to get class name, return 825 if (GetClassName(hWnd, temp, sizeof(temp)) == 0) 826 return TRUE; 827 // Compare class name to that of vim, if not, return 828 if (_strnicmp(temp, "vim", sizeof("vim")) != 0) 829 return TRUE; 830 // First check if the number of vim instance exceeds MAX_HWND 831 CShellExt *cs = (CShellExt*) lParam; 832 if (cs->m_cntOfHWnd >= MAX_HWND) return TRUE; 833 // Now we get the vim window, put it into some kind of array 834 cs->m_hWnd[cs->m_cntOfHWnd] = hWnd; 835 cs->m_cntOfHWnd ++; 836 837 return TRUE; // continue enumeration (otherwise this would be false) 838 } 839 840 BOOL CShellExt::LoadMenuIcon() 841 { 842 char vimExeFile[BUFSIZE]; 843 getGvimName(vimExeFile, 1); 844 if (vimExeFile[0] == '\0') 845 return FALSE; 846 HICON hVimIcon; 847 if (ExtractIconEx(vimExeFile, 0, NULL, &hVimIcon, 1) == 0) 848 return FALSE; 849 m_hVimIconBitmap = IconToBitmap(hVimIcon, 850 GetSysColorBrush(COLOR_MENU), 851 GetSystemMetrics(SM_CXSMICON), 852 GetSystemMetrics(SM_CYSMICON)); 853 return TRUE; 854 } 855 856 static char * 857 searchpath(char *name) 858 { 859 static char widename[2 * BUFSIZE]; 860 static char location[2 * BUFSIZE + 2]; 861 862 // There appears to be a bug in FindExecutableA() on Windows NT. 863 // Use FindExecutableW() instead... 864 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)name, -1, 865 (LPWSTR)widename, BUFSIZE); 866 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", 867 (LPWSTR)location) > (HINSTANCE)32) 868 { 869 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, 870 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); 871 return widename; 872 } 873 return (char *)""; 874 } 875 876 STDMETHODIMP CShellExt::InvokeGvim(HWND hParent, 877 LPCSTR /* pszWorkingDir */, 878 LPCSTR /* pszCmd */, 879 LPCSTR /* pszParam */, 880 int /* iShowCmd */) 881 { 882 wchar_t m_szFileUserClickedOn[BUFSIZE]; 883 wchar_t cmdStrW[BUFSIZE]; 884 UINT i; 885 886 for (i = 0; i < cbFiles; i++) 887 { 888 DragQueryFileW((HDROP)medium.hGlobal, 889 i, 890 m_szFileUserClickedOn, 891 sizeof(m_szFileUserClickedOn)); 892 893 getGvimInvocationW(cmdStrW); 894 wcscat(cmdStrW, L" \""); 895 896 if ((wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 2) < BUFSIZE) 897 { 898 wcscat(cmdStrW, m_szFileUserClickedOn); 899 wcscat(cmdStrW, L"\""); 900 901 STARTUPINFOW si; 902 PROCESS_INFORMATION pi; 903 904 ZeroMemory(&si, sizeof(si)); 905 si.cb = sizeof(si); 906 907 // Start the child process. 908 if (!CreateProcessW(NULL, // No module name (use command line). 909 cmdStrW, // Command line. 910 NULL, // Process handle not inheritable. 911 NULL, // Thread handle not inheritable. 912 FALSE, // Set handle inheritance to FALSE. 913 0, // No creation flags. 914 NULL, // Use parent's environment block. 915 NULL, // Use parent's starting directory. 916 &si, // Pointer to STARTUPINFO structure. 917 &pi) // Pointer to PROCESS_INFORMATION structure. 918 ) 919 { 920 MessageBox( 921 hParent, 922 _("Error creating process: Check if gvim is in your path!"), 923 _("gvimext.dll error"), 924 MB_OK); 925 } 926 else 927 { 928 CloseHandle( pi.hProcess ); 929 CloseHandle( pi.hThread ); 930 } 931 } 932 else 933 { 934 MessageBox( 935 hParent, 936 _("Path length too long!"), 937 _("gvimext.dll error"), 938 MB_OK); 939 } 940 } 941 942 return NOERROR; 943 } 944 945 946 STDMETHODIMP CShellExt::InvokeSingleGvim(HWND hParent, 947 LPCSTR /* pszWorkingDir */, 948 LPCSTR /* pszCmd */, 949 LPCSTR /* pszParam */, 950 int /* iShowCmd */, 951 int useDiff) 952 { 953 wchar_t m_szFileUserClickedOn[BUFSIZE]; 954 wchar_t *cmdStrW; 955 size_t cmdlen; 956 size_t len; 957 UINT i; 958 959 cmdlen = BUFSIZE; 960 cmdStrW = (wchar_t *) malloc(cmdlen * sizeof(wchar_t)); 961 if (cmdStrW == NULL) 962 return E_FAIL; 963 getGvimInvocationW(cmdStrW); 964 965 if (useDiff) 966 wcscat(cmdStrW, L" -d"); 967 for (i = 0; i < cbFiles; i++) 968 { 969 DragQueryFileW((HDROP)medium.hGlobal, 970 i, 971 m_szFileUserClickedOn, 972 sizeof(m_szFileUserClickedOn)); 973 974 len = wcslen(cmdStrW) + wcslen(m_szFileUserClickedOn) + 4; 975 if (len > cmdlen) 976 { 977 cmdlen = len + BUFSIZE; 978 wchar_t *cmdStrW_new = (wchar_t *)realloc(cmdStrW, cmdlen * sizeof(wchar_t)); 979 if (cmdStrW_new == NULL) 980 { 981 free(cmdStrW); 982 return E_FAIL; 983 } 984 cmdStrW = cmdStrW_new; 985 } 986 wcscat(cmdStrW, L" \""); 987 wcscat(cmdStrW, m_szFileUserClickedOn); 988 wcscat(cmdStrW, L"\""); 989 } 990 991 STARTUPINFOW si; 992 PROCESS_INFORMATION pi; 993 994 ZeroMemory(&si, sizeof(si)); 995 si.cb = sizeof(si); 996 997 // Start the child process. 998 if (!CreateProcessW(NULL, // No module name (use command line). 999 cmdStrW, // Command line. 1000 NULL, // Process handle not inheritable. 1001 NULL, // Thread handle not inheritable. 1002 FALSE, // Set handle inheritance to FALSE. 1003 0, // No creation flags. 1004 NULL, // Use parent's environment block. 1005 NULL, // Use parent's starting directory. 1006 &si, // Pointer to STARTUPINFO structure. 1007 &pi) // Pointer to PROCESS_INFORMATION structure. 1008 ) 1009 { 1010 MessageBox( 1011 hParent, 1012 _("Error creating process: Check if gvim is in your path!"), 1013 _("gvimext.dll error"), 1014 MB_OK); 1015 } 1016 else 1017 { 1018 CloseHandle(pi.hProcess); 1019 CloseHandle(pi.hThread); 1020 } 1021 free(cmdStrW); 1022 1023 return NOERROR; 1024 } 1025