views:

70

answers:

3

Hello,

I have written a program with C#, that creates a logfile and fills this by using log4net. This program starts powershell-scripts. The scripts use log4net, too. It works:

> C#:   
> ps.Runspace.SessionStateProxy.SetVariable("myMethod",(Action<Level,string>)myMethod);
> ps.AddCommand(System.IO.Path.Combine(pathScripts, testSkripte[i].ToString()));  
> ps.Invoke();

> Powershell:                     
> $ScriptLog.Invoke([log4net.Core.Level]::Debug, "TestFile_Debug")                     
> $ScriptLog.Invoke([log4net.Core.Level]::Warn, "TestFile_Warn") $ScriptLog         
> $ScriptLog.Invoke([log4net.Core.Level]::Error, "TestFile_Error")

Now I want add to use the standard Write-Error, Write-Debug, etc. CMDlets in my Script.
(looks like here - answer of Hinek).

Powershell:
   Write-Warning "Write-Warning"      
  AND         
   Write-Error "Write-Error"

works, but the following doesn´t work:

Write-Debug "Write-Debug"         (I don´t get an item in my logfile)   OR    
Write-Debug "Write-Debug" -debug  (for this I get an item in my logfile, but ...)

... I get an error in my logfile, too. The error looks like this:

[2010-10-22 13:10:58,097] DEBUG : Write-Debug                                     
[2010-10-22 13:10:58,113] ERROR : Cannot invoke this function because the current
                                  host does not implement it

(I think to have all namespaces.)

What the error-message means and what can I do again this?

thanks

+4  A: 

The message “the current host does not implement it” tells that you should provide a host that implements missed features. Presumably you should implement your own PSHost and PSHostUserInterface, at least what you really need there. In the latter class you implement methods like WriteDebugLine and WriteErrorLine. Then cmdlets Write-Warning and Write-Debug trigger that methods internally.

Complete example of a host with user interface and those methods: http://msdn.microsoft.com/en-us/library/ee706577(v=VS.85).aspx (Perhaps you don’t need most of other methods, provide some dummies then)

Roman Kuzmin
Ok. Now I had test your suggestion. I mean that is very circuitous, because I need three classes, which have a lot of methods I don´t need. Are there no shorter way? (I thought about parameters or so, because the errormessages and the warnings work. Have you another idea, too? thank you.
Rotaney
@Roman's definitely right, and there aren't any shortcuts. The PowerShell hosting API is VERY complicated. What you'll want to do is just provide dummy methods (throw new NotImplementedException) for everything you don't need.
Jaykul
@Rotaney: you need 2 classes (host and UI). The "3rd class" is your application, you have it anyway.
Roman Kuzmin
+2  A: 

Here is a workaround solution: override the default command Write-Debug (which is actually not implemented) with a function:

function global:Write-Debug
(
    [string]
    $Message,
    [switch]
    $Debug
)
{
    # check both the $Debug switch and the $DebugPreference variable:
    if ($Debug -or ($DebugPreference -ne 'SilentlyContinue')) {
        # do job; in this demo just write to the console:
        [Console]::WriteLine("DEBUG: $Message")
    }
}

In C# put this code into a string and invoke it once in the same runspace where the main script will be invoked. This “profile” code installs the global function Write-Debug which is semantically the same as the original cmdlet. Then, when the main code calls Write-Debug that function is called, not the default cmdlet.

P.S. I have not tried this way, I prefer to use my own host (see my other answer). But this way should work fine in many cases (not all, perhaps).

Roman Kuzmin
+1  A: 

Hello,

Now I had found the answer myself:

C#-Code:
    using (ps = PowerShell.Create())
    {
         int a = testSkripte.Count;
         for (int i = 0; i < a; i++)
         {                        
            ps.Runspace.SessionStateProxy.SetVariable("portalpfad", pathExecuteScript);
            ps.Runspace.SessionStateProxy.SetVariable("ScriptLog", (Action<Level, string>)ScriptLog);


   //add following row:
   ps.Runspace.SessionStateProxy.SetVariable("DebugPreference", "Continue");



        ps.AddCommand(System.IO.Path.Combine(pathScripts, testSkripte[i].ToString()));
        ps.Streams.Debug.DataAdded += new EventHandler<DataAddedEventArgs>(Debug_DataAdded);
        ps.Streams.Warning.DataAdded += new EventHandler<DataAddedEventArgs>(Warning_DataAdded);
        ps.Streams.Error.DataAdded += new EventHandler<DataAddedEventArgs>(Error_DataAdded);   
        ps.Invoke();                        
     }

and this for write-debug:

Powershell-Code:

    usings ... 

    #set variable
    $DebugPreference

    #Now Write (-Debug) without Error
                    Write-Debug "Write-Debug"
                    Write-Warning "Write-Warning"
                    Write-Error "Ende --> Write-Error"
Rotaney
Good find. Still, it looks like your application is not so trivial. A custom host approach is scalable and therefore more attractive for not trivial solutions. Imagine: tomorrow your worker script wants to call `Write-Progress`, `Read-Host`, and etc. Having a custom host you will add that yet missed features relatively easy.
Roman Kuzmin