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