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