ServerManager.CommitChanges( ) throws Error: "Cannot write configuration file" - c#

Here is my code to create an application
public static bool CreateApplication(String websiteName, String applicationName, String AppDIR,String appPoolName)
{
try
{
var windowsDir = Environment.GetEnvironmentVariable("SystemRoot");
Process.Start(windowsDir+#"\system32\inetsrv\appcmd","unlock config -section:system.webServer/security/authentication/windowsAuthentication");
Process.Start(windowsDir+#"\system32\inetsrv\appcmd","unlock config -section:system.webServer/security/authentication/anonymousAuthentication");
ServerManager iisManager = new ServerManager();
if (!applicationName.Contains("/"))
applicationName = "/" + applicationName;
var app = iisManager.Sites[websiteName].Applications.Add(applicationName, AppDIR);
app.ApplicationPoolName = appPoolName;
var config = app.GetWebConfiguration();
var anonsection = config.GetSection("system.webServer/security/authentication/anonymousAuthentication", iisManager.Sites[websiteName].Name + applicationName);
anonsection["enabled"] = false;
var winsection = config.GetSection("system.webServer/security/authentication/windowsAuthentication", iisManager.Sites[websiteName].Name + applicationName);
winsection["enabled"] = true;
iisManager.CommitChanges(); //Blows up here
return true;
}
catch
{
return false;
}
}
Everything is fine until it hit's the commit changes method call.
It throws this error
Error: Cannot write configuration file
All of the code is verified as working except for where I change the enabled values to false and true.
When I added these in it started to explode.
Is there any way to fix this from code, that can be distributed to other machines?
Update:
After re-locking the files
like this
Process.Start(windowsDir + #"\system32\inetsrv\appcmd", "lock config -section:system.webServer/security/authentication/windowsAuthentication");
Process.Start(windowsDir + #"\system32\inetsrv\appcmd", "lock config -section:system.webServer/security/authentication/anonymousAuthentication");
I now get this error
Error: Cannot commit configuration changes because the file has
changed on disk

I also ran into the second of your two issues (Error: Cannot commit configuration changes because the file has changed on disk) for our c# application which we use to configure IIS servers as part of our deploy operation.
For me in the end it was unintentionally nesting two using statements wrapped around the ServerManager object that caused the problem.
For your particular situation I would try to add a using statement for your reference to ServerManager and ensure that the command line appcmd calls are outside the using statement or moved within the using statement but translated into API calls via the ServerManager object.

Related

REST not working on published version, works locally

I always had a working version in Azure but somehow it does not work anymore, I copied the database and I created new connections. When I run in localhost all works fine, however when I publish it does not work anymore, unless I delete the analytics section of the REST
this is what I got:
// GET PROGRAM (IF EXIST)
[Route("api/Program/{ProgramCode}/{id_user}/{id_client}")]
public object GetProgramSettingsFromFile(string ProgramCode, string id_user, int id_client)
{
AnalyticsUser newEntry = new AnalyticsUser
{
id_user = id_user,
ProgramCode = ProgramCode,
DateTime = DateTime.Now,
id_client = id_client
};
db.AnalyticsUsers.Add(newEntry);
db.SaveChanges();
string secondPart = "api/program/" + ProgramCode + ".json";
var allText = (new WebClient()).DownloadString(uriPath + secondPart);
object jsonObject = JsonConvert.DeserializeObject(allText);
return jsonObject;
}
the above works fine in local host, but when published it does not work. Error 500 internal server error. So when i escape the analytics section like this:
// GET PROGRAM (IF EXIST)
[Route("api/Program/{ProgramCode}/{id_user}/{id_client}")]
public object GetProgramSettingsFromFile(string ProgramCode, string id_user, int id_client)
{
//AnalyticsUser newEntry = new AnalyticsUser
//{
// id_user = id_user,
// ProgramCode = ProgramCode,
// DateTime = DateTime.Now,
// id_client = id_client
//};
//db.AnalyticsUsers.Add(newEntry);
//db.SaveChanges();
string secondPart = "api/program/" + ProgramCode + ".json";
var allText = (new WebClient()).DownloadString(uriPath + secondPart);
object jsonObject = JsonConvert.DeserializeObject(allText);
return jsonObject;
}
it works fine on localhost and on published. So what is going in here? I can't seem to replicate the error locally since it all works fine. Is there any idea?
alright it was something in the Azure SQL server that was producing the error. For other that might encounter the same problem, cause localhost connecting to the database was working fine, also using the webconfig connection string, means that something is blocking it when published.
In Azure SQL Database Firewall settings there is a switch:
Allow Azure services and resources to access this server
This one had to be to yes (was on No)
Now it's working fine (the SAVE analytics setting is an internal connection) that is why ta was working without fine but with it got blocked.
Because you can run normally locally, you can debug or find the ConnectionString in the project (appconfig.json or web.config file).
Check your ConnectionString and replace the SQL Server connection string in the production environment.
You can configure the strings of the production environment in the configuration file or on the portal.
Tips:
If you don't have azure sql server, as long as the sql server that can be accessed on the public network is also possible.
Note that the table structure and stored procedures or functions of the database in the production environment must be consistent.

RemotingConfiguration lock my file, how to use log4net's RemotingAppender?

client.cs
RemotingAppender remotingAppender = new RemotingAppender();
remotingAppender.Sink = "tcp://localhost:15642/LoggingSinkInConsoleDaemon";
remotingAppender.BufferSize = 1;
remotingAppender.ActivateOptions();
BasicConfigurator.Configure(remotingAppender);
log.Info("everything is ok!");
server.cs
LogManager.GetRepository().PluginMap.Add(new Plugin.RemoteLoggingServerPlugin("LoggingSinkInConsoleDaemon"));
client.exe log to server.exe, everything is ok, and after client.exe exit, i use Unlocker.exe(which can be found here) find that client.exe is locked by server.exe(which means i can't delete client.exe yet because it is used by server.exe), i locate the error which is caused by RemotingAppender, but i don't know how to resolve.
i think Remoting handle the RemotingAppender's request, and it have locked client.exe, how can i release the lock?
======================update 1===================================
client.cs
var repo = LogManager.GetRepository();
var app = repo.GetAppenders().Where(x => x.GetType() == typeof(RemotingAppender)).FirstOrDefault();
var remotingAppender = app as RemotingAppender;
var root = ((log4net.Repository.Hierarchy.Hierarchy)LogManager.GetRepository()).Root;
var attachable = root as IAppenderAttachable;
attachable.RemoveAppender(remotingAppender);
i tried above it works, but i lose my log.
i search the source, RemoveAppender remove the logger from list, so it doesn't solve the problem
I'm not sure at all but have you tried remotingAppender.Close(); when you finish with it ?

WUA IUpdate2.CopyToCache throws WU_E_INVALID_UPDATE_TYPE

I have some problems getting the WUA API to run as expected.
I want to manually copy a update file to a machine and then feed it to the WUA.
The call IUpdate2.CopyToCache causes an error that confuses me slightly. The update is definitively correct. Using a file name that does not even exist also results in the same error!
There is another strange thing I noticed: I searched for an update and found it in the API, but writing it to disk does not work at all. The code executes and there is no error reported, but the directory stays empty.
IUpdateSearcher updateSearcher = new UpdateSearcher();
updateSearcher.Online = true;
ISearchResult searchResult = updateSearcher.Search("UpdateID='" + wsusID + "'");
if (searchResult.ResultCode == OperationResultCode.orcSucceeded && searchResult.Updates.Count == 1)
{
IUpdate update = searchResult.Updates[0];
Console.WriteLine(update.KBArticleIDs[0]);
Console.WriteLine(update.Title);
Console.WriteLine(update.Identity.UpdateID);
Console.WriteLine("IsInstalled=" + update.IsInstalled);
Console.WriteLine("IsDownloaded=" + update.IsDownloaded);
// this line does nothing
update.CopyFromCache("C:\\Test\\", true);
// this returns error code 0
int errorCode = Marshal.GetLastWin32Error();
var update2 = (IUpdate2)update;
var s = new StringCollection();
// this file has been manually downloaded and exists!
s.Add(#"C:\test\Windows6.1-KB2518869-x64.msu");
// this throws the exception (0x80240026 - WU_E_INVALID_UPDATE_TYPE)
update2.CopyToCache(s);
}
Why is the CopyFromCache not doing anything and why is CopyToCache throwing this weird Exception, even if the file does not exist?
API reference: http://msdn.microsoft.com/en-us/library/windows/desktop/aa386101(v=VS.85).aspx
The problem with your code is that the specified update is not a real update.
It is a container for a bundle of updates. Try to copy the file to the cache for the bundled update.
// Example
Console.WriteLine("Bundled update=" + update.BundledUpdates[0].Title);
var s = new StringCollection();
s.Add(#"C:\test\Windows6.1-KB2518869-x64.msu");
((IUpdate2)update.BundledUpdates[0]).CopyToCache(s);

IManExt ImportCmd trouble

I have been writing a small application using C# to copy a document into an individuals 'My Documents' folder on our DMS server.
I've beased the code around the listing provided in the 'WorkSite SDK 8: Utilize the IMANEXT2Lib.IManRefileCmd to File New Document Folders' blog.
Using this code in a WinForm application I have no problems copying the file from the source folder into the users DMS 'My Documents' folder.
However if I use the code in a command line application/.dll or any other type of application (other than WinForm) during the copy process I receive the error messages;
1.
Error occurred when try to log the event!
IManExt: Error occurred when try to log the event!
Access is denied.
2.
The document was imported to the database, but could not be added to
the folder.
IManExt: The document was imported to the database, but could not be
added to the folder.
IManExt.LogRuleEventsCmd.1: Error occurred when try to log the event!
IManExt.LogRuleEventsCmd.1: Access is denied.
Error occurred when try to log the event!
-%-
Does anyone know why I'd receiving the 'Access Denied' error messages when using a non-WinForms application to copy documents?
What would I need to do to get around this issue?
Any help would be amazing!
Code in place:
public void moveToDMS(String servName, String dBName, String foldName)
{
const string SERVERNAME = servName; //Server name
const string DATABASENAME = dBName; //Database name
const string FOLDERNAME = foldName; //Matter alias of workspace
IManDMS dms = new ManDMSClass();
IManSession sess = dms.Sessions.Add(SERVERNAME);
sess.TrustedLogin();
//Get destination database.
IManDatabase db = sess.Databases.ItemByName(DATABASENAME);
//Get destination folder by folder and owner name.
IManFolderSearchParameters fparms = dms.CreateFolderSearchParameters();
fparms.Add(imFolderAttributeID.imFolderOwner, sess.UserID);
fparms.Add(imFolderAttributeID.imFolderName, FOLDERNAME);
//Build a database list in which to search.
ManStrings dblist = new ManStringsClass();
dblist.Add(db.Name);
IManFolders results = sess.WorkArea.SearchFolders(dblist, fparms);
if (results.Empty == true)
{
//No results returned based on the search criteria.
Console.WriteLine("NO RESULTS FOUND!");
}
IManDocumentFolder fldr = null;
if (results.Empty == false)
{
//Assuming there is only one workspace returned from the results.
fldr = (IManDocumentFolder)results.ItemByIndex(1);
}
if (fldr != null)
{
// Import file path
string docPath = #"C:\Temp\";
string docName = "MyWord.doc";
// Create an instance of the ContextItems Collection Object.
ContextItems context = new ContextItemsClass();
// Invoke ImportCmd to import a new document to WorkSite database.
ImportCmd impCmd = new ImportCmdClass();
// The WorkSite object you pass in can be a database, session, or folder.
// Depends on in where you want the imported doc to be stored.
context.Add("IManDestinationObject", fldr); //The destination folder.
// Filename set here is used for easy example, a string variable is normally used here
context.Add("IManExt.Import.FileName", docPath + docName);
// Document Author
context.Add("IManExt.Import.DocAuthor", sess.UserID); //Example of a application type.
// Document Class
context.Add("IManExt.Import.DocClass", "BLANK"); //Example of a document class.
//context.Add("IManExt.Import.DocClass", "DOC"); //Example of a document class.
// Document Description (optional)
context.Add("IManExt.Import.DocDescription", docName); //Using file path as example of a description.
// Skip UI
context.Add("IManExt.NewProfile.ProfileNoUI", true);
impCmd.Initialize(context);
impCmd.Update();
if (impCmd.Status == (int)CommandStatus.nrActiveCommand)
{
impCmd.Execute();
bool brefresh = (bool)context.Item("IManExt.Refresh");
if (brefresh == true)
{
//Succeeded in importing a document to WorkSite
IManDocument doc = (IManDocument)context.Item("ImportedDocument");
//Succeeded in filing the new folder under the folder.
Console.WriteLine("New document number, " + doc.Number + ", is successfully filed to " + fldr.Name + " folder.");
}
}
}
}
Just in case this helps someone else.
It seems my issue was the result of a threading issue.
I noticed the C# winform apps I had created were automatically set to run on a single 'ApartmentState' thread ([STAThread]).
Whereas the console applications & class library thread state and management hadn't been defined within the project and was being handled with the default .NET config.
To get this to work: In the console application, I just added the [STAThread] tag on the line above my Main method call.
In the class library, I defined a thread for the function referencing the IMANxxx.dll and set ApartmentState e.g.
Thread t = new Thread(new ThreadStart(PerformSearchAndMove));
t.SetApartmentState(ApartmentState.STA);
t.Start();
In both cases ensuring single 'ApartmentState' thread was implemented set would resolve the issue.

How can I programmatically run the ASP.Net Development Server using C#?

I have ASP.NET web pages for which I want to build automated tests (using WatiN & MBUnit). How do I start the ASP.Net Development Server from my code? I do not want to use IIS.
This is what I used that worked:
using System;
using System.Diagnostics;
using System.Web;
...
// settings
string PortNumber = "1162"; // arbitrary unused port #
string LocalHostUrl = string.Format("http://localhost:{0}", PortNumber);
string PhysicalPath = Environment.CurrentDirectory // the path of compiled web app
string VirtualPath = "";
string RootUrl = LocalHostUrl + VirtualPath;
// create a new process to start the ASP.NET Development Server
Process process = new Process();
/// configure the web server
process.StartInfo.FileName = HttpRuntime.ClrInstallDirectory + "WebDev.WebServer.exe";
process.StartInfo.Arguments = string.Format("/port:{0} /path:\"{1}\" /virtual:\"{2}\"", PortNumber, PhysicalPath, VirtualPath);
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
// start the web server
process.Start();
// rest of code...
From what I know, you can fire up the dev server from the command prompt with the following path/syntax:
C:\Windows\Microsoft.NET\Framework\v2.0.50727\Webdev.WebServer.exe /port:[PORT NUMBER] /path: [PATH TO ROOT]
...so I could imagine you could easily use Process.Start() to launch the particulars you need through some code.
Naturally you'll want to adjust that version number to whatever is most recent/desired for you.
Building upon #Ray Vega's useful answer, and #James McLachlan's important update for VS2010, here is my implementation to cover VS2012 and fallback to VS2010 if necessary. I also chose not to select only on Environment.Is64BitOperatingSystem because it went awry on my system. That is, I have a 64-bit system but the web server was in the 32-bit folder. My code therefore looks first for the 64-bit folder and falls back to the 32-bit one if necessary.
public void LaunchWebServer(string appWebDir)
{
var PortNumber = "1162"; // arbitrary unused port #
var LocalHostUrl = string.Format("http://localhost:{0}", PortNumber);
var VirtualPath = "/";
var exePath = FindLatestWebServer();
var process = new Process
{
StartInfo =
{
FileName = exePath,
Arguments = string.Format(
"/port:{0} /nodirlist /path:\"{1}\" /virtual:\"{2}\"",
PortNumber, appWebDir, VirtualPath),
CreateNoWindow = true,
UseShellExecute = false
}
};
process.Start();
}
private string FindLatestWebServer()
{
var exeCandidates = new List<string>
{
BuildCandidatePaths(11, true), // vs2012
BuildCandidatePaths(11, false),
BuildCandidatePaths(10, true), // vs2010
BuildCandidatePaths(10, false)
};
return exeCandidates.Where(f => File.Exists(f)).FirstOrDefault();
}
private string BuildCandidatePaths(int versionNumber, bool isX64)
{
return Path.Combine(
Environment.GetFolderPath(isX64
? Environment.SpecialFolder.CommonProgramFiles
: Environment.SpecialFolder.CommonProgramFilesX86),
string.Format(
#"microsoft shared\DevServer\{0}.0\WebDev.WebServer40.EXE",
versionNumber));
}
I am hoping that an informed reader might be able to supply the appropriate incantation for VS2013, as it apparently uses yet a different scheme...
You can easily use Process Explorer to find complete command line options needed for manually start it.
Start Process Explorer while debugging your website. For VS2012, expand 'devenv.exe' node. Right-click on 'WebDev.WebServer20.exe' and from there you can see Path and Command Line values.

Categories