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