I have a web API which simply
clone a .docx file
convert that cloned .docx to a .pdf format
using DocumentFormat.OpenXml.Packaging;
[HttpPost("clone")]
public IActionResult CloneBillFromTemplate()
{
var templateFilePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Bill", "PaymentTempl.docx");
var clonedFilePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Bill", "ClonedBill.docx");
var pdfFilePath = System.IO.Path.Combine(Directory.GetCurrentDirectory(), "Bill", "FinalBill.pdf");
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open(templateFilePath, true))
{
var clonedDoc = wordDoc.Clone(clonedFilePath);
System.Threading.Thread.Sleep(500);
clonedDoc.Save();
clonedDoc.Close();
}
using (var converter = new OfficeConverter.Converter())
{
converter.Convert(clonedFilePath, pdfFilePath);
}
return Ok();
}
Everything works fine when debugging (for sure :3) and also on IIS
But when I deploy to Azure App service, I got this type of error (stack trace + exception message).
Could not read registry to check Word version
Could not find registry key Word.Application\CurVer
at OfficeConverter.Word..ctor()
at OfficeConverter.Converter.get_Word()
at OfficeConverter.Converter.Convert(String inputFile, String outputFile, Stream logStream) at ....
Could you guys help me on this? Thanks all!!!
**feel free to ask for more information you need to detect this issue
Update
Looks like this is the issue with the pdf converter pacakge I'm using, not the OpenXML
It seems your application is relying on the Windows registry in a way that is not supported. If you are running on a Linux App Service, that would be the first thing to swap out, though my guess is that you are already running on Windows.
Apps have read-only access to much (though not all) of the registry of the virtual machine they are running on. In practice, this means registry keys that allow read-only access to the local Users group are accessible by apps. One area of the registry that is currently not supported for either read or write access is the HKEY_CURRENT_USER hive.
Write-access to the registry is blocked, including access to any per-user registry keys.
https://learn.microsoft.com/en-us/azure/app-service/operating-system-functionality#registry-access
If you can't refactor your code to not rely on such dependencies, I would suggest you put your application inside a Windows docker container. If you can run that locally, it should run on App Service as well.
Related
I have written a .Net Windows Forms application that uses the common application data folder to store logfiles and user accounts. The application is distributed using an install shield project and runs perfect on all different Windows versions.
Some parts of the code from different files is shown below
// Defining the path to use (in ProductInfo class)
public static string CommonApplicationDataPath
{
get
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
path = StringHelper.EnsureEndsWithSlash(path);
path += Vendor + #"\" + ProductName + #"\";
return path;
}
}
// Configuring the logger and user manager instances at startup
Logger.Configure(string.Empty, ProductInfo.Password, ProductInfo.CommonApplicationDataPath);
UserManager.Configure(User.Empty, ProductInfo.Password, ProductInfo.CommonApplicationDataPath,
ProductInfo.UserLimitCount);
// Example method for saving the users to file (in UserManager class)
public bool SaveUsers(AppUsers appUsers)
{
AppUsersSerializer serializer = new AppUsersSerializer(_password, _fileName);
if (serializer.Serialize(appUsers) == true)
{
return true;
}
else
{
Logger.Instance.Log(Logs.ErrorB.UserSave, _fileName);
return false;
}
}
I would now like to publish the application via Windows Store and have used the MSIX Packaging Tool. To sign the package I have created a self signed certificate and added it to the Trusted Root Certificate Authorities. The .msix package is install on the same PC as my old desktop version of the app.
The problem I have is that the application is not able to write to the files located in the CommonApplicationData folder. The application can read and load the data, but not update and write the changes to the files. Thus, the path to the files is correct, but some write permission seems to be missing. I have tried different capabilities on the package and even ticked all, but without any effect.
I have also browsed to the C:\Program Files\WindowsApps\<my app package>\ folder and checked the structure of the application and located the files. They are there, but only readable for the app. Removing the files will not create new ones when they should be added as done in the old desktop Windows Forms version.
The application is quite big and contains lots of functionality which runs great in the Windows Store app context. The only missing piece is the above mentioned issues with the file writing.
Any advice would be really appreciated.
After some continued searching on different websites I came across a viable solution for my issue.
The Microsoft MSDN blog described how to use the different folders in an appropriate way.
http://blogs.msdn.microsoft.com/appconsult/2017/03/06/handling-data-in-a-converted-desktop-app-with-the-desktop-bridge/
The proposed solution is to change:
string path = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
to:
string path = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
It will place the files in the user's local directory, meaning the data will be available only for the current user. Sharing the same log file and user accounts between different users of the application, will thus not be possible but that is ok for now.
You may also need to make sure that the folder exists:
C:\Users\<user>\AppData\Local\<vendor>\<product> because it might not always be created during installation of your application. It depends if it has user specific settings or not.
CommonApplicationData folder read-only after using MSIX Packaging Tool
If you have converted your app to store app, we could regard it as UWP app, In general, we store the user info with LocalSettings class that could keep the data during the app updating.
var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
// Create a simple setting.
localSettings.Values["exampleSetting"] = "Hello Windows";
// Read data from a simple setting.
Object value = localSettings.Values["exampleSetting"];
if (value == null)
{
// No data.
}
else
{
// Access data in value.
}
// Delete a simple setting.
localSettings.Values.Remove("exampleSetting");
For more detail, please refer Store and retrieve settings and other app data
I have a code which is similar this:
string file;
using (StreamReader r = new StreamReader("xml.xml"))
{
file = r.ReadToEnd();
}
XElement xml = XElement.Parse(file);
using (XmlWriter w = XmlWriter.Create("xml.xml")) //The point of problem!
{
w.WriteStartDocument();
...;
w.WriteEndDocument();
}
When I try run it like a console application is everything all right. But problems start when I want to use it in an ASP.NET application. At the using line it throws UnauthorizedAccessException exception with a description "access to the path is denied". Why?
You need to check which account your application Pool is using to access your server files/folders, for example, make one code to copy one file to application folder, check all security info, copy and paste on this problem folder, normally use this account "IIS_IURRS" give full control to test only...
If IIS/the web server is configured correctly, an account with a very limited set of permissions is used. As your path points to the application directory, it is very likely that the application pool account is not allowed to write to this location.
If you run the code in a console application, your user's permissions are applied and it is more than likely that you are allowed to write to the output folder of the project as Visual Studio writes the build output there under your account.
I would not recommend to change the application pool account or the permissions of the application folder in the file system - it is a very sensible limitation that limits the amount of trouble an attacker can possibly make.
Therefore I'd recommend to either move the file to a folder that the account can write to without changing permissions or define a special one outside of the application folder hierarchy that the account is given permissions to.
Also keep in mind that multiple users might access the file at the same time, so a database might be a better choice to store the data.
I try open .doc file with interop, when I run in Visual studio result is a file but I publish web to IIS result is null.
I added folder Desktop to C:\Windows\SysWOW64\config\systemprofile, I run website, process Word run but still no return result.
I'm using Windows server 2012 64bit and Microsoft Office 2007. Why result in IIS return null and solution?
var wordApp = new Application { Visible = false };
var path = Request.PhysicalApplicationPath + "File\\TestFile.doc";
object srcPath = path;
var wordDoc = wordApp.Documents.Open(ref srcPath);
Refer Link
Sometimes with Office interop stuff it can be awkward.
If you use a single user to access the word application i.e. your asp.net service username.
Then what you can do is login to the computer using that username and open up word, this then adds all the registry and file system changes that the office applications seem to use. Note: if you are planning on using Excel as well then you need to open up excel also.
Another method is to create a new user account for accessing Word, then do the above and in your code behind impersonate that user so you're not giving the Web Application User access to word.
Hope this helps
p.s. When working with office interop it's important to watch for dirty references to objects. Leaving dirty references leaves ghost Word/Excel processes (visible only in task manager).
One thing I always do to help is to create a new AppDomain, this means there is no risk to the current process. Then I always release the objects as follows
if (item != null)
{
while (Marshal.ReleaseComObject(item) > 0);
item = null;
}
Where item is a document object, an application object etc. This code should go in a Finally block so that it always runs
Just in case you're new to office interop this one kept me busy for quite some time when I was just starting out.
I want to get the list of application which runs on windows startup programatically.
i see those application in msconfig->startup.
but when i see it in C:\User\Appdata\Microsoft\windows\start menu\programs\startup
it shows folder is empty.
How to get those list of startup applications programatically C#.
Those in msconfig that you see are in the registry, although they are not all there is. You can try read
/Software/Microsoft/Windows/CurrentVersion/Run
and
/Software/Microsoft/Windows/CurrentVersion/RunOnce
In both HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER
You can find information about those at MSDN and support.microsoft.
Try the article Read, write and delete from registry with C# at CodeProject to get you started with the registry.
Now, as I said above, they are not all there is. Listing those will still miss services and drivers, and also some other code that will start async, such as Explorer extensions, Control Panel extensions, codecs for audio and video among others. All that without considering that the client machine may not use explorer as shell.
I recommend you to have a look at autoruns at sysinternals. You can also use its command line tool to get the info you want.
There is a good article: Understand and Control Startup Apps with the System Configuration Utility.
Also there is a great tool to view listing of all startup programs: Autoruns.
For example, you can enumerate string values of the HKLM\Software\Microsoft\Windows\CurrentVersion\Run key:
const string runKey = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run";
using (RegistryKey startupKey = Registry.LocalMachine.OpenSubKey(runKey))
{
var valueNames = startupKey.GetValueNames();
// Name => File path
Dictionary<string, string> appInfos = valueNames
.Where(valueName => startupKey.GetValueKind(valueName) == RegistryValueKind.String)
.ToDictionary(valueName => valueName, valueName => startupKey.GetValue(valueName).ToString());
}
I have this simple code that records appends a log to a text file:
public static void RecordToFile(string filename, Log log)
{
TextWriter textWriter = new StreamWriter(Constants.APP_PATH +
"\\" + filename, true);
textWriter.WriteLine(log.ToString());
textWriter.Close();
}
This works perfectly in a Windows Forms application. However, using the instsrv and srvany trick, I made this a Windows Service. The service runs fine, accesses the database, performs queries and all... Except for this StreamWriter. The log just doesn't get updated as it should. Any ideas why?
Most likely the service is running under user credentials that does not have access rights to that directory.
So check the properties dialog for the service, and check the Log On tab to see what it logs on as.
Possible Reasons:
Constants.APP_PATH is pointing to a mapped drive - services don't run in the same environment as a logged-in user, so the path may not be valid
Permissions - depending on what user the service is running as, it may not have access to the same set of directories that the WinForms app did
Without more information there isn't much that anyone can help with. In what way, exactly, does it not function? Do you get an exception? Does it just fail silently?
Just a couple of tips...
1) Don't use string concatenation to create file paths. Use System.IO.Path.Combine instead. Like this:
TextWriter textWriter = new StreamWriter(
System.IO.Path.Combine(Constants.APP_PATH, filename), true);
2) Enclose your writer in a using() block. Like so:
using(TextWriter textWriter = new StreamWriter(
System.IO.Path.Combine(Constants.APP_PATH, filename), true))
{
textWriter.WriteLine(log.ToString());
}
3) Verify that the account the service is using has access to create/overwrite files in that directory. Often service accounts like LOCAL_SYSTEM or NETWORK_SERVICE aren't going to have the same permissions as user accounts would. This could explain why it works as a user but not as a service. It could also be that your APP_PATH constant points to something user specific, like a drive mapping to a network share. Drive mappings don't span users, so this could also be an issue.
Without any more information I'd guess that the account your service is running under doesn't have rights to open the file for writing.