WDK Mini Filter Example
mspyUser.c
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) 1989-2002 Microsoft Corporation
4 
5 Module Name:
6 
7  mspyUser.c
8 
9 Abstract:
10 
11  This file contains the implementation for the main function of the
12  user application piece of MiniSpy. This function is responsible for
13  controlling the command mode available to the user to control the
14  kernel mode driver.
15 
16 Environment:
17 
18  User mode
19 
20 --*/
21 
22 #include <DriverSpecs.h>
23 _Analysis_mode_(_Analysis_code_type_user_code_)
24 
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <windows.h>
28 #include <assert.h>
29 #include "mspyLog.h"
30 #include <strsafe.h>
31 
32 #define SUCCESS 0
33 #define USAGE_ERROR 1
34 #define EXIT_INTERPRETER 2
35 #define EXIT_PROGRAM 4
36 
37 #define INTERPRETER_EXIT_COMMAND1 "go"
38 #define INTERPRETER_EXIT_COMMAND2 "g"
39 #define PROGRAM_EXIT_COMMAND "exit"
40 #define CMDLINE_SIZE 256
41 #define NUM_PARAMS 40
42 
43 #define MINISPY_NAME L"MiniSpy"
44 
45 DWORD
47  _In_ int argc,
48  _In_reads_(argc) char *argv[],
49  _In_ PLOG_CONTEXT Context
50  );
51 
52 VOID
54  VOID
55  );
56 
57 VOID
59  _In_ DWORD Code
60  )
61 
62 /*++
63 
64 Routine Description:
65 
66  This routine will display an error message based off of the Win32 error
67  code that is passed in. This allows the user to see an understandable
68  error message instead of just the code.
69 
70 Arguments:
71 
72  Code - The error code to be translated.
73 
74 Return Value:
75 
76  None.
77 
78 --*/
79 
80 {
81  WCHAR buffer[MAX_PATH] = { 0 };
82  DWORD count;
83  HMODULE module = NULL;
84  HRESULT status;
85 
86  count = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM,
87  NULL,
88  Code,
89  0,
90  buffer,
91  sizeof(buffer) / sizeof(WCHAR),
92  NULL);
93 
94 
95  if (count == 0) {
96 
97  count = GetSystemDirectory( buffer,
98  sizeof(buffer) / sizeof( WCHAR ) );
99 
100  if (count==0 || count > sizeof(buffer) / sizeof( WCHAR )) {
101 
102  //
103  // In practice we expect buffer to be large enough to hold the
104  // system directory path.
105  //
106 
107  printf(" Could not translate error: %d\n", Code);
108  return;
109  }
110 
111 
112  status = StringCchCat( buffer,
113  sizeof(buffer) / sizeof( WCHAR ),
114  L"\\fltlib.dll" );
115 
116  if (status != S_OK) {
117 
118  printf(" Could not translate error: %d\n", Code);
119  return;
120  }
121 
122  module = LoadLibraryExW( buffer, NULL, LOAD_LIBRARY_AS_DATAFILE );
123 
124  //
125  // Translate the Win32 error code into a useful message.
126  //
127 
128  count = FormatMessage (FORMAT_MESSAGE_FROM_HMODULE,
129  module,
130  Code,
131  0,
132  buffer,
133  sizeof(buffer) / sizeof(WCHAR),
134  NULL);
135 
136  if (module != NULL) {
137 
138  FreeLibrary( module );
139  }
140 
141  //
142  // If we still couldn't resolve the message, generate a string
143  //
144 
145  if (count == 0) {
146 
147  printf(" Could not translate error: %d\n", Code);
148  return;
149  }
150  }
151 
152  //
153  // Display the translated error.
154  //
155 
156  printf(" %ws\n", buffer);
157 }
158 
159 //
160 // Main uses a loop which has an assignment in the while
161 // conditional statement. Suppress the compiler's warning.
162 //
163 
164 #pragma warning(push)
165 #pragma warning(disable:4706) // assignment within conditional expression
166 
167 int _cdecl
169  _In_ int argc,
170  _In_reads_(argc) char *argv[]
171  )
172 /*++
173 
174 Routine Description:
175 
176  Main routine for minispy
177 
178 Arguments:
179 
180 Return Value:
181 
182 --*/
183 {
184  HANDLE port = INVALID_HANDLE_VALUE;
185  HRESULT hResult = S_OK;
186  DWORD result;
187  ULONG threadId;
188  HANDLE thread = NULL;
189  LOG_CONTEXT context;
190  CHAR inputChar;
191 
192  //
193  // Initialize handle in case of error
194  //
195 
196  context.ShutDown = NULL;
197 
198  //
199  // Open the port that is used to talk to
200  // MiniSpy.
201  //
202 
203  printf( "Connecting to filter's port...\n" );
204 
205  hResult = FilterConnectCommunicationPort( MINISPY_PORT_NAME,
206  0,
207  NULL,
208  0,
209  NULL,
210  &port );
211 
212  if (IS_ERROR( hResult )) {
213 
214  printf( "Could not connect to filter: 0x%08x\n", hResult );
215  DisplayError( hResult );
216  goto Main_Exit;
217  }
218 
219  //
220  // Initialize the fields of the LOG_CONTEXT
221  //
222 
223  context.Port = port;
224  context.ShutDown = CreateSemaphore( NULL,
225  0,
226  1,
227  L"MiniSpy shut down" );
228  context.CleaningUp = FALSE;
229  context.LogToFile = FALSE;
230  context.LogToScreen = FALSE; //don't start logging yet
231  context.NextLogToScreen = TRUE;
232  context.OutputFile = NULL;
233 
234  if (context.ShutDown == NULL) {
235 
236  result = GetLastError();
237  printf( "Could not create semaphore: %d\n", result );
238  DisplayError( result );
239  goto Main_Exit;
240  }
241 
242  //
243  // Check the valid parameters for startup
244  //
245 
246  if (argc > 1) {
247 
248  if (InterpretCommand( argc - 1, &(argv[1]), &context ) == USAGE_ERROR) {
249 
250  goto Main_Exit;
251  }
252  }
253 
254  //
255  // Create the thread to read the log records that are gathered
256  // by MiniSpy.sys.
257  //
258  printf( "Creating logging thread...\n" );
259  thread = CreateThread( NULL,
260  0,
262  (LPVOID)&context,
263  0,
264  &threadId);
265 
266  if (!thread) {
267 
268  result = GetLastError();
269  printf( "Could not create logging thread: %d\n", result );
270  DisplayError( result );
271  goto Main_Exit;
272  }
273 
274  //
275  // Check to see what devices we are attached to from
276  // previous runs of this program.
277  //
278 
279  ListDevices();
280 
281  //
282  // Process commands from the user
283  //
284 
285  printf( "\nHit [Enter] to begin command mode...\n\n" );
286  fflush( stdout );
287 
288  //
289  // set screen logging state
290  //
291 
292  context.LogToScreen = context.NextLogToScreen;
293 
294  while (inputChar = (CHAR)getchar()) {
295 
296  CHAR *parms[NUM_PARAMS];
297  CHAR commandLine[CMDLINE_SIZE+1];
298  INT parmCount, count;
299  DWORD returnValue = SUCCESS;
300  BOOL newParm;
301  CHAR ch;
302 
303  if (inputChar == '\n') {
304 
305  //
306  // Start command interpreter. First we must turn off logging
307  // to screen if we are. Also, remember the state of logging
308  // to the screen, so that we can reinstate that when command
309  // interpreter is finished.
310  //
311 
312  context.NextLogToScreen = context.LogToScreen;
313  context.LogToScreen = FALSE;
314 
315  while (returnValue != EXIT_INTERPRETER) {
316 
317  //
318  // Print prompt
319  //
320  printf( ">" );
321 
322  //
323  // Read in next line, keeping track of the number of parameters
324  // as we go.
325  //
326 
327  parmCount = 0;
328  newParm = TRUE;
329  for ( count = 0;
330  (count < CMDLINE_SIZE) && ((ch = (CHAR)getchar()) != '\n');
331  count++)
332  {
333  commandLine[count] = ch;
334 
335  if (newParm && (ch != ' ')) {
336 
337  parms[parmCount++] = &commandLine[count];
338  }
339 
340  if (parmCount >= NUM_PARAMS) {
341 
342  break;
343  }
344 
345  //
346  // Always insert NULL's for spaces
347  //
348 
349  if (ch == ' ') {
350 
351  newParm = TRUE;
352  commandLine[count] = 0;
353 
354  } else {
355 
356  newParm = FALSE;
357  }
358  }
359 
360  commandLine[count] = '\0';
361 
362  if (parmCount == 0) {
363 
364  continue;
365  }
366 
367  //
368  // We've got our parameter count and parameter list, so
369  // send it off to be interpreted.
370  //
371 
372  returnValue = InterpretCommand( parmCount, parms, &context );
373 
374  if (returnValue == EXIT_PROGRAM) {
375 
376  // Time to stop the program
377  goto Main_Cleanup;
378  }
379  }
380 
381  //
382  // Set LogToScreen appropriately based on any commands seen
383  //
384 
385  context.LogToScreen = context.NextLogToScreen;
386 
387  if (context.LogToScreen) {
388 
389  printf( "Should be logging to screen...\n" );
390  }
391  }
392  }
393 
394 Main_Cleanup:
395 
396  //
397  // Clean up the threads, then fall through to Main_Exit
398  //
399 
400  printf( "Cleaning up...\n" );
401 
402  //
403  // Set the Cleaning up flag to TRUE to notify other threads
404  // that we are cleaning up
405  //
406  context.CleaningUp = TRUE;
407 
408  //
409  // Wait for everyone to shut down
410  //
411 
412  WaitForSingleObject( context.ShutDown, INFINITE );
413 
414  if (context.LogToFile) {
415 
416  fclose( context.OutputFile );
417  }
418 
419 Main_Exit:
420 
421  //
422  // Clean up the data that is always around and exit
423  //
424 
425  if(context.ShutDown) {
426 
427  CloseHandle( context.ShutDown );
428  }
429 
430  if (thread) {
431 
432  CloseHandle( thread );
433  }
434 
435  if (INVALID_HANDLE_VALUE != port) {
436  CloseHandle( port );
437  }
438  return 0;
439 }
440 
441 #pragma warning(pop)
442 
443 DWORD
445  _In_ int argc,
446  _In_reads_(argc) char *argv[],
447  _In_ PLOG_CONTEXT Context
448  )
449 /*++
450 
451 Routine Description:
452 
453  Process options from the user
454 
455 Arguments:
456 
457 Return Value:
458 
459 --*/
460 {
461  LONG parmIndex;
462  PCHAR parm;
463  HRESULT hResult;
464  DWORD returnValue = SUCCESS;
465  CHAR buffer[BUFFER_SIZE];
466  DWORD bufferLength;
467  PWCHAR instanceString;
468  WCHAR instanceName[INSTANCE_NAME_MAX_CHARS + 1];
469 
470  //
471  // Interpret the command line parameters
472  //
473  for (parmIndex = 0; parmIndex < argc; parmIndex++) {
474 
475  parm = argv[parmIndex];
476 
477  if (parm[0] == '/') {
478 
479  //
480  // Have the beginning of a switch
481  //
482 
483  switch (parm[1]) {
484 
485  case 'a':
486  case 'A':
487 
488  //
489  // Attach to the specified drive letter
490  //
491 
492  parmIndex++;
493 
494  if (parmIndex >= argc) {
495 
496  //
497  // Not enough parameters
498  //
499 
500  goto InterpretCommand_Usage;
501  }
502 
503  parm = argv[parmIndex];
504 
505  printf( " Attaching to %s... ", parm );
506 
507  bufferLength = MultiByteToWideChar( CP_ACP,
508  MB_ERR_INVALID_CHARS,
509  parm,
510  -1,
511  (LPWSTR)buffer,
512  BUFFER_SIZE/sizeof( WCHAR ) );
513 
514  if (bufferLength == 0) {
515 
516  //
517  // We do not expect the user to provide a parm that
518  // causes buffer to overflow.
519  //
520 
521  goto InterpretCommand_Usage;
522  }
523 
524  hResult = FilterAttach( MINISPY_NAME,
525  (PWSTR)buffer,
526  NULL, // instance name
527  sizeof( instanceName ),
528  instanceName );
529 
530  if (SUCCEEDED( hResult )) {
531 
532  printf( " Instance name: %S\n", instanceName );
533 
534  } else {
535 
536  printf( "\n Could not attach to device: 0x%08x\n", hResult );
537  DisplayError( hResult );
538  returnValue = SUCCESS;
539  }
540 
541  break;
542 
543  case 'd':
544  case 'D':
545 
546  //
547  // Detach to the specified drive letter
548  //
549 
550  parmIndex++;
551 
552  if (parmIndex >= argc) {
553 
554  //
555  // Not enough parameters
556  //
557 
558  goto InterpretCommand_Usage;
559  }
560 
561  parm = argv[parmIndex];
562 
563  printf( " Detaching from %s\n", parm );
564  bufferLength = MultiByteToWideChar( CP_ACP,
565  MB_ERR_INVALID_CHARS,
566  parm,
567  -1,
568  (LPWSTR)buffer,
569  BUFFER_SIZE/sizeof( WCHAR ) );
570 
571  if (bufferLength == 0) {
572 
573  //
574  // We do not expect the user to provide a parm that
575  // causes buffer to overflow.
576  //
577 
578  goto InterpretCommand_Usage;
579  }
580 
581  //
582  // Get the next argument to see if it is an InstanceId
583  //
584 
585  parmIndex++;
586 
587  if (parmIndex >= argc) {
588 
589  instanceString = NULL;
590 
591  } else {
592 
593  if (argv[parmIndex][0] == '/') {
594 
595  //
596  // This is just the next command, so don't
597  // internet it as the InstanceId.
598  //
599 
600  instanceString = NULL;
601  parmIndex--;
602 
603  } else {
604 
605  parm = argv[parmIndex];
606  bufferLength = MultiByteToWideChar( CP_ACP,
607  MB_ERR_INVALID_CHARS,
608  parm,
609  -1,
610  (LPWSTR)instanceName,
611  sizeof( instanceName )/sizeof( WCHAR ) );
612 
613  if (bufferLength == 0) {
614 
615  //
616  // We do not expect the user to provide a parm that
617  // causes buffer to overflow.
618  //
619 
620  goto InterpretCommand_Usage;
621  }
622 
623  instanceString = instanceName;
624  }
625  }
626 
627  //
628  // Detach from the volume and instance specified.
629  //
630 
631  hResult = FilterDetach( MINISPY_NAME,
632  (PWSTR)buffer,
633  instanceString );
634 
635  if (IS_ERROR( hResult )) {
636 
637  printf( " Could not detach from device: 0x%08x\n", hResult );
638  DisplayError( hResult );
639  returnValue = SUCCESS;
640  }
641  break;
642 
643  case 'l':
644  case 'L':
645 
646  //
647  // List all devices that are currently being monitored
648  //
649 
650  ListDevices();
651  break;
652 
653  case 's':
654  case 'S':
655 
656  //
657  // Output logging results to screen, save new value to
658  // instate when command interpreter is exited.
659  //
660  if (Context->NextLogToScreen) {
661 
662  printf( " Turning off logging to screen\n" );
663 
664  } else {
665 
666  printf( " Turning on logging to screen\n" );
667  }
668 
669  Context->NextLogToScreen = !Context->NextLogToScreen;
670  break;
671 
672  case 'f':
673  case 'F':
674 
675  //
676  // Output logging results to file
677  //
678 
679  if (Context->LogToFile) {
680 
681  printf( " Stop logging to file \n" );
682  Context->LogToFile = FALSE;
683  assert( Context->OutputFile );
684  _Analysis_assume_( Context->OutputFile != NULL );
685  fclose( Context->OutputFile );
686  Context->OutputFile = NULL;
687 
688  } else {
689 
690  parmIndex++;
691 
692  if (parmIndex >= argc) {
693 
694  //
695  // Not enough parameters
696  //
697 
698  goto InterpretCommand_Usage;
699  }
700 
701  parm = argv[parmIndex];
702  printf( " Log to file %s\n", parm );
703 
704  if (fopen_s( &Context->OutputFile, parm, "w" ) != 0 ) {
705  assert( Context->OutputFile );
706  }
707 
708  Context->LogToFile = TRUE;
709  }
710  break;
711 
712  default:
713 
714  //
715  // Invalid switch, goto usage
716  //
717  goto InterpretCommand_Usage;
718  }
719 
720  } else {
721 
722  //
723  // Look for "go" or "g" to see if we should exit interpreter
724  //
725 
726  if (!_strnicmp( parm,
728  sizeof( INTERPRETER_EXIT_COMMAND1 ))) {
729 
730  returnValue = EXIT_INTERPRETER;
731  goto InterpretCommand_Exit;
732  }
733 
734  if (!_strnicmp( parm,
736  sizeof( INTERPRETER_EXIT_COMMAND2 ))) {
737 
738  returnValue = EXIT_INTERPRETER;
739  goto InterpretCommand_Exit;
740  }
741 
742  //
743  // Look for "exit" to see if we should exit program
744  //
745 
746  if (!_strnicmp( parm,
748  sizeof( PROGRAM_EXIT_COMMAND ))) {
749 
750  returnValue = EXIT_PROGRAM;
751  goto InterpretCommand_Exit;
752  }
753 
754  //
755  // Invalid parameter
756  //
757  goto InterpretCommand_Usage;
758  }
759  }
760 
761 InterpretCommand_Exit:
762  return returnValue;
763 
764 InterpretCommand_Usage:
765  printf("Valid switches: [/a <drive>] [/d <drive>] [/l] [/s] [/f [<file name>]]\n"
766  " [/a <drive>] starts monitoring <drive>\n"
767  " [/d <drive> [<instance id>]] detaches filter <instance id> from <drive>\n"
768  " [/l] lists all the drives the monitor is currently attached to\n"
769  " [/s] turns on and off showing logging output on the screen\n"
770  " [/f [<file name>]] turns on and off logging to the specified file\n"
771  " If you are in command mode:\n"
772  " [enter] will enter command mode\n"
773  " [go|g] will exit command mode\n"
774  " [exit] will terminate this program\n"
775  );
776  returnValue = USAGE_ERROR;
777  goto InterpretCommand_Exit;
778 }
779 
780 
781 ULONG
783  _In_ LPCWSTR VolumeName
784  )
785 /*++
786 
787 Routine Description:
788 
789  Determine if our filter is attached to this volume
790 
791 Arguments:
792 
793  VolumeName - The volume we are checking
794 
795 Return Value:
796 
797  TRUE - we are attached
798  FALSE - we are not attached (or we couldn't tell)
799 
800 --*/
801 {
802  PWCHAR filtername;
803  CHAR buffer[1024];
804  PINSTANCE_FULL_INFORMATION data = (PINSTANCE_FULL_INFORMATION)buffer;
805  HANDLE volumeIterator = INVALID_HANDLE_VALUE;
806  ULONG bytesReturned;
807  ULONG instanceCount = 0;
808  HRESULT hResult;
809 
810  //
811  // Enumerate all instances on this volume
812  //
813 
814  hResult = FilterVolumeInstanceFindFirst( VolumeName,
815  InstanceFullInformation,
816  data,
817  sizeof(buffer)-sizeof(WCHAR),
818  &bytesReturned,
819  &volumeIterator );
820 
821  if (IS_ERROR( hResult )) {
822 
823  return instanceCount;
824  }
825 
826  do {
827 
828  assert((data->FilterNameBufferOffset+data->FilterNameLength) <= (sizeof(buffer)-sizeof(WCHAR)));
829  _Analysis_assume_((data->FilterNameBufferOffset+data->FilterNameLength) <= (sizeof(buffer)-sizeof(WCHAR)));
830 
831  //
832  // Get the name. Note that we are NULL terminating the buffer
833  // in place. We can do this because we don't care about the other
834  // information and we have guaranteed that there is room for a NULL
835  // at the end of the buffer.
836  //
837 
838 
839  filtername = Add2Ptr(data,data->FilterNameBufferOffset);
840  filtername[data->FilterNameLength/sizeof( WCHAR )] = L'\0';
841 
842  //
843  // Bump the instance count when we find a match
844  //
845 
846  if (_wcsicmp(filtername,MINISPY_NAME) == 0) {
847 
848  instanceCount++;
849  }
850 
851  } while (SUCCEEDED( FilterVolumeInstanceFindNext( volumeIterator,
852  InstanceFullInformation,
853  data,
854  sizeof(buffer)-sizeof(WCHAR),
855  &bytesReturned ) ));
856 
857  //
858  // Close the handle
859  //
860 
861  FilterVolumeInstanceFindClose( volumeIterator );
862  return instanceCount;
863 }
864 
865 
866 void
868  VOID
869  )
870 /*++
871 
872 Routine Description:
873 
874  Display the volumes we are attached to
875 
876 Arguments:
877 
878 Return Value:
879 
880 --*/
881 {
882  UCHAR buffer[1024];
883  PFILTER_VOLUME_BASIC_INFORMATION volumeBuffer = (PFILTER_VOLUME_BASIC_INFORMATION)buffer;
884  HANDLE volumeIterator = INVALID_HANDLE_VALUE;
885  ULONG volumeBytesReturned;
886  HRESULT hResult = S_OK;
887  WCHAR driveLetter[15] = { 0 };
888  ULONG instanceCount;
889 
890  try {
891 
892  //
893  // Find out size of buffer needed
894  //
895 
896  hResult = FilterVolumeFindFirst( FilterVolumeBasicInformation,
897  volumeBuffer,
898  sizeof(buffer)-sizeof(WCHAR), //save space to null terminate name
899  &volumeBytesReturned,
900  &volumeIterator );
901 
902  if (IS_ERROR( hResult )) {
903 
904  leave;
905  }
906 
907  assert( INVALID_HANDLE_VALUE != volumeIterator );
908 
909  //
910  // Output the header
911  //
912 
913  printf( "\n"
914  "Dos Name Volume Name Status \n"
915  "-------------- ------------------------------------ --------\n" );
916 
917  //
918  // Loop through all of the filters, displaying instance information
919  //
920 
921  do {
922 
923  assert((FIELD_OFFSET(FILTER_VOLUME_BASIC_INFORMATION,FilterVolumeName) + volumeBuffer->FilterVolumeNameLength) <= (sizeof(buffer)-sizeof(WCHAR)));
924  _Analysis_assume_((FIELD_OFFSET(FILTER_VOLUME_BASIC_INFORMATION,FilterVolumeName) + volumeBuffer->FilterVolumeNameLength) <= (sizeof(buffer)-sizeof(WCHAR)));
925 
926  volumeBuffer->FilterVolumeName[volumeBuffer->FilterVolumeNameLength/sizeof( WCHAR )] = UNICODE_NULL;
927 
928  instanceCount = IsAttachedToVolume(volumeBuffer->FilterVolumeName);
929 
930  printf( "%-14ws %-36ws %s",
931  (SUCCEEDED( FilterGetDosName(
932  volumeBuffer->FilterVolumeName,
933  driveLetter,
934  sizeof(driveLetter)/sizeof(WCHAR) )) ? driveLetter : L""),
935  volumeBuffer->FilterVolumeName,
936  (instanceCount > 0) ? "Attached" : "");
937 
938  if (instanceCount > 1) {
939 
940  printf( " (%d)\n", instanceCount );
941 
942  } else {
943 
944  printf( "\n" );
945  }
946 
947  } while (SUCCEEDED( hResult = FilterVolumeFindNext( volumeIterator,
948  FilterVolumeBasicInformation,
949  volumeBuffer,
950  sizeof(buffer)-sizeof(WCHAR), //save space to null terminate name
951  &volumeBytesReturned ) ));
952 
953  if (HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) == hResult) {
954 
955  hResult = S_OK;
956  }
957 
958  } finally {
959 
960  if (INVALID_HANDLE_VALUE != volumeIterator) {
961 
962  FilterVolumeFindClose( volumeIterator );
963  }
964 
965  if (IS_ERROR( hResult )) {
966 
967  if (HRESULT_FROM_WIN32( ERROR_NO_MORE_ITEMS ) == hResult) {
968 
969  printf( "No volumes found.\n" );
970 
971  } else {
972 
973  printf( "Volume listing failed with error: 0x%08x\n",
974  hResult );
975  }
976  }
977  }
978 }
979 
#define SUCCESS
#define BUFFER_SIZE
Definition: mspyLog.h:26
BOOLEAN LogToScreen
Definition: mspyLog.h:35
#define INTERPRETER_EXIT_COMMAND1
FILE * OutputFile
Definition: mspyLog.h:37
DWORD InterpretCommand(_In_ int argc, _In_reads_(argc) char *argv[], _In_ PLOG_CONTEXT Context)
Definition: mspyUser.c:444
#define Add2Ptr(P, I)
Definition: minispy.h:238
HANDLE ShutDown
Definition: mspyLog.h:46
DWORD WINAPI RetrieveLogRecords(_In_ LPVOID lpParameter)
Definition: mspyLog.c:97
BOOLEAN NextLogToScreen
Definition: mspyLog.h:39
#define PROGRAM_EXIT_COMMAND
return TRUE
#define INVALID_HANDLE_VALUE
Definition: nc.h:58
BOOLEAN LogToFile
Definition: mspyLog.h:36
BOOLEAN CleaningUp
Definition: mspyLog.h:45
#define EXIT_PROGRAM
void ListDevices(VOID)
Definition: mspyUser.c:867
#define INTERPRETER_EXIT_COMMAND2
#define MINISPY_PORT_NAME
Definition: minispy.h:70
HANDLE Port
Definition: mspyLog.h:34
NcLoadRegistryStringRetry NULL
Definition: ncinit.c:53
#define USAGE_ERROR
int _cdecl main(_In_ int argc, _In_reads_(argc) char *argv[])
Definition: mspyUser.c:168
_Analysis_mode_(_Analysis_code_type_user_code_)
Definition: mspyUser.c:23
#define EXIT_INTERPRETER
VOID DisplayError(_In_ DWORD Code)
Definition: user/utility.c:26
#define NUM_PARAMS
ULONG IsAttachedToVolume(_In_ LPCWSTR VolumeName)
Definition: mspyUser.c:782
#define CMDLINE_SIZE
#define MINISPY_NAME

Social Network


Services Overview

Architect, implement and test file system filter drivers for a wide range of functionality. We can offer several levels of assistance to meet your specific.

Contact Us

You are welcome to contact us for salse or partnership.

Sales: sales@easefilter.com
Support: support@easefilter.com
Info: info@easefilter.com