"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.
Related
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
This declaration has no problem accessing the file in my network.
MyApp = new Excel.Application();
MyApp.Visible = false;
MyBook = MyApp.Workbooks.Open("//NetworkFolderPath/File.xlsx"); //This line
MySheet = (Excel.Worksheet)MyBook.Sheets[1];
The problem arises when I am done editing my file and I try to save it, using the "SaveCopyAs()" method.
MyBook.SaveCopyAs("//NetworkFolderPath/File2.xlsx");
MyBook.Close(0);
The Exception I get is
An exception of type 'System.Runtime.InteropServices.COMException' occurred in Application.dll but was not handled in user code
Additional information: Microsoft Excel cannot access the file '//NetworkFolderPath/File2.xlsx'. There are several possible reasons:
1. The file name or path does not exist.
2. The file is being used by another program.
3. The workbook you are trying to save has the same name as a currently open workbook.
For Number 1: The file does exist inside the folderm and I accessed the file via the path, so I have ruled out number 1.
For Number 2: I am not sure if it could be this reason, some explanation would be nice
For Number 3: I have given the workbook a different name, but I am not sure if this will solve the problem.
I am really not sure where to go from here. All help is appreciated.
I've seen this path does not exist error when Offline Files are turned on and there is corruption in the cache. Try resetting the cache using the instructions.
Try this, it saves my 2 weeks effort and the application is saving the pdf on sahredpath with secured id (Credential)
if you have any question please comment below !happy to help you
============================
public class Impersonate
{
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out int phToken);
[DllImport("kernel32.dll")]
private static extern int FormatMessage(int dwFlags, string lpSource, int dwMessageId, int dwLanguageId,
StringBuilder lpBuffer, int nSize, string[] Arguments);
private const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;
private const int LOGON32_PROVIDER_DEFAULT = 0;
private const int FORMAT_MESSAGE_FROM_SYSTEM = 0x1000;
private static WindowsImpersonationContext winImpersonationContext = null;
public static void ImpersonateUser(string domain, string userName, string password)
{
//Benutzer einloggen
int userToken = 0;
bool loggedOn = (LogonUser(userName, domain, password, LOGON32_LOGON_NETWORK_CLEARTEXT,
LOGON32_PROVIDER_DEFAULT, out userToken) != 0);
if (loggedOn == false)
{
int apiError = Marshal.GetLastWin32Error();
StringBuilder errorMessage = new StringBuilder(1024);
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, null, apiError, 0, errorMessage, 1024, null);
throw new Exception(errorMessage.ToString());
}
WindowsIdentity identity = new WindowsIdentity((IntPtr)userToken);
winImpersonationContext = identity.Impersonate();
}
public static void UndoImpersonation()
{
if (winImpersonationContext != null)
{
winImpersonationContext.Undo();
}
}
}
2. Call Impersonate
Impersonate.ImpersonateUser("domain", "user name", "password");
//Your Code as the new User
DirectoryInfo _dirInfo = new DirectoryInfo(#"file path");
FileInfo[] _files = FileExtension.GetFilesByExtensions(_dirInfo, ".xls", ".xlsx").ToArray();
Impersonate.UndoImpersonation();
==============================
Ref:- https://forums.asp.net/t/2126720.aspx?Access+Denied+while+reading+writing+directory+in+Network+File+Share+through+Service+Account
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);
}
I scanned through my local home network using Bonjour 1.0(.NET) for services with regtype = "_http._tcp":
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
DNSSDService browse = service.Browse(0, 0, "_http._tcp", null, eventManager);
When I find the service the method: static void eventManager_ServiceFound(DNSSDService browser, DNSSDFlags flags, uint ifIndex, string serviceName, string regtype, string domain) is invoked.
Then in that method I want to use method: DNSSDService GetAddrInfo(DNSSDFlags glags, uint ifIndex, DNSSDAddressFamily addressFamily,string hostname, DNSSDEventManager eventManager) to get to know service's ip address and port.
The problem is that I do not know know what string hostname should be and thus I do not get any results: ** Please look at the: ???WHAT HERE?? in the code**
static void eventManager_ServiceFound(DNSSDService browser, DNSSDFlags flags, uint ifIndex, string serviceName, string regtype, string domain) {
Console.WriteLine("browser: " + browser + "\nDNSSDFlags " + flags+ "\nifIndex " + ifIndex +"\nserviceName: " + serviceName + "\nregtype: " +regtype+ "\ndomain: "+ domain);
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.AddressFound += new _IDNSSDEvents_AddressFoundEventHandler(eventManager_AddressFound);
DNSSDAddressFamily family = new DNSSDAddressFamily();
browser.GetAddrInfo(flags, ifIndex, family, ???WHAT HERE???, eventManager);
}
private static void eventManager_AddressFound(DNSSDService service, DNSSDFlags flags, uint ifIndex, string hostname, DNSSDAddressFamily addressFamily, string address, uint ttl) {
Console.WriteLine("----------------------------------------");
Console.WriteLine("FOUND THE ADDRESS");
Console.WriteLine("----------------------------------------");
}
Output:
browser: System.__ComObject
DNSSDFlags 2
ifIndex 32
serviceName: AXIS M1011-W - 00408CBEEAE5
regtype: _http._tcp.
domain: local.
Full code:
using Bonjour;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
DNSSDService service = new DNSSDService();
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.ServiceFound += new _IDNSSDEvents_ServiceFoundEventHandler(eventManager_ServiceFound);
// DNSSDService browse = service.Browse(0, 0, "_axis-video._tcp", null, eventManager);
DNSSDService browse = service.Browse(0, 0, "_http._tcp", null, eventManager);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
static void eventManager_ServiceFound(DNSSDService browser, DNSSDFlags flags, uint ifIndex, string serviceName, string regtype, string domain) {
Console.WriteLine("browser: " + browser + "\nDNSSDFlags " + flags+ "\nifIndex " + ifIndex +"\nserviceName: " + serviceName + "\nregtype: " +regtype+ "\ndomain: "+ domain);
DNSSDEventManager eventManager = new DNSSDEventManager();
eventManager.AddressFound += new _IDNSSDEvents_AddressFoundEventHandler(eventManager_AddressFound);
DNSSDAddressFamily family = new DNSSDAddressFamily();
browser.GetAddrInfo(flags, ifIndex, family, ?????WHAT HERE?????, eventManager);
}
private static void eventManager_AddressFound(DNSSDService service, DNSSDFlags flags, uint ifIndex, string hostname, DNSSDAddressFamily addressFamily, string address, uint ttl) {
Console.WriteLine("----------------------------------------");
Console.WriteLine("FOUND ADDRESS");
Console.WriteLine("----------------------------------------");
}
}
}
Something that I'd check first is the TXT record associated with the _http._tcp record. From the screenshot you have the _axis-video._tcp selected and it shows you the macaddress associated with it. Many _http._tcp entries record the url needed to get to it, or the port/ipaddress needed. I'd dig into that first.
Otherwise you may have to resolve the macaddress into an ipaddress. I googled and come with http://www.mostthingsweb.com/2011/11/reading-arp-entries-with-c/
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?