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("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("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 {"gettext", (FARPROC*)&dyn_libintl_gettext}, 156 {"textdomain", (FARPROC*)&dyn_libintl_textdomain}, 157 {"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 // Retieve 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 strncat(temp, title, BUFSIZE - 1); 639 InsertMenu(hMenu, 640 indexMenu++, 641 MF_STRING|MF_BYPOSITION, 642 idCmd++, 643 temp); 644 } 645 // InsertMenu(hMenu, indexMenu++, MF_SEPARATOR|MF_BYPOSITION, 0, NULL); 646 647 // Must return number of menu items we added. 648 return ResultFromShort(idCmd-idCmdFirst); 649 } 650 651 // 652 // FUNCTION: CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO) 653 // 654 // PURPOSE: Called by the shell after the user has selected on of the 655 // menu items that was added in QueryContextMenu(). 656 // 657 // PARAMETERS: 658 // lpcmi - Pointer to an CMINVOKECOMMANDINFO structure 659 // 660 // RETURN VALUE: 661 // 662 // 663 // COMMENTS: 664 // 665 666 STDMETHODIMP CShellExt::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) 667 { 668 HRESULT hr = E_INVALIDARG; 669 670 // If HIWORD(lpcmi->lpVerb) then we have been called programmatically 671 // and lpVerb is a command that should be invoked. Otherwise, the shell 672 // has called us, and LOWORD(lpcmi->lpVerb) is the menu ID the user has 673 // selected. Actually, it's (menu ID - idCmdFirst) from QueryContextMenu(). 674 if (!HIWORD(lpcmi->lpVerb)) 675 { 676 UINT idCmd = LOWORD(lpcmi->lpVerb); 677 678 if (idCmd >= m_edit_existing_off) 679 { 680 // Existing with vim instance 681 hr = PushToWindow(lpcmi->hwnd, 682 lpcmi->lpDirectory, 683 lpcmi->lpVerb, 684 lpcmi->lpParameters, 685 lpcmi->nShow, 686 idCmd - m_edit_existing_off); 687 } 688 else 689 { 690 switch (idCmd) 691 { 692 case 0: 693 hr = InvokeGvim(lpcmi->hwnd, 694 lpcmi->lpDirectory, 695 lpcmi->lpVerb, 696 lpcmi->lpParameters, 697 lpcmi->nShow); 698 break; 699 case 1: 700 hr = InvokeSingleGvim(lpcmi->hwnd, 701 lpcmi->lpDirectory, 702 lpcmi->lpVerb, 703 lpcmi->lpParameters, 704 lpcmi->nShow, 705 0); 706 break; 707 case 2: 708 hr = InvokeSingleGvim(lpcmi->hwnd, 709 lpcmi->lpDirectory, 710 lpcmi->lpVerb, 711 lpcmi->lpParameters, 712 lpcmi->nShow, 713 1); 714 break; 715 } 716 } 717 } 718 return hr; 719 } 720 721 STDMETHODIMP CShellExt::PushToWindow(HWND hParent, 722 LPCSTR pszWorkingDir, 723 LPCSTR pszCmd, 724 LPCSTR pszParam, 725 int iShowCmd, 726 int idHWnd) 727 { 728 HWND hWnd = m_hWnd[idHWnd]; 729 730 // Show and bring vim instance to foreground 731 if (IsIconic(hWnd) != 0) 732 ShowWindow(hWnd, SW_RESTORE); 733 else 734 ShowWindow(hWnd, SW_SHOW); 735 SetForegroundWindow(hWnd); 736 737 // Post the selected files to the vim instance 738 PostMessage(hWnd, WM_DROPFILES, (WPARAM)medium.hGlobal, 0); 739 740 return NOERROR; 741 } 742 743 STDMETHODIMP CShellExt::GetCommandString(UINT_PTR idCmd, 744 UINT uFlags, 745 UINT FAR *reserved, 746 LPSTR pszName, 747 UINT cchMax) 748 { 749 if (uFlags == GCS_HELPTEXT && cchMax > 35) 750 lstrcpy(pszName, _("Edits the selected file(s) with Vim")); 751 752 return NOERROR; 753 } 754 755 BOOL CALLBACK CShellExt::EnumWindowsProc(HWND hWnd, LPARAM lParam) 756 { 757 char temp[BUFSIZE]; 758 759 // First do a bunch of check 760 // No invisible window 761 if (!IsWindowVisible(hWnd)) return TRUE; 762 // No child window ??? 763 // if (GetParent(hWnd)) return TRUE; 764 // Class name should be Vim, if failed to get class name, return 765 if (GetClassName(hWnd, temp, sizeof(temp)) == 0) 766 return TRUE; 767 // Compare class name to that of vim, if not, return 768 if (_strnicmp(temp, "vim", sizeof("vim")) != 0) 769 return TRUE; 770 // First check if the number of vim instance exceeds MAX_HWND 771 CShellExt *cs = (CShellExt*) lParam; 772 if (cs->m_cntOfHWnd >= MAX_HWND) return TRUE; 773 // Now we get the vim window, put it into some kind of array 774 cs->m_hWnd[cs->m_cntOfHWnd] = hWnd; 775 cs->m_cntOfHWnd ++; 776 777 return TRUE; // continue enumeration (otherwise this would be false) 778 } 779 780 #ifdef WIN32 781 // This symbol is not defined in older versions of the SDK or Visual C++. 782 783 #ifndef VER_PLATFORM_WIN32_WINDOWS 784 # define VER_PLATFORM_WIN32_WINDOWS 1 785 #endif 786 787 static DWORD g_PlatformId; 788 789 // 790 // Set g_PlatformId to VER_PLATFORM_WIN32_NT (NT) or 791 // VER_PLATFORM_WIN32_WINDOWS (Win95). 792 // 793 static void 794 PlatformId(void) 795 { 796 static int done = FALSE; 797 798 if (!done) 799 { 800 OSVERSIONINFO ovi; 801 802 ovi.dwOSVersionInfoSize = sizeof(ovi); 803 GetVersionEx(&ovi); 804 805 g_PlatformId = ovi.dwPlatformId; 806 done = TRUE; 807 } 808 } 809 810 # ifndef __BORLANDC__ 811 static char * 812 searchpath(char *name) 813 { 814 static char widename[2 * BUFSIZE]; 815 static char location[2 * BUFSIZE + 2]; 816 817 // There appears to be a bug in FindExecutableA() on Windows NT. 818 // Use FindExecutableW() instead... 819 PlatformId(); 820 if (g_PlatformId == VER_PLATFORM_WIN32_NT) 821 { 822 MultiByteToWideChar(CP_ACP, 0, (LPCTSTR)name, -1, 823 (LPWSTR)widename, BUFSIZE); 824 if (FindExecutableW((LPCWSTR)widename, (LPCWSTR)"", 825 (LPWSTR)location) > (HINSTANCE)32) 826 { 827 WideCharToMultiByte(CP_ACP, 0, (LPWSTR)location, -1, 828 (LPSTR)widename, 2 * BUFSIZE, NULL, NULL); 829 return widename; 830 } 831 } 832 else 833 { 834 if (FindExecutableA((LPCTSTR)name, (LPCTSTR)"", 835 (LPTSTR)location) > (HINSTANCE)32) 836 return location; 837 } 838 return ""; 839 } 840 # endif 841 #endif 842 843 STDMETHODIMP CShellExt::InvokeGvim(HWND hParent, 844 LPCSTR pszWorkingDir, 845 LPCSTR pszCmd, 846 LPCSTR pszParam, 847 int iShowCmd) 848 { 849 char m_szFileUserClickedOn[BUFSIZE]; 850 char cmdStr[BUFSIZE]; 851 UINT i; 852 853 for (i = 0; i < cbFiles; i++) 854 { 855 DragQueryFile((HDROP)medium.hGlobal, 856 i, 857 m_szFileUserClickedOn, 858 sizeof(m_szFileUserClickedOn)); 859 860 getGvimName(cmdStr, 0); 861 strcat(cmdStr, " \""); 862 863 if ((strlen(cmdStr) + strlen(m_szFileUserClickedOn) + 2) < BUFSIZE) 864 { 865 strcat(cmdStr, m_szFileUserClickedOn); 866 strcat(cmdStr, "\""); 867 868 STARTUPINFO si; 869 PROCESS_INFORMATION pi; 870 871 ZeroMemory(&si, sizeof(si)); 872 si.cb = sizeof(si); 873 874 // Start the child process. 875 if (!CreateProcess(NULL, // No module name (use command line). 876 cmdStr, // Command line. 877 NULL, // Process handle not inheritable. 878 NULL, // Thread handle not inheritable. 879 FALSE, // Set handle inheritance to FALSE. 880 0, // No creation flags. 881 NULL, // Use parent's environment block. 882 NULL, // Use parent's starting directory. 883 &si, // Pointer to STARTUPINFO structure. 884 &pi) // Pointer to PROCESS_INFORMATION structure. 885 ) 886 { 887 MessageBox( 888 hParent, 889 _("Error creating process: Check if gvim is in your path!"), 890 _("gvimext.dll error"), 891 MB_OK); 892 } 893 else 894 { 895 CloseHandle( pi.hProcess ); 896 CloseHandle( pi.hThread ); 897 } 898 } 899 else 900 { 901 MessageBox( 902 hParent, 903 _("Path length too long!"), 904 _("gvimext.dll error"), 905 MB_OK); 906 } 907 } 908 909 return NOERROR; 910 } 911 912 913 STDMETHODIMP CShellExt::InvokeSingleGvim(HWND hParent, 914 LPCSTR pszWorkingDir, 915 LPCSTR pszCmd, 916 LPCSTR pszParam, 917 int iShowCmd, 918 int useDiff) 919 { 920 char m_szFileUserClickedOn[BUFSIZE]; 921 char *cmdStr; 922 size_t cmdlen; 923 size_t len; 924 UINT i; 925 926 cmdlen = BUFSIZE; 927 cmdStr = (char *)malloc(cmdlen); 928 getGvimName(cmdStr, 0); 929 if (useDiff) 930 strcat(cmdStr, " -d"); 931 for (i = 0; i < cbFiles; i++) 932 { 933 DragQueryFile((HDROP)medium.hGlobal, 934 i, 935 m_szFileUserClickedOn, 936 sizeof(m_szFileUserClickedOn)); 937 938 len = strlen(cmdStr) + strlen(m_szFileUserClickedOn) + 4; 939 if (len > cmdlen) 940 { 941 cmdlen = len + BUFSIZE; 942 cmdStr = (char *)realloc(cmdStr, cmdlen); 943 } 944 strcat(cmdStr, " \""); 945 strcat(cmdStr, m_szFileUserClickedOn); 946 strcat(cmdStr, "\""); 947 } 948 949 STARTUPINFO si; 950 PROCESS_INFORMATION pi; 951 952 ZeroMemory(&si, sizeof(si)); 953 si.cb = sizeof(si); 954 955 // Start the child process. 956 if (!CreateProcess(NULL, // No module name (use command line). 957 cmdStr, // Command line. 958 NULL, // Process handle not inheritable. 959 NULL, // Thread handle not inheritable. 960 FALSE, // Set handle inheritance to FALSE. 961 0, // No creation flags. 962 NULL, // Use parent's environment block. 963 NULL, // Use parent's starting directory. 964 &si, // Pointer to STARTUPINFO structure. 965 &pi) // Pointer to PROCESS_INFORMATION structure. 966 ) 967 { 968 MessageBox( 969 hParent, 970 _("Error creating process: Check if gvim is in your path!"), 971 _("gvimext.dll error"), 972 MB_OK); 973 } 974 else 975 { 976 CloseHandle(pi.hProcess); 977 CloseHandle(pi.hThread); 978 } 979 980 free(cmdStr); 981 982 return NOERROR; 983 } 984