C# - Powershell AddPSSnapin Causes errors - c#

So, I import the System.Management.Automation dll and I'm trying to run a New-Mailbox command with params
so I use:
RunspaceConfiguration config = RunspaceConfiguration.Create();
PSSnapInException psEx = null;
config.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.E2010", out psEx);
That's all fine and dandy... but when I go to run the application I get the following:
Cannot load Windows PowerShell snap-in Microsoft.Exchange.Management.PowerShell.E2010 because of the following error: The type initializer for 'Microsoft.Exchange.Data.Directory.Globals' threw an exception.
So, I did some research online and found that I need to change from Any CPU to x86 as the platform target.
However, when I do that I get a HTTP Error 503. The service is unavailable. error
I am almost positive that I have to run it as a 32 bit process so that it can use the snapin (which from other reading seems to be what the snapin is running under)
I did change the app pool to Enable 32-bit Applications to True. Which is when I get the error.
I've read other posts... but, I'm not sure how to get past this Service unavailable thing.
I've tried using a x64 build and get Could not load file or assembly 'EmailAdminWeb2' or one of its dependencies. An attempt was made to load a program with an incorrect format.

You don't use this at all:
config.AddPSSnapIn("your snapin here", out psEx);
instead.... just use a connection as follows:
WSManConnectionInfo connectionInfo = new WSManConnectionInfo(new Uri("http://yourdomainhere/Powershell/Microsoft.Exchange"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", PsCreds);
Runspace runspace = RunspaceFactory.CreateRunspace(connectionInfo);
Now run your commands and you're good to go.
Quick notes:
Make sure you're app is targeting x64 not Any CPU or x86
If you're using .net 4.5 (or 4) make sure you're app pools are set properly (v4.0 not v2.0) and that you have Enable 32bit apps set to false

Related

C# Class Library throws FileNotFoundException while trying to load another library

I try to connect to a fiscal device with a C#.
I use this documentation to do so: http://integration.atol.ru/api-en/#connection-to-project
So basically I have a driver of the device installed on my PC (fprt10.dll) and there is a "wrapper" assembly that allows me to work with this driver from C# (Atol.Drivers10.Fptr.dll). I import this wrapper into my project as a reference.
I have the following constructor in my class:
public MyClass()
{
IFptr fiscalPrinter = new Fptr();
// Here comes several settings to configure connection
fiscalPrinter.applySingleSettings();
fiscalPrinter.open();
fiscalPrinter.beep();
fiscalPrinter.close();
}
To test the solution I use another application, that loads my Class Library as a dependency.
When I call a constructor of MyClass I get an exception:
System.IO.FileNotFoundException: Driver not installed
at Atol.Drivers10.Fptr.Fptr.loadDriver(String path)
at Atol.Drivers10.Fptr.Fptr..ctor()
at MySolution.MyClass.MyClass()
...
If I create instance of Fptr with a path to the driver
IFptr fiscalPrinter = new Fptr(#"C:\path\fptr10.dll")
I get the slightly different exception, but I believe the problem is the same:
System.IO.FileNotFoundException: Can`t load driver library "C:\path\fptr10.dll"
at Atol.Drivers10.Fptr.Fptr.raiseNotFoundError(String path, Exception reason)
at Atol.Drivers10.Fptr.Fptr.loadDriver(String path)
at Atol.Drivers10.Fptr.Fptr..ctor(String libraryPath)
at MySolution.MyClass.MyClass()
...
But when I create a Console Application and put in there exact same code (both versions with path and without), everything works: the device beeps, there are no exceptions.
What could be the reason for that behavior and how to fix this?
The issue may be one of the following
The test application is using 'target platform' different than the console application which works fine. The device driver folders expected for each platform could be different. e.g. Changing the targeted platform from 'any CPU' to 'x64' / 'x86' (depending on the type of OS where you are running it) will help
Try running the test application from admin command prompt. Permissions issue may reflect as 'file not found' (instead of 'file could not be loaded').
Use an assembly binding viewer tool to debug the issue further
Refer to Could not load file or assembly or one of its dependencies for more discussion and inputs on the assembly loading issues.
Thank you samiksc.
The issue was in the test app. The driver and OS that I use are both x64, but the test application is x86. With x86 driver everything works.

Telnet is not recognized command when cmd.exe is started from Process.Start in C#

I have already switch on the telnet option on control panel - program/features.
Though telnet worked fine manually, but when I run below from my C# library, I am getting this error :
"'telnet' is not recognized as an internal or external command,
operable program or batch file."
This is the code I use to start Telnet:
Process.Start("cmd", "/k telnet")
What makes the difference between this and running it from the command prompt manually and how do I overcome this difference?
Telnet is a program, always installed in the system folder. Which also means, it's always in the path. You don't need to start cmd.exe in order to start telnet.exe. Just use
Process.Start("telnet.exe");
Use the arguments parameter to pass arguments to telnet itself, eg the host, log file etc:
Process.Start("telnet.exe","localhost 80 -f mylog.txt");
There other, probably better options.
Use a Telnet Library
You can use a Telnet library and connect to a server directly, eg using the Telnet NuGet package :
using (Client client = new Client("localhost",80, new System.Threading.CancellationToken()))
{
await client.WriteLine("abcd");
string response = await client.ReadAsync();
Console.WriteLine(response);
}
Use PowerShell
Telnet is often used to test connectivity to a web server, not to actually send commands. In this case, you could use PowerShell's Test-NetConnection to receive diagnostics about the connection:
using System.Management.Automation;
var ps = PowerShell.Create()
.AddCommand("test-netconnection")
.AddArgument("localhost")
.AddParameter("CommonTCPPort", "HTTP");
var results = cmd.Invoke();
foreach (dynamic result in results)
{
Console.WriteLine($"{result.TcpTestSucceeded} {result.SourceAddress}");
}
Make sure you add the correct Powershell NuGet package, Microsoft.PowerShell.5.ReferenceAssemblies, not the unsupported and abandoned System.Management.Automation package
The advantage of PowerShell is that you can chain commands and scripts together just by adding more AddComand, AddScript calls. Each command will receive the output of the previous one. You could use Import-CSV to read a list of servers and ports from a file and pipe the output to Test-NetConnection
On installing Telnet Client via Control Panel - Programs and Features - Turn Windows features on or off there is installed telnet.exe into directory %SystemRoot%\System32.
The directory %SystemRoot%\System32 is for 64-bit applications on 64-bit Windows. Therefore the installed executable telnet.exe is also the 64-bit version. This directory contains also the 64-bit version of cmd.exe.
System environment variable PATH contains %SystemRoot%\System32 which is responsible for finding %SystemRoot%\System32\cmd.exe and %SystemRoot%\System32\telnet.exe when being executed without file extension and without path.
But there is additionally the directory %SystemRoot%\SysWOW64 for 32-bit applications containing the 32-bit versions of the executables.
Microsoft explains with the documentation pages WOW64 Implementation Details and File System Redirector how a file system access to %SystemRoot%\System32 by a 32-bit application is redirected automatically to %SystemRoot%\SysWOW64.
There is no 32-bit version of telnet.exe installed into %SystemRoot%\SysWOW64 on installing Telnet Client.
So what happens on:
Process.Start("cmd", "/k telnet")
When the C# library is compiled as 64-bit library used by a 64-bit application, 64-bit Windows finds and starts %SystemRoot%\System32\cmd.exe which finds and then starts %SystemRoot%\System32\telnet.exe.
But when the C# library is compiled as 32-bit library used by a 32-bit application, 64-bit Windows finds and starts via file system redirector %SystemRoot%\SysWOW64\cmd.exe which cannot find telnet.* with a file extension listed in environment variable PATHEXT in current directory or any directory of environment variable PATH because there is no file telnet.exe in directory %SystemRoot%\SysWOW64.
The best solution is definitely to use a static (or dynamic) telnet library in the C# library to be independent on telnet.exe as suggested by Panagiotis Kanavos. In my point of view it is a shame for every C# programmer using external executables via a process call for which C# code can be quite easily also written by the programmer. Using any world wide web search engine with the search term C# telnet returns lots of pages with solutions, for example C# Telnet Library on Stack Overflow.
Of course it is also possible to get first value of environment variable SystemRoot using GetEnvironmentVariable method to get path to Windows directory, or even better using GetWindowsDirectory or GetSystemWindowsDirectory method.
Then concatenate this string value with "\\System32\\telnet.exe" to a new string and check if the file with that full path exists using File.Exists method. If that file exists on 32-bit Windows with 32-bit version of telnet.exe installed if C# application is also a 32-bit application or on 64-bit Windows with 64-bit telnet.exe installed if C# application is a 64-bit application, then this file name with full path and file extension can be used in a process call.
Otherwise concatenate the Windows directory path with "\\Sysnative\\telnet.exe" and check if file with that full path exists. If this is true on 64-bit Windows with 64-bit version of telnet.exe installed if C# application is a 32-bit application, it would be possible to run from within 32-bit application the 64-bit telnet client executable using this path.
But if that fails also, telnet.exe is not installed at all which is the reason why usage of telnet.exe from within a C# code application is in general not advisable.
Honestly, I don't understand why to have code in a C# library which just starts a command process executing telnet.exe without options and therefore requiring user input and which keeps the command process running after telnet client session terminated. The C# library function can be replaced by a shortcut to telnet.exe on Windows desktop or in Windows start menu of the user.

C# cannot load powershell snapin despite being registered

I am trying to use a custom snapin for powershell in my C# program. (The C# program is a modified version this)
I'm using the standard approach to setup the runspace:
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(scriptText);
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
If I run this as is and scriptText contains the command "Get-PSSnapin" my snapin is missing. If I run "Get-PSSnapin -Registered" then I can see my snapin. If I use Add-PSSnapin, then it returns nothing and followup commands that I issue (which are implemented in the snapin will return):
...is not recognized as the name of a cmdlet, function, script file,
or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again
So, I amended my code to add the cmdlet to the runconfig:
RunspaceConfiguration runConfig = RunspaceConfiguration.Create();
PSSnapInException psEx = null;
runConfig.AddPSSnapIn("mySnapin", out psEx);
Runspace runspace = RunspaceFactory.CreateRunspace(runConfig);
Now, when I run it, I get:
error in script : Cannot invoke this function because the current host
does not implement it.
I tried both the x86 and 64 version. The snapin gets installed in the x86 programs so I did this by compiling for x86. If I do the 64 version, I get:
"Error in script : The Windows PowerShell snap-in 'mySnapin' is not
installed on this machine".
Confirming that this is specific to x86.
I also tried loading this as a module (desperation), but it doesn't work that way either. When I run:
get-command <command in mySnapin> | select Module, PsSnapin
The output doesn't have a module, just the snapin, hence it was silly to begin with.
I should note that this is installed by another program and I have no control over where it is install. I thought that might be a factor, but since it shows up with -Register I think it is finding without issue.
Edit:
I also tried import-module <path to dll>. This doesn't output anything and subsequent calls to functions in my snapin yield:
error in script : Cannot invoke this function because the current
host does not implement it.
Then again in C# since I wasn't clear on whether the import will be preserved across calls.
pipeline.Commands.Add("Import-Module");
var command = pipeline.Commands[0];
command.Parameters.Add("Name", #"<path to my snapin .dll>");
Any help getting this rolling would be greatly appreciated. Thank you!

Run Exchange Powershell command from C#

I am trying to run EMC commands in C#. I am running this from my personal PC that has exchange management tools installed on it.
Our exchange servers have 2007 running on them.
The thing is, when I run Powershell or EMC, I need to run as a different user that has exchange server 2007 permissions since my individual profile doesn't have these permissions.
That being said, this is my code I have running on my personal PC:
RunspaceConfiguration config = RunspaceConfiguration.Create();
PSSnapInException snapEx = null;
PSSnapInInfo info = config.AddPSSnapIn("Microsoft.Exchange.Management.Powershell.Admin", out snapEx);
Runspace runspace = RunspaceFactory.CreateRunspace(config);
runspace.Open();
Command createCMD = new Command("Get-Mailbox ID");
Pipeline pipe = runspace.CreatePipeline();
pipe.Commands.Add(createCMD);
Collection<PSObject> results = pipe.Invoke();
The error I am getting is:
The Windows PowerShell snap-in Microsoft.Exchange.Management.Powershell.Admin is not installed on this computer.
I am getting it when I try and add the Microsoft.Exchange.Management.Powershell.Admin snapIn.
I feel this has something to do with my permissions on my individual profile, but I am not entirely sure. If it is true, how do I fix this.
EDIT
The reason I say it sounds like permissions is because I am able to open powershell and add the snapin. However when I run a command such as get-mailboxstatistics myUserId it throws an error saying MyServer\MyStorageGroup does not exist. However, when I shift-rightCLick and run as different user and use the credentials of my exchange admin account, I am able to run these commands.
If an error says it is not installed on your computer, why do you suspect it has something to do with permissions?
As this post suggests, please check if you have installed the 2007 version of the tools, as the Snapin in question is not available on the 2010 version.
Try the following steps:
Open up a powershell editor of your choice and add the PSSnapin there. If it works, the Snapin is available, if not, it is really not installed on your machine.
If it is available try to set your build configuration from x86 to 64bit or vice versa.
Eventually you can install the .dll in question by hand. Referring to this answer from Keith hill you have to issue the following Powershell commands
$snapinPath = 'Microsoft.Exchange.Management.PowerShell.Admin.dll'
C:\Windows\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe /i $snapinPath
Errors like this are often a 32 bit/64 bit problem. For example, the snapin might be registered as a 32 bit and your C# program is 64 bit or vice versa.
Sometimes you can fix this by running the other version of InstallUtil, e.g.
$snapinPath = 'Microsoft.Exchange.Management.PowerShell.Admin.dll'
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe /i $snapinPath
After fixing that, I think you'll hit another problem with how you're creating the command. You don't specify arguments when creating a command. Instead, you write something like:
Command createCMD = new Command("Get-Mailbox");
createCMD.Parameters.Add(null, "ID");

Error when reading audio file?

I have the following function that I am attempting to use to determine the length of an MP3 file:
public static string GetMP3DurationBackup(string Filename)
{
string Duration = null;
WMPLib.WindowsMediaPlayer w = new WMPLib.WindowsMediaPlayer();
WMPLib.IWMPMedia m = w.newMedia(Filename);
if (m != null)
{
Duration = m.durationString;
}
w.close();
return Duration;
}
I have run into an issue where I get the following error:
Retrieving the COM class factory for component with CLSID
{6BF52A52-394A-11D3-B153-00C04F79FAA6} failed due to the following
error: 80040154..
when I call the above function from my web application (call below):
string test = MediaUtil.GetMP3DurationBackup(#"C:\Temp\Audio\bad.mp3");
But when I call it from a console application test harness I created (exact same call as above) it works fine. I have set the project that contains the function to target x86 in the Build properties, but that did not fix the issue.
Does anyone know why this would happen? Suggestions on where to start to debug this?
UPDATED FOR BOUNTY:
Ok, I've tried a number of things but I am still getting this error. Among other things I have tried the steps below which I felt were the most promising, but no dice:
Went into my registry and confirmed that the value at:
HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{6BF52A52-394A-11d3-B153-00C04F79FAA6}\InprocServer32
is refering to C:\WINDOWS\SysWOW64\wmp.dll
Opened command prompt,
navigated to C:\WINDOWS\SysWow64, ran: regsvr32.exe wmp.dll
I have created a console app test harness and I am able to reproduce the error if I run the test project in x64. If I switch it to x86 it works fine.
Does anyone have any idea of why the above would not resolve the issue? Suggestions on where to look next?
You say it doesn't work in x64, but you try to register the 32-bit version of wmp.dll (C:\Windows\SysWow64 contains 32-bit assemblies).
Try to register the x64 version of wmp.dll, which is located in C:\Windows\System32 on a 64-bit platform.
If you don't have this file then there probably is no 64bit Windows Media Player available for your platform. But there is a workaround:
Create a 32-bit console application that takes the mp3 filename as command line argument and outputs the duration to stdout using Console.WriteLine, then in the webapp, you call the console application and capture the output like in this example on MSDN
Give this lib a whirl. Its fast and has no special requirements for software to be installed on the machine.
http://naudio.codeplex.com/

Categories

Resources