mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-02 17:53:37 +00:00
695 lines
17 KiB
C
695 lines
17 KiB
C
/*
|
|
* msvcrt.dll spawn/exec functions
|
|
*
|
|
* Copyright 1996,1998 Marcus Meissner
|
|
* Copyright 1996 Jukka Iivonen
|
|
* Copyright 1997,2000 Uwe Bonnes
|
|
* Copyright 2000 Jon Griffiths
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*
|
|
* FIXME:
|
|
* -File handles need some special handling. Sometimes children get
|
|
* open file handles, sometimes not. The docs are confusing
|
|
* -No check for maximum path/argument/environment size is done
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "msvcrt.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msvcrt);
|
|
|
|
/* INTERNAL: Spawn a child process */
|
|
static MSVCRT_intptr_t msvcrt_spawn(int flags, const char* exe, char* cmdline, char* env)
|
|
{
|
|
STARTUPINFOA si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
if ((unsigned)flags > MSVCRT__P_DETACH)
|
|
{
|
|
*MSVCRT__errno() = MSVCRT_EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
memset(&si, 0, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
msvcrt_create_io_inherit_block(&si);
|
|
if (!CreateProcessA(exe, cmdline, NULL, NULL, TRUE,
|
|
flags == MSVCRT__P_DETACH ? DETACHED_PROCESS : 0,
|
|
env, NULL, &si, &pi))
|
|
{
|
|
msvcrt_set_errno(GetLastError());
|
|
MSVCRT_free(si.lpReserved2);
|
|
return -1;
|
|
}
|
|
|
|
MSVCRT_free(si.lpReserved2);
|
|
switch(flags)
|
|
{
|
|
case MSVCRT__P_WAIT:
|
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
|
GetExitCodeProcess(pi.hProcess,&pi.dwProcessId);
|
|
CloseHandle(pi.hProcess);
|
|
CloseHandle(pi.hThread);
|
|
return pi.dwProcessId;
|
|
case MSVCRT__P_DETACH:
|
|
CloseHandle(pi.hProcess);
|
|
pi.hProcess = 0;
|
|
/* fall through */
|
|
case MSVCRT__P_NOWAIT:
|
|
case MSVCRT__P_NOWAITO:
|
|
CloseHandle(pi.hThread);
|
|
return (MSVCRT_intptr_t)pi.hProcess;
|
|
case MSVCRT__P_OVERLAY:
|
|
MSVCRT__exit(0);
|
|
}
|
|
return -1; /* can't reach here */
|
|
}
|
|
|
|
/* INTERNAL: Convert argv list to a single 'delim'-separated string, with an
|
|
* extra '\0' to terminate it
|
|
*/
|
|
static char* msvcrt_argvtos(const char* const* arg, char delim)
|
|
{
|
|
const char* const* a;
|
|
long size;
|
|
char* p;
|
|
char* ret;
|
|
|
|
if (!arg && !delim)
|
|
{
|
|
/* Return NULL for an empty environment list */
|
|
return NULL;
|
|
}
|
|
|
|
/* get length */
|
|
a = arg;
|
|
size = 0;
|
|
while (*a)
|
|
{
|
|
size += strlen(*a) + 1;
|
|
a++;
|
|
}
|
|
|
|
ret = MSVCRT_malloc(size + 1);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
/* fill string */
|
|
a = arg;
|
|
p = ret;
|
|
while (*a)
|
|
{
|
|
int len = strlen(*a);
|
|
memcpy(p,*a,len);
|
|
p += len;
|
|
*p++ = delim;
|
|
a++;
|
|
}
|
|
if (delim && p > ret) p[-1] = 0;
|
|
else *p = 0;
|
|
return ret;
|
|
}
|
|
|
|
/* INTERNAL: Convert va_list to a single 'delim'-separated string, with an
|
|
* extra '\0' to terminate it
|
|
*/
|
|
static char* msvcrt_valisttos(const char* arg0, va_list alist, char delim)
|
|
{
|
|
va_list alist2;
|
|
long size;
|
|
const char *arg;
|
|
char* p;
|
|
char *ret;
|
|
|
|
#ifdef HAVE_VA_COPY
|
|
va_copy(alist2,alist);
|
|
#else
|
|
# ifdef HAVE___VA_COPY
|
|
__va_copy(alist2,alist);
|
|
# else
|
|
alist2 = alist;
|
|
# endif
|
|
#endif
|
|
|
|
if (!arg0)
|
|
{
|
|
/* Return NULL for an empty environment list */
|
|
return NULL;
|
|
}
|
|
|
|
/* get length */
|
|
arg = arg0;
|
|
size = 0;
|
|
do {
|
|
size += strlen(arg) + 1;
|
|
arg = va_arg(alist, char*);
|
|
} while (arg != NULL);
|
|
|
|
ret = MSVCRT_malloc(size + 1);
|
|
if (!ret)
|
|
return NULL;
|
|
|
|
/* fill string */
|
|
arg = arg0;
|
|
p = ret;
|
|
do {
|
|
int len = strlen(arg);
|
|
memcpy(p,arg,len);
|
|
p += len;
|
|
*p++ = delim;
|
|
arg = va_arg(alist2, char*);
|
|
} while (arg != NULL);
|
|
if (delim && p > ret) p[-1] = 0;
|
|
else *p = 0;
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _cwait (MSVCRT.@)
|
|
*/
|
|
MSVCRT_intptr_t CDECL _cwait(int *status, MSVCRT_intptr_t pid, int action)
|
|
{
|
|
HANDLE hPid = (HANDLE)pid;
|
|
int doserrno;
|
|
|
|
action = action; /* Remove warning */
|
|
|
|
if (!WaitForSingleObject(hPid, INFINITE))
|
|
{
|
|
if (status)
|
|
{
|
|
DWORD stat;
|
|
GetExitCodeProcess(hPid, &stat);
|
|
*status = (int)stat;
|
|
}
|
|
return pid;
|
|
}
|
|
doserrno = GetLastError();
|
|
|
|
if (doserrno == ERROR_INVALID_HANDLE)
|
|
{
|
|
*MSVCRT__errno() = MSVCRT_ECHILD;
|
|
*MSVCRT___doserrno() = doserrno;
|
|
}
|
|
else
|
|
msvcrt_set_errno(doserrno);
|
|
|
|
return status ? *status = -1 : -1;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execl (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _execl(const char* name, const char* arg0, ...)
|
|
{
|
|
va_list ap;
|
|
char * args;
|
|
MSVCRT_intptr_t ret;
|
|
|
|
va_start(ap, arg0);
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
va_end(ap);
|
|
|
|
ret = msvcrt_spawn(MSVCRT__P_OVERLAY, name, args, NULL);
|
|
MSVCRT_free(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execle (MSVCRT.@)
|
|
*/
|
|
MSVCRT_intptr_t CDECL _execle(const char* name, const char* arg0, ...)
|
|
{
|
|
FIXME("stub\n");
|
|
return -1;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execlp (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _execlp(const char* name, const char* arg0, ...)
|
|
{
|
|
va_list ap;
|
|
char * args;
|
|
MSVCRT_intptr_t ret;
|
|
char fullname[MAX_PATH];
|
|
|
|
_searchenv(name, "PATH", fullname);
|
|
|
|
va_start(ap, arg0);
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
va_end(ap);
|
|
|
|
ret = msvcrt_spawn(MSVCRT__P_OVERLAY, fullname[0] ? fullname : name, args, NULL);
|
|
MSVCRT_free(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execlpe (MSVCRT.@)
|
|
*/
|
|
MSVCRT_intptr_t CDECL _execlpe(const char* name, const char* arg0, ...)
|
|
{
|
|
FIXME("stub\n");
|
|
return -1;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execv (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _execv(const char* name, char* const* argv)
|
|
{
|
|
return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execve (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL MSVCRT__execve(const char* name, char* const* argv, const char* const* envv)
|
|
{
|
|
return _spawnve(MSVCRT__P_OVERLAY, name, (const char* const*) argv, envv);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execvpe (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _execvpe(const char* name, char* const* argv, const char* const* envv)
|
|
{
|
|
char fullname[MAX_PATH];
|
|
|
|
_searchenv(name, "PATH", fullname);
|
|
return _spawnve(MSVCRT__P_OVERLAY, fullname[0] ? fullname : name,
|
|
(const char* const*) argv, envv);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _execvp (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _execvp(const char* name, char* const* argv)
|
|
{
|
|
return _execvpe(name, argv, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _spawnl (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnl(int flags, const char* name, const char* arg0, ...)
|
|
{
|
|
va_list ap;
|
|
char * args;
|
|
MSVCRT_intptr_t ret;
|
|
|
|
va_start(ap, arg0);
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
va_end(ap);
|
|
|
|
ret = msvcrt_spawn(flags, name, args, NULL);
|
|
MSVCRT_free(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _spawnle (MSVCRT.@)
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnle(int flags, const char* name, const char* arg0, ...)
|
|
{
|
|
va_list ap;
|
|
char *args, *envs = NULL;
|
|
const char * const *envp;
|
|
MSVCRT_intptr_t ret;
|
|
|
|
va_start(ap, arg0);
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
va_end(ap);
|
|
|
|
va_start(ap, arg0);
|
|
while (va_arg( ap, char * ) != NULL) /*nothing*/;
|
|
envp = va_arg( ap, const char * const * );
|
|
if (envp) envs = msvcrt_argvtos(envp, 0);
|
|
va_end(ap);
|
|
|
|
ret = msvcrt_spawn(flags, name, args, envs);
|
|
|
|
MSVCRT_free(args);
|
|
MSVCRT_free(envs);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*********************************************************************
|
|
* _spawnlp (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnlp(int flags, const char* name, const char* arg0, ...)
|
|
{
|
|
va_list ap;
|
|
char * args;
|
|
MSVCRT_intptr_t ret;
|
|
char fullname[MAX_PATH];
|
|
|
|
_searchenv(name, "PATH", fullname);
|
|
|
|
va_start(ap, arg0);
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
va_end(ap);
|
|
|
|
ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, NULL);
|
|
MSVCRT_free(args);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _spawnlpe (MSVCRT.@)
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnlpe(int flags, const char* name, const char* arg0, ...)
|
|
{
|
|
va_list ap;
|
|
char *args, *envs = NULL;
|
|
const char * const *envp;
|
|
MSVCRT_intptr_t ret;
|
|
char fullname[MAX_PATH];
|
|
|
|
_searchenv(name, "PATH", fullname);
|
|
|
|
va_start(ap, arg0);
|
|
args = msvcrt_valisttos(arg0, ap, ' ');
|
|
va_end(ap);
|
|
|
|
va_start(ap, arg0);
|
|
while (va_arg( ap, char * ) != NULL) /*nothing*/;
|
|
envp = va_arg( ap, const char * const * );
|
|
if (envp) envs = msvcrt_argvtos(envp, 0);
|
|
va_end(ap);
|
|
|
|
ret = msvcrt_spawn(flags, fullname[0] ? fullname : name, args, envs);
|
|
|
|
MSVCRT_free(args);
|
|
MSVCRT_free(envs);
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _spawnve (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnve(int flags, const char* name, const char* const* argv,
|
|
const char* const* envv)
|
|
{
|
|
char * args = msvcrt_argvtos(argv,' ');
|
|
char * envs = msvcrt_argvtos(envv,0);
|
|
char fullname[MAX_PATH];
|
|
const char *p;
|
|
int len;
|
|
MSVCRT_intptr_t ret = -1;
|
|
|
|
TRACE(":call (%s), params (%s), env (%s)\n",debugstr_a(name),debugstr_a(args),
|
|
envs?"Custom":"Null");
|
|
|
|
/* no check for NULL name.
|
|
native doesn't do it */
|
|
|
|
p = memchr(name, '\0', MAX_PATH);
|
|
if( !p )
|
|
p = name + MAX_PATH - 1;
|
|
len = p - name;
|
|
|
|
/* extra-long names are silently truncated. */
|
|
memcpy(fullname, name, len);
|
|
|
|
for( p--; p >= name; p-- )
|
|
{
|
|
if( *p == '\\' || *p == '/' || *p == ':' || *p == '.' )
|
|
break;
|
|
}
|
|
|
|
/* if no extension is given, assume .exe */
|
|
if( (p < name || *p != '.') && len <= MAX_PATH - 5 )
|
|
{
|
|
FIXME("only trying .exe when no extension given\n");
|
|
memcpy(fullname+len, ".exe", 4);
|
|
len += 4;
|
|
}
|
|
|
|
fullname[len] = '\0';
|
|
|
|
if (args)
|
|
{
|
|
ret = msvcrt_spawn(flags, fullname, args, envs);
|
|
MSVCRT_free(args);
|
|
}
|
|
MSVCRT_free(envs);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _spawnv (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnv(int flags, const char* name, const char* const* argv)
|
|
{
|
|
return _spawnve(flags, name, argv, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _spawnvpe (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnvpe(int flags, const char* name, const char* const* argv,
|
|
const char* const* envv)
|
|
{
|
|
char fullname[MAX_PATH];
|
|
_searchenv(name, "PATH", fullname);
|
|
return _spawnve(flags, fullname[0] ? fullname : name, argv, envv);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _spawnvp (MSVCRT.@)
|
|
*
|
|
* Like on Windows, this function does not handle arguments with spaces
|
|
* or double-quotes.
|
|
*/
|
|
MSVCRT_intptr_t CDECL _spawnvp(int flags, const char* name, const char* const* argv)
|
|
{
|
|
return _spawnvpe(flags, name, argv, NULL);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _popen (MSVCRT.@)
|
|
* FIXME: convert to _wpopen and call that from here instead? But it
|
|
* would have to convert the command back to ANSI to call msvcrt_spawn,
|
|
* less than ideal.
|
|
*/
|
|
MSVCRT_FILE* CDECL MSVCRT__popen(const char* command, const char* mode)
|
|
{
|
|
static const char wcmd[] = "cmd", cmdFlag[] = " /C ", comSpec[] = "COMSPEC";
|
|
MSVCRT_FILE *ret;
|
|
BOOL readPipe = TRUE;
|
|
int textmode, fds[2], fdToDup, fdToOpen, fdStdHandle = -1, fdStdErr = -1;
|
|
const char *p;
|
|
char *cmdcopy;
|
|
DWORD comSpecLen;
|
|
|
|
TRACE("(command=%s, mode=%s)\n", debugstr_a(command), debugstr_a(mode));
|
|
|
|
if (!command || !mode)
|
|
return NULL;
|
|
|
|
textmode = *__p__fmode() & (MSVCRT__O_BINARY | MSVCRT__O_TEXT);
|
|
for (p = mode; *p; p++)
|
|
{
|
|
switch (*p)
|
|
{
|
|
case 'W':
|
|
case 'w':
|
|
readPipe = FALSE;
|
|
break;
|
|
case 'B':
|
|
case 'b':
|
|
textmode |= MSVCRT__O_BINARY;
|
|
textmode &= ~MSVCRT__O_TEXT;
|
|
break;
|
|
case 'T':
|
|
case 't':
|
|
textmode |= MSVCRT__O_TEXT;
|
|
textmode &= ~MSVCRT__O_BINARY;
|
|
break;
|
|
}
|
|
}
|
|
if (_pipe(fds, 0, textmode) == -1)
|
|
return NULL;
|
|
|
|
fdToDup = readPipe ? 1 : 0;
|
|
fdToOpen = readPipe ? 0 : 1;
|
|
|
|
if ((fdStdHandle = _dup(fdToDup)) == -1)
|
|
goto error;
|
|
if (_dup2(fds[fdToDup], fdToDup) != 0)
|
|
goto error;
|
|
if (readPipe)
|
|
{
|
|
if ((fdStdErr = _dup(MSVCRT_STDERR_FILENO)) == -1)
|
|
goto error;
|
|
if (_dup2(fds[fdToDup], MSVCRT_STDERR_FILENO) != 0)
|
|
goto error;
|
|
}
|
|
|
|
_close(fds[fdToDup]);
|
|
|
|
comSpecLen = GetEnvironmentVariableA(comSpec, NULL, 0);
|
|
if (!comSpecLen)
|
|
comSpecLen = strlen(wcmd) + 1;
|
|
cmdcopy = HeapAlloc(GetProcessHeap(), 0, comSpecLen + strlen(cmdFlag)
|
|
+ strlen(command));
|
|
if (!GetEnvironmentVariableA(comSpec, cmdcopy, comSpecLen))
|
|
strcpy(cmdcopy, wcmd);
|
|
strcat(cmdcopy, cmdFlag);
|
|
strcat(cmdcopy, command);
|
|
if (msvcrt_spawn(MSVCRT__P_NOWAIT, NULL, cmdcopy, NULL) == -1)
|
|
{
|
|
_close(fds[fdToOpen]);
|
|
ret = NULL;
|
|
}
|
|
else
|
|
{
|
|
ret = MSVCRT__fdopen(fds[fdToOpen], mode);
|
|
if (!ret)
|
|
_close(fds[fdToOpen]);
|
|
}
|
|
HeapFree(GetProcessHeap(), 0, cmdcopy);
|
|
_dup2(fdStdHandle, fdToDup);
|
|
_close(fdStdHandle);
|
|
if (readPipe)
|
|
{
|
|
_dup2(fdStdErr, MSVCRT_STDERR_FILENO);
|
|
_close(fdStdErr);
|
|
}
|
|
return ret;
|
|
|
|
error:
|
|
if (fdStdHandle != -1) _close(fdStdHandle);
|
|
if (fdStdErr != -1) _close(fdStdErr);
|
|
_close(fds[0]);
|
|
_close(fds[1]);
|
|
return NULL;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _wpopen (MSVCRT.@)
|
|
*/
|
|
MSVCRT_FILE* CDECL MSVCRT__wpopen(const MSVCRT_wchar_t* command, const MSVCRT_wchar_t* mode)
|
|
{
|
|
FIXME("(command=%s, mode=%s): stub\n", debugstr_w(command), debugstr_w(mode));
|
|
return NULL;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _pclose (MSVCRT.@)
|
|
*/
|
|
int CDECL MSVCRT__pclose(MSVCRT_FILE* file)
|
|
{
|
|
return MSVCRT_fclose(file);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* system (MSVCRT.@)
|
|
*/
|
|
int CDECL MSVCRT_system(const char* cmd)
|
|
{
|
|
char* cmdcopy;
|
|
int res;
|
|
|
|
/* Make a writable copy for CreateProcess */
|
|
cmdcopy=_strdup(cmd);
|
|
/* FIXME: should probably launch cmd interpreter in COMSPEC */
|
|
res=msvcrt_spawn(MSVCRT__P_WAIT, NULL, cmdcopy, NULL);
|
|
MSVCRT_free(cmdcopy);
|
|
return res;
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _loaddll (MSVCRT.@)
|
|
*/
|
|
MSVCRT_intptr_t CDECL _loaddll(const char* dllname)
|
|
{
|
|
return (MSVCRT_intptr_t)LoadLibraryA(dllname);
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _unloaddll (MSVCRT.@)
|
|
*/
|
|
int CDECL _unloaddll(MSVCRT_intptr_t dll)
|
|
{
|
|
if (FreeLibrary((HMODULE)dll))
|
|
return 0;
|
|
else
|
|
{
|
|
int err = GetLastError();
|
|
msvcrt_set_errno(err);
|
|
return err;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************
|
|
* _getdllprocaddr (MSVCRT.@)
|
|
*/
|
|
void * CDECL _getdllprocaddr(MSVCRT_intptr_t dll, const char *name, int ordinal)
|
|
{
|
|
if (name)
|
|
{
|
|
if (ordinal != -1) return NULL;
|
|
return GetProcAddress( (HMODULE)dll, name );
|
|
}
|
|
if (HIWORD(ordinal)) return NULL;
|
|
return GetProcAddress( (HMODULE)dll, (LPCSTR)(ULONG_PTR)ordinal );
|
|
}
|