Network IO using Credentials - c#

Is it possible to move files from a network location that requires credentials to another network location that also requires credentials without mapping any drive. (ie: Without any use of P/Invoke)
Example:
FileInfo fi = new FileInfo(#"\\SomeComputer\SomeDrive\SomeFolder\someFile.txt");
fi.MoveTo(#"\\AnotherComputer\AnotherDrive\AnotherFolder\AnotherFile.txt");
This works fine if the source and destination network drives are already mapped but if they are not It doesn't.

Try something like:
[DllImport("advapi32.dll")]
private static extern int LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
/// <summary>
/// Used for logging on the domain
/// </summary>
public enum LogonProvider
{
/// <summary>
///
/// </summary>
LOGON32_PROVIDER_DEFAULT = 0,
/// <summary>
///
/// </summary>
LOGON32_PROVIDER_WINNT35 = 1,
/// <summary>
///
/// </summary>
LOGON32_PROVIDER_WINNT40 = 2,
/// <summary>
///
/// </summary>
LOGON32_PROVIDER_WINNT50 = 3
};
/// <summary>
/// Used for logging on across the domain
/// </summary>
public enum LogonType
{
/// <summary>
///
/// </summary>
LOGON32_LOGON_INTERACTIVE = 2,
/// <summary>
///
/// </summary>
LOGON32_LOGON_NETWORK = 3,
/// <summary>
///
/// </summary>
LOGON32_LOGON_BATCH = 4,
/// <summary>
///
/// </summary>
LOGON32_LOGON_SERVICE = 5,
/// <summary>
///
/// </summary>
LOGON32_LOGON_UNLOCK = 6,
/// <summary>
///
/// </summary>
LOGON32_LOGON_NETWORK_CLEARTEXT = 8,
/// <summary>
///
/// </summary>
LOGON32_LOGON_NEW_CREDENTIALS = 9
}
IntPtr token = new IntPtr();
LogonUser(<username>, <domain>, <password>, (int)LogonType.LOGON32_LOGON_NEW_CREDENTIALS, (int)LogonProvider.LOGON32_PROVIDER_WINNT50, ref token);
WindowsIdentity w = new WindowsIdentity(token);
w.Impersonate();
This impersonates a domain user, and can then be used to copy files.

No. You'd need to p/invoke something. This functionality is not provided in the BCL.

Related

How to change folders permission to the current user by using admin credentials?

Ok, so I've been searching for a while and the title explains pretty much what I want to do.
Also, there is no problem hard-coding the admin credentials in the code.
Initially I wrote some code in c# that ALMOST solved the problem:
private void button2_Click(object sender, EventArgs e)
{
DirectoryInfo myDirectoryInfo = new DirectoryInfo(textBox1.Text);
DirectorySecurity myDirectorySecurity = myDirectoryInfo.GetAccessControl();
string User = System.Environment.UserDomainName + "\\" + comboBox1.SelectedItem.ToString();
myDirectorySecurity.AddAccessRule(new FileSystemAccessRule(User, FileSystemRights.FullControl, InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow));
//myDirectorySecurity.AddAccessRule(new FileSystemAccessRule(User, FileSystemRights.Write, InheritanceFlags.ContainerInherit, PropagationFlags.None, AccessControlType.Allow));
myDirectoryInfo.SetAccessControl(myDirectorySecurity);
MessageBox.Show("Permissions Altered Successfully" + User);
}
This works just fine if I use on a folder that I already have permission, but what I need is a method that uses admin credentials to grant permission to a folder that a regular user don't have.
Later I tried to write some stuff in vbscript:
strHomeFolder = "C:\test"
strUser = " DOMAIN\user"
Set WshShell = CreateObject("WScript.Shell")
WshShell.Run "%COMSPEC% /c Echo Y| cacls "& strHomeFolder & " /e /c /g "& strUser &":F", 2, True
But i couldn't find a way to pass the admin credentials.
So lastly I wrote another code to try to get it done:
private void button1_Click(object sender, EventArgs e)
{
try
{
//string passwordPre = "PASSWORD";
//char[] passwordChars = passwordPre.ToCharArray();
//SecureString password = new SecureString();
//foreach (char c in passwordChars)
//{
// password.AppendChar(c);
//}
ProcessStartInfo p = new ProcessStartInfo(#"D:\\test.vbs");
//p.UseShellExecute = false;
//p.UserName = "username";
//p.Domain = "DOMAIN";
//p.Password = password;
Process.Start(p);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
This time I just tried to pass admin credentials using a process, but it generated the message: The specified executable is not a valid application for this OS plataform.
So, is there any method that I can use to pass credentials? (Can be in c# or vbscript).
Thanks in advance.
Impersonation will solve your problem. When you execute the code within the impersonated context, logic placed within that context will be executed with the previlege of the impersonated user. Following class reards the impersonation configuration values from the web.config file. You can modify it read from app.config or whatever the source.
Required configurations
User Name
Password
Domain Name
Impersonation Class
public class Impersonator : IDisposable
{
#region Win32 Advanced API calls
/// <summary>
/// Logons the user.
/// </summary>
/// <param name="lpszUserName">Name of the LPSZ user.</param>
/// <param name="lpszDomain">The LPSZ domain.</param>
/// <param name="lpszPassword">The LPSZ password.</param>
/// <param name="dwLogOnType">Type of the dw log on.</param>
/// <param name="dwLogOnProvider">The dw log on provider.</param>
/// <param name="phToken">The ph token.</param>
/// <returns></returns>
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true,
BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern int LogonUser(String lpszUserName,
String lpszDomain,
String lpszPassword,
int dwLogOnType,
int dwLogOnProvider,
ref IntPtr phToken);
/// <summary>
/// Duplicates the token.
/// </summary>
/// <param name="hToken">The h token.</param>
/// <param name="impersonationLevel">The impersonation level.</param>
/// <param name="hNewToken">The h new token.</param>
/// <returns></returns>
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true,
BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
/// <summary>
/// Reverts to self.
/// </summary>
/// <returns></returns>
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true,
BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern bool RevertToSelf();
/// <summary>
/// Closes the handle.
/// </summary>
/// <param name="handle">The handle.</param>
/// <returns></returns>
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true,
BestFitMapping = false, ThrowOnUnmappableChar = true)]
private static extern bool CloseHandle(IntPtr handle);
#endregion
#region Fields
/// <summary>
/// Field to hold the impersonation Context
/// </summary>
WindowsImpersonationContext impersonationContext;
/// <summary>
/// Track whether Dispose has been called.
/// </summary>
private bool disposed;
#region Constants
/// <summary>
/// Logon32 Logon Interactive
/// </summary>
public const int INTERACTIVE_NUMBER = 2;
/// <summary>
/// Logon32 Provider Default
/// </summary>
public const int DEFAULT_NUMBER = 0;
/// <summary>
/// Impersonating user name key
/// </summary>
public const string ImpersonatingUserNameKey = "ImpersonatingUserName";
/// <summary>
/// Impersonating user password key
/// </summary>
public const string ImpersonatingPasswordKey = "ImpersonatingUserPassword";
/// <summary>
/// Impersonating user domain key
/// </summary>
public const string ImpersonatingDomainNameKey = "ImpersonatingDomain";
#endregion
#endregion
#region Construction/Destruction/Initialization
/// <summary>
/// Constructor of the impersonator
/// </summary>
public Impersonator()
{
if (!ImpersonateUser(ConfigurationManager.AppSettings[ImpersonatingUserNameKey],
ConfigurationManager.AppSettings[ImpersonatingDomainNameKey],
ConfigurationManager.AppSettings[ImpersonatingPasswordKey]))
{
//TODO: Log Exception
}
}
#endregion
#region Public Methods
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
/// <summary>
/// Impersonate User with the given user credentials
/// </summary>
/// <param name="userName">User Name</param>
/// <param name="domain">Domain</param>
/// <param name="password">Password</param>
/// <returns>True if success, false otherwise</returns>
private bool ImpersonateUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
IntPtr token = IntPtr.Zero;
IntPtr tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUser(userName, domain, password, INTERACTIVE_NUMBER,
DEFAULT_NUMBER, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
CloseHandle(token);
if (tokenDuplicate != IntPtr.Zero)
CloseHandle(tokenDuplicate);
return false;
}
/// <summary>
/// Undo impersonation
/// </summary>
private void StopImpersonation()
{
impersonationContext.Undo();
}
#endregion
#region Protected Methods
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
StopImpersonation();
}
// Note disposing has been done.
disposed = true;
}
}
#endregion
}
How to call the method
Using(Impersonator impersonator = new Impersonator())
{
//Write the folder accessing logic here
}

What the C# equivalent of "mklink /J"?

I know how to create a symbolic link in windows in a .bat script:
mklink /J <LinkPath> <OriginalResourcePath>
How to do the same thing in C# ?
I've not been happy with the googling, because i'm a beginner in C# and I probably don't use the right terms. Anybody can indicate the API to use please ?
Warning: The question is not clear as it refers to symbolic links but at the same time refers to the /J switch that is used to create a junction. This answer refers to "how to create a symbolic link in c#" (without the /J). Instead, For creating junctions, please refer to In .NET, how do I Create a Junction in NTFS, as opposed to a Symlink?.
This is how symbolic links can be created:
using System.Runtime.InteropServices;
using System.IO;
namespace ConsoleApplication
{
class Program
{
[DllImport("kernel32.dll")]
static extern bool CreateSymbolicLink(
string lpSymlinkFileName, string lpTargetFileName, SymbolicLink dwFlags);
enum SymbolicLink
{
File = 0,
Directory = 1
}
static void Main(string[] args)
{
string symbolicLink = #"c:\bar.txt";
string fileName = #"c:\temp\foo.txt";
using (var writer = File.CreateText(fileName))
{
writer.WriteLine("Hello World");
}
CreateSymbolicLink(symbolicLink, fileName, SymbolicLink.File);
}
}
}
This will create a symbolic link file called bar.txt on the C:-drive which links to the foo.txt text file stored in the C:\temp directory.
Here is a project that has the PInvoke signatures for creating junctions: http://www.codeproject.com/Articles/15633/Manipulating-NTFS-Junction-Points-in-NET. The code is copied below:
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace Util {
/// <summary>
/// Provides access to NTFS junction points in .Net.
/// </summary>
public static class JunctionPoint {
/// <summary>
/// The file or directory is not a reparse point.
/// </summary>
private const int ERROR_NOT_A_REPARSE_POINT = 4390;
/// <summary>
/// The reparse point attribute cannot be set because it conflicts with an existing attribute.
/// </summary>
private const int ERROR_REPARSE_ATTRIBUTE_CONFLICT = 4391;
/// <summary>
/// The data present in the reparse point buffer is invalid.
/// </summary>
private const int ERROR_INVALID_REPARSE_DATA = 4392;
/// <summary>
/// The tag present in the reparse point buffer is invalid.
/// </summary>
private const int ERROR_REPARSE_TAG_INVALID = 4393;
/// <summary>
/// There is a mismatch between the tag specified in the request and the tag present in the reparse point.
/// </summary>
private const int ERROR_REPARSE_TAG_MISMATCH = 4394;
/// <summary>
/// Command to set the reparse point data block.
/// </summary>
private const int FSCTL_SET_REPARSE_POINT = 0x000900A4;
/// <summary>
/// Command to get the reparse point data block.
/// </summary>
private const int FSCTL_GET_REPARSE_POINT = 0x000900A8;
/// <summary>
/// Command to delete the reparse point data base.
/// </summary>
private const int FSCTL_DELETE_REPARSE_POINT = 0x000900AC;
/// <summary>
/// Reparse point tag used to identify mount points and junction points.
/// </summary>
private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
/// <summary>
/// This prefix indicates to NTFS that the path is to be treated as a non-interpreted
/// path in the virtual file system.
/// </summary>
private const string NonInterpretedPathPrefix = #"\??\";
[Flags]
private enum EFileAccess : uint {
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
}
[Flags]
private enum EFileShare : uint {
None = 0x00000000,
Read = 0x00000001,
Write = 0x00000002,
Delete = 0x00000004,
}
private enum ECreationDisposition : uint {
New = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5,
}
[Flags]
private enum EFileAttributes : uint {
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[StructLayout(LayoutKind.Sequential)]
private struct REPARSE_DATA_BUFFER {
/// <summary>
/// Reparse point tag. Must be a Microsoft reparse point tag.
/// </summary>
public uint ReparseTag;
/// <summary>
/// Size, in bytes, of the data after the Reserved member. This can be calculated by:
/// (4 * sizeof(ushort)) + SubstituteNameLength + PrintNameLength +
/// (namesAreNullTerminated ? 2 * sizeof(char) : 0);
/// </summary>
public ushort ReparseDataLength;
/// <summary>
/// Reserved; do not use.
/// </summary>
public ushort Reserved;
/// <summary>
/// Offset, in bytes, of the substitute name string in the PathBuffer array.
/// </summary>
public ushort SubstituteNameOffset;
/// <summary>
/// Length, in bytes, of the substitute name string. If this string is null-terminated,
/// SubstituteNameLength does not include space for the null character.
/// </summary>
public ushort SubstituteNameLength;
/// <summary>
/// Offset, in bytes, of the print name string in the PathBuffer array.
/// </summary>
public ushort PrintNameOffset;
/// <summary>
/// Length, in bytes, of the print name string. If this string is null-terminated,
/// PrintNameLength does not include space for the null character.
/// </summary>
public ushort PrintNameLength;
/// <summary>
/// A buffer containing the unicode-encoded path string. The path string contains
/// the substitute name string and print name string.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3FF0)]
public byte[] PathBuffer;
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
IntPtr InBuffer, int nInBufferSize,
IntPtr OutBuffer, int nOutBufferSize,
out int pBytesReturned, IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
/// <summary>
/// Creates a junction point from the specified directory to the specified target directory.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="targetDir">The target directory to create</param>
/// <param name="sourceDir">The source directory to alias</param>
/// <param name="overwrite">If true overwrites an existing reparse point or empty directory</param>
/// <exception cref="IOException">Thrown when the junction point could not be created or when
/// an existing directory was found and <paramref name="overwrite" /> if false</exception>
public static void Create(string sourceDir, string targetDir, bool overwrite) {
sourceDir = Path.GetFullPath(sourceDir);
if (!Directory.Exists(sourceDir))
throw new IOException($"Source path does not exist or is not a directory.");
if (Directory.Exists(targetDir))
throw new IOException($"Directory '{targetDir}' already exists.");
Directory.CreateDirectory(targetDir);
using (SafeFileHandle handle = OpenReparsePoint(targetDir, EFileAccess.GenericWrite)) {
byte[] sourceDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(sourceDir));
REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();
reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparseDataBuffer.ReparseDataLength = (ushort)(sourceDirBytes.Length + 12);
reparseDataBuffer.SubstituteNameOffset = 0;
reparseDataBuffer.SubstituteNameLength = (ushort)sourceDirBytes.Length;
reparseDataBuffer.PrintNameOffset = (ushort)(sourceDirBytes.Length + 2);
reparseDataBuffer.PrintNameLength = 0;
reparseDataBuffer.PathBuffer = new byte[0x3ff0];
Array.Copy(sourceDirBytes, reparseDataBuffer.PathBuffer, sourceDirBytes.Length);
int inBufferSize = Marshal.SizeOf(reparseDataBuffer);
IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize);
try {
Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);
int bytesReturned;
bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
inBuffer, sourceDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result)
ThrowLastWin32Error($"Unable to create junction point '{sourceDir}' -> '{targetDir}'.");
} finally {
Marshal.FreeHGlobal(inBuffer);
}
}
}
/// <summary>
/// Deletes a junction point at the specified source directory along with the directory itself.
/// Does nothing if the junction point does not exist.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
public static void Delete(string junctionPoint) {
if (!Directory.Exists(junctionPoint)) {
if (File.Exists(junctionPoint))
throw new IOException("Path is not a junction point.");
return;
}
using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite)) {
REPARSE_DATA_BUFFER reparseDataBuffer = new REPARSE_DATA_BUFFER();
reparseDataBuffer.ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
reparseDataBuffer.ReparseDataLength = 0;
reparseDataBuffer.PathBuffer = new byte[0x3ff0];
int inBufferSize = Marshal.SizeOf(reparseDataBuffer);
IntPtr inBuffer = Marshal.AllocHGlobal(inBufferSize);
try {
Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);
int bytesReturned;
bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_DELETE_REPARSE_POINT,
inBuffer, 8, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result)
ThrowLastWin32Error("Unable to delete junction point.");
} finally {
Marshal.FreeHGlobal(inBuffer);
}
try {
Directory.Delete(junctionPoint);
} catch (IOException ex) {
throw new IOException("Unable to delete junction point.", ex);
}
}
}
/// <summary>
/// Determines whether the specified path exists and refers to a junction point.
/// </summary>
/// <param name="path">The junction point path</param>
/// <returns>True if the specified path represents a junction point</returns>
/// <exception cref="IOException">Thrown if the specified path is invalid
/// or some other error occurs</exception>
public static bool Exists(string path) {
if (!Directory.Exists(path))
return false;
using (SafeFileHandle handle = OpenReparsePoint(path, EFileAccess.GenericRead)) {
string target = InternalGetTarget(handle);
return target != null;
}
}
/// <summary>
/// Gets the target of the specified junction point.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
/// <returns>The target of the junction point</returns>
/// <exception cref="IOException">Thrown when the specified path does not
/// exist, is invalid, is not a junction point, or some other error occurs</exception>
public static string GetTarget(string junctionPoint) {
using (SafeFileHandle handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericRead)) {
string target = InternalGetTarget(handle);
if (target == null)
throw new IOException("Path is not a junction point.");
return target;
}
}
private static string InternalGetTarget(SafeFileHandle handle) {
int outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
IntPtr outBuffer = Marshal.AllocHGlobal(outBufferSize);
try {
int bytesReturned;
bool result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT,
IntPtr.Zero, 0, outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);
if (!result) {
int error = Marshal.GetLastWin32Error();
if (error == ERROR_NOT_A_REPARSE_POINT)
return null;
ThrowLastWin32Error("Unable to get information about junction point.");
}
REPARSE_DATA_BUFFER reparseDataBuffer = (REPARSE_DATA_BUFFER)
Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));
if (reparseDataBuffer.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
return null;
string targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);
if (targetDir.StartsWith(NonInterpretedPathPrefix))
targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length);
return targetDir;
} finally {
Marshal.FreeHGlobal(outBuffer);
}
}
private static SafeFileHandle OpenReparsePoint(string reparsePoint, EFileAccess accessMode) {
SafeFileHandle reparsePointHandle = new SafeFileHandle(CreateFile(reparsePoint, accessMode,
EFileShare.Read | EFileShare.Write | EFileShare.Delete,
IntPtr.Zero, ECreationDisposition.OpenExisting,
EFileAttributes.BackupSemantics | EFileAttributes.OpenReparsePoint, IntPtr.Zero), true);
if (Marshal.GetLastWin32Error() != 0)
ThrowLastWin32Error("Unable to open reparse point.");
return reparsePointHandle;
}
private static void ThrowLastWin32Error(string message) {
throw new IOException(message, Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
}
}
}
Another way it to use mklink. It turns out that mklink is an internal command so you need cmd.exe to call it:
var psi=new ProcessStartInfo("cmd.exe"," /C mklink \""+link_name+"\" \""+ target_name + "\"");
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
Process.Start(psi).WaitForExit();
You can add /J or /d etc as you need.
ref: Run Command Prompt Commands
For anyone using .NET 6 or greater this has been simplified a great deal and is now part of the framework.
using System.IO;
private void MyMethod(string source, string target)
{
// Create symbolic link for directory (answers this question)
Directory.CreateSymbolicLink(source, target);
// Create symbolic link for file (additional reference)
File.CreateSymbolicLink(source, target);
}

Force window to blink when a particular event occurs in C# / WPF

I am using C# / WPF to make an application. In that application, I want to blink the window if a particular event occurs so that user of that application knows that something happened. How can I get this in my C# WPF application.
Like in Yahoo Messenger, if you get a message then the message window blinks to get your focus, I want to use that effect in my application.
Flashing the window and taskbar in a similar way to IM notifications can be accomplished in WPF using the following code. It uses PlatformInvoke to call the WinAPI function FlashWindowEx using the Win32 Handle of the WPF Application.Current.MainWindow
Code
public class FlashWindowHelper
{
private IntPtr mainWindowHWnd;
private Application theApp;
public FlashWindowHelper(Application app)
{
this.theApp = app;
}
public void FlashApplicationWindow()
{
InitializeHandle();
Flash(this.mainWindowHWnd, 5);
}
public void StopFlashing()
{
InitializeHandle();
if (Win2000OrLater)
{
FLASHWINFO fi = CreateFlashInfoStruct(this.mainWindowHWnd, FLASHW_STOP, uint.MaxValue, 0);
FlashWindowEx(ref fi);
}
}
private void InitializeHandle()
{
if (this.mainWindowHWnd == IntPtr.Zero)
{
// Delayed creation of Main Window IntPtr as Application.Current passed in to ctor does not have the MainWindow set at that time
var mainWindow = this.theApp.MainWindow;
this.mainWindowHWnd = new System.Windows.Interop.WindowInteropHelper(mainWindow).Handle;
}
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool FlashWindowEx(ref FLASHWINFO pwfi);
[StructLayout(LayoutKind.Sequential)]
private struct FLASHWINFO
{
/// <summary>
/// The size of the structure in bytes.
/// </summary>
public uint cbSize;
/// <summary>
/// A Handle to the Window to be Flashed. The window can be either opened or minimized.
/// </summary>
public IntPtr hwnd;
/// <summary>
/// The Flash Status.
/// </summary>
public uint dwFlags;
/// <summary>
/// The number of times to Flash the window.
/// </summary>
public uint uCount;
/// <summary>
/// The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink rate.
/// </summary>
public uint dwTimeout;
}
/// <summary>
/// Stop flashing. The system restores the window to its original stae.
/// </summary>
public const uint FLASHW_STOP = 0;
/// <summary>
/// Flash the window caption.
/// </summary>
public const uint FLASHW_CAPTION = 1;
/// <summary>
/// Flash the taskbar button.
/// </summary>
public const uint FLASHW_TRAY = 2;
/// <summary>
/// Flash both the window caption and taskbar button.
/// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
/// </summary>
public const uint FLASHW_ALL = 3;
/// <summary>
/// Flash continuously, until the FLASHW_STOP flag is set.
/// </summary>
public const uint FLASHW_TIMER = 4;
/// <summary>
/// Flash continuously until the window comes to the foreground.
/// </summary>
public const uint FLASHW_TIMERNOFG = 12;
/// <summary>
/// Flash the spacified Window (Form) until it recieves focus.
/// </summary>
/// <param name="hwnd"></param>
/// <returns></returns>
public static bool Flash(IntPtr hwnd)
{
// Make sure we're running under Windows 2000 or later
if (Win2000OrLater)
{
FLASHWINFO fi = CreateFlashInfoStruct(hwnd, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
return FlashWindowEx(ref fi);
}
return false;
}
private static FLASHWINFO CreateFlashInfoStruct(IntPtr handle, uint flags, uint count, uint timeout)
{
FLASHWINFO fi = new FLASHWINFO();
fi.cbSize = Convert.ToUInt32(Marshal.SizeOf(fi));
fi.hwnd = handle;
fi.dwFlags = flags;
fi.uCount = count;
fi.dwTimeout = timeout;
return fi;
}
/// <summary>
/// Flash the specified Window (form) for the specified number of times
/// </summary>
/// <param name="hwnd">The handle of the Window to Flash.</param>
/// <param name="count">The number of times to Flash.</param>
/// <returns></returns>
public static bool Flash(IntPtr hwnd, uint count)
{
if (Win2000OrLater)
{
FLASHWINFO fi = CreateFlashInfoStruct(hwnd, FLASHW_ALL | FLASHW_TIMERNOFG, count, 0);
return FlashWindowEx(ref fi);
}
return false;
}
/// <summary>
/// A boolean value indicating whether the application is running on Windows 2000 or later.
/// </summary>
private static bool Win2000OrLater
{
get { return Environment.OSVersion.Version.Major >= 5; }
}
}
Usage
var helper = new FlashWindowHelper(Application.Current);
// Flashes the window and taskbar 5 times and stays solid
// colored until user focuses the main window
helper.FlashApplicationWindow();
// Cancels the flash at any time
helper.StopFlashing();
You could use TaskBarItem class to make the taskbar icon of your app blink.
Here is something that can help you achieve it.
Then you can flash, shake, fade-in fade-out, or whatever one of the other zillion FX using WPF Animations.
It's very simple and requires almost no code at all, if you have Expression Blend the job is made even easier.
Setting the ProgressState property to TaskbarItemProgressState.Indeterminate will blink the icon in green. You don have to use the progress bar.
http://msdn.microsoft.com/en-us/library/system.windows.shell.taskbaritemprogressstate.aspx

Extended PageSize Information in C#

I have to print a custom label to a thermal printer. I have everything setup and working with one exception: the rolls of labels have two labels per row, but the C# printing objects can't seem to see that.
When I query the PageSize information, it tells me that the label is 3.15" x 0.75". While this is true for the entire label, it doesn't give me any information about the size of each individual label, or the spacing between.
Digging into the driver ini files, there is a line that looks like PageSize84 = THT-6-423,3150,2,1500,750,150,125,1. All of the information I need seems to be listed in this line (2 columns, 1500 wide, 750 tall), I just have no idea how to access it from C#. I've been scouring the web today, and I've had no luck.
I could always hard code the information for now, but this doesn't future proof the code if manufacturing changes labels.
If you are trying to read an ini file to get the values you can use this.
IniFile.ReadIniValue("[Tag]", "Server", #"C:\my.ini");
#region Usings
using System.Text;
using System.Runtime.InteropServices;
#endregion
/// <summary>
/// Communicates with ini files
/// </summary>
public static class IniFile
{
#region Declarations
#endregion
#region Constructor/Deconstructor
/// <summary>
/// Initializes a new instance of the <see cref="IniFile"/> class.
/// </summary>
static IniFile()
{
}
#endregion
#region Properties
#endregion
#region Win32_API
[DllImport("kernel32")]
private static extern int GetPrivateProfileString(
string section,
string key, string def,
StringBuilder retVal,
int size, string filePath);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool WritePrivateProfileString(string lpAppName,
string lpKeyName, string lpString, string lpFileName);
#endregion
/// <summary>
/// Reads the ini value.
/// </summary>
/// <param name="section">The section.</param>
/// <param name="key">The key.</param>
/// <param name="iniFilePath">The ini file path.</param>
/// <returns>Value stored in key</returns>
/// <exception cref="FileNotFoundException"></exception>
public static string ReadIniValue(string section, string key, string iniFilePath)
{
if(!File.Exists(iniFilePath))
{
throw new FileNotFoundException();
}
const int size = 255;
var buffer = new StringBuilder(size);
var len = GetPrivateProfileString(section, key, string.Empty, buffer, size, iniFilePath);
if (len > 0)
{
return buffer.ToString();
}
return string.Empty;
}
/// <summary>
/// Writes the ini value.
/// </summary>
/// <param name="section">The section.</param>
/// <param name="keyname">The keyname.</param>
/// <param name="valueToWrite">The value to write.</param>
/// <param name="iniFilePath">The ini file path.</param>
/// <returns>true if write was successful, false otherwise</returns>
public static bool WriteIniValue(string section,string keyname,string valueToWrite,string iniFilePath)
{
return WritePrivateProfileString(section, keyname, valueToWrite, iniFilePath);
}
}

How to create a hardlink in C#?

How to create a hardlink in C#? Any code snippet, please?
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode )]
static extern bool CreateHardLink(
string lpFileName,
string lpExistingFileName,
IntPtr lpSecurityAttributes
);
Usage:
CreateHardLink(newLinkPath,sourcePath, IntPtr.Zero);
The BCL doesn't provide this, so you'll have to resort to p/invoke
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode )]
static extern bool CreateHardLink(
string lpFileName,
string lpExistingFileName,
IntPtr lpSecurityAttributes
);
And use it e.g. like
CreateHardLink(#"c:\temp\New Link", #"c:\temp\Original File",IntPtr.Zero);
If you mean the NTFS Hardlinks:
The following is (an introduction of text on dotnetspark) :
Unfortunately, neither hard links nor soft links are supported by the .NET Framework. Therefore, you will need to dig into the Windows API to allow your application to consume this feature.
You can create a hard link using a single line of code using a simple call to the Win32 function, CreateHardLink(), that resides in the Kernel32.dll library. The definition of this function is as follows:
BOOL CreateHardLink(
LPCTSTR lpFileName,
LPCTSTR lpExistingFileName,
LPSECURITY_ATTRIBUTES lpSecurityAttributes
);
If you only want to create a hardlink to a File the answer
using System.Runtime.InteropServices;
...
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode )]
static extern bool CreateHardLink(
string lpFileName,
string lpExistingFileName,
IntPtr lpSecurityAttributes
);
seems to be best.
But if you want to create a hardlink of a Folder then it's a little bit more tricky.
Here I found some code to create/delete Folder hardlinks (junctions). I tested the code and it worked properly.
I post the code here, just in case the site goes offline:
JunctionPoint.cs
// File: RollThroughLibrary/CreateMaps/JunctionPoint.cs
// User: Adrian Hum/
//
// Created: 2017-11-19 2:46 PM
// Modified: 2017-11-19 6:10 PM
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
namespace CreateMaps
{
/// <summary>
/// Provides access to NTFS junction points in .Net.
/// </summary>
[SuppressMessage("ReSharper", "InconsistentNaming")]
[SuppressMessage("ReSharper", "UnusedMember.Local")]
[SuppressMessage("ReSharper", "MemberCanBePrivate.Local")]
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Local")]
public static class JunctionPoint
{
/// <summary>
/// The file or directory is not a reparse point.
/// </summary>
private const int ERROR_NOT_A_REPARSE_POINT = 4390;
/// <summary>
/// The reparse point attribute cannot be set because it conflicts with an existing attribute.
/// </summary>
private const int ERROR_REPARSE_ATTRIBUTE_CONFLICT = 4391;
/// <summary>
/// The data present in the reparse point buffer is invalid.
/// </summary>
private const int ERROR_INVALID_REPARSE_DATA = 4392;
/// <summary>
/// The tag present in the reparse point buffer is invalid.
/// </summary>
private const int ERROR_REPARSE_TAG_INVALID = 4393;
/// <summary>
/// There is a mismatch between the tag specified in the request and the tag present in the reparse point.
/// </summary>
private const int ERROR_REPARSE_TAG_MISMATCH = 4394;
/// <summary>
/// Command to set the reparse point data block.
/// </summary>
private const int FSCTL_SET_REPARSE_POINT = 0x000900A4;
/// <summary>
/// Command to get the reparse point data block.
/// </summary>
private const int FSCTL_GET_REPARSE_POINT = 0x000900A8;
/// <summary>
/// Command to delete the reparse point data base.
/// </summary>
private const int FSCTL_DELETE_REPARSE_POINT = 0x000900AC;
/// <summary>
/// Reparse point tag used to identify mount points and junction points.
/// </summary>
private const uint IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003;
/// <summary>
/// This prefix indicates to NTFS that the path is to be treated as a non-interpreted
/// path in the virtual file system.
/// </summary>
private const string NonInterpretedPathPrefix = #"\??\";
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool DeviceIoControl(IntPtr hDevice, uint dwIoControlCode,
IntPtr InBuffer, int nInBufferSize,
IntPtr OutBuffer, int nOutBufferSize,
out int pBytesReturned, IntPtr lpOverlapped);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr CreateFile(
string lpFileName,
EFileAccess dwDesiredAccess,
EFileShare dwShareMode,
IntPtr lpSecurityAttributes,
ECreationDisposition dwCreationDisposition,
EFileAttributes dwFlagsAndAttributes,
IntPtr hTemplateFile);
/// <summary>
/// Creates a junction point from the specified directory to the specified target directory.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
/// <param name="targetDir">The target directory</param>
/// <param name="overwrite">If true overwrites an existing reparse point or empty directory</param>
/// <exception cref="IOException">
/// Thrown when the junction point could not be created or when
/// an existing directory was found and <paramref name="overwrite" /> if false
/// </exception>
public static void Create(string junctionPoint, string targetDir, bool overwrite)
{
targetDir = Path.GetFullPath(targetDir);
if (!Directory.Exists(targetDir))
throw new IOException("Target path does not exist or is not a directory.");
if (Directory.Exists(junctionPoint))
{
if (!overwrite)
throw new IOException("Directory already exists and overwrite parameter is false.");
}
else
{
Directory.CreateDirectory(junctionPoint);
}
using (var handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
{
var targetDirBytes = Encoding.Unicode.GetBytes(NonInterpretedPathPrefix + Path.GetFullPath(targetDir));
var reparseDataBuffer =
new REPARSE_DATA_BUFFER
{
ReparseTag = IO_REPARSE_TAG_MOUNT_POINT,
ReparseDataLength = (ushort)(targetDirBytes.Length + 12),
SubstituteNameOffset = 0,
SubstituteNameLength = (ushort)targetDirBytes.Length,
PrintNameOffset = (ushort)(targetDirBytes.Length + 2),
PrintNameLength = 0,
PathBuffer = new byte[0x3ff0]
};
Array.Copy(targetDirBytes, reparseDataBuffer.PathBuffer, targetDirBytes.Length);
var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
var inBuffer = Marshal.AllocHGlobal(inBufferSize);
try
{
Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);
int bytesReturned;
var result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_SET_REPARSE_POINT,
inBuffer, targetDirBytes.Length + 20, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result)
ThrowLastWin32Error("Unable to create junction point.");
}
finally
{
Marshal.FreeHGlobal(inBuffer);
}
}
}
/// <summary>
/// Deletes a junction point at the specified source directory along with the directory itself.
/// Does nothing if the junction point does not exist.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
public static void Delete(string junctionPoint)
{
if (!Directory.Exists(junctionPoint))
{
if (File.Exists(junctionPoint))
throw new IOException("Path is not a junction point.");
return;
}
using (var handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericWrite))
{
var reparseDataBuffer = new REPARSE_DATA_BUFFER
{
ReparseTag = IO_REPARSE_TAG_MOUNT_POINT,
ReparseDataLength = 0,
PathBuffer = new byte[0x3ff0]
};
var inBufferSize = Marshal.SizeOf(reparseDataBuffer);
var inBuffer = Marshal.AllocHGlobal(inBufferSize);
try
{
Marshal.StructureToPtr(reparseDataBuffer, inBuffer, false);
int bytesReturned;
var result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_DELETE_REPARSE_POINT,
inBuffer, 8, IntPtr.Zero, 0, out bytesReturned, IntPtr.Zero);
if (!result)
ThrowLastWin32Error("Unable to delete junction point.");
}
finally
{
Marshal.FreeHGlobal(inBuffer);
}
try
{
Directory.Delete(junctionPoint);
}
catch (IOException ex)
{
throw new IOException("Unable to delete junction point.", ex);
}
}
}
/// <summary>
/// Determines whether the specified path exists and refers to a junction point.
/// </summary>
/// <param name="path">The junction point path</param>
/// <returns>True if the specified path represents a junction point</returns>
/// <exception cref="IOException">
/// Thrown if the specified path is invalid
/// or some other error occurs
/// </exception>
public static bool Exists(string path)
{
if (!Directory.Exists(path))
return false;
using (var handle = OpenReparsePoint(path, EFileAccess.GenericRead))
{
var target = InternalGetTarget(handle);
return target != null;
}
}
/// <summary>
/// Gets the target of the specified junction point.
/// </summary>
/// <remarks>
/// Only works on NTFS.
/// </remarks>
/// <param name="junctionPoint">The junction point path</param>
/// <returns>The target of the junction point</returns>
/// <exception cref="IOException">
/// Thrown when the specified path does not
/// exist, is invalid, is not a junction point, or some other error occurs
/// </exception>
public static string GetTarget(string junctionPoint)
{
using (var handle = OpenReparsePoint(junctionPoint, EFileAccess.GenericRead))
{
var target = InternalGetTarget(handle);
if (target == null)
throw new IOException("Path is not a junction point.");
return target;
}
}
private static string InternalGetTarget(SafeFileHandle handle)
{
var outBufferSize = Marshal.SizeOf(typeof(REPARSE_DATA_BUFFER));
var outBuffer = Marshal.AllocHGlobal(outBufferSize);
try
{
int bytesReturned;
var result = DeviceIoControl(handle.DangerousGetHandle(), FSCTL_GET_REPARSE_POINT,
IntPtr.Zero, 0, outBuffer, outBufferSize, out bytesReturned, IntPtr.Zero);
if (!result)
{
var error = Marshal.GetLastWin32Error();
if (error == ERROR_NOT_A_REPARSE_POINT)
return null;
ThrowLastWin32Error("Unable to get information about junction point.");
}
var reparseDataBuffer = (REPARSE_DATA_BUFFER)
Marshal.PtrToStructure(outBuffer, typeof(REPARSE_DATA_BUFFER));
if (reparseDataBuffer.ReparseTag != IO_REPARSE_TAG_MOUNT_POINT)
return null;
var targetDir = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer,
reparseDataBuffer.SubstituteNameOffset, reparseDataBuffer.SubstituteNameLength);
if (targetDir.StartsWith(NonInterpretedPathPrefix))
targetDir = targetDir.Substring(NonInterpretedPathPrefix.Length);
return targetDir;
}
finally
{
Marshal.FreeHGlobal(outBuffer);
}
}
private static SafeFileHandle OpenReparsePoint(string reparsePoint, EFileAccess accessMode)
{
var reparsePointHandle = new SafeFileHandle(CreateFile(reparsePoint, accessMode,
EFileShare.Read | EFileShare.Write | EFileShare.Delete,
IntPtr.Zero, ECreationDisposition.OpenExisting,
EFileAttributes.BackupSemantics | EFileAttributes.OpenReparsePoint, IntPtr.Zero), true);
if (Marshal.GetLastWin32Error() != 0)
ThrowLastWin32Error("Unable to open reparse point.");
return reparsePointHandle;
}
private static void ThrowLastWin32Error(string message)
{
throw new IOException(message, Marshal.GetExceptionForHR(Marshal.GetHRForLastWin32Error()));
}
[Flags]
private enum EFileAccess : uint
{
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000
}
[Flags]
private enum EFileShare : uint
{
None = 0x00000000,
Read = 0x00000001,
Write = 0x00000002,
Delete = 0x00000004
}
private enum ECreationDisposition : uint
{
New = 1,
CreateAlways = 2,
OpenExisting = 3,
OpenAlways = 4,
TruncateExisting = 5
}
[Flags]
private enum EFileAttributes : uint
{
Readonly = 0x00000001,
Hidden = 0x00000002,
System = 0x00000004,
Directory = 0x00000010,
Archive = 0x00000020,
Device = 0x00000040,
Normal = 0x00000080,
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
Encrypted = 0x00004000,
Write_Through = 0x80000000,
Overlapped = 0x40000000,
NoBuffering = 0x20000000,
RandomAccess = 0x10000000,
SequentialScan = 0x08000000,
DeleteOnClose = 0x04000000,
BackupSemantics = 0x02000000,
PosixSemantics = 0x01000000,
OpenReparsePoint = 0x00200000,
OpenNoRecall = 0x00100000,
FirstPipeInstance = 0x00080000
}
[StructLayout(LayoutKind.Sequential)]
private struct REPARSE_DATA_BUFFER
{
/// <summary>
/// Reparse point tag. Must be a Microsoft reparse point tag.
/// </summary>
public uint ReparseTag;
/// <summary>
/// Size, in bytes, of the data after the Reserved member. This can be calculated by:
/// (4 * sizeof(ushort)) + SubstituteNameLength + PrintNameLength +
/// (namesAreNullTerminated ? 2 * sizeof(char) : 0);
/// </summary>
public ushort ReparseDataLength;
/// <summary>
/// Reserved; do not use.
/// </summary>
public ushort Reserved;
/// <summary>
/// Offset, in bytes, of the substitute name string in the PathBuffer array.
/// </summary>
public ushort SubstituteNameOffset;
/// <summary>
/// Length, in bytes, of the substitute name string. If this string is null-terminated,
/// SubstituteNameLength does not include space for the null character.
/// </summary>
public ushort SubstituteNameLength;
/// <summary>
/// Offset, in bytes, of the print name string in the PathBuffer array.
/// </summary>
public ushort PrintNameOffset;
/// <summary>
/// Length, in bytes, of the print name string. If this string is null-terminated,
/// PrintNameLength does not include space for the null character.
/// </summary>
public ushort PrintNameLength;
/// <summary>
/// A buffer containing the unicode-encoded path string. The path string contains
/// the substitute name string and print name string.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x3FF0)]
public byte[] PathBuffer;
}
}
}
Usage:
using CreateMaps;
...
JunctionPoint.Create(#"C:\Temp\HardlinkFolderToCreate", #"C:\Temp\ExistingFolder", false);
Process.Start("mklink /H", String.Format("{0} {1}", link, target));

Categories