This Question is Answered

1 "correct" answer available (4 pts) 2 "helpful" answers available (2 pts)
7 Replies Last post: Nov 20, 2007 9:01 PM by Spiros Kalantzis  
Nyst   21 posts since
Mar 25, 2007
Currently Being Moderated

Mar 28, 2007 5:51 PM

Redirect output & error streams of a console app to log files

Hello,

 

I`m trying to create a process of a console application with other user`s AccessToken from LogonUserW.

Also there is a need to redirects that processes output and error to log files.

Handles to files are created with the 'inherit' flag in SecurityAttributes set to 'true', the flag 'stdHandles' in StartupInfo is set to 'true' because I`ve improved StartupInfo to have setUseStdHandles() setter.

 

At first attempts I got a "not enought privileges " error but that was solved temporary by adding my user to "Replace a process level token" in Local Policies
User Rights Assi... (need to do that from the code in future)

 

Well, the problem is that the files that are created, remain zero length and i know that the console app writes output and error (cause i wrote it too in C#) .

 

How can i correctly redirect the console output to these files??

 

Here`s the code:

 

            AccessToken accToken = new AccessToken(userName, domain, password,

                    AccessToken.LOGON32_LOGON_INTERACTIVE

                    );

           

            //  Creating a new StartupInfo

            StartupInfo.Options startInfoOpts = new StartupInfo.Options();

            startInfoOpts.setUseStdHandles(true);

           

            // These security attributes have 'true' on the ability to inherit handles

            SecurityAttributes secAtt = new SecurityAttributes();

            secAtt.setInheritHandle(true);

           

            StartupInfo startInfo = new StartupInfo(startInfoOpts);

            Handle h_stdOut = new Handle();

            Function createF = Kernel32.getInstance().getFunction("CreateFileW");

            createF .invoke(fileHandle, new Parameter[] {

                  new Str(filename),

                new UInt32(GENERIC_READ  ),     //int GENERIC_READ = 0x80000000;

              new UInt32(0),

               new Pointer(secAtt),

                new UInt32(CREATE_ALWAYS),           // int CREATE_ALWAYS = 2;

                 new UInt32(Normal | Overlapped),    //int Normal  = 0x00000080   int Overlapped       = 0x40000000

                 new Pointer(null, true)

        });

            startInfo.setStdOutput(h_stdOut);

            Handle h_stdErr =    new Handle();

            createF .invoke(stdErr , new Parameter[] {

                  new Str(filename),

                new UInt32(GENERIC_READ  ),     //int GENERIC_READ = 0x80000000;

              new UInt32(0),

               new Pointer(secAtt),

                new UInt32(CREATE_ALWAYS),           // int CREATE_ALWAYS = 2;

                 new UInt32(Normal | Overlapped),    //int Normal  = 0x00000080   int Overlapped       = 0x40000000

                 new Pointer(null, true)

        });

            startInfo.setStdError(h_stdErr);

           

            //  Process options

            ProcessOptions procOptions = new ProcessOptions();

            procOptions.setUnicodeEnvironment(true);

           

            //  Environment keys and values arrays to structure

            ProcessVariables procvars = new ProcessVariables();

            for(int i = 0 ; i < evironmentKeys.length; i++){

                procvars.setValue(evironmentKeys, evironmentValues);

            }

           

            //  Invoking

            ut.loginfo("Process invocation start", _log);

            com.jniwrapper.win32.process.Process proc = new com.jniwrapper.win32.process.Process(

                    accToken,

                    null,

                    getCommandLine(),

                    secAtt1,

                    secAtt1,

                    true,

                    procOptions,

                    procvars,

                    workDir,

                    startInfo

                    );

 

Thank you.

Attachments:
Serge Piletsky TeamDev Ltd. 670 posts since
Apr 24, 2006
Currently Being Moderated
1. Mar 28, 2007 10:28 PM in response to: Nyst
Re: Redirect output & error streams of a console app to log files

Hi,

 

Here is the example that demonstrates how to redirect StdOut and StdErr streams to the specified files:

 


/**
 * This example demonstrates how to redirect STDOUT and STDERR process streams to files.
 */
public class RedirectStdStreamsExample {

    final static long CREATE_ALWAYS = 2;

    final static long FILE_ATTRIBUTE_NORMAL = 0x00000080;

    final static long STANDARD_RIGHTS_REQUIRED = 0x000F0000L;
    final static long SYNCHRONIZE = 0x00100000L;
    final static long FILE_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x3FF;

    public static void main(String[] args) {


        // These security attributes have 'true' on the ability to inherit handles
        SecurityAttributes securityAttributes = new SecurityAttributes();
        securityAttributes.setInheritHandle(true);

        String stdOutFileName = "C:/Temp/stdOut.txt";
        String stdErrFileName = "C:/Temp/stdErr.txt";

        // Creating a new StartupInfo
        StartupInfo.Options startInfoOpts = new StartupInfo.Options();
        startInfoOpts.setUseStdHandles(true);
        StartupInfo startInfo = new StartupInfo(startInfoOpts);

        Function createF = Kernel32.getInstance().getFunction("CreateFileW");

        Handle stdOut = new Handle();
        createF.invoke(stdOut, new Parameter[]{
                new WideString(stdOutFileName),
                new UInt32(FILE_ALL_ACCESS),
                new UInt32(0),
                new Pointer(securityAttributes),
                new UInt32(CREATE_ALWAYS),
                new UInt32(FILE_ATTRIBUTE_NORMAL),
                new Pointer(null, true),
        });
        System.out.println("New StdOut: " + stdOut);
        startInfo.getStdOutput().setValue(stdOut.getValue());

        Handle stdErr = new Handle();
        createF.invoke(stdErr, new Parameter[]{
                new WideString(stdErrFileName),
                new UInt32(FILE_ALL_ACCESS),
                new UInt32(0),
                new Pointer(securityAttributes),
                new UInt32(CREATE_ALWAYS),
                new UInt32(FILE_ATTRIBUTE_NORMAL),
                new Pointer(null, true)
        });
        System.out.println("New StdErr: " + stdErr);
        startInfo.getStdError().setValue(stdErr.getValue());

        ProcessOptions procOptions = new ProcessOptions();
        
        com.jniwrapper.win32.process.Process process = new com.jniwrapper.win32.process.Process(
                null,
                "java.exe -help", // "java.exe -version" will write information to new stderr handle 
                securityAttributes,
                null,
                true, // inheritHandles
                procOptions,
                new ProcessVariables(),
                null,
                startInfo
        );
        process.waitFor();
        process.close();

        Handle.closeHandle(stdErr);
        Handle.closeHandle(stdOut);
    }
}

 

Please let me know if you have any further questions.

 

-Serge

Kevin D. Sandal   3 posts since
May 25, 2007
Currently Being Moderated
3. Jul 23, 2007 11:45 PM in response to: Nyst
Re: Redirect output & error streams of a console app to log files

Only works if the ProcessVariables parameter is null. No other derivative works. Since I need to modify the CLASSPATH value before launching another Java app, are there other options?

Serge Piletsky TeamDev Ltd. 670 posts since
Apr 24, 2006
Currently Being Moderated
4. Aug 13, 2007 4:40 PM in response to: Kevin D. Sandal
Re: Redirect output & error streams of a console app to log files

Hi Kevin,

 

The purpose of that example is to demonstrate how to redirect stdOut and stdErr streams to the specified files. The ProcessVariables parameter which is used in the function call is not null. And this example does work. You can verify this using the example below :

 


        ProcessVariables processVariables = new ProcessVariables();
        String javaHome = processVariables.getValue("JAVA_HOME");
        System.out.println("javaHome = " + javaHome);
        String classPath = processVariables.getValue("CLASSPATH");
        System.out.println("classPath = " + classPath);

        com.jniwrapper.win32.process.Process process = new com.jniwrapper.win32.process.Process(
                null,
                "java.exe -version", // "java.exe -version" will write information to new stderr handle 
                securityAttributes,
                null,
                true, // inheritHandles
                procOptions,
                processVariables,
                null,
                startInfo
        );

        process.waitFor();
        process.close();

        Handle.closeHandle(stdErr);
        Handle.closeHandle(stdOut);

 

You will find that 'processVariables' parameter is not null and it contains various variables. And also you can set the new required values:

 


     processVariables.setValue("CLASSPATH", "newValue");

 

-Serge

Kevin D. Sandal   3 posts since
May 25, 2007
Currently Being Moderated
5. Aug 28, 2007 6:22 PM in response to: Nyst
Re: Redirect output & error streams of a console app to log files

User error. Thanks for setting me straight.

Spiros Kalantzis   2 posts since
Nov 17, 2007
Currently Being Moderated
6. Nov 17, 2007 1:41 AM in response to: Nyst
Re: Redirect output & error streams of a console app to log files

I am having a similar problem as Kevin with the ProcessVariable. When I pass null, everything works. When I try to set my own environment, I don't get what I want. I have attached a simple example of trying to create a cmd.exe in WinXP SP2. The window pops up but when I call set, I get a bad environment. Here is the code snipper:

 

    StartupInfo startupInfo = new StartupInfo();

   

    String workingDirectory = "c:
windows
system32";

    String command = "cmd /c cmd.exe";

   

    //  Environment variables

    final String[] envArray = new String[]{"ENV1=Value1"};

   

    ProcessVariables env = new ProcessVariables();

    for (int i = 0; i < envArray.length; i++) {

      String envVar = envArray+;

     

      if (envVar.indexOf((int) '\u0000') != -1)

        envVar = envVar.replaceFirst("\u0000.*", "");

     

      int idx = envVar.indexOf('=');

      if (idx>1) {

        String VarName = envVar.substring(0,idx);

        String VarValue = envVar.substring(idx+1);;

        env.setValue(VarName, VarValue);

      }

    }

   

    com.jniwrapper.win32.process.Process process;

   

    //The process to be executed

    try {

      process = new com.jniwrapper.win32.process.Process(null,command,null,null,false, new ProcessOptions(true), env, workingDirectory, startupInfo);

      process.waitFor();

      process.close(); 

    }

    catch (Throwable e) {

      throw new IOException("Failed to execute command: "command": "+e.getMessage());

    }

  }

 

Any ideas. I must be doing something wrong.

Attachments:
Spiros Kalantzis   2 posts since
Nov 17, 2007
Currently Being Moderated
7. Nov 20, 2007 9:01 PM in response to: Nyst
Re: Redirect output & error streams of a console app to log files

OK.. I found my problem. I needed to set the ProcessOptions.setUnicodeEnvironment(true) to fix the environment. Also, I found that I could not use new ProcessVariables() and then call the setValue(variable, value) method because that set the parent process environment. What I wanted to do is pass the desired environment to the child process. To do this I had to call new ProcessVariables(String[] childEnvVariables).

 

In addition, I realized that there is a bug in v2.9.5 of the JNIWrapper that prevented me from accomplising the above.

More Like This

  • Retrieving data ...