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