Skip to content

Commit

Permalink
Better DLL detection, synchronous command execution
Browse files Browse the repository at this point in the history
 * Added a libversion() function to the DLL which is used by the Vim
   script to verify that the DLL exists & can be loaded and that there
   isn't a version mismatch between the Vim script and the DLL.

 * Added the option of waiting for the external command to finish
   by removing execute() from the DLL and adding execute_synchronous()
   as well as execute_asynchronous(). The Vim script still contains the
   single function xolox#shell#execute() however it used to take
   printf()-style variable arguments and now always requires two
   arguments: the command to execute and whether to wait for the command
   to finish, where 1 = yes and 0 = no.

 * Because the Vim script already contained code to build command-lines
   but that functionality was removed from the xolox#shell#execute()
   function I've decided to make the function used to build
   command-lines public. This way there's no loss in functionality, only
   ease of use.
  • Loading branch information
xolox committed Jun 14, 2010
1 parent 5045306 commit 82f6cb0
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 40 deletions.
64 changes: 42 additions & 22 deletions autoload.vim
@@ -1,6 +1,6 @@
" Vim autoload plug-in
" Maintainer: Peter Odding <peter@peterodding.com>
" Last Change: June 13, 2010
" Last Change: June 14, 2010
" URL: http://peterodding.com/code/vim/shell

" This Vim script enables tighter integration between Vim and its environment
Expand Down Expand Up @@ -52,16 +52,17 @@ function! xolox#shell#openurl(url) " {{{1
throw printf(s:enoimpl, s:script, 'openurl', s:contact)
endfunction

function! xolox#shell#execute(cmd, ...) " {{{1
let cmd = s:make_cmdline(a:cmd, a:000)
function! xolox#shell#execute(command, synchronous) " {{{1
if s:is_windows() && s:has_dll()
let error = s:library_call('execute', cmd)
let fn = 'execute_' . (a:synchronous ? '' : 'a') . 'synchronous'
let error = s:library_call(fn, a:command)
if error != ''
let msg = '%s: execute(%s) failed! (error: %s)'
throw printf(msg, s:script, strtrans(cmd), strtrans(error))
let msg = '%s: %s(%s) failed! (error: %s)'
throw printf(msg, s:script, fn, strtrans(a:command), strtrans(error))
endif
else
if has('unix')
let cmd = a:command
if has('unix') && !a:synchronous
let cmd = '(' . cmd . ') &'
endif
let output = system(cmd)
Expand Down Expand Up @@ -109,6 +110,16 @@ function! xolox#shell#fullscreen() " {{{1
return 0
endfunction

function! xolox#shell#buildcmd(cmd, args) " {{{1
if a:args == []
return a:cmd
else
let args = map(copy(a:args), 'shellescape(v:val)')
call insert(args, a:cmd, 0)
return call('printf', args)
endif
endfunction

" Supporting functions. {{{1

function! s:is_windows() " {{{2
Expand All @@ -119,28 +130,37 @@ if s:is_windows()

let s:library = expand('<sfile>:p:h') . '\shell.dll'

function! s:has_dll() " {{{2
return filereadable(s:library)
endfunction

function! s:library_call(fn, arg) " {{{2
return libcall(s:library, a:fn, a:arg)
endfunction

endif
function! s:find_dll_version() " {{{2
try
return s:library_call('libversion', '')
catch
let msg = "%s: Failed to load %s DLL!"
let lib = fnamemodify(s:library, ':~')
echohl warningmsg
echomsg printf(msg, s:script, lib)
echohl none
endtry
return '?'
endfunction

function! s:make_cmdline(cmd, args) " {{{2
if a:args == []
return a:cmd
else
let args = map(copy(a:args), 'shellescape(v:val)')
call insert(args, a:cmd, 0)
return call('printf', args)
endif
endfunction
function! s:has_dll() " {{{2
" Check that the DLL is available using libversion() before calling any of
" the other functions. This is only done the first time this plug-in needs
" to call the DLL and also makes sure the right version is loaded.
if !exists('s:library_version')
let s:library_version = s:find_dll_version()
endif
return s:library_version == '0.2'
endfunction

endif

function! s:execute(cmd, args) " {{{2
let cmd = s:make_cmdline(a:cmd, a:args)
let cmd = xolox#shell#buildcmd(a:cmd, a:args)
let output = system(cmd)
call s:handle_error(cmd, output)
return output
Expand Down
62 changes: 44 additions & 18 deletions dll/shell.c
@@ -1,9 +1,10 @@
/* This is a dynamic link library for Vim on Windows that makes the following
* features available to Vim:
*
* - Open the user's preferred web browser with a given URL;
* - Execute external programs *without* showing a command prompt;
* - Toggle Vim's full-screen state using a bit of Windows API magic.
* - Open the user's preferred web browser with a given URL;
* - Execute external commands *without* showing a command prompt,
* optionally waiting for the command to finish;
* - Toggle Vim's full-screen state using a bit of Windows API magic.
*
* If you want to compile this library yourself you need to have the Microsoft
* Windows SDK installed, you can find a download link on the following web
Expand All @@ -14,14 +15,14 @@
*
* Open the Windows SDK command prompt and run the following command:
*
* CL /LD shell.c shell32.lib user32.lib
* CL /LD shell.c shell32.lib user32.lib
*
* This should create the dynamic link library "shell.dll" which you can call
* from Vim using for example :call libcall('c:/shell.dll', 'fullscreen', 1).
*
* Happy vimming!
*
* - Peter Odding <peter@peterodding.com>
* - Peter Odding <peter@peterodding.com>
*/

#define _WIN32_WINNT 0x0500 /* GetConsoleWindow() */
Expand All @@ -35,7 +36,7 @@ static char buffer[1024 * 10];
#undef MessageBox
#define MessageBox(message) MessageBoxA(NULL, message, "Vim Library", 0)

static char *GetError(void)
static char *GetError(void) /* {{{1 */
{
int i;

Expand All @@ -49,42 +50,67 @@ static char *GetError(void)
return buffer;
}

static char *Success(char *result)
static char *Success(char *result) /* {{{1 */
{
/* printf("OK\n"); */
return result;
}

static char *Failure(char *result)
static char *Failure(char *result) /* {{{1 */
{
/* if (result && strlen(result)) MessageBox(result); */
return result;
}

__declspec(dllexport)
char *execute(char *command)
static char *execute(char *command, int wait) /* {{{1 */
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
if (CreateProcess(0, command, 0, 0, 0, CREATE_NO_WINDOW, 0, 0, &si, &pi)) {
if (wait) {
WaitForSingleObject(pi.hProcess, INFINITE);
/* long exit_code; */
/* TODO: GetExitCodeProcess( pi.hProcess, &exit_code); */
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return Success(NULL);
} else {
return Failure(GetError());
}
}

__declspec(dllexport)
char *openurl(char *path)
char *execute_synchronous(char *command) /* {{{1 */
{
return execute(command, 1);
}

__declspec(dllexport)
char *execute_asynchronous(char *command) /* {{{1 */
{
return execute(command, 0);
}

__declspec(dllexport)
char *libversion(char *ignored) /* {{{1 */
{
(void)ignored;
return Success("0.2");
}

__declspec(dllexport)
char *openurl(char *path) /* {{{1 */
{
ShellExecute(NULL, "open", path, NULL, NULL, SW_SHOWNORMAL);
return Success(NULL);
}

__declspec(dllexport)
char *fullscreen(long enable)
char *fullscreen(long enable) /* {{{1 */
{
HWND window;
LONG styles;
Expand All @@ -98,7 +124,7 @@ char *fullscreen(long enable)
return Failure("Could not query window styles!");

if (enable) styles ^= WS_CAPTION | WS_THICKFRAME;
else styles |= WS_CAPTION | WS_THICKFRAME;
else styles |= WS_CAPTION | WS_THICKFRAME;

if (!SetWindowLong(window, GWL_STYLE, styles))
return Failure("Could not apply window styles!");
Expand All @@ -122,13 +148,13 @@ char *fullscreen(long enable)
return Success(NULL);
}

/* TODO: The quest to embedding a command prompt in Vim :)
* This doesn't quite work and I'm afraid it never will.
*/

__declspec(dllexport)
char *console(char *command)
char *console(char *command) /* {{{1 */
{
/* TODO: The quest to embedding a command prompt in Vim :)
* This doesn't quite work and I'm afraid it never will.
*/

HWND gvim, console;
LONG styles;

Expand Down

0 comments on commit 82f6cb0

Please sign in to comment.