Implemented CreateToolhelp32Snapshot, including two of the routines
using snapshots, Process32First and Process32Next.

diff --git a/include/process.h b/include/process.h
index 46eb24a..51e6207 100644
--- a/include/process.h
+++ b/include/process.h
@@ -108,6 +108,8 @@
     LCID             locale;           /* c4 Locale to be queried by GetThreadLocale (NT) */
     /* The following are Wine-specific fields */
     void            *server_pid;       /*    Server id for this process */
+    struct _PDB32   *list_next;        /*    List reference - list of PDB's */
+    struct _PDB32   *list_prev;        /*    List reference - list of PDB's */
 } PDB32;
 
 /* Process flags */
@@ -157,5 +159,9 @@
                               PROCESS_INFORMATION *info );
 extern void PROCESS_SuspendOtherThreads(void);
 extern void PROCESS_ResumeOtherThreads(void);
+extern int  	PROCESS_PDBList_Getsize (void);
+extern PDB32*  	PROCESS_PDBList_Getfirst (void);
+extern PDB32*  	PROCESS_PDBList_Getnext (PDB32*);
 
 #endif  /* __WINE_PROCESS_H */
+
diff --git a/include/tlhelp32.h b/include/tlhelp32.h
new file mode 100644
index 0000000..b8b798a
--- /dev/null
+++ b/include/tlhelp32.h
@@ -0,0 +1,29 @@
+#ifndef __WINE_TLHELP32_H
+#define __WINE_TLHELP32_H
+
+#include "windows.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+  /*===================================================================
+   *  Arguments for Toolhelp routines
+   */
+
+  /*
+   * CreateToolhelp32Snapshot
+   */
+
+#define TH32CS_SNAPHEAPLIST 0x00000001
+#define TH32CS_SNAPPROCESS  0x00000002
+#define TH32CS_SNAPTHREAD   0x00000004
+#define TH32CS_SNAPMODULE   0x00000008
+#define TH32CS_SNAPALL     (TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE)
+#define TH32CS_INHERIT     0x80000000
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* __WINE_TLHELP32_H */
diff --git a/include/toolhelp.h b/include/toolhelp.h
index bb0a572..f2b1629 100644
--- a/include/toolhelp.h
+++ b/include/toolhelp.h
@@ -2,10 +2,13 @@
 #define __WINE_TOOLHELP_H
 
 #include "windows.h"
+#include "tlhelp32.h"
 
 #define MAX_DATA	11
 #define MAX_MODULE_NAME	9
+#ifndef MAX_PATH
 #define MAX_PATH	255
+#endif
 #define MAX_CLASSNAME	255
 
 #pragma pack(1)
diff --git a/misc/toolhelp.c b/misc/toolhelp.c
index c3cb60a..387727f 100644
--- a/misc/toolhelp.c
+++ b/misc/toolhelp.c
@@ -8,11 +8,54 @@
 #include <string.h>
 #include <unistd.h>
 #include <ctype.h>
+#include <assert.h>
 #include "windows.h"
 #include "win.h"
+#include "winerror.h"
+#include "tlhelp32.h"
 #include "toolhelp.h"
 #include "debug.h"
 #include "heap.h"
+#include "process.h"
+#include "k32obj.h"
+
+/*
+ * Support for toolhelp's snapshots.  They
+ * are supposed to be Kernel32 Objects.
+ * Only the Destroy() method is implemented
+ */
+
+static void SNAPSHOT_Destroy( K32OBJ *obj );
+
+const K32OBJ_OPS SNAPSHOT_Ops =
+{
+  NULL,    		/* signaled */
+  NULL,   		/* satisfied */
+  NULL,     		/* add_wait */
+  NULL,  		/* remove_wait */
+  NULL,			/* read */
+  NULL,			/* write */
+  SNAPSHOT_Destroy	/* destroy */
+};
+
+/* The K32 snapshot object object */
+/* Process snapshot kernel32 object */
+typedef struct _Process32Snapshot
+{
+  K32OBJ		header;
+
+  DWORD			numProcs;
+  DWORD			arrayCounter;
+  /*
+   * Store a reference to the PDB list.  
+   * Insuure in the alloc and dealloc routines for this structure that
+   * I increment and decrement the pdb->head.refcount, so that the
+   * original pdb will stay around for as long as I use it, but it's
+   * not locked forver into memory.
+   */
+  PDB32		**processArray;
+}
+SNAPSHOT_OBJECT;
 
 /* FIXME: to make this working, we have to callback all these registered 
  * functions from all over the WINE code. Someone with more knowledge than
@@ -103,10 +146,298 @@
 }
 
 /***********************************************************************
+ *	     SNAPSHOT_Destroy
+ *
+ * Deallocate K32 snapshot objects
+ */
+static void SNAPSHOT_Destroy (K32OBJ *obj)
+{
+  int	i;
+  SNAPSHOT_OBJECT *snapshot = (SNAPSHOT_OBJECT *) obj;
+  assert (obj->type == K32OBJ_CHANGE);
+
+  if (snapshot->processArray)
+    {	
+      for (i = 0; snapshot->processArray[i] && i <snapshot->numProcs; i++)
+	{
+	  K32OBJ_DecCount (&snapshot->processArray[i]->header);
+	}
+      HeapFree (GetProcessHeap (), 0, snapshot->processArray);
+      snapshot->processArray = NULL;      
+    }	
+
+  obj->type = K32OBJ_UNKNOWN;
+  HeapFree (GetProcessHeap (), 0, snapshot);
+}
+
+/***********************************************************************
  *           CreateToolHelp32Snapshot			(KERNEL32.179)
  *	see "Undocumented Windows"
  */
-HANDLE32 WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD th32ProcessID) {
-	FIXME(toolhelp,"(0x%08lx,0x%08lx), stub!\n",dwFlags,th32ProcessID);
-	return INVALID_HANDLE_VALUE32;
+HANDLE32 WINAPI CreateToolhelp32Snapshot(DWORD dwFlags, DWORD
+					 th32ProcessID) 
+{
+  HANDLE32		ssHandle;
+  SNAPSHOT_OBJECT	*snapshot;
+  int			numProcesses;
+  int			i;
+  PDB32*		pdb;
+
+  printf ("%x & TH32CS_INHERIT (%x) = %x %s\n", dwFlags, 
+	  TH32CS_INHERIT, TH32CS_INHERIT, 
+	  dwFlags & TH32CS_INHERIT, 
+	  dwFlags & TH32CS_INHERIT ? "TRUE" : "FALSE");  
+  printf ("%x & TH32CS_SNAPHEAPLIST (%x) = %x %s\n", dwFlags, 
+	  TH32CS_SNAPHEAPLIST, TH32CS_SNAPHEAPLIST, 
+	  dwFlags & TH32CS_SNAPHEAPLIST, 
+	  dwFlags & TH32CS_SNAPHEAPLIST ? "TRUE" : "FALSE");  
+  printf ("%x & TH32CS_SNAPMODULE (%x) = %x %s\n", dwFlags, 
+	  TH32CS_SNAPMODULE, TH32CS_SNAPMODULE, 
+	  dwFlags & TH32CS_SNAPMODULE, 
+	  dwFlags & TH32CS_SNAPMODULE ? "TRUE" : "FALSE");  
+  printf ("%x & TH32CS_SNAPPROCESS (%x) = %x %s\n", dwFlags, 
+	  TH32CS_SNAPPROCESS, TH32CS_SNAPPROCESS, 
+	  dwFlags & TH32CS_SNAPPROCESS, 
+	  dwFlags & TH32CS_SNAPPROCESS ? "TRUE" : "FALSE");  
+  printf ("%x & TH32CS_SNAPTHREAD (%x) = %x %s\n", dwFlags, 
+	  TH32CS_SNAPTHREAD, TH32CS_SNAPTHREAD, 
+	  dwFlags & TH32CS_SNAPTHREAD, 
+	  dwFlags & TH32CS_SNAPTHREAD ? "TRUE" : "FALSE");  
+
+  /**** FIXME: Not implmented ***/
+  if (dwFlags & TH32CS_INHERIT)
+    {
+      FIXME(toolhelp,"(0x%08lx (TH32CS_INHERIT),0x%08lx), stub!\n",
+	    dwFlags,th32ProcessID);
+
+      return INVALID_HANDLE_VALUE32;
+    }
+  if (dwFlags & TH32CS_SNAPHEAPLIST)
+    {
+      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPHEAPLIST),0x%08lx), stub!\n",
+	    dwFlags,th32ProcessID);
+      return INVALID_HANDLE_VALUE32;
+    }
+  if (dwFlags & TH32CS_SNAPMODULE)
+    {
+      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPMODULE),0x%08lx), stub!\n",
+	    dwFlags,th32ProcessID);
+      return INVALID_HANDLE_VALUE32;
+    }
+
+  if (dwFlags & TH32CS_SNAPPROCESS)
+    {
+      TRACE (toolhelp, "(0x%08lx (TH32CS_SNAPMODULE),0x%08lx)\n",
+	    dwFlags,th32ProcessID);
+      snapshot = HeapAlloc (GetProcessHeap (), 0, sizeof
+			    (SNAPSHOT_OBJECT)); 
+      if (!snapshot)
+	{
+	  return INVALID_HANDLE_VALUE32;
+	}
+
+      snapshot->header.type = K32OBJ_TOOLHELP_SNAPSHOT;
+      snapshot->header.refcount = 1;
+      snapshot->arrayCounter = 0;
+  
+      /*
+       * Lock here, to prevent processes from being created or
+       * destroyed while the snapshot is gathered
+       */
+
+      SYSTEM_LOCK ();
+      numProcesses = PROCESS_PDBList_Getsize ();
+
+      snapshot->processArray = (PDB32**) 
+	HeapAlloc (GetProcessHeap (), 0, sizeof (PDB32*) * numProcesses); 
+
+      if (!snapshot->processArray)
+        {
+          HeapFree (GetProcessHeap (), 0, snapshot->processArray);
+	  SetLastError (INVALID_HANDLE_VALUE32);
+	  ERR (toolhelp, "Error allocating %d bytes for snapshot\n", 
+	       sizeof (PDB32*) * numProcesses); 	       
+          return INVALID_HANDLE_VALUE32;
+        }
+
+      snapshot->numProcs = numProcesses;
+
+      pdb = PROCESS_PDBList_Getfirst ();
+      for (i = 0; pdb && i < numProcesses; i++)
+	{
+	  TRACE (toolhelp, "Saving ref to pdb %ld\n", PDB_TO_PROCESS_ID(pdb));
+	  snapshot->processArray[i] = pdb;
+	  K32OBJ_IncCount (&pdb->header);
+	  pdb = PROCESS_PDBList_Getnext (pdb);
+	}
+      SYSTEM_UNLOCK ();
+
+      ssHandle = HANDLE_Alloc (PROCESS_Current (), &snapshot->header,
+			       FILE_ALL_ACCESS, TRUE, -1);
+      if (ssHandle == INVALID_HANDLE_VALUE32) 
+	{
+	  /*  HANDLE_Alloc is supposed to deallocate the 
+	   *  heap memory if it fails.  This code doesn't need to.
+	   */
+	  SetLastError (INVALID_HANDLE_VALUE32);
+	  ERR (toolhelp, "Error allocating handle\n");
+	  return INVALID_HANDLE_VALUE32;
+	}
+      
+      TRACE (toolhelp, "snapshotted %d processes, expected %d\n",
+	    i, numProcesses);
+      return ssHandle;
+    }
+
+  if (dwFlags & TH32CS_SNAPTHREAD)
+    {
+      FIXME(toolhelp,"(0x%08lx (TH32CS_SNAPMODULE),0x%08lx), stub!\n",
+	    dwFlags,th32ProcessID);
+      return INVALID_HANDLE_VALUE32;
+    }
+
+  return INVALID_HANDLE_VALUE32;
+}
+
+/***********************************************************************
+ *		Process32First
+ * Return info about the first process in a toolhelp32 snapshot
+ */
+BOOL32 WINAPI Process32First(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
+{
+  PDB32           *pdb; 
+  SNAPSHOT_OBJECT *snapshot;
+  int             i;
+
+  TRACE (toolhelp, "(0x%08lx,0x%08lx)\n", (DWORD) hSnapshot,
+	 (DWORD) lppe);
+
+  if (lppe->dwSize < sizeof (PROCESSENTRY32))
+    {
+      SetLastError (ERROR_INSUFFICIENT_BUFFER);
+      ERR (toolhelp, "Result buffer too small\n");
+      return FALSE;
+    }
+
+  SYSTEM_LOCK ();
+  snapshot = (SNAPSHOT_OBJECT*) HANDLE_GetObjPtr (PROCESS_Current (),
+						  hSnapshot,
+						  K32OBJ_UNKNOWN,
+						  FILE_ALL_ACCESS,
+						  NULL); 
+  if (!snapshot)
+    {
+      SYSTEM_UNLOCK ();
+      SetLastError (ERROR_INVALID_HANDLE);
+      ERR (toolhelp, "Error retreiving snapshot\n");
+      return FALSE;
+    }
+
+  snapshot->arrayCounter = i = 0;
+  pdb = snapshot->processArray[i];
+
+  if (!pdb)
+    {
+      SetLastError (ERROR_NO_MORE_FILES);
+      ERR (toolhelp, "End of snapshot array\n");
+      return FALSE;
+    }
+
+  TRACE (toolhelp, "Returning info on process %d, id %ld\n", 
+	 i, PDB_TO_PROCESS_ID (pdb));
+
+  lppe->cntUsage = 1;
+  lppe->th32ProcessID = PDB_TO_PROCESS_ID (pdb);
+  lppe->th32DefaultHeapID = (DWORD) pdb->heap; 
+  lppe->cntThreads = pdb->threads; 
+  lppe->th32ParentProcessID = PDB_TO_PROCESS_ID (pdb->parent); 
+  lppe->pcPriClassBase = 6; /* FIXME: this is a made-up value */
+  lppe->dwFlags = -1;       /* FIXME: RESERVED by Microsoft :-) */
+  if (pdb->exe_modref) 
+    {
+      lppe->th32ModuleID = (DWORD) pdb->exe_modref->module;
+      strncpy (lppe->szExeFile, pdb->exe_modref->longname, 
+	       sizeof (lppe->szExeFile)); 
+    }
+  else
+    {
+      lppe->th32ModuleID = (DWORD) 0;
+      strcpy (lppe->szExeFile, "");
+    }
+
+  SYSTEM_UNLOCK ();
+
+  return TRUE;
+}
+
+/***********************************************************************
+ *		Process32Next
+ * Return info about the "next" process in a toolhelp32 snapshot
+ */
+BOOL32 WINAPI Process32Next(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
+{
+  PDB32           *pdb; 
+  SNAPSHOT_OBJECT *snapshot;
+  int             i;
+
+  TRACE (toolhelp, "(0x%08lx,0x%08lx)\n", (DWORD) hSnapshot,
+	 (DWORD) lppe);
+
+  if (lppe->dwSize < sizeof (PROCESSENTRY32))
+    {
+      SetLastError (ERROR_INSUFFICIENT_BUFFER);
+      ERR (toolhelp, "Result buffer too small\n");
+      return FALSE;
+    }
+
+  SYSTEM_LOCK ();
+  snapshot = (SNAPSHOT_OBJECT*) HANDLE_GetObjPtr (PROCESS_Current (),
+						  hSnapshot,
+						  K32OBJ_UNKNOWN,
+						  FILE_ALL_ACCESS,
+						  NULL); 
+  if (!snapshot)
+    {
+      SYSTEM_UNLOCK ();
+      SetLastError (ERROR_INVALID_HANDLE);
+      ERR (toolhelp, "Error retreiving snapshot\n");
+      return FALSE;
+    }
+
+  snapshot->arrayCounter ++;
+  i = snapshot->arrayCounter;
+  pdb = snapshot->processArray[i];
+
+  if (!pdb || snapshot->arrayCounter >= snapshot->numProcs)
+    {
+      SetLastError (ERROR_NO_MORE_FILES);
+      ERR (toolhelp, "End of snapshot array\n");
+      return FALSE;
+    }
+
+  TRACE (toolhelp, "Returning info on process %d, id %ld\n", 
+	 i, PDB_TO_PROCESS_ID (pdb));
+
+  lppe->cntUsage = 1;
+  lppe->th32ProcessID = PDB_TO_PROCESS_ID (pdb);
+  lppe->th32DefaultHeapID = (DWORD) pdb->heap; 
+  lppe->cntThreads = pdb->threads; 
+  lppe->th32ParentProcessID = PDB_TO_PROCESS_ID (pdb->parent); 
+  lppe->pcPriClassBase = 6; /* FIXME: this is a made-up value */
+  lppe->dwFlags = -1;       /* FIXME: RESERVED by Microsoft :-) */
+  if (pdb->exe_modref) 
+    {
+      lppe->th32ModuleID = (DWORD) pdb->exe_modref->module;
+      strncpy (lppe->szExeFile, pdb->exe_modref->longname, 
+	       sizeof (lppe->szExeFile)); 
+    }
+  else
+    {
+      lppe->th32ModuleID = (DWORD) 0;
+      strcpy (lppe->szExeFile, "");
+    }
+
+  SYSTEM_UNLOCK ();
+
+  return TRUE;
 }
diff --git a/scheduler/k32obj.c b/scheduler/k32obj.c
index 7eaab60..f6543bf 100644
--- a/scheduler/k32obj.c
+++ b/scheduler/k32obj.c
@@ -23,6 +23,7 @@
 extern const K32OBJ_OPS MEM_MAPPED_FILE_Ops;
 extern const K32OBJ_OPS DEVICE_Ops;
 extern const K32OBJ_OPS CONSOLE_Ops;
+extern const K32OBJ_OPS SNAPSHOT_Ops;
 
 static const K32OBJ_OPS K32OBJ_NullOps =
 {
diff --git a/scheduler/process.c b/scheduler/process.c
index 19d7503..1432ea7 100644
--- a/scheduler/process.c
+++ b/scheduler/process.c
@@ -22,7 +22,6 @@
 #include "task.h"
 #include "server.h"
 #include "debug.h"
-#include "toolhelp.h"
 
 static BOOL32 PROCESS_Signaled( K32OBJ *obj, DWORD thread_id );
 static BOOL32 PROCESS_Satisfied( K32OBJ *obj, DWORD thread_id );
@@ -42,7 +41,8 @@
 };
 
 static DWORD PROCESS_InitialProcessID = 0;
-
+static PDB32 *PROCESS_PDBList = NULL;
+static DWORD PROCESS_PDBList_Size = 0;
 
 /***********************************************************************
  *           PROCESS_Current
@@ -177,6 +177,105 @@
     return TRUE;
 }
 
+/***********************************************************************
+ *	     PROCESS_PDBList_Insert
+ * Insert this PDB into the global PDB list
+ */
+
+static void PROCESS_PDBList_Insert (PDB32 *pdb)
+{
+  TRACE (process, "Inserting PDB 0x%0lx, #%ld current\n", 
+	 PDB_TO_PROCESS_ID (pdb), PROCESS_PDBList_Size);
+
+  SYSTEM_LOCK (); 	/* FIXME: Do I need to worry about this ?
+			 * I.e., could more than one process be
+			 * created at once ?
+			 */
+  if (PROCESS_PDBList == NULL)
+    {
+      PROCESS_PDBList = pdb;
+      pdb->list_next = NULL;
+      pdb->list_prev = NULL;
+    }
+  else
+    {
+      PDB32 *first = PROCESS_PDBList, *last = PROCESS_PDBList;
+      if (first->list_prev) last = first->list_prev;
+
+      PROCESS_PDBList = pdb;
+      pdb->list_next = first;
+      pdb->list_prev = last;
+      last->list_next = pdb;
+      first->list_prev = pdb;
+    }
+  PROCESS_PDBList_Size ++;
+  SYSTEM_UNLOCK ();
+}
+
+/***********************************************************************
+ *	     PROCESS_PDBList_Remove
+ * Remove this PDB from the global PDB list
+ */
+
+static void PROCESS_PDBList_Remove (PDB32 *pdb)
+{
+  PDB32 *next = pdb->list_next, *prev = pdb->list_prev;
+  
+  TRACE (process, "Removing PDB 0x%0lx, #%ld current\n", 
+	 PDB_TO_PROCESS_ID (pdb), PROCESS_PDBList_Size);
+
+  SYSTEM_LOCK ();
+
+  if (prev == next)
+    {
+      next->list_prev = NULL;
+      next->list_next = NULL;
+    }
+  else
+    {
+      if (next) next->list_prev = prev;
+      if (prev) prev->list_next = next;
+    }
+  
+  if (pdb == PROCESS_PDBList)
+    {
+      PROCESS_PDBList = next ? next : prev;
+    }
+  PROCESS_PDBList_Size --;
+
+  SYSTEM_UNLOCK ();
+}
+
+/***********************************************************************
+ *	     PROCESS_PDBList_Getsize
+ * Return the number of items in the global PDB list
+ */
+
+int	PROCESS_PDBList_Getsize ()
+{
+  return PROCESS_PDBList_Size;
+}
+
+/***********************************************************************
+ * 	     PROCESS_PDBList_Getfirst
+ * Return the head of the PDB list
+ */
+
+PDB32*	PROCESS_PDBList_Getfirst ()
+{
+  return PROCESS_PDBList;
+}
+
+/***********************************************************************
+ * 	     PROCESS_PDBList_Getnext
+ * Return the "next" pdb as referenced from the argument.
+ * If at the end of the list, return NULL.
+ */
+
+PDB32*	PROCESS_PDBList_Getnext (PDB32 *pdb)
+{
+  return (pdb->list_next != PROCESS_PDBList) ? pdb->list_next : NULL;
+}
 
 /***********************************************************************
  *           PROCESS_FreePDB
@@ -185,6 +284,13 @@
  */
 static void PROCESS_FreePDB( PDB32 *pdb )
 {
+    /*
+     * FIXME: 
+     * If this routine is called because PROCESS_CreatePDB fails, the
+     * following call to PROCESS_PDBList_Remove will probably screw
+     * up.  
+     */
+    PROCESS_PDBList_Remove (pdb);
     pdb->header.type = K32OBJ_UNKNOWN;
     if (pdb->handle_table) HANDLE_CloseAll( pdb, NULL );
     ENV_FreeEnvironment( pdb );
@@ -230,6 +336,8 @@
 
     if (!HANDLE_CreateTable( pdb, TRUE )) goto error;
 
+    PROCESS_PDBList_Insert (pdb);
+
     return pdb;
 
 error:
@@ -934,17 +1042,3 @@
     SYSTEM_UNLOCK();
 }
 
-BOOL32 WINAPI Process32First(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
-{
-  FIXME (process, "(0x%08x,%p), stub!\n", hSnapshot, lppe);
-  SetLastError (ERROR_NO_MORE_FILES);
-  return FALSE;
-}
-
-BOOL32 WINAPI Process32Next(HANDLE32 hSnapshot, LPPROCESSENTRY32 lppe)
-{
-  FIXME (process, "(0x%08x,%p), stub!\n", hSnapshot, lppe);
-  SetLastError (ERROR_NO_MORE_FILES);
-  return FALSE;
-}
-