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