3

I have C#/WPF application that calls into a third-party native dll (created by the Matlab compiler) to perform an intensive calculation, all within a single process. This dll dumps a bunch of debug info to the console, presumably by writing to stdout and/or stderr. This info is meaningless to users, but would assist greatly in debugging when things go wrong. If I set my application type to Console Application, I get a console window alongside the real application window and can see the debug output, but then the user sees it too. Is there any way for me to redirect or echo my own process's stdout/stderr to a logfile without needing to put a console window on the screen?

Things that don't work:

  1. Using Console.SetOut()/SetError(). This only seems to affect writes that go through the Console object itself, which is obviously not what happens in native code.
  2. Hooking the Process.GetCurrentProcess().OutputDataReceived event. This only works if the process was started with a flag indicating that it should redirect stdout, which is not something I can set for my own process after the fact (as far as I know).
  3. Reading from Process.GetCurrentProcess().StandardOutput. Again, this requires the process to have been started with redirection enabled.
  4. P/Invoking SetStdHandle. This allows me to redirect stdout/stderr from my own C# code to a file, but the native dll is unaffected.

Redirecting from the command line (MyApp.exe > logfile.txt 2>&1) does work, so I'm sure the dll is writing to stdout and stderr, but all attempts to redirect the native half of things after launching the process have failed.

dlf
  • 9,045
  • 4
  • 32
  • 58
  • who wrote the code originally..? do you have access to the `SRC` if so change the output to write that useless garbage that users don't need to see to a log file .. – MethodMan Nov 06 '15 at 21:22
  • @MethodMan It's third party. I could submit a change request to the group that created it (and will if I have to), but I'd prefer a solution that doesn't require a new release on their end. – dlf Nov 06 '15 at 21:24
  • in all due respect who would write code that does that these days anyway.. could you perhaps write code that hides their console window.. have you thought about that.. perhaps add some `WaitForExit` type code on your end as well as hiding their window..? if so check out this post http://stackoverflow.com/questions/5377423/hide-console-window-from-process-start-c-sharp the `CreateNoWindow` is what you probably want to do – MethodMan Nov 06 '15 at 21:26
  • @MethodMan Hiding the console window isn't an issue; since it's a WPF app, the console won't show up at all unless I tell it to. The trick is getting the info that's being written to stdout without actually showing a console window. – dlf Nov 06 '15 at 21:33
  • Ah ha.. ok now I understand what you are saying.. – MethodMan Nov 06 '15 at 21:33
  • here are some examples here I am quite sure if you are using command line args that you can use at least 2 of the answers provided here http://stackoverflow.com/questions/186822/capturing-console-output-from-a-net-application-c – MethodMan Nov 06 '15 at 21:37
  • @MethodMan Since I'm not launching a new process to call into the dll, there's nowhere for me to set the `RedirectStandardOutput` flag. If there were some way to change that flag for the current running process I think that would work, but I don't know of any way to do this. Now, if you launch the app from the command line as TheApp.exe > logfile.txt, that works, but I can't really expect users to do that. I could instead have the installer create a shortcut that has the redirection built into it, but even that is somewhat fragile. – dlf Nov 06 '15 at 21:45
  • Possible duplicate of [How to write to a program's StdOut Stream directly in C#?](https://stackoverflow.com/q/2346253). Note that you cannot redirect stdout directly from C#, but there is a native function you can p/invoke (see the first answer). Consider also the advice at [Capturing stdout from unmanaged DLL in C# caller](https://stackoverflow.com/a/12755754). If you agree your question is a duplicate, please vote to close using the link above. – Peter Duniho Nov 06 '15 at 22:38
  • @PeterDuniho The first one isn't precisely the same question, but its accepted answer (p/invoke to `SetStdHandle`) will work for mine too. Not sure whether to call that a duplicate or not... – dlf Nov 06 '15 at 23:33
  • It's less important that the question be stated exactly the same, and more important that it is _answered_ in the same way that yours would have been answered usefully. Given that AFAIK the only way to address your specific question is to use `SetStdHandle()`, that's why I proposed it as a duplicate. I wanted to give you a chance to look it over though, which is why I didn't just mark your question as a duplicate outright. Since its answer does seem to address your need, I will go ahead and do that now. – Peter Duniho Nov 06 '15 at 23:38
  • I'd probably disagree that its a duplicate. 2 + 2 = 4, and 16 / 4 = 4. Not worded the same, but the answer is the same. – Kcvin Nov 06 '15 at 23:43
  • @PeterDuniho what's so specific about it that makes it close-able? The title is how to capture Console output (or stdout). A hidden console application which creates the process & starts WPF message pump to avoid the [InvalidOperationException](https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput(v=vs.110).aspx) allows you to read the stdout and CopyToAsync any stream you desire. Every comment/answer mentioning this method is incomplete and can be a potential answer for this question, and not the marked dupe. – Kcvin Nov 07 '15 at 00:08
  • @Netscape: you are missing the point. Your analogy is incorrect; a better one would be to compare 2 + 2 = 4 and 3 + 3 = 6, where the answer is "sum the two values". The question here is not about capturing stdout for _any_ process, but to redirect it for one's _own_ process. That is answered precisely by the marked duplicate. – Peter Duniho Nov 07 '15 at 01:03
  • @PeterDuniho And the answer to the question here is yes, you start up the process differently. Echoing would entail allowing it go to stdout (which probably shouldn't be redirected anyways) and also writing it to a file. The question isn't only about redirection, as you can tell in OPs attempts and acknowledgment of not knowing how to set the redirection flags. Clearly OP doesn't "need" to "map" the output, rather OP is looking for potential solutions where the "duplicate" is one. You should proceed to go close all the MVVM-INPC questions for the WPF tag since 50% come down to "implement INPC" – Kcvin Nov 07 '15 at 01:19
  • 1
    @Netscape: _"go close all the MVVM-INPC questions for the WPF"_ -- um, okay. I would probably agree that many such questions _should_ be closed as duplicates. People ask way too many questions that have already been answered, and INPC is an especially fertile area for finding such examples. But I don't have the time for full-blown search-and-destroy missions, so I'll let you take care of that one. As for this question, if you disagree with my judgment, just go ahead and vote to reopen already. You're wasting your time arguing with me about it. – Peter Duniho Nov 07 '15 at 01:24
  • I'm not arguing. I would provide a full, alternative solution but I can't, the question is closed. I can't vote to reopen, from the phone app at least... but goes to show closing a question single-handedly due to misinterpretation or "AFAIK" reasoning can prevent potentially good material from being posted. – Kcvin Nov 07 '15 at 01:43
  • I have to ask that this be reopened. The "duplicate" answer does indeed allow me to redirect stdout/stderror output originating from my own C# code to a file, but output from within the native dll still bypasses this somehow (I didn't notice this at first). – dlf Nov 09 '15 at 14:42
  • Since it doesn't look like this is going to be reopened, I'll just say for the benefit of anyone who finds this in the future that MatLab does its own thing for IO, and the key is to notice that a MatLab dll gives you a `LibNameInitializeWithHandlers()` function in addition to the standard `LibNameInitialize()` one. – dlf Nov 16 '15 at 14:43
  • @dlf It's indeed the trick. To call native `LibNameInitializeWithHandlers` with function pointers it is expecting declare the following delegate somewhere in your C# code `[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]public delegate int OutputHandlerFcn(string s);` then marshall it as `[DLLImport(...)]static extern bool LibNameInitializeWithHandlers(OutputHandlerFcn error_handler, OutputHandlerFcn print_handler);`. You can then assign delegate to `(str) => { if (str==null) return 0; ... -- your code here -- ... return str.Length; }` for instance. – CitizenInsane Mar 12 '16 at 09:23
  • This way you can redirect matlab process outputs to wherever you need. – CitizenInsane Mar 12 '16 at 09:27
  • NB: Don't pass anonymous delegate to native side directly but assign "hard-coded" function instead. Indeed garbage collector may collect anonymous function not knowing that native side is using it. – CitizenInsane Mar 12 '16 at 09:35

0 Answers0