1 /* Asynchronous subprocess implementation for Win32
2 Copyright (C) 1985, 1986, 1987, 1988, 1992, 1993, 1994, 1995
3 Free Software Foundation, Inc.
4 Copyright (C) 1995 Sun Microsystems, Inc.
5 Copyright (C) 1995, 1996 Ben Wing.
7 This file is part of XEmacs.
9 XEmacs is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by the
11 Free Software Foundation; either version 2, or (at your option) any
14 XEmacs is distributed in the hope that it will be useful, but WITHOUT
15 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 You should have received a copy of the GNU General Public License
20 along with XEmacs; see the file COPYING. If not, write to
21 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 Boston, MA 02111-1307, USA. */
24 /* Written by Kirill M. Katsnelson <kkm@kis.ru>, April 1998 */
46 /* Arbitrary size limit for code fragments passed to run_in_other_process */
47 #define FRAGMENT_CODE_SIZE 32
49 /* Bound by winnt.el */
50 Lisp_Object Qnt_quote_process_args;
52 /* Implementation-specific data. Pointed to by Lisp_Process->process_data */
53 struct nt_process_data
58 #define NT_DATA(p) ((struct nt_process_data*)((p)->process_data))
60 /*-----------------------------------------------------------------------*/
62 /*-----------------------------------------------------------------------*/
64 /* This one breaks process abstraction. Prototype is in console-msw.h,
65 used by select_process method in event-msw.c */
67 get_nt_process_handle (struct Lisp_Process *p)
69 return (NT_DATA (p)->h_process);
72 /*-----------------------------------------------------------------------*/
73 /* Running remote threads. See Microsoft Systems Journal 1994 Number 5 */
74 /* Jeffrey Richter, Load Your 32-bit DLL into Another Process's Address..*/
75 /*-----------------------------------------------------------------------*/
85 * Allocate SIZE bytes in H_PROCESS address space. Fill in PMC used
86 * further by other routines. Return nonzero if successful.
88 * The memory in other process is allocated by creating a suspended
89 * thread. Initial stack of that thread is used as the memory
90 * block. The thread entry point is the routine ExitThread in
91 * kernel32.dll, so the allocated memory is freed just by resuming the
92 * thread, which immediately terminates after that.
96 alloc_process_memory (HANDLE h_process, size_t size,
99 LPTHREAD_START_ROUTINE adr_ExitThread =
100 (LPTHREAD_START_ROUTINE)
101 GetProcAddress (GetModuleHandle ("kernel32"), "ExitThread");
104 MEMORY_BASIC_INFORMATION mbi;
106 pmc->h_process = h_process;
107 pmc->h_thread = CreateRemoteThread (h_process, NULL, size,
108 adr_ExitThread, NULL,
109 CREATE_SUSPENDED, &dw_unused);
110 if (pmc->h_thread == NULL)
113 /* Get context, for thread's stack pointer */
114 context.ContextFlags = CONTEXT_CONTROL;
115 if (!GetThreadContext (pmc->h_thread, &context))
118 /* Determine base address of the committed range */
119 if (sizeof(mbi) != VirtualQueryEx (h_process,
121 (LPDWORD)context.Esp - 1,
122 #elif defined (_ALPHA_)
123 (LPDWORD)context.IntSp - 1,
125 #error Unknown processor architecture
130 /* Change the page protection of the allocated memory to executable,
132 if (!VirtualProtectEx (h_process, mbi.BaseAddress, size,
133 PAGE_EXECUTE_READWRITE, &dw_unused))
136 pmc->address = mbi.BaseAddress;
140 ResumeThread (pmc->h_thread);
146 free_process_memory (process_memory* pmc)
148 ResumeThread (pmc->h_thread);
152 * Run ROUTINE in the context of process determined by H_PROCESS. The
153 * routine is passed the address of DATA as parameter. The ROUTINE must
154 * not be longer than ROUTINE_CODE_SIZE bytes. DATA_SIZE is the size of
157 * Note that the code must be positionally independent, and compiled
158 * without stack checks (they cause implicit calls into CRT so will
159 * fail). DATA should not refer any data in calling process, as both
160 * routine and its data are copied into remote process. Size of data
161 * and code together should not exceed one page (4K on x86 systems).
163 * Return the value returned by ROUTINE, or (DWORD)-1 if call failed.
166 run_in_other_process (HANDLE h_process,
167 LPTHREAD_START_ROUTINE routine,
168 LPVOID data, size_t data_size)
171 CONST size_t code_size = FRAGMENT_CODE_SIZE;
172 /* Need at most 3 extra bytes of memory, for data alignment */
173 size_t total_size = code_size + data_size + 3;
178 /* Allocate memory */
179 if (!alloc_process_memory (h_process, total_size, &pm))
183 if (!WriteProcessMemory (h_process, pm.address, (LPVOID)routine,
190 remote_data = (LPBYTE)pm.address + ((code_size + 4) & ~3);
191 if (!WriteProcessMemory (h_process, remote_data, data, data_size, NULL))
197 /* Execute the remote copy of code, passing it remote data */
198 h_thread = CreateRemoteThread (h_process, NULL, 0,
199 (LPTHREAD_START_ROUTINE) pm.address,
200 remote_data, 0, &dw_unused);
201 if (h_thread == NULL)
204 /* Wait till thread finishes */
205 WaitForSingleObject (h_thread, INFINITE);
207 /* Free remote memory */
208 free_process_memory (&pm);
210 /* Return thread's exit code */
213 GetExitCodeThread (h_thread, &exit_code);
214 CloseHandle (h_thread);
219 free_process_memory (&pm);
223 /*-----------------------------------------------------------------------*/
224 /* Sending signals */
225 /*-----------------------------------------------------------------------*/
228 * We handle the following signals:
230 * SIGKILL, SIGTERM, SIGQUIT, SIGHUP - These four translate to ExitProcess
231 * executed by the remote process
232 * SIGINT - The remote process is sent CTRL_BREAK_EVENT
234 * The MSVC5.0 compiler feels free to re-order functions within a
235 * compilation unit, so we have no way of finding out the size of the
236 * following functions. Therefore these functions must not be larger than
237 * FRAGMENT_CODE_SIZE.
245 void (WINAPI *adr_ExitProcess) (UINT);
249 sigkill_proc (sigkill_data* data)
251 (*data->adr_ExitProcess)(255);
256 * Sending break or control c
260 BOOL (WINAPI *adr_GenerateConsoleCtrlEvent) (DWORD, DWORD);
265 sigint_proc (sigint_data* data)
267 return (*data->adr_GenerateConsoleCtrlEvent) (data->event, 0);
275 BOOL (WINAPI *adr_SetConsoleCtrlHandler) (LPVOID, BOOL);
279 sig_enable_proc (sig_enable_data* data)
281 (*data->adr_SetConsoleCtrlHandler) (NULL, FALSE);
286 * Send signal SIGNO to process H_PROCESS.
287 * Return nonzero if successful.
290 /* This code assigns a return value of GetProcAddress to function pointers
291 of many different types. Instead of heavy obscure casts, we just disable
292 warnings about assignments to different function pointer types. */
293 #pragma warning (disable : 4113)
296 send_signal (HANDLE h_process, int signo)
298 HMODULE h_kernel = GetModuleHandle ("kernel32");
301 assert (h_kernel != NULL);
311 d.adr_ExitProcess = GetProcAddress (h_kernel, "ExitProcess");
312 assert (d.adr_ExitProcess);
313 retval = run_in_other_process (h_process,
314 (LPTHREAD_START_ROUTINE)sigkill_proc,
321 d.adr_GenerateConsoleCtrlEvent =
322 GetProcAddress (h_kernel, "GenerateConsoleCtrlEvent");
323 assert (d.adr_GenerateConsoleCtrlEvent);
324 d.event = CTRL_C_EVENT;
325 retval = run_in_other_process (h_process,
326 (LPTHREAD_START_ROUTINE)sigint_proc,
334 return (int)retval > 0 ? 1 : 0;
338 * Enable CTRL_C_EVENT handling in a new child process
341 enable_child_signals (HANDLE h_process)
343 HMODULE h_kernel = GetModuleHandle ("kernel32");
346 assert (h_kernel != NULL);
347 d.adr_SetConsoleCtrlHandler =
348 GetProcAddress (h_kernel, "SetConsoleCtrlHandler");
349 assert (d.adr_SetConsoleCtrlHandler);
350 run_in_other_process (h_process, (LPTHREAD_START_ROUTINE)sig_enable_proc,
354 #pragma warning (default : 4113)
357 * Signal error if SIGNO is not supported
360 validate_signal_number (int signo)
362 if (signo != SIGKILL && signo != SIGTERM
363 && signo != SIGQUIT && signo != SIGINT
365 signal_simple_error ("Signal number not supported", make_int (signo));
368 /*-----------------------------------------------------------------------*/
369 /* Process methods */
370 /*-----------------------------------------------------------------------*/
373 * Allocate and initialize Lisp_Process->process_data
377 nt_alloc_process_data (struct Lisp_Process *p)
379 p->process_data = xnew_and_zero (struct nt_process_data);
383 nt_finalize_process_data (struct Lisp_Process *p, int for_disksave)
385 assert (!for_disksave);
386 if (NT_DATA(p)->h_process)
387 CloseHandle (NT_DATA(p)->h_process);
391 * Initialize XEmacs process implementation once
394 nt_init_process (void)
396 /* Initialize winsock */
398 /* Request Winsock v1.1 Note the order: (minor=1, major=1) */
399 WSAStartup (MAKEWORD (1,1), &wsa_data);
403 * Fork off a subprocess. P is a pointer to newly created subprocess
404 * object. If this function signals, the caller is responsible for
405 * deleting (and finalizing) the process object.
407 * The method must return PID of the new process, a (positive??? ####) number
408 * which fits into Lisp_Int. No return value indicates an error, the method
409 * must signal an error instead.
413 signal_cannot_launch (Lisp_Object image_file, DWORD err)
415 mswindows_set_errno (err);
416 signal_simple_error_2 ("Error starting", image_file, lisp_strerror (errno));
420 nt_create_process (struct Lisp_Process *p,
421 Lisp_Object *argv, int nargv,
422 Lisp_Object program, Lisp_Object cur_dir)
424 HANDLE hmyshove, hmyslurp, hprocin, hprocout;
426 BOOL do_io, windowed;
429 /* Find out whether the application is windowed or not */
431 /* SHGetFileInfo tends to return ERROR_FILE_NOT_FOUND on most
432 errors. This leads to bogus error message. */
434 char *p = strrchr ((char *)XSTRING_DATA (program), '.');
436 (stricmp (p, ".exe") == 0 ||
437 stricmp (p, ".com") == 0 ||
438 stricmp (p, ".bat") == 0 ||
439 stricmp (p, ".cmd") == 0))
441 image_type = SHGetFileInfo ((char *)XSTRING_DATA (program), 0,NULL,
446 char progname[MAX_PATH];
447 sprintf (progname, "%s.exe", (char *)XSTRING_DATA (program));
448 image_type = SHGetFileInfo (progname, 0, NULL, 0, SHGFI_EXETYPE);
451 signal_cannot_launch (program, (GetLastError () == ERROR_FILE_NOT_FOUND
452 ? ERROR_BAD_FORMAT : GetLastError ()));
453 windowed = HIWORD (image_type) != 0;
456 /* Decide whether to do I/O on process handles, or just mark the
457 process exited immediately upon successful launching. We do I/O if the
458 process is a console one, or if it is windowed but windowed_process_io
460 do_io = !windowed || windowed_process_io ;
464 /* Create two unidirectional named pipes */
466 SECURITY_ATTRIBUTES sa;
468 sa.nLength = sizeof(sa);
469 sa.bInheritHandle = TRUE;
470 sa.lpSecurityDescriptor = NULL;
472 CreatePipe (&hprocin, &hmyshove, &sa, 0);
473 CreatePipe (&hmyslurp, &hprocout, &sa, 0);
475 /* Stupid Win32 allows to create a pipe with *both* ends either
476 inheritable or not. We need process ends inheritable, and local
477 ends not inheritable. */
478 DuplicateHandle (GetCurrentProcess(), hmyshove, GetCurrentProcess(), &htmp,
479 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
481 DuplicateHandle (GetCurrentProcess(), hmyslurp, GetCurrentProcess(), &htmp,
482 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
486 /* Convert an argv vector into Win32 style command line by a call to
487 lisp function `nt-quote-process-args' which see (in winnt.el)*/
490 Lisp_Object args_or_ret = Qnil;
493 GCPRO1 (args_or_ret);
495 for (i = 0; i < nargv; ++i)
496 args_or_ret = Fcons (*argv++, args_or_ret);
497 args_or_ret = Fnreverse (args_or_ret);
498 args_or_ret = Fcons (program, args_or_ret);
500 args_or_ret = call1 (Qnt_quote_process_args, args_or_ret);
502 if (!STRINGP (args_or_ret))
503 /* Luser wrote his/her own clever version */
504 error ("Bogus return value from `nt-quote-process-args'");
506 command_line = alloca_array (char, (XSTRING_LENGTH (program)
507 + XSTRING_LENGTH (args_or_ret) + 2));
508 strcpy (command_line, XSTRING_DATA (program));
509 strcat (command_line, " ");
510 strcat (command_line, XSTRING_DATA (args_or_ret));
512 UNGCPRO; /* args_or_ret */
515 /* Set `proc_env' to a nul-separated array of the strings in
516 Vprocess_environment terminated by 2 nuls. */
519 extern int compare_env (const char **strp1, const char **strp2);
521 REGISTER Lisp_Object tem;
522 REGISTER char **new_env;
523 REGISTER int new_length = 0, i, new_space;
526 for (tem = Vprocess_environment;
528 && STRINGP (XCAR (tem)));
532 /* new_length + 1 to include terminating 0. */
533 env = new_env = alloca_array (char *, new_length + 1);
535 /* Copy the Vprocess_environment strings into new_env. */
536 for (tem = Vprocess_environment;
538 && STRINGP (XCAR (tem)));
542 char *string = (char *) XSTRING_DATA (XCAR (tem));
543 /* See if this string duplicates any string already in the env.
544 If so, don't put it in.
545 When an env var has multiple definitions,
546 we keep the definition that comes first in process-environment. */
547 for (; ep != new_env; ep++)
549 char *p = *ep, *q = string;
553 /* The string is malformed; might as well drop it. */
567 /* Sort the environment variables */
568 new_length = new_env - env;
569 qsort (env, new_length, sizeof (char *), compare_env);
571 /* Work out how much space to allocate */
573 for (i = 0; i < new_length; i++)
575 new_space += strlen(env[i]) + 1;
579 /* Allocate space and copy variables into it */
580 penv = proc_env = alloca(new_space);
581 for (i = 0; i < new_length; i++)
583 strcpy(penv, env[i]);
584 penv += strlen(env[i]) + 1;
592 PROCESS_INFORMATION pi;
596 si.dwFlags = STARTF_USESHOWWINDOW;
597 si.wShowWindow = windowed ? SW_SHOWNORMAL : SW_HIDE;
600 si.hStdInput = hprocin;
601 si.hStdOutput = hprocout;
602 si.hStdError = hprocout;
603 si.dwFlags |= STARTF_USESTDHANDLES;
606 err = (CreateProcess (NULL, command_line, NULL, NULL, TRUE,
607 CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP
609 proc_env, (char *) XSTRING_DATA (cur_dir), &si, &pi)
610 ? 0 : GetLastError ());
614 /* These just have been inherited; we do not need a copy */
615 CloseHandle (hprocin);
616 CloseHandle (hprocout);
619 /* Handle process creation failure */
624 CloseHandle (hmyshove);
625 CloseHandle (hmyslurp);
627 signal_cannot_launch (program, GetLastError ());
630 /* The process started successfully */
633 NT_DATA(p)->h_process = pi.hProcess;
634 init_process_io_handles (p, (void*)hmyslurp, (void*)hmyshove, 0);
638 /* Indicate as if the process has exited immediately. */
639 p->status_symbol = Qexit;
640 CloseHandle (pi.hProcess);
644 enable_child_signals (pi.hProcess);
646 ResumeThread (pi.hThread);
647 CloseHandle (pi.hThread);
649 /* Hack to support Windows 95 negative pids */
650 return ((int)pi.dwProcessId < 0
651 ? -(int)pi.dwProcessId : (int)pi.dwProcessId);
656 * This method is called to update status fields of the process
657 * structure. If the process has not existed, this method is expected
660 * The method is called only for real child processes.
664 nt_update_status_if_terminated (struct Lisp_Process* p)
667 if (GetExitCodeProcess (NT_DATA(p)->h_process, &exit_code)
668 && exit_code != STILL_ACTIVE)
672 /* The exit code can be a code returned by process, or an
673 NTSTATUS value. We cannot accurately handle the latter since
674 it is a full 32 bit integer */
675 if (exit_code & 0xC0000000)
677 p->status_symbol = Qsignal;
678 p->exit_code = exit_code & 0x1FFFFFFF;
682 p->status_symbol = Qexit;
683 p->exit_code = exit_code;
689 * Stuff the entire contents of LSTREAM to the process output pipe
692 /* #### If only this function could be somehow merged with
693 unix_send_process... */
696 nt_send_process (Lisp_Object proc, struct lstream* lstream)
698 struct Lisp_Process *p = XPROCESS (proc);
700 /* use a reasonable-sized buffer (somewhere around the size of the
701 stream buffer) so as to avoid inundating the stream with blocked
703 Bufbyte chunkbuf[128];
710 chunklen = Lstream_read (lstream, chunkbuf, 128);
712 break; /* perhaps should abort() if < 0?
713 This should never happen. */
715 /* Lstream_write() will never successfully write less than the
716 amount sent in. In the worst case, it just buffers the
718 writeret = Lstream_write (XLSTREAM (DATA_OUTSTREAM(p)), chunkbuf,
720 Lstream_flush (XLSTREAM (DATA_OUTSTREAM(p)));
723 p->status_symbol = Qexit;
724 p->exit_code = ERROR_BROKEN_PIPE;
728 deactivate_process (proc);
729 error ("Broken pipe error sending to process %s; closed it",
730 XSTRING_DATA (p->name));
735 while (Lstream_was_blocked_p (XLSTREAM (p->pipe_outstream)))
737 /* Buffer is full. Wait, accepting input; that may allow
738 the program to finish doing output and read more. */
739 Faccept_process_output (Qnil, Qzero, make_int (wait_ms));
740 Lstream_flush (XLSTREAM (p->pipe_outstream));
741 wait_ms = min (1000, 2 * wait_ms);
748 * Send a signal number SIGNO to PROCESS.
749 * CURRENT_GROUP means send to the process group that currently owns
750 * the terminal being used to communicate with PROCESS.
751 * This is used for various commands in shell mode.
752 * If NOMSG is zero, insert signal-announcements into process's buffers
755 * If we can, we try to signal PROCESS by sending control characters
756 * down the pty. This allows us to signal inferiors who have changed
757 * their uid, for which killpg would return an EPERM error.
759 * The method signals an error if the given SIGNO is not valid
763 nt_kill_child_process (Lisp_Object proc, int signo,
764 int current_group, int nomsg)
766 struct Lisp_Process *p = XPROCESS (proc);
768 /* Signal error if SIGNO cannot be sent */
769 validate_signal_number (signo);
772 if (!send_signal (NT_DATA(p)->h_process, signo))
773 error ("Cannot send signal to process");
777 * Kill any process in the system given its PID.
779 * Returns zero if a signal successfully sent, or
780 * negative number upon failure
783 nt_kill_process_by_pid (int pid, int signo)
788 /* Signal error if SIGNO cannot be sent */
789 validate_signal_number (signo);
791 /* Try to open the process with required privileges */
792 h_process = OpenProcess (PROCESS_CREATE_THREAD
793 | PROCESS_QUERY_INFORMATION
794 | PROCESS_VM_OPERATION
797 if (h_process == NULL)
800 send_result = send_signal (h_process, signo);
802 CloseHandle (h_process);
804 return send_result ? 0 : -1;
807 /*-----------------------------------------------------------------------*/
808 /* Sockets connections */
809 /*-----------------------------------------------------------------------*/
812 /* #### Hey MS, how long Winsock 2 for '95 will be in beta? */
814 #define SOCK_TIMER_ID 666
815 #define XM_SOCKREPLY (WM_USER + 666)
818 get_internet_address (Lisp_Object host, struct sockaddr_in *address,
821 char buf [MAXGETHOSTSTRUCT];
826 address->sin_family = AF_INET;
828 /* First check if HOST is already a numeric address */
830 unsigned long inaddr = inet_addr (XSTRING_DATA (host));
831 if (inaddr != INADDR_NONE)
833 address->sin_addr.s_addr = inaddr;
838 /* Create a window which will receive completion messages */
839 hwnd = CreateWindow ("STATIC", NULL, WS_OVERLAPPED, 0, 0, 1, 1,
840 NULL, NULL, NULL, NULL);
843 /* Post name resolution request */
844 hasync = WSAAsyncGetHostByName (hwnd, XM_SOCKREPLY, XSTRING_DATA (host),
849 /* Set a timer to poll for quit every 250 ms */
850 SetTimer (hwnd, SOCK_TIMER_ID, 250, NULL);
855 GetMessage (&msg, hwnd, 0, 0);
856 if (msg.message == XM_SOCKREPLY)
858 /* Ok, got an answer */
859 if (WSAGETASYNCERROR(msg.lParam) == NO_ERROR)
863 else if (msg.message == WM_TIMER && msg.wParam == SOCK_TIMER_ID)
867 WSACancelAsyncRequest (hasync);
868 KillTimer (hwnd, SOCK_TIMER_ID);
869 DestroyWindow (hwnd);
873 DispatchMessage (&msg);
877 KillTimer (hwnd, SOCK_TIMER_ID);
878 DestroyWindow (hwnd);
881 /* BUF starts with struct hostent */
882 struct hostent* he = (struct hostent*) buf;
883 address->sin_addr.s_addr = *(unsigned long*)he->h_addr_list[0];
889 nt_canonicalize_host_name (Lisp_Object host)
891 struct sockaddr_in address;
893 if (!get_internet_address (host, &address, ERROR_ME_NOT))
896 if (address.sin_family == AF_INET)
897 return build_string (inet_ntoa (address.sin_addr));
902 /* open a TCP network connection to a given HOST/SERVICE. Treated
903 exactly like a normal process when reading and writing. Only
904 differences are in status display and process deletion. A network
905 connection has no PID; you cannot signal it. All you can do is
906 deactivate and close it via delete-process */
909 nt_open_network_stream (Lisp_Object name, Lisp_Object host, Lisp_Object service,
910 Lisp_Object protocol, void** vinfd, void** voutfd)
912 struct sockaddr_in address;
919 if (!EQ (protocol, Qtcp))
920 error ("Unsupported protocol \"%s\"",
921 string_data (symbol_name (XSYMBOL (protocol))));
924 port = htons ((unsigned short) XINT (service));
927 struct servent *svc_info;
928 CHECK_STRING (service);
929 svc_info = getservbyname ((char *) XSTRING_DATA (service), "tcp");
931 error ("Unknown service \"%s\"", XSTRING_DATA (service));
932 port = svc_info->s_port;
935 get_internet_address (host, &address, ERROR_ME);
936 address.sin_port = port;
938 s = socket (address.sin_family, SOCK_STREAM, 0);
940 report_file_error ("error creating socket", list1 (name));
942 /* We don't want to be blocked on connect */
944 unsigned long nonblock = 1;
945 ioctlsocket (s, FIONBIO, &nonblock);
948 retval = connect (s, (struct sockaddr *) &address, sizeof (address));
949 if (retval != NO_ERROR && WSAGetLastError() != WSAEWOULDBLOCK)
952 /* Wait while connection is established */
965 /* Poll for quit every 250 ms */
967 tv.tv_usec = 250 * 1000;
971 nsel = select (0, NULL, &fdset, &fdset, &tv);
975 /* Check: was connection successful or not? */
977 nsel = select (0, NULL, NULL, &fdset, &tv);
985 /* We are connected at this point */
987 DuplicateHandle (GetCurrentProcess(), (HANDLE)s,
988 GetCurrentProcess(), (LPHANDLE)voutfd,
989 0, FALSE, DUPLICATE_SAME_ACCESS);
994 report_file_error ("connection failed", list2 (host, name));
999 /*-----------------------------------------------------------------------*/
1000 /* Initialization */
1001 /*-----------------------------------------------------------------------*/
1004 process_type_create_nt (void)
1006 PROCESS_HAS_METHOD (nt, alloc_process_data);
1007 PROCESS_HAS_METHOD (nt, finalize_process_data);
1008 PROCESS_HAS_METHOD (nt, init_process);
1009 PROCESS_HAS_METHOD (nt, create_process);
1010 PROCESS_HAS_METHOD (nt, update_status_if_terminated);
1011 PROCESS_HAS_METHOD (nt, send_process);
1012 PROCESS_HAS_METHOD (nt, kill_child_process);
1013 PROCESS_HAS_METHOD (nt, kill_process_by_pid);
1015 PROCESS_HAS_METHOD (nt, canonicalize_host_name);
1016 PROCESS_HAS_METHOD (nt, open_network_stream);
1017 #ifdef HAVE_MULTICAST
1018 #error I won't do this until '95 has winsock2
1019 PROCESS_HAS_METHOD (nt, open_multicast_group);
1025 syms_of_process_nt (void)
1027 defsymbol (&Qnt_quote_process_args, "nt-quote-process-args");
1031 vars_of_process_nt (void)