I am trying to create a manager for my autostarts. It should read an XML file and then start my programs with a custom delay. For example:
<startup id="0">
<name>Realtek Audio Manager</name>
<process arguments="-s">C:\Program Files\Realtek\Audio\HDA\RtkNGUI64.exe</process>
<delay>5</delay>
</startup>
This runs the specified process (C:\Program Files\...\RtkNGUI64.exe -s) after 5 seconds.
Now, three of the programs won't start, giving me a System.ComponentModel.Win32Exception: "Das System kann die angegebene Datei nicht finden." ("The system was not able to find the specified file.")
But the XML is parsed correctly, and the file I want to start is at the location I specified in the XML file.
The problem concerns only these three files:
Intel HotkeysCmd - C:\Windows\System32\hkcmd.exe
Intel GFX Tray - C:\Windows\System32\igfxtray.exe
Intel Persistance - C:\Windows\System32\igfxpers.exe
I think that the problem comes from the location of the files: they all are located in C:\Windows\System32, and all the other, working programs are located outside (C:\Program Files, C:\Program Files (x86), D:\Program Files, %AppData%)
Do I have to give my program some kind of access rights to start programs in C:\Windows\System32? How would I do that?
If not, what could be the reason I get errors with these programs?
EDIT - my code:
delegate(object o)
{
var s = (Startup) o;
var p = new System.Diagnostics.Process
{
StartInfo =
new System.Diagnostics.ProcessStartInfo(s.Process, s.Arguments)
};
try
{
s.Process = #"C:\Windows\System32\igfxtray.exe"; // For debugging purposes
System.Diagnostics.Process.Start(s.Process);
icon.ShowBalloonTip(2000, "StartupManager",
"\"" + s.Name + "\" has been started.",
System.Windows.Forms.ToolTipIcon.Info);
}
catch (System.ComponentModel.Win32Exception)
{
icon.ShowBalloonTip(2000, "StartupManager",
"\"" + s.Name + "\" could not be found.",
System.Windows.Forms.ToolTipIcon.Error);
}
}
Clearly you are using a 64-bit version of Windows. The c:\windows\system32 and c:\program files directories are subject to a feature called "file system redirection". It is an appcompat feature, it helps to ensure that 32-bit processes don't try to use 64-bit executables. They'll get redirected to c:\windows\syswow64 and c:\program files (x86).
So when you try to start a file in c:\program files\realtek\etcetera, your 32-bit program will be redirected to c:\program files (x86)\realtek\etcetera. A directory that doesn't exist, kaboom. Same ingredient for igfxtray.exe
You'll need to change your program's platform target so it can run as a native 64-bit process and avoid the redirection problem you now have. Project + Properties, Build tab, change the "Platform target" setting to AnyCPU.
Related
I need to do crash dump analysis for a .net exe using Windbg.
I read multiple blogs on MSDN to setup my Windbg but looks like it still has issues loading 'mscordacwks.dll'.
Upon running this command i get this:
Failed to load data access DLL, 0x80004005 Verify that 1) you have a
recent build of the debugger (6.2.14 or newer)
2) the file mscordacwks.dll that matches your version of mscorwks.dll is
in the version directory
3) or, if you are debugging a dump file, verify that the file
mscordacwks_.dll is on your symbol path.
4) you are debugging on the same architecture as the dump file.
For example, an IA64 dump file must be debugged on an IA64
machine.
You can also run the debugger command .cordll to control the
debugger's load of mscordacwks.dll. .cordll -ve -u -l will do a
verbose reload. If that succeeds, the SOS command should work on
retry.
If you are debugging a minidump, you need to make sure that your
executable path is pointing to mscorwks.dll as well.
Here are quick details of my environment:
EXE is 32 -bit so using x86 debugger
In the following path: C:\Windows\Microsoft.NET\Framework\v2.0.50727 the two DLLs are located
:
->mscorwks.dll
->mscordacwks.dll file version: 2.0.50727.5485
Symbol path is : c:\debug\symbols
Debugging tools(i.e. Windbg installation) is located at: C:\Program Files (x86)\Windows Kits\10\Debuggers\x86
Copied mscorwks.dll and mscordacwks.dll in this path and renamed to(as per some blog on MSDN) :
-> mscordacwks_x86_x86_2.0.50727.5485
-> mscorwks_x86_x86_2.0.50727.5485
Not sure if these names are correct or not.
Still it never worked.
I'm creating a C# app that should use lpr.exe and pass arguments to it. I'm currently trying to do it this way:
DirectoryInfo filePathDirectory = new DirectoryInfo(filePath);
Process a = new Process();
a.StartInfo.FileName = #"C:\Windows\System32\lpr.exe";
a.StartInfo.Arguments = "-SServerName.Domain.net -Plp " + "\"" + filePathDirectory + "\"";
a.StartInfo.UseShellExecute = false;
a.Start();
a.WaitForExit();
But whenever I get to a.Start(); I get an unhandled Win32 exception which states "The system cannot find the file specified". This is really confusing me becuase, at first I thought it was my arguments, but turns out, I can pass the exact same arguments from a VB app and get it to work.
Update 1:
The VB code that works is:
Dim RPname As String
RPname = FileName.ToString
Dim a As New Process
a.StartInfo.FileName = "C:\Windows\system32\lpr.exe"
a.StartInfo.Arguments = "-SServerName.Domain.net -Plp " & Chr(34) & RPname & Chr(34)
a.StartInfo.UseShellExecute = False
a.Start()
a.WaitForExit()
What's more is that my issue doesn't seem to be with the arguments statement, as I can comment it out and I still receive the error.
Update 2:
The error I get at the start of the process is: The system cannot find the file specified. I do not get the error if I change the FileName to "C:\Windows\System32\cmd.exe", that works fine...
You are using a DirectoryInfo object in your c# code and concetanate it. Try to change your code to this:
try
{
DirectoryInfo filePathDirectory = new DirectoryInfo(filePath);
Process a = new Process();
a.StartInfo.FileName = #"C:\Windows\syswow64\lpr.exe"; // ADAPTED to the new path!! worked!
// use filePathDirectory.FullName!!
a.StartInfo.Arguments = "-SServerName.Domain.net -Plp " + "\"" + filePathDirectory.FullName + "\"";
// or change it to - found it more readable imo
a.StartInfo.Arguments = string.Format(
"-SServerName.Domain.net -Plp \"{0}\"",
filePathDirectory.FullName);
a.StartInfo.UseShellExecute = false;
a.Start();
a.WaitForExit();
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
EDIT
Found the solution to your problem! First - credits go to #Sundeep according to his given answer. He pointet me to a webSite indicating that
Yes I have, but 64-bit files located in c:\windows\system32 is not
seen by a 32-bit command prompt (c:\windows\syswow64\cmd.exe) which is
launched when call at batch file from a 32-bit application.
And furthermore
... trying to get a 32 bit program to launch
lpr.exe inside of Windows 2008 R2. It fails because lpr.exe does not
exist in the 32 bit view of the O/S. ...
For a work-around, I copied lpr*.* from a 32 bit O/S system32 folder
into SYSWOW64 on the 2008 R2 machine...
Change your code to this:
DirectoryInfo filePathDirectory = new DirectoryInfo(filePath);
Process a = new Process();
a.StartInfo.FileName = #"C:\Windows\Sysnative\lpr.exe";
a.StartInfo.Arguments = "-SServerName.Domain.net -Plp " + "\"" + filePathDirectory + "\"";
a.StartInfo.UseShellExecute = false;
a.Start();
a.WaitForExit();
Pilgerstorfer Franz' explanation is exactly right, but especially in an environment like mine, going and moving the location of the lpr.exe on every machine is a lot of work. The simple solution is to use the sysnative directory.
http://www.samlogic.net/articles/sysnative-folder-64-bit-windows.htm
The 'Sysnative' folder is invisible in Windows Explorer If you
start Windows Explorer and open the Windows folder on your hard disk,
you may notice that the Sysnative folder is not shown. The main reason
to this is that Windows Explorer is a 64-bit program (when run in a
64-bit Windows), and the Sysnative folder is only visible and
accessible from 32-bit software. If 64-bit software need access to the
64-bit system folder in Windows, the only option is to use the
System32 folder name (for example: C:\Windows\System32).
Using the 'Sysnative' folder will help you access 64-bit tools from
32-bit code Some tools in a 64-bit Windows only exist in a 64-bit
version; there is no 32-bit version available. And some of these tools
are located in the 64-bit System32 folder. One example is the nbtstat
tool that is used to help troubleshoot NetBIOS name resolution
problems. If you try to run the nbtstat tool from 32-bit code (for
example from an application or from script) and use a path like
C:\Windows\System32, you will get a "File not found" error. The file
can not be found; although Windows Explorer shows that the nbtstat
program file actually is located in the C:\Windows\System32 folder.
The solution to this (somewhat confusing) problem is to include the
virtual Sysnative folder in the folder path when you want to run the
tool. For example like this: C:\Windows\Sysnative\nbtstat.exe The
file path above will give you access to the 64-bit nbtstat tool from a
32-bit application or from a 32-bit script. We recommend you to read
this article / blog post (at Scottie’s Tech.Info) to get more details
about this.
I've built a Compact Framework application and I'm using WmAutoUpdate to deploy new versions to the mobile devices (http://www.sebastianvogelsang.com/2009/09/23/wmautoupdate-a-net-compact-framework-auto-update-library/). Has anyone used this? It's cool but I've got a problem.
If I cause the application to crash half-way through updating it is supposed to recover by copying the backup version back into the main directory. This doesn't work because the exe file is "locked" by the operating system because it is currently in use. I can verify this is the case because I can't delete it using Windows Explorer either. The error details are:
System.IO.IOException was unhandled
Message="IOException"
StackTrace:
at System.IO.__Error.WinIOError(Int32 errorCode, String str)
at System.IO.File.Move(String sourceFileName, String destFileName)
at WmAutoUpdate.Updater.assertPreviousUpdate()
at WmAutoUpdate.Updater..ctor(String url)
The error occurs on this line in Updater.assertPreviousUpdate():
File.Move(f, appPath + "\\" + getFilenameFromPath(f));
The code manages to update the application exe file when it's allowed to run normally (I'm not sure how). The problem is that it doesn't work when rolling back.
Cheer
Mark
I've used WmAutoUpdate and I've found the same problem. The issue is that you can move the files of the actual running process, but you cannot overwrite them. If you check the update part, WmAutoUpdate moves the running application to a backup directory and then it writes the update version to the original directory. I have fixed the rollback part this way:
if (Directory.Exists(backupDir))
{
string tmpDir = Path.Combine(Path.GetTempPath(),Path.GetFileNameWithoutExtension(Path.GetTempFileName()));
Directory.Move(appPath, tmpDir);
Directory.Move(backupDir, appPath);
}
First we move the running application files to a random directory in Temp. Then we copy the backup folder to the application original directory. Of course, this will generate a .TMP file in the Temp directory of your device, and a folder with the actual running process. You will have to delete this temporary folder once in a while in production code.
I'm launching the path C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk via Process.Start, but it fails with The system cannot find the file specified.
The link shows up on a dir, so it exists.
Can it be permissions?
Notes:
The path is auto-discovered by iterating over the Start Menu directory.
I can launch it via explorer and command line.
Clarifications:
Code is as follows:
public void Execute() { Process.Start(_shortcut.FullName);}
_shortcut is of type FileInfo
_shortcut.Exists is true, so the file can be found
replacing _shortcut.FullName with the explicit path #"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk" has the same effect.
This is a WPF app using Caliburn and MEF.
Running as Administrator has the same effect.
This here on the other hand seems to work:
[Fact]
public void TestIisManager()
{
var path = new FileInfo(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk");
Process.Start(path.FullName);
}
It does seem to be a bit "environment" based.
Second clarification:
It seems to work in a Windows 7 x86 but not in a Windows 7 x64.
I ran into this recently. Windows Forms based solution, VS2013, x64 machine. Process.Start() could not launch applications via .lnk file. Using process explorer, it seemed that the target specified in the .lnk file was resolving incorrectly to c:\program files (x86)... instead of c:\program files... I followed Bruno's excellent advice, but then again my Target was already marked as "AnyCPU".
After some head scratching, it turned out there's a new compiler flag in VS11+ called "Prefer 32-bit" that was checked by default. This was forcing the EXE output to be 32-bit even though my OS was 64-bit and platform was set to AnyCPU. After I unchecked and recompiled, the problem was fixed.
More reading at: http://blogs.microsoft.co.il/sasha/2012/04/04/what-anycpu-really-means-as-of-net-45-and-visual-studio-11/
Found the issue.
The WPF application was compiled as x86 (all other dlls were compiled as AnyCPU), and when launching some executables or links in a 64 bit machine it failed.
Changing the "Platform Target" to AnyCPU fixes this.
This may not actually relate to your situation, but you can launch the IIS Manager by using
Process.Start("inetmgr.exe")
If you want to continue to use the shortcut, it will probably work if you start the process using a ProcessStartInfo and set ProcessStartInfo.UseShellExecute to true
Can you make sure that you are trying this from an STA thread? You can see whether the apartment state is a problem if the following sample succeeds:
using System;
using System.Diagnostics;
public class Program
{
// make sure to call Process.Start from an STA thread
[STAThread]
static void Main(string[] args)
{
Process.Start(#"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk");
}
}
Process.Start calls ShellExecute under the hood to run the file passed. As described by Raymond Chen, shell functions require an STA thread:
One possible reason why ShellExecute returns SE_ERR_ACCESSDENIED and ShellExecuteEx returns ERROR_ACCESS_DENIED
As stated already, you will see the "The system cannot find the file specified" error because Windows is looking for inetmgr.exe in ...\SysWOW64\intsrv\ (caused by file system redirection), but it only exists in ...\System32\intsrv\.
This is caused by your 32-bit executable attempting to launch a 64-bit executable. As suggested, not using a 32-bit executable will solve this, but for anyone who must build for 32-bit (a WiX installer bundle in my case), try the following.
Using the start menu LNK/shortcut to inetmgr.exe instead of the executable is a good start, but an extra level of distance is required. This can be provided by using explorer.exe, which can be launched from a 32-bit executable:
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.FileName = "explorer.exe";
startInfo.Arguments = "/seperate /root,\"C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\Administrative Tools\\IIS Manager.lnk\"";
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo = startInfo;
process.Start();
It's a bit of a hack, but try launching it like this:
string path = #"C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Administrative Tools\IIS Manager.lnk";
Process.Start("cmd.exe", String.Format("/k \"\"{0}\"\"",path));
Note the double quotes needed to save the spaces in the path.
That way, you might see a more precise error message and/or walk around in the command environment afterwards to see what is wrong with the path.
Okay, I've checked Environment.SpecialFolder, but there's nothing in there for this.
I want to get the home directory of the current user in C#. (e.g. c:\documents and settings\user under XP, c:\users\user under Vista, and /home/user under Unix.)
I know I can read enviroment variables to find this out, but I want to do this in a cross-platform way.
Is there any way I can do this with .NET (preferably using mscorlib)?
UPDATE: Okay, this is the code I ended up using:
string homePath = (Environment.OSVersion.Platform == PlatformID.Unix ||
Environment.OSVersion.Platform == PlatformID.MacOSX)
? Environment.GetEnvironmentVariable("HOME")
: Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%");
You are looking for Environment.SpecialFolder.UserProfile which refers to C:\Users\myname on Windows and /home/myname on Unix/Linux:
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)
Note that Environment.SpecialFolder.Personal is My Documents (or Documents in win7 and above), but same as home directory on Unix/Linux.
Environment.SpecialFolder.Personal doesn't actually return the home folder, it returns the My Documents folder. The safest way to get the home folder on Win32 is to read %HOMEDRIVE%%HOMEPATH%. Reading environment variables is actually very portable to do (across Unix and Windows), so I'm not sure why the poster wanted to not do it.
Edited to add: For crossplatform (Windows/Unix) C#, I'd read $HOME on Unix and OSX and %HOMEDRIVE%%HOMEPATH% on Windows.
I believe what you are looking for is:
System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)
For reference, it is infact contained in mscorlib.
In DotNetCore 1.1 System.Environment.SpecialFolder does not exist. It might exist in 2.0-beta. Until then, to do this you can use the following:
var envHome = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "HOMEPATH" : "HOME";
var home = Environment.GetEnvironmentVariable(envHome);`
The bottom line answer is No. The is no simple System based method in .NET to get the Home directory such that we could expect an implementation in both .NET on Windows and in Mono.
You will need to do some OS detection and branch to OS specific code.
When you say cross-platform way, what other OSs do you need to support? You may need to do some simple OS detection to select the method for finding the home directory if you're running on a non-Windows OS.
This website seems to give a way to do what you need in Windows.
This can be done using GetEnvironmentVariable in System.IO:
public string GetUserHome() {
var homeDrive = Environment.GetEnvironmentVariable("HOMEDRIVE");
if (!string.IsNullOrWhiteSpace(homeDrive))
{
var homePath = Environment.GetEnvironmentVariable("HOMEPATH");
if (!string.IsNullOrWhiteSpace(homePath))
{
var fullHomePath = homeDrive + Path.DirectorySeparatorChar + homePath;
return Path.Combine(fullHomePath, "myFolder");
}
else
{
throw new Exception("Environment variable error, there is no 'HOMEPATH'");
}
}
else
{
throw new Exception("Environment variable error, there is no 'HOMEDRIVE'");
}
}
Then it produces under windows: C:\\\\Users\\myusername\\myFolder
Note that if you use
var fullHomePath = Path.Combine(homeDrive.ToString(), homePath.ToString())
it fails because it produces: \\Users\\myusername\\myFolder
I don't have a machine to test it on, but %HOMESHARE% might work for you. Otherwise, here's a pretty good list of environment variables.
Just for future reference, the list of pre-defined variables in Windows 10, taken from Windows 10 default environment variables.
VARIABLE
WINDOWS 10
%ALLUSERSPROFILE%
C:\ProgramData
%APPDATA%
C:\Users{username}\AppData\Roaming
%CD%
Current directory full path.(cmd only)
%CMDCMDLINE%
Returns exact command line used to start current cmd.exe session.(cmd only)
%CMDEXTVERSION%
Number of current command processor extensions.(cmd only)
%CommonProgramFiles%
C:\Program Files\Common Files
%CommonProgramFiles(x86)%
C:\Program Files (x86)\Common Files
%CommonProgramW6432%
C:\Program Files\Common Files
%COMPUTERNAME%
The computer name of the current local system.
%COMSPEC%
C:\Windows\System32\cmd.exe
%DATE%
Current date in format determined by Date command.(cmd only)
%ERRORLEVEL%
Number defining exit status of previous command or program.(cmd only)
%HOMEDRIVE%
C:|
%HOMEPATH%
C:\Users{username}
%LOCALAPPDATA%
C:\Users{username}\AppData\Local
%LOGONSERVER%
\{domain_logon_server}\MicrosoftAccount
%NUMBER_OF_PROCESSORS%
8
%OS%
Windows_NT
%PATH%
C:\Windows;C:\Windows\System32;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0
%PathExt%
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
%PROCESSOR_ARCHITECTURE%
AMD64
%PROCESSOR_IDENTIFIER%
Intel64 Family 6 Model 158 Stepping 9, GenuineIntel
%PROCESSOR_LEVEL%
6
%PROCESSOR_REVISION%
9e09
%PROGRAMDATA%
C:\ProgramData
%PROGRAMFILES%
C:\Program Files
%ProgramW6432%
C:\Program Files
%PROGRAMFILES(X86)%
C:\Program Files (x86)
%PROMPT%
Code for current command prompt format.Code is usually $P$G(cmd only)
%PSModulePath%
C:\Windows\system32\WindowsPowerShell\v1.0\Modules|
%PUBLIC%
C:\Users\Public
%RANDOM%
To get random number between 0 and 32767.(cmd only)
%SessionName%
When logging on directly to machine, returns "Console".When client connects via terminal server session, is combinationof connection name, followed by pound symbol {#} and session number.
%SystemDrive%
C:
%SystemRoot%
C:\Windows
%TEMP%
C:\Users{username}\AppData\Local\Temp
%TMP%
C:\Users{username}\AppData\Local\Temp
%TIME%
Current time in format determined by Time command.(cmd only)
%USERDOMAIN%
The network domain name associated with the current user.
%USERDOMAIN_ROAMINGPROFILE%
The network domain name associated with the current roaming profile.
%USERNAME%
{username}
%USERPROFILE%
C:\Users{username}
%WINDIR%
C:\Windows