Feb 282009
 

What I mean is, if we type ‘Start->Run->Cmd.exe’ then command prompt directory is our user directory, for e.g. ‘c:\Documents and settings\Nibu’. So how can we do this through code is the question? Directory that we see when command prompt starts up is called working directory. Every executable in windows has a working directory which is in fact the first location it searches for a file. So using ‘CreateProcess’ it’s possible to set a working directory, since it has a function parameter which corresponds to this feature. So here is a simple dedicated function which sets working directory for command prompt…

void OpenCmdInDir( LPCTSTR lpctszDir )
{
   STARTUPINFO StrInf =  { 0 };
   StrInf.cb = sizeof( StrInf );

   PROCESS_INFORMATION ProcInfo = { 0 };
   VERIFY( CreateProcess( NULL, "C:\\Windows\\system32\\cmd.exe", NULL, NULL, FALSE, 0, 0, lpctszDir, &StrInf, &ProcInfo ));

   // Closing handles since not needed further
   CloseHandle( ProcInfo.hProcess );
   CloseHandle( ProcInfo.hThread );
}

// Usage
OpenCmdInDir( _T( "C:\\Windows\\System32\\Drivers" ));

See screenshot…

Command prompt working directory

Command prompt working directory

Jan 082008
 

Here is a function that extracts the name of the owner of a process…

bool ExtractProcessOwner( HANDLE hProcess_i,
                          CString& csOwner_o )
{
   // Get process token
   HANDLE hProcessToken = NULL;
   if ( !::OpenProcessToken( hProcess_i, TOKEN_READ, &hProcessToken ) || !hProcessToken )
   {
      return false;
   }

   // First get size needed, TokenUser indicates we want user information from given token
   DWORD dwProcessTokenInfoAllocSize = 0;
   ::GetTokenInformation(hProcessToken, TokenUser, NULL, 0, &dwProcessTokenInfoAllocSize);

   // Call should have failed due to zero-length buffer.
   if( ::GetLastError() == ERROR_INSUFFICIENT_BUFFER )
   {
      // Allocate buffer for user information in the token.
      PTOKEN_USER pUserToken = reinterpret_cast<ptoken_user>( new BYTE[dwProcessTokenInfoAllocSize] );
      if (pUserToken != NULL)
      {
         // Now get user information in the allocated buffer
         if (::GetTokenInformation( hProcessToken, TokenUser, pUserToken, dwProcessTokenInfoAllocSize, &dwProcessTokenInfoAllocSize ))
         {
            // Some vars that we may need
            SID_NAME_USE   snuSIDNameUse;
            TCHAR          szUser[MAX_PATH] = { 0 };
            DWORD          dwUserNameLength = MAX_PATH;
            TCHAR          szDomain[MAX_PATH] = { 0 };
            DWORD          dwDomainNameLength = MAX_PATH;

            // Retrieve user name and domain name based on user's SID.
            if ( ::LookupAccountSid( NULL,
                                     pUserToken->User.Sid,
                                     szUser,
                                     &dwUserNameLength,
                                     szDomain,
                                     &dwDomainNameLength,
                                     &snuSIDNameUse ))
            {
               // Prepare user name string
               csOwner_o = _T("\\\\");
               csOwner_o += szDomain;
               csOwner_o += _T("\\");
               csOwner_o += szUser;

               // We are done!
               CloseHandle( hProcessToken );
               delete [] pUserToken;

               // We succeeded
               return true;
            }//End if
         }// End if

         delete [] pUserToken;
      }// End if
   }// End if

   CloseHandle( hProcessToken );

   // Oops trouble
   return false;
}// End GetProcessOwner

Details

The key to this implementation is the function GetTokenInformation and the token information class TokenUser. Take a look in MSDN for this function. Take note of the information classes available. GetTokenInformation returns the users security identifier(SID) using which we called the function LookupAccountSid which returns the corresponding users name.

You may also need the SeDebugPrivilege set to execute this function successfully.