Creating a C#/.NET CGI Executable
The gentleman asking the question mentioned that he couldn’t get it to work so I quickly created a small program to see if there were any gotchas. It turns out it works fine.
The following small program demonstrates all of this:
using System;
using System.IO;
using System.Collections;
using System.Text;
namespace CGITestApplication
{
/// <summary>
/// Summary description for Class1.
/// </summary>
class CGITest
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void
{
// *** Use this for debugging –
// Hit the link then attach debugger to this process
// and then pause to continue
// System.Threading.Thread.Sleep(30000);
// ***
IDictionary Dict = Environment.GetEnvironmentVariables();
StringBuilder sb = new System.Text.StringBuilder();
foreach(DictionaryEntry Item in Dict)
{
sb.Append((string) Item.Key + " - " + (string) Item.Value + "\r\n");
}
// *** Read individual values
string QueryString = Environment.GetEnvironmentVariable("QUERY_STRING");
// *** Read all the incoming form data both text and binary
string FormData = "";
byte[] Data = null;
if (Environment.GetEnvironmentVariable("REQUEST_METHOD") == "POST")
{
Stream s = Console.OpenStandardInput();
BinaryReader br = new BinaryReader(s);
string Length =
Environment.GetEnvironmentVariable("CONTENT_LENGTH");
int Size = Int32.Parse( Length);
Data = new byte[Size];
br.Read(Data,0,Size);
// *** don’t close the reader!
FormData = System.Text.Encoding.Default.GetString(Data,0,Size);
}
Console.Write(
@"HTTP/1.1 200 OK
Content-type: text/html
<html>
Hello World
<pre>
<b>Environment and Server Variables:</b>
" + sb.ToString() + @"
<b>Form Vars (if any):</b>
" + FormData + @"
</pre>
</html>
");
}
}
}
Couple of things of interest here in regards to retrieving the POST content. Note that I explicitly open the input stream here in order to retrieve the content as binary data. You’ll want to do this incase the data retrieved is something like a file upload or other binary content. If you just need default text you can use the Console.In TextReader to grab the content in the default encoding format. Make sure you don't close the Reader() or the request will fail.
To set this up with IIS you’ll need to do a few things:
- Create a virtual directory (or use an existing one)
- Make sure you enable Scripts and Executables if you’re
going to access the EXE directly with a URL - In IIS 6 make sure you allow the EXE to be launched in
the Web Extensions (otherwise you get a 404 error) - Stick the CGI executable into this directory
Now obviously CGI is pretty much legacy technology, and on IIS especially this is probably a waste of time since ASP.NET is so much easier and more optimized. However, for other Web Servers it might a be a quick and dirty way to get something up and running.
Realize also that CGI especially on IIS all fairly slow (you can feel the lag time between hits), so I hardly recommend this. But if you’re using a non MS server and you must support CGI there it is.
If you are using a CGI executable I would highly recommend you hide that fact behind a script map extension rather than accessing the EXE directly. Move the EXE into some directory that's not Web accessible and then create a script map to it. So for example, on IIS you can assign the .CGITEST extension to the above EXE file and then access the EXE through any accesses to this script extension.
Alright, I MUST have better things to do with my time, eh? <g>
Other Posts you might also like
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Getting the Client IP Address in ASP.NET Core
- Resolving Paths To Server Relative Paths in .NET Code
- Preventing iOS Textbox Auto Zooming and ViewPort Sizing
The Voices of Reason
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
But your example gives the following:
Unhandled Exception: System.IO.FileLoadException: Could not load file or assembly 'SimpleDotNetCGI_2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG)) File name: 'SimpleDotNetCGI_2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' ---> System.ArgumentException: Invalid directory on URL. at System.Security.Policy.PolicyLevel.GenericResolve(Evidence evidence, Boolean& allConst) at System.Security.Policy.PolicyLevel.Resolve(Evidence evidence, Int32 count, Char[] serializedEvidence) at System.Security.PolicyManager.ResolveHelper(Evidence evidence, PermissionSet request, Boolean systemPolicy) at System.Security.HostSecurityManager.ResolvePolicy(Evidence evidence) at System.Security.PolicyManager.Resolve(Evidence evidence, PermissionSet request) at System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Boolean checkExecutionPermission) at System.Security.SecurityManager.ResolvePolicy(Evidence evidence, PermissionSet reqdPset, PermissionSet optPset, PermissionSet denyPset, PermissionSet& denied, Int32& grantedIsUnrestricted, Boolean checkExecutionPermission)
# re: Creating a C#/.NET CGI Executable
One thing I forgot to mention is that you will need proper permissions to execute the EXE and any dependent assembly. It looks from your stacktrace that you're either missing a dependent assembly or don't have rights to load the dependency. The security environment will vary with your Web Server, as the server will load the EXE under a specific account (NETWORK SERVICE under IIS 6 I think).
# THANKS!
Too bad google only references your weblog on its 5 or 6th page...
# re: Creating a C#/.NET CGI Executable
Thanks,
Vlad
# re: Creating a C#/.NET CGI Executable
i can see that its very easy to create something like this but i am trying to and i get the folowing error:
CGI Error
The specified CGI application misbehaved by not returning a complete set of HTTP headers. The headers it did return are:
Unhandled Exception: System.ArgumentException: Invalid directory on URL.
any suggestions?
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
"CGI Error
The specified CGI application misbehaved by not returning a complete set of HTTP headers. The headers it did return are:
Unhandled Exception: System.ArgumentException: The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG))"
I added a try {} catch {} block that encompasses the whole program, but it doesn't capture the error, so the error is somewhere else -- I'm not that familiar with CGI, but it doesn't look like a permissions error to me.
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
I have found a use for this, but need to know what extra code would be required in order to actually process the page in the PATH_TRANSLATED environment variable and return the generated HTML. Is this possible?
# re: Creating a C#/.NET CGI Executable
Of course various Web Servers return the content of this variable differently so you may have to do some additional checking for SERVER_NAME and bracketing.
# re: Creating a C#/.NET CGI Executable
Originally the entire folder was authorized using the membership condition --> URL file:///c:/cgibin/* with FullTrust, at the machine level.
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
I'm quite new to CGI, but with a very large experience in C# & ASP.NET programming.
I'd like to retrieve a page's content, which is built with IFrames that retrieve a CGI content.
When I capture my WebBrowser document_Complete event, i see an empty innerHTML for those IFrames (src="www.a.com/file1.cgi" attribute...).
My question is how can I retrieve those IFrames' content???
# re: Creating a C#/.NET CGI Executable
Unhandled Exception: System.ArgumentException: The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG))
I seen in the Event Viewer that there was a DCOM error. I located the DCOM object in question (Visual Studio Just-In-Time Debugger) and added the "Internet Guest Account" to the Launch Permissions. I am no longer receiving the error in the Event Viewer, but I am still receiving the same error on my web page when attempting to access the cgi script. As well, I have added Full Control permissions to the above mentioned user as well as all other users listed for the directory the file is in as well as the file itself. (This is my local machine, so too many permissions is not a huge deal.)
Please help. This is getting old fast.
# re: Creating a C#/.NET CGI Executable
the line after content-type MUST be blank, and have no spaces on it... the example above has a space on that line
@"HTTP/1.1 200 OK
Content-type: text/html
<--- Make sure there is no spaces here!
<html>
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
The type initializer for 'System.ServiceModel.ClientBase`1' threw an exception.
Help plz.
# re: Creating a C#/.NET CGI Executable
Unhandled Exception: System.ArgumentException: The parameter is incorrect. (Exception from HRESULT: 0x80070057 (E_INVALIDARG))
I have no idea what to do!
Many thanks
Chris
# re: Creating a C#/.NET CGI Executable
http://support.microsoft.com/kb/829393/
Any opinions as I am a bit stuck!
Thanks
Chris
# re: Creating a C#/.NET CGI Executable
Unhandled Exception: System.TypeInitializationException: The type initializer for 'System.ServiceModel.ClientBase`1' threw an exception. ---> System.TypeInitializationException: The type initializer for 'System.ServiceModel.DiagnosticUtility' threw an exception. ---> System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize ---> System.ArgumentException: Illegal characters in path. at System.Security.Permissions.FileIOPermission.HasIllegalCharacters(String[] str) at System.Security.Permissions.FileIOPermission.AddPathList(FileIOPermissionAccess access, AccessControlActions control, String[] pathListOrig, Boolean checkForDuplicates, Boolean needFullPath, Boolean copyPathList) at System.Security.Permissions.FileIOPermission..ctor(FileIOPermissionAccess access, String path) at System.AppDomainSetup.VerifyDir(String dir, Boolean normalize) at System.Configuration.ClientConfigPaths..ctor(String exePath, Boolean includeUserConfig) at System.Configuration.ClientConfigPaths.GetPaths(String exePath, Boolean includeUserConfig) at System.Configuration.ClientConfigurationHost.get_ConfigPaths() at System.Configuration.ClientConfigurationHost.GetStreamName(String configPath) at System.Configuration.ClientConfigurationSystem..ctor() at System.Configuration.ConfigurationManager.EnsureConfigurationSystem() --- End of inner exception stack trace --- at System.Configuration.ConfigurationManager.EnsureConfigurationSystem() at System.Configuration.ConfigurationManager.GetSection(String sectionName) at System.Configuration.PrivilegedConfigurationManager.GetSection(String sectionName) at System.Diagnostics.DiagnosticsConfiguration.Initialize() at System.Diagnostics.DiagnosticsConfiguration.get_Sources() at System.Diagnostics.TraceSource.Initialize() at System.Diagnostics.TraceSource.get_Listeners() at System.ServiceModel.Diagnostics.DiagnosticTrace.UnsafeRemoveDefaultTraceListener(PiiTraceSource piiTraceSource) at System.ServiceModel.Diagnostics.DiagnosticTrace.CreateTraceSource() at System.ServiceModel.Diagnostics.DiagnosticTrace..ctor(TraceSourceKind sourceType, String traceSourceName, String eventSourceName) at System.ServiceModel.DiagnosticUtility.InitDiagnosticTraceImpl(TraceSourceKind sourceType, String traceSourceName) at System.ServiceModel.DiagnosticUtility.InitializeTracing() at System.ServiceModel.DiagnosticUtility..cctor() --- End of inner exception stack trace --- at System.ServiceModel.DiagnosticUtility.get_Utility() at System.ServiceModel.ClientBase`1..cctor() --- End of inner exception stack trace --- at System.ServiceModel.ClientBase`1.InitializeChannelFactoryRef() at System.ServiceModel.ClientBase`1..ctor() at hcAdmin.UserManager.UserManagerSoapClient..ctor() at hcAdmin.UserManagerCI..ctor() at hcAdmin.Program.UserAlreadyExists(String newUserName, String hostUserName, String hostPassword, String ownerName) at hcAdmin.Program.doAutomatic() at hcAdmin.Program.Main()
I've tried a bunch of things I gathered here and there on forums to try and make it work, but to no vain. I've tried stuff like:
-I've installed the .net framework 3.5 feature on the server
-I've added the I_USR in the security for the folder and even gave it full control for testing purposes.
-I've copied the whole content of the \bin\debug folder to the cgi-win folder, in order to have the APPLICATION.exe.config file.
-I've added the dll's from the \obj\debug\TempPE folder.
Most of that stuff (except the .net framework) didnt make much sense to try (at least to me), but I did anyways. So yeah I'm running out of dumb things to try. Any suggestion?
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
Console.WriteLine("content-type: text/html");
Console.WriteLine("");
Console.WriteLine("HELLO WORLD!");
That should work, and if it doesn't then something else is the problem, however if it does, you should be able to figure out what is different here to what is in the example and then continue building your application accordingly.
# re: Creating a C#/.NET CGI Executable
Thank you so much!
I've spent a whole day trying to fix this, on an app that was working until I created a new IIS web site for it.
Cheers.
# re: Creating a C#/.NET CGI Executable
Since you obviously know what you're doing, maybe you can throw some guidance my way. I have a .NET Local System Service I created to talk to my application. However, I am now looking to port this application to a web-based version (accessible across all browsers (including mobile). At first I thought about just creating XHTML pages and using a C#.NET backend CGI program to talk to my service on the web server. But it sounds like from what you have indicated CGI isn't on it's way out, it is already out. I realize I could do all this with .NET web services and and ASP.NET page, but then it won't be compatible across all browsers. Do you have any suggestions/guidance for this poor soul? :-)
Thank you!
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
# re: Creating a C#/.NET CGI Executable
"Make sure you don't close the Reader() or the request will fail." Can you explain how it fails and why, please? I feel very uncomfortable not closing a resource I have opened!
# re: Creating a C#/.NET CGI Executable
Like, some non-MS Server but including the .NET Framework so that this CGI actually works, right? :-)