How to set Environment variables permanently in C# - c#

I am using the following code to get and set environment variables.
public static string Get( string name, bool ExpandVariables=true ) {
if ( ExpandVariables ) {
return System.Environment.GetEnvironmentVariable( name );
} else {
return (string)Microsoft.Win32.Registry.LocalMachine.OpenSubKey( #"SYSTEM\CurrentControlSet\Control\Session Manager\Environment\" ).GetValue( name, "", Microsoft.Win32.RegistryValueOptions.DoNotExpandEnvironmentNames );
}
}
public static void Set( string name, string value ) {
System.Environment.SetEnvironmentVariable( name, value );
}
The problem I face, is even when the program is running as administrator, the environment variable lasts only as long as the program is running. I have confirmed this by running a Get on the variable I set in a previous instance.
Example usage of above
Set("OPENSSL_CONF", #"c:\openssl\openssl.cfg");
And to retrieve
MessageBox.Show( Get("OPENSSL_CONF") );
While the program is running, after using Set, the value is returned using Get without any issue. The problem is the environment variable isn't permanent (being set on the system).
It also never shows up under advanced properties.
Thanks in advance.

While the program is running, after using Set, the value is returned
using Get without any issue. The problem is the environment variable
isn't permanent (being set on the system).
Thats because the overload of SetEnvironmentVariable that you're using stores in the process variables. From the docs:
Calling this method is equivalent to calling the
SetEnvironmentVariable(String, String, EnvironmentVariableTarget)
overload with a value of EnvironmentVariableTarget.Process for the
target argument.
You need to use the overload specifying EnvironmentVariableTarget.Machine instead:
public static void Set(string name, string value)
{
Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Machine);
}

According to MSDN the method you are using is just modifying the variable for the runtime of the process.
Try the overload described here: https://msdn.microsoft.com/library/96xafkes%28v=vs.110%29.aspx

This kind of question has already been asked multiple times, check the following links for more information:
Set Env Variable - 1
Set Env Variable - 2
Set Env Variable - Tutorial

Here's an example that permanently updates the User PATH variable by programmatically editing the registry:
// Admin Permission not-required:
// HKCU\Environment\Path
// Admin Permission required:
// HKLM\SYSTEM\CurrentControlSet\Control
// \Session Manager\Environment\Path
public static void UserPathAppend(string path, int verbose=1) {
string oldpath = UserPathGet();
List<string> newpathlist = oldpath.Split(';').ToList();
newpathlist.Add(path);
string newpath = String.Join(";", newpathlist.ToArray());
UserPathSet(newpath);
UpdateEnvPath();
if (verbose!=0) {
System.Windows.MessageBox.Show(
"PATH APPEND:\n\n"
+ path + "\n\n"
+ "OLD HKCU PATH:\n\n"
+ oldpath + "\n\n"
+ "NEW HKCU PATH:\n\n"
+ newpath + "\n\n"
+ "REGISTRY KEY MODIFIED:\n\n"
+ "HKCU\\Environment\\Path\n\n"
+ "NOTE:\n\n"
+ "'Command Path' is a concat of 'HKLM Path' & 'HKCU Path'.\n",
"Updated Current User Path Environment Variable"
);
}
}
public static void UserPathPrepend(string path, int verbose=1) {
string oldpath = UserPathGet();
List<string> newpathlist = oldpath.Split(';').ToList();
newpathlist.Insert(0, path);
string newpath = String.Join(";", newpathlist.ToArray());
UserPathSet(newpath);
UpdateEnvPath();
if (verbose != 0) {
System.Windows.MessageBox.Show(
"PATH PREPEND:\n\n"
+ path + "\n\n"
+ "OLD HKCU PATH:\n\n"
+ oldpath + "\n\n"
+ "NEW HKCU PATH:\n\n"
+ newpath + "\n\n"
+ "REGISTRY KEY MODIFIED:\n\n"
+ "HKCU\\Environment\\Path\n\n"
+ "NOTE:\n\n"
+ "'Command Path' is a concat of 'HKLM Path' & 'HKCU Path'.\n",
"Updated Current User Path Environment Variable"
);
}
}
public static string UserPathGet()
{
// Reads Registry Path "HKCU\Environment\Path"
string subKey = "Environment";
Microsoft.Win32.RegistryKey sk = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(subKey);
if (sk == null)
return null;
else
return sk.GetValue("Path").ToString();
}
public static void UserPathSet(string newpath)
{
// Writes Registry Path "HKCU\Environment\Path"
string subKey = "Environment";
Microsoft.Win32.RegistryKey sk1 = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(subKey);
sk1.SetValue("Path", newpath);
}
//===========================================================
// Private: This part required if you don't want to logout
// and login again to see Path Variable update
//===========================================================
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern IntPtr SendMessageTimeout(IntPtr hWnd,
uint Msg, UIntPtr wParam, string lParam,
SendMessageTimeoutFlags fuFlags,
uint uTimeout, out UIntPtr lpdwResult);
private enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0, SMTO_BLOCK = 0x1,
SMTO_ABORTIFHUNG = 0x2, SMTO_NOTIMEOUTIFNOTHUNG = 0x8
}
private static void UpdateEnvPath() {
// SEE: https://support.microsoft.com/en-us/help/104011/how-to-propagate-environment-variables-to-the-system
// Need to send WM_SETTINGCHANGE Message to
// propagage changes to Path env from registry
IntPtr HWND_BROADCAST = (IntPtr)0xffff;
const UInt32 WM_SETTINGCHANGE = 0x001A;
UIntPtr result;
IntPtr settingResult
= SendMessageTimeout(HWND_BROADCAST,
WM_SETTINGCHANGE, (UIntPtr)0,
"Environment",
SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
5000, out result);
}

Related

get data from printer local port

I have done a virtual printer by using some sample code. But I don't know how to get data from it when it starts printing.
When it starts printing It gives notification that ERROR IN PRITNING.
And Why there is "C:/MyLocalPort.txt".
Anyone Help me to get data this virtual printer sets to print.
Here is the Sample Code:
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Management;
namespace virtualPrinter
{
public static class PrinterClass // class which carries SetDefaultPrinter function
{
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetDefaultPrinter(string Printer);
}
class myPrinterClass
{
public static void getPrinterNames()
{
foreach (string printer in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
{
MessageBox.Show(printer);
}
}
public static void installPrinter(string printerName) //works on win 7,8,8.1,10 on both x84 and x64
{
Winspool.AddLocalPort(#"C:\MyLocalPort.txt");
//https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/rundll32-printui
// /if Installs a printer by using an .inf file.
// /b[name] Specifies the base printer name.
// /#[file] Specifies a command-line argument file and directly inserts the text in that file into the command line.
// /f[file] Species the Universal Naming Convention (UNC) path and name of the .inf file name or the output file name, depending on the task that you are performing. Use /F[file] to specify a dependent .inf file.
// /r[port] Specifies the port name.
// /m[model] Specifies the driver model name. (This value can be specified in the .inf file.)
string arg;
arg = "printui.dll , PrintUIEntry /if /b " + "\"" + printerName + "\"" + #" /f C:\Windows\inf\ntprint.inf /r " + "\"" + #"C:\MyLocalPort.txt" + "\"" + " /m " + "\"" + "Generic / Text Only" + "\""; //initial arg
ProcessStartInfo p = new ProcessStartInfo();
p.FileName = "rundll32.exe";
p.Arguments = arg;
p.WindowStyle = ProcessWindowStyle.Hidden;
try
{
Process.Start(p);
MessageBox.Show(printerName + " installed succesfully!");
}
catch (Exception ex)
{
MessageBox.Show("Something went wrong. Try again!" );
}
}
public static bool printerExists(string printerName)
{
bool res = false;
foreach (string printer in System.Drawing.Printing.PrinterSettings.InstalledPrinters)
{
if (printer == printerName)
{
res = true;
}
}
return res;
}
public static void uninstallPrinter(string printerName)
{
string arg;
ProcessStartInfo p = new ProcessStartInfo();
arg = "printui.dll, PrintUIEntry /dl /n " + "\"" + printerName + "\"";
if (printerExists(printerName))
{
p.FileName = "rundll32.exe";
p.Arguments = arg;
p.WindowStyle = ProcessWindowStyle.Hidden;
try
{
Process.Start(p);
MessageBox.Show(printerName + " unistalled successfully");
}
catch (Exception ex)
{
MessageBox.Show(ex.InnerException.ToString());
}
p = null;
}
}
public static string GetLocalIPAddress() //erxomeno feature
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
{
return ip.ToString();
}
}
throw new Exception("No network adapters with an IPv4 address in the system!");
}
public static class Winspool
{
[StructLayout(LayoutKind.Sequential)]
private class PRINTER_DEFAULTS
{
public string pDatatype;
public IntPtr pDevMode;
public int DesiredAccess;
}
[DllImport("winspool.drv", EntryPoint = "XcvDataW", SetLastError = true)]
private static extern bool XcvData(
IntPtr hXcv,
[MarshalAs(UnmanagedType.LPWStr)] string pszDataName,
IntPtr pInputData,
uint cbInputData,
IntPtr pOutputData,
uint cbOutputData,
out uint pcbOutputNeeded,
out uint pwdStatus);
[DllImport("winspool.drv", EntryPoint = "OpenPrinterA", SetLastError = true)]
private static extern int OpenPrinter(
string pPrinterName,
ref IntPtr phPrinter,
PRINTER_DEFAULTS pDefault);
[DllImport("winspool.drv", EntryPoint = "ClosePrinter")]
private static extern int ClosePrinter(IntPtr hPrinter);
public static int AddLocalPort(string portName)
{
PRINTER_DEFAULTS def = new PRINTER_DEFAULTS();
def.pDatatype = null;
def.pDevMode = IntPtr.Zero;
def.DesiredAccess = 1; //Server Access Administer
IntPtr hPrinter = IntPtr.Zero;
int n = OpenPrinter(",XcvMonitor Local Port", ref hPrinter, def);
if (n == 0)
return Marshal.GetLastWin32Error();
if (!portName.EndsWith("\0"))
portName += "\0"; // Must be a null terminated string
// Must get the size in bytes. Rememeber .NET strings are formed by 2-byte characters
uint size = (uint)(portName.Length * 2);
// Alloc memory in HGlobal to set the portName
IntPtr portPtr = Marshal.AllocHGlobal((int)size);
Marshal.Copy(portName.ToCharArray(), 0, portPtr, portName.Length);
uint needed; // Not that needed in fact...
uint xcvResult; // Will receive de result here
XcvData(hPrinter, "AddPort", portPtr, size, IntPtr.Zero, 0, out needed, out xcvResult);
ClosePrinter(hPrinter);
Marshal.FreeHGlobal(portPtr);
return (int)xcvResult;
}
}
}
}
Ok, I Can't do this that way.
First I have to create a port monitor I have used mfilemon.dll
Then I created a port with that port monitor
Then Pasted All Driver files in System Driver Dictionary.
Then finally Added Printer And Done.
You can refer this
https://github.com/Gohulan/Virtual-Printer/blob/main/PrinterSetup/SpoolerHelper.cs
This contains all the steps to create a virtual printer to make a print to pdf virtual printer

Can't delete files from a shared folder with File.Delete()

"System.UnauthorizedAccessException" - I've seen this message on my screen for 2 days and just couldn't figured out why.
I've checked all the possible reasons in my knowledge to make it work but fail. Here is the summary of what I have checked:
The file in the shared drive (mapped drive in the local network) is not ready only and the full access permissions are granted to everyone. The folders in the file path are also been set to grant full access permissions to everyone.
I can delete the file manually
the file was created by the same user (me) and I need to delete it when there is a revision. (delete the old one and put the new one in)
when I change the destination folder to local drive (c:..), everything works well. No problems to delete files. But it won't work (System.UnauthorizedAccessException) when I change it to the shared drive (H: drive).
Here are the codes related to the exception:
fdname = inipath + Convert.ToString(ynum);
if (! Directory.Exists(fdname))
System.IO.Directory.CreateDirectory(fdname);
fdname = inipath + Convert.ToString(ynum) + "\\" + Convert.ToString(fpathnum);
if (!Directory.Exists(fdname))
System.IO.Directory.CreateDirectory(fdname);
fdname = inipath + Convert.ToString(ynum) + "\\" + Convert.ToString(fpathnum) + "\\" + Convert.ToString(salodrnum);
if (!Directory.Exists(fdname))
{
System.IO.Directory.CreateDirectory(fdname);
File.SetAttributes(fdname, FileAttributes.Normal);
// File.SetAttributes(fdname, File.GetAttributes(fdname) & ~FileAttributes.ReadOnly); //remove read ony
}
if (File.Exists(fdname + #"\PS" + salodrnum + ".pdf"))
{
File.SetAttributes(fdname + #"\PS" + salodrnum + ".pdf", FileAttributes.Normal);
File.Delete(fdname + #"\PS" + salodrnum + ".pdf");
}
doc.ExportToDisk(CrystalDecisions.Shared.ExportFormatType.PortableDocFormat, fdname + #"\PS" + salodrnum + ".pdf");
File.SetAttributes(fdname + #"\PS" + salodrnum + ".pdf", FileAttributes.Normal);
procForm.Close();
It is a permission issue but I just couldn't figure out where the problem is. Here is the debugging details:
System.UnauthorizedAccessException
HResult=0x80070005
Message=Access to the path 'H:\OrderFiles\21\21003\2100337\PS2100337.pdf' is denied.
Source=mscorlib
StackTrace:
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalDelete(String path, Boolean checkHost)
at System.IO.File.Delete(String path)
at OpenOrder.PSForm.crystalReportViewer1_Load(Object sender, EventArgs
e) in C:\SG100sys\Source Codes\OpenOrder\OpenOrder\PSForm.cs:line 188
This exception was originally thrown at this call stack:
[External Code]
OpenOrder.PSForm.crystalReportViewer1_Load(object, System.EventArgs) in PSForm.cs
Probably off-topic, but in case it helps.
I remember having that headache a while ago while trying to replace files on a mapped drive in a machine that had to interact with that and other network locations, often with different credentials. I ended up using a custom impersonation context and passing the domain and credentials each time. I have this method in my FileUtil lib:
public static void InteractWithNetworkFolder(Action MethodThatInteractsWithTheFolder, string Domain, string Username, string Password)
{
var ImpersonationContext = new WrappedImpersonationContext(Domain, Username, Password);
ImpersonationContext.Enter();
MethodThatInteractsWithTheFolder.Invoke();
ImpersonationContext.Leave();
}
In your case, you'd use it like this:
FileUtil.InteractWithNetworkFolder(() => File.Delete(PathToYourFile), Domain, Username, Password)
And the impersonation context (credit to it's creator) looks like this:
public sealed class WrappedImpersonationContext
{
public enum LogonType : int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkClearText = 8,
NewCredentials = 9
}
public enum LogonProvider : int
{
Default = 0, // LOGON32_PROVIDER_DEFAULT
WinNT35 = 1,
WinNT40 = 2, // Use the NTLM logon provider.
WinNT50 = 3 // Use the negotiate logon provider.
}
[DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain,
String lpszPassword, LogonType dwLogonType, LogonProvider dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll")]
public extern static bool CloseHandle(IntPtr handle);
private string _domain, _password, _username;
private IntPtr _token;
private WindowsImpersonationContext _context;
private bool IsInContext
{
get { return _context != null; }
}
public WrappedImpersonationContext(string domain, string username, string password)
{
_domain = String.IsNullOrEmpty(domain) ? "." : domain;
_username = username;
_password = password;
}
// Changes the Windows identity of this thread. Make sure to always call Leave() at the end.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Enter()
{
if (IsInContext)
return;
_token = IntPtr.Zero;
bool logonSuccessfull = LogonUser(_username, _domain, _password, LogonType.NewCredentials, LogonProvider.WinNT50, ref _token);
if (!logonSuccessfull)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
WindowsIdentity identity = new WindowsIdentity(_token);
_context = identity.Impersonate();
Debug.WriteLine(WindowsIdentity.GetCurrent().Name);
}
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Leave()
{
if (!IsInContext)
return;
_context.Undo();
if (_token != IntPtr.Zero)
{
CloseHandle(_token);
}
_context = null;
}
}
Please try to check for less convoluted solutions before jumping into this one.

File extension for encrypted file C# [duplicate]

I've written a program that edits a specific filetype , and I want to give the user the option to set my application as the default editor for this filetype (since I don't want an installer) on startup.
I've tried to write a re-useable method that associates a file for me (preferably on any OS, although I'm running Vista) by adding a key to HKEY_CLASSES_ROOT, and am using it with my application, but it doesn't seem to work.
public static void SetAssociation(string Extension, string KeyName, string OpenWith, string FileDescription)
{
RegistryKey BaseKey;
RegistryKey OpenMethod;
RegistryKey Shell;
RegistryKey CurrentUser;
BaseKey = Registry.ClassesRoot.CreateSubKey(Extension);
BaseKey.SetValue("", KeyName);
OpenMethod = Registry.ClassesRoot.CreateSubKey(KeyName);
OpenMethod.SetValue("", FileDescription);
OpenMethod.CreateSubKey("DefaultIcon").SetValue("", "\"" + OpenWith + "\",0");
Shell = OpenMethod.CreateSubKey("Shell");
Shell.CreateSubKey("edit").CreateSubKey("command").SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
Shell.CreateSubKey("open").CreateSubKey("command").SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
BaseKey.Close();
OpenMethod.Close();
Shell.Close();
CurrentUser = Registry.CurrentUser.CreateSubKey(#"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\" + Extension);
CurrentUser = CurrentUser.OpenSubKey("UserChoice", RegistryKeyPermissionCheck.ReadWriteSubTree, System.Security.AccessControl.RegistryRights.FullControl);
CurrentUser.SetValue("Progid", KeyName, RegistryValueKind.String);
CurrentUser.Close();
}
Any idea why it doesn't work? An example use might be
SetAssociation(".ucs", "UCS_Editor_File", Application.ExecutablePath, "UCS File");
The part of the method that uses "CurrentUser" seems to work if I do the same using regedit, but using my application it doesn't.
The answer was a lot simpler than I expected. Windows Explorer has its own override for the open with application, and I was trying to modify it in the last lines of code. If you just delete the Explorer override, then the file association will work.
I also told explorer that I had changed a file association by calling the unmanaged function SHChangeNotify() using P/Invoke
public static void SetAssociation(string Extension, string KeyName, string OpenWith, string FileDescription)
{
// The stuff that was above here is basically the same
// Delete the key instead of trying to change it
var CurrentUser = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\" + Extension, true);
CurrentUser.DeleteSubKey("UserChoice", false);
CurrentUser.Close();
// Tell explorer the file association has been changed
SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}
[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
Here's a complete example:
public class FileAssociation
{
public string Extension { get; set; }
public string ProgId { get; set; }
public string FileTypeDescription { get; set; }
public string ExecutableFilePath { get; set; }
}
public class FileAssociations
{
// needed so that Explorer windows get refreshed after the registry is updated
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
private const int SHCNE_ASSOCCHANGED = 0x8000000;
private const int SHCNF_FLUSH = 0x1000;
public static void EnsureAssociationsSet()
{
var filePath = Process.GetCurrentProcess().MainModule.FileName;
EnsureAssociationsSet(
new FileAssociation
{
Extension = ".ucs",
ProgId = "UCS_Editor_File",
FileTypeDescription = "UCS File",
ExecutableFilePath = filePath
});
}
public static void EnsureAssociationsSet(params FileAssociation[] associations)
{
bool madeChanges = false;
foreach (var association in associations)
{
madeChanges |= SetAssociation(
association.Extension,
association.ProgId,
association.FileTypeDescription,
association.ExecutableFilePath);
}
if (madeChanges)
{
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
}
}
public static bool SetAssociation(string extension, string progId, string fileTypeDescription, string applicationFilePath)
{
bool madeChanges = false;
madeChanges |= SetKeyDefaultValue(#"Software\Classes\" + extension, progId);
madeChanges |= SetKeyDefaultValue(#"Software\Classes\" + progId, fileTypeDescription);
madeChanges |= SetKeyDefaultValue($#"Software\Classes\{progId}\shell\open\command", "\"" + applicationFilePath + "\" \"%1\"");
return madeChanges;
}
private static bool SetKeyDefaultValue(string keyPath, string value)
{
using (var key = Registry.CurrentUser.CreateSubKey(keyPath))
{
if (key.GetValue(null) as string != value)
{
key.SetValue(null, value);
return true;
}
}
return false;
}
You can do that in a managed way via ClickOnce. No fussing with the registry yourself. This is available via tooling (i.e. no xml) in VS2008 and above (including Express) on Project Properties => Publish => Options => File Associations
Solution above did not work for me with Windows 10.
Here is my solution to open files with the .myExt extension with %localappdata%\MyApp\MyApp.exe for current user. Optimised after reading comments.
String App_Exe = "MyApp.exe";
String App_Path = "%localappdata%";
SetAssociation_User("myExt", App_Path + App_Exe, App_Exe);
public static void SetAssociation_User(string Extension, string OpenWith, string ExecutableName)
{
try {
using (RegistryKey User_Classes = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes\\", true))
using (RegistryKey User_Ext = User_Classes.CreateSubKey("." + Extension))
using (RegistryKey User_AutoFile = User_Classes.CreateSubKey(Extension + "_auto_file"))
using (RegistryKey User_AutoFile_Command = User_AutoFile.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command"))
using (RegistryKey ApplicationAssociationToasts = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\ApplicationAssociationToasts\\", true))
using (RegistryKey User_Classes_Applications = User_Classes.CreateSubKey("Applications"))
using (RegistryKey User_Classes_Applications_Exe = User_Classes_Applications.CreateSubKey(ExecutableName))
using (RegistryKey User_Application_Command = User_Classes_Applications_Exe.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command"))
using (RegistryKey User_Explorer = Registry.CurrentUser.CreateSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + Extension))
using (RegistryKey User_Choice = User_Explorer.OpenSubKey("UserChoice"))
{
User_Ext.SetValue("", Extension + "_auto_file", RegistryValueKind.String);
User_Classes.SetValue("", Extension + "_auto_file", RegistryValueKind.String);
User_Classes.CreateSubKey(Extension + "_auto_file");
User_AutoFile_Command.SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
ApplicationAssociationToasts.SetValue(Extension + "_auto_file_." + Extension, 0);
ApplicationAssociationToasts.SetValue(#"Applications\" + ExecutableName + "_." + Extension, 0);
User_Application_Command.SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
User_Explorer.CreateSubKey("OpenWithList").SetValue("a", ExecutableName);
User_Explorer.CreateSubKey("OpenWithProgids").SetValue(Extension + "_auto_file", "0");
if (User_Choice != null) User_Explorer.DeleteSubKey("UserChoice");
User_Explorer.CreateSubKey("UserChoice").SetValue("ProgId", #"Applications\" + ExecutableName);
}
SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}
catch (Exception excpt)
{
//Your code here
}
}
[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
If you write the keys into HKEY_CURRENT_USER\Software\Classes instead of HKEY_CLASSES_ROOT, this should work without administrator privileges under Vista and later.
You are using an old version of Visual Studio, Vista is going to treat your program as a "legacy" Windows app. And redirect the registry writes you make. Include a manifest in your program so you'll look Vista-aware. This manifest is automatically included by VS2008 and up.
Beware that this still won't solve the problem for your user, she's very unlikely to run your app with UAC turned off. You'll need to write a separate app that has a manifest as linked and asks for administrator privileges. It needs the manifest with requestedExecutionLevel set to requireAdministrator.
If you're using Visual Studio 2015 then install the setup and deployment extension. Create a Setup Wizard, and then attach your .exe file to it. Right click your main program in the solution explorer go to -view, -file types, and then right click on the file types and select add new file type. Change all the properties to your needs and then build the MSI installer.
NOTE: I re-read your question and realized that you did not want an installer. Sorry about that, although you should consider using one because it gives you a lot more customization over your program(s).
the actual way to associate your file extension with your own programm:
using Microsoft.Win32;
using System;
using System.IO;
using System.Runtime.InteropServices;
private static void RegisterForFileExtension(string extension, string applicationPath)
{
RegistryKey FileReg = Registry.CurrentUser.CreateSubKey("Software\\Classes\\" + extension);
FileReg.CreateSubKey("shell\\open\\command").SetValue("", $"\"{applicationPath}\" \"%1\"");
FileReg.Close();
SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}
[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
EDIT: Thx, i changed the solution with your suggestion.
the line
FileReg.CreateSubKey("shell\open\command").SetValue("", applicationPath + " %1");
should be modified to
FileReg.CreateSubKey("shell\open\command").SetValue("", $"\"{applicationPath}\" \"%1\"");
if you don't want have problem with spaces in the path like:
C:\my folder\my file.txt

Copy file to remote computer with C# script and NAnt ( A specified logon session does not exist )

I'm currently working on a script (using NAnt and C#) running on a TeamCity server, which is to create and deploy an MSI file on a remote computer. The remote computer is set up with a shared folder which the TeamCity server is to connect to and copy the MSI file into, though the script. And it works the first time I run it without any problem.
But when I run the script a second time, it throws an exception with the following the message when it tries to connect to the share on the remote computer;
"A specified logon session does not exist. It may already have been terminated."
I use the solution from this thread to copy the MSI file from the TeamCity server to the remote computer using logon cridentials.
The following is the C# script in my NAnt file which does the file copy:
//Script main entry point
public static void ScriptMain(Project project)
{
NetworkCredential deployTargetCridentials = new NetworkCredential(project.Properties["deploy.remotesvr.username"],
project.Properties["deploy.remotesvr.password"]);
string connection = #"\\" + project.Properties["deploy.remotesvr"] + project.Properties["deploy.remotesvr.sharepath"];
string sourceFile = project.Properties["wix.output.dir"] + #"\" + project.Properties["wix.output.file"] + ".msi";
string destinationFile = #"\\" + project.Properties["deploy.remotesvr"] +
project.Properties["deploy.remotesvr.sharepath"] + #"\" +
project.Properties["deploy.remotesvr.deployfile"];
//Copy installation file to deploy share
Copy(project
, project.Properties["wix.output.dir"]
, project.Properties["wix.output.file"] + ".msi"
, #"\\" + project.Properties["deploy.remotesvr"] + project.Properties["deploy.remotesvr.sharepath"]
, project.Properties["deploy.remotesvr.deployfile"]
, deployTargetCridentials);
////
}
//Copy MSI
public static void Copy(Project project, string sourcePath, string sourceFile, string destinationPath, string destinationFile, NetworkCredential cridentials)
{
string source = sourcePath + #"\" + sourceFile;
string destination = destinationPath + #"\" + destinationFile;
try
{
project.Log(Level.Info, " ");
project.Log(Level.Info, "Copying " + source + " to " + destination);
project.Log(Level.Info, " Connecting to copy share: " + destinationPath);
using (new NetworkConnection(destinationPath, cridentials))
{
project.Log(Level.Info, " Copying file");
File.Copy(source, destination, true);
}
project.Log(Level.Info, "Copy successfull!");
}
catch (Exception ex)
{
project.Log(Level.Warning, "WARNING: Could not copy file: " + ex.Message.ToString().Trim().Replace("\r\n", ""));
}
}
////
//NetworkConnection class
public class NetworkConnection : IDisposable
{
string _networkName;
public NetworkConnection(string networkName, NetworkCredential credentials)
{
_networkName = networkName;
NetResource netResource = new NetResource();
netResource.Scope = ResourceScope.GlobalNetwork;
netResource.ResourceType = ResourceType.Disk;
netResource.DisplayType = ResourceDisplaytype.Share;
netResource.RemoteName = networkName;
int result = WNetAddConnection2(netResource, credentials.Password, credentials.UserName, 0);
if (result != 0 && result != 1219)
{
throw new Win32Exception(result);
}
}
~NetworkConnection()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
WNetCancelConnection2(_networkName, 0, true);
}
[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags);
[DllImport("mpr.dll")]
private static extern int WNetCancelConnection2(string name, int flags, bool force);
}
[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
public ResourceScope Scope;
public ResourceType ResourceType;
public ResourceDisplaytype DisplayType;
public int Usage;
public string LocalName;
public string RemoteName;
public string Comment;
public string Provider;
}
public enum ResourceScope : int
{
Connected = 1,
GlobalNetwork,
Remembered,
Recent,
Context
};
public enum ResourceType : int
{
Any = 0,
Disk = 1,
Print = 2,
Reserved = 8
}
public enum ResourceDisplaytype : int
{
Generic = 0x0,
Domain = 0x01,
Server = 0x02,
Share = 0x03,
File = 0x04,
Group = 0x05,
Network = 0x06,
Root = 0x07,
Shareadmin = 0x08,
Directory = 0x09,
Tree = 0x0a,
Ndscontainer = 0x0b
}
The exception occurs at the following line in Copy;
using (new NetworkConnection(destinationPath, cridentials))
When I tried to run the same copy code in a test application, it copied the file every time. Its only when I run it through the script that the exception occurs.
Does anyone have any idea to why this is?
Have you tried testing for / closing any existing connections prior to opening a new one?

problem with Associate file type in my program

i have this code for Associate *.sdf files to my C# program:
public class FileAssociation
{
// Associate file extension with progID, description, icon and application
public static void Associate(string extension,
string progID, string description, string icon, string application)
{
Registry.ClassesRoot.CreateSubKey(extension).SetValue("", progID);
if (progID != null && progID.Length > 0)
using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(progID))
{
if (description != null)
key.SetValue("", description);
if (icon != null)
key.CreateSubKey("DefaultIcon").SetValue("", ToShortPathName(icon));
if (application != null)
key.CreateSubKey(#"Shell\Open\Command").SetValue("",
ToShortPathName(application) + " \"%1\"");
}
}
// Return true if extension already associated in registry
public static bool IsAssociated(string extension)
{
return (Registry.ClassesRoot.OpenSubKey(extension, false) != null);
}
[DllImport("Kernel32.dll")]
private static extern uint GetShortPathName(string lpszLongPath,
[Out] StringBuilder lpszShortPath, uint cchBuffer);
// Return short path format of a file name
private static string ToShortPathName(string longName)
{
StringBuilder s = new StringBuilder(1000);
uint iSize = (uint)s.Capacity;
uint iRet = GetShortPathName(longName, s, iSize);
return s.ToString();
}
}
and i use it like this:
if (!FileAssociation.IsAssociated(".sdf"))
FileAssociation.Associate(".sdf", "ClassID.ProgID", "sdf File", #"d:\ICO.ico", #"D:\OpenSDF.exe");
i try this too:
if (FileAssociation.IsAssociated(".sdf"))
FileAssociation.Associate(".sdf", "ClassID.ProgID", "sdf File", #"d:\ICO.ico", #"D:\OpenSDF.exe");
my problem is when the file Already associated with another program it wont work !
for example: the *.sdf files on my computer Associate to open with Visual-studio 2008
i run this code - and Nothing happens !!
what i can do ?
thanks in advance
Try deleting the keys first then writing your own.

Categories