Related
Question:
In C/C++/C#. (I need it for C#, but C and C++ is also fine).
How can I do a mount -a on Linux.
I mean programmatically, without starting a process like
system("mount -a");
Edit:
Note the "-a".
My question is not actually about how to mount A mountpoint.
It's about how to mount ALL mountpoints in /etc/fstab.
That means parsing the file, extracting the mountpoints, check if already mounted, and only if not already mounted, mount...
Check out the man page by typing man 2 mount. It talks about a system call that can avoid the actual use of system():
#include <sys/mount.h>
int mount(const char *source, const char *target, const char *filesystemtype,
unsigned long mountflags, const void *data);
#Ignacio Vazquez-Abrams:
About your "no way to perform this in C#" ...
Proof that you're wrong by contradiction:
The bellow code is capable of doing the same as
(apt-get install jfsutils)
dd if=/dev/zero of=jfs.dsk bs=1048576 count=150
mkfs.jfs -O jfs.dsk
mkdir -p /mnt/jfs
mount /volumes/jfs.dsk /mnt/jfs -t jfs -o loop
umount /mnt/jfs/
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Syscalls
{
public class Linux
{
// apt-get source util-linux
// ./mount/loop.h
// ./mount/mount.c
// ./mount/lomount.c
// ./include/linux_version.h
// ./lib/linux_version.c
// ./include/linux_reboot.h
protected const int LOOP_SET_FD = 0x4C00;
protected const int LOOP_CLR_FD = 0x4C01;
protected const int LOOP_GET_STATUS = 0x4C03;
protected const int LOOP_SET_STATUS = 0x4C02;
protected const int LOOP_GET_STATUS64 = 0x4C05;
protected const int LOOP_SET_STATUS64 = 0x4C04;
protected const int LO_NAME_SIZE = 64;
protected const int LO_KEY_SIZE = 32;
protected const int PATH_MAX = 4096;
// MS_RELATIME //(default for Linux >= 2.6.30)
// MS_STRICTATIME //(default for Linux < 2.6.30)
// http://harmattan-dev.nokia.com/docs/library/html/manpages/headers/sys/mount.html
public enum MountFlags : ulong
{
MS_RDONLY = 1, // Mount read-only.
MS_NOSUID = 2, // Ignore suid and sgid bits.
MS_NODEV = 4, // Disallow access to device special files.
MS_NOEXEC = 8, // Disallow program execution.
MS_SYNCHRONOUS = 16, // Writes are synced at once.
MS_REMOUNT = 32, // Alter flags of a mounted FS.
MS_MANDLOCK = 64, // Allow mandatory locks on an FS.
S_WRITE = 128, // Write on file/directory/symlink.
S_APPEND = 256, // Append-only file.
S_IMMUTABLE = 512, // Immutable file.
MS_NOATIME = 1024, // Do not update access times.
MS_NODIRATIME = 2048, // Do not update directory access times.
MS_BIND = 4096, // Bind directory at different place.
}; // End Enum MountFlags : ulong
/*
// http://unix.superglobalmegacorp.com/Net2/newsrc/sys/fcntl.h.html
[Flags]
protected enum OpenFlags : int
{
// open-only flags
O_RDONLY = 0x0000, // open for reading only
O_WRONLY = 0x0001, // open for writing only
O_RDWR = 0x0002, // open for reading and writing
O_ACCMODE = 0x0003, // mask for above modes
//#ifdef KERNEL
FREAD = 0x0001,
FWRITE = 0x0002,
//#endif
O_NONBLOCK = 0x0004, // no delay
O_APPEND = 0x0008, // set append mode
//#ifndef _POSIX_SOURCE
O_SHLOCK = 0x0010, // open with shared file lock
O_EXLOCK = 0x0020, // open with exclusive file lock
O_ASYNC = 0x0040, // signal pgrp when data ready
O_FSYNC = 0x0080, // synchronous writes
//#endif
O_CREAT = 0x0200, // create if nonexistant
O_TRUNC = 0x0400, // truncate to zero length
O_EXCL = 0x0800, // error if already exists
//#ifdef KERNEL
FMARK = 0x1000, // mark during gc()
FDEFER = 0x2000, // defer for next gc pass
FHASLOCK = 0x4000 // descriptor holds advisory lock
} // End Enum OpenFlags : int
*/
[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
protected struct loop_info
{
public int lo_number;
public System.UIntPtr lo_device; //my_dev_t lo_device; // my_dev_t: long unsigned int
public System.UIntPtr lo_inode; //unsigned long lo_inode;
public System.UIntPtr lo_rdevice; //my_dev_t lo_rdevice;// my_dev_t: long unsigned int
public int lo_offset;
public int lo_encrypt_type;
public int lo_encrypt_key_size;
public int lo_flags;
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_NAME_SIZE)]
public string lo_name; //char lo_name[LO_NAME_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_KEY_SIZE)]
public string lo_encrypt_key; //unsigned char lo_encrypt_key[LO_KEY_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 2)]
public System.UIntPtr[] lo_init; //unsigned long lo_init[2];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 4)]
public string reserved; //char reserved[4];
}; // End Struct loop_info
protected struct loop_info64
{
public System.UInt64 lo_device;
public System.UInt64 lo_inode;
public System.UInt64 lo_rdevice;
public System.UInt64 lo_offset;
public System.UInt64 lo_sizelimit; /* bytes, 0 == max available */
public System.UInt32 lo_number;
public System.UInt32 lo_encrypt_type;
public System.UInt32 lo_encrypt_key_size;
public System.UInt32 lo_flags;
// http://stackoverflow.com/questions/1725855/uint8-t-vs-unsigned-char
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_NAME_SIZE)]
public string lo_file_name; // uint8_t lo_file_name[LO_NAME_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_NAME_SIZE)]
public string lo_crypt_name; // uint8_t lo_crypt_name[LO_NAME_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = LO_KEY_SIZE)]
public string lo_encrypt_key; // uint8_t lo_encrypt_key[LO_KEY_SIZE];
[System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 2)]
public System.UInt64[] lo_init;
}; // End Struct loop_info64
// http://www.student.cs.uwaterloo.ca/~cs350/common/os161-src-html/kern_2include_2kern_2stat_8h.html
protected static bool S_ISBLK(int mode)
{
const uint S_IFMT = 070000;
const uint S_IFBLK = 050000;
return (((mode) & S_IFMT) == S_IFBLK);
} // End Function S_ISBLK
public static int KERNEL_VERSION()
{
Mono.Unix.Native.Utsname unameres = new Mono.Unix.Native.Utsname();
Mono.Unix.Native.Syscall.uname(out unameres);
System.Text.RegularExpressions.Match ma = System.Text.RegularExpressions.Regex.Match(unameres.release, #"(\d+).(\d+).(\d+)(-)?(\d+)?");
string strMajor = ma.Groups[1].Value;
string strMinor = ma.Groups[2].Value;
string strTiny = ma.Groups[3].Value;
string strPatchlevel = ma.Groups[5].Value;
int a = System.Convert.ToInt32(strMajor);
int b = System.Convert.ToInt32(strMinor);
int c = System.Convert.ToInt32(strTiny);
return KERNEL_VERSION(a, b, c);
} // End Function KERNEL_VERSION
//# define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
public static int KERNEL_VERSION(int a, int b, int c)
{
return (((a) << 16) + ((b) << 8) + (c));
}
public static string CreateVirtualDisk(int iSize)
{
string strBaseDirectory = #"/volumes/";
string strFileName = System.Guid.NewGuid().ToString().Replace("-", "") + ".dsk";
string strFileNameAndPath = System.IO.Path.Combine(strBaseDirectory, strFileName);
using (var fs = new System.IO.FileStream(strFileNameAndPath, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
{
fs.SetLength(iSize);
} // End Using fs
return strFileNameAndPath;
} // End Function CreateVirtualDisk
// umount("/mnt/testdisk");
public static bool umount(string strMountPoint)
{
int status = UnsafeNativeMethods.umount(strMountPoint);
if (status == 0)
Console.WriteLine("Successfully unmounted device.");
else
Console.WriteLine("Unmount status: " + status.ToString());
if (status == 0)
return true;
return false;
} // End Function Unmount
public static string find_unused_loop_device()
{
string dev;
int fd;
Mono.Unix.Native.Stat statbuf;
loop_info loopinfo = new loop_info();
loop_info64 lo64 = new loop_info64();
for (int i = 0; i <= 7; i++)
{
dev = "/dev/loop" + i.ToString();
if (System.Convert.ToBoolean(Mono.Unix.Native.Syscall.stat(dev, out statbuf)) == (false && S_ISBLK((int)statbuf.st_mode)))
{
if ((fd = Mono.Unix.Native.Syscall.open(dev, Mono.Unix.Native.OpenFlags.O_RDONLY)) >= 0)
{
// This block was commented out initially
if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS64, ref lo64) == 0)
{
if (Mono.Unix.Native.Syscall.GetLastError() == Mono.Unix.Native.Errno.ENXIO)
{ // probably free
Mono.Unix.Native.Syscall.close(fd);
return dev;
}
}
if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS, ref loopinfo) != 0)
{
// http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/include/asm-generic/errno-base.h#L9
// ENXIO - No such device or address
// The device accessed by a command is physically not present,
// or the address of the device is not present
if (Mono.Unix.Native.Syscall.GetLastError() == Mono.Unix.Native.Errno.ENXIO)
{
// that means the device is most-likely free
Mono.Unix.Native.Syscall.close(fd);
return dev;
}
} // End if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS, ref loopinfo) != 0)
Mono.Unix.Native.Syscall.close(fd);
} // End if ((fd = UnsafeNativeMethods.open(dev, OpenFlags.O_RDONLY)) >= 0)
} // End if (System.Convert.ToBoolean(Mono.Unix.Native.Syscall.stat(dev, out statbuf)) == (false && S_ISBLK((int)statbuf.st_mode)))
} // Next i
return null;
} // End Function find_unused_loop_device
public static int set_loop(string device, string file, int offset, ref int loopro)
{
loop_info loopinfo = new loop_info();
int fd = 0, ffd = 0;
Mono.Unix.Native.OpenFlags mode;
mode = loopro != 0 ? Mono.Unix.Native.OpenFlags.O_RDONLY : Mono.Unix.Native.OpenFlags.O_RDWR;
if (
(
ffd = Mono.Unix.Native.Syscall.open(file, mode)
) < 0
&&
(
(!System.Convert.ToBoolean((int)loopro))
&&
(
Mono.Unix.Native.Syscall.GetLastError() != Mono.Unix.Native.Errno.EROFS
||
(ffd = Mono.Unix.Native.Syscall.open(file, mode = Mono.Unix.Native.OpenFlags.O_RDONLY))
< 0
)
)
) // if
{
Console.WriteLine("Error: file: " + file);
//perror_msg("%s", file);
return 1;
} // End if
if ((fd = Mono.Unix.Native.Syscall.open(device, mode)) < 0)
{
Mono.Unix.Native.Syscall.close(ffd);
Console.WriteLine("Error: device: " + device);
//perror_msg("%s", device);
return 1;
}
loopro = System.Convert.ToInt32(mode == Mono.Unix.Native.OpenFlags.O_RDONLY);
//memset(&loopinfo, 0, sizeof(loopinfo));
//safe_strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
//strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
loopinfo.lo_name = string.IsNullOrEmpty(file) ? null : file.Substring(0, Math.Min(file.Length, LO_NAME_SIZE));
loopinfo.lo_offset = offset;
loopinfo.lo_encrypt_key_size = 0;
if (UnsafeNativeMethods.ioctl(fd, LOOP_SET_FD, ffd) < 0)
{
Console.WriteLine("ioctl: LOOP_SET_FD");
//perror_msg("ioctl: LOOP_SET_FD");
Mono.Unix.Native.Syscall.close(fd);
Mono.Unix.Native.Syscall.close(ffd);
return 1;
}
if (UnsafeNativeMethods.ioctl(fd, LOOP_SET_STATUS, ref loopinfo) < 0)
{
int ro = 0;
UnsafeNativeMethods.ioctl(fd, LOOP_CLR_FD, ref ro);
//perror_msg("ioctl: LOOP_SET_STATUS");
Console.WriteLine("ioctl: LOOP_SET_STATUS");
Mono.Unix.Native.Syscall.close(fd);
Mono.Unix.Native.Syscall.close(ffd);
return 1;
}
Mono.Unix.Native.Syscall.close(fd);
Mono.Unix.Native.Syscall.close(ffd);
return 0;
} // End Function set_loop
public static int del_loop(string device)
{
int fd;
if ((fd = Mono.Unix.Native.Syscall.open(device, Mono.Unix.Native.OpenFlags.O_RDONLY)) < 0)
{
//perror_msg("%s", device);
Console.WriteLine("Error description: " + Mono.Unix.Native.Syscall.strerror(Mono.Unix.Native.Syscall.GetLastError()));
return 0;
}
int r = 0;
if (UnsafeNativeMethods.ioctl(fd, LOOP_CLR_FD, ref r) < 0)
{
//perror_msg("ioctl: LOOP_CLR_FD");
Console.WriteLine("ioctl: LOOP_CLR_FD\nError description: " + Mono.Unix.Native.Syscall.strerror(Mono.Unix.Native.Syscall.GetLastError()));
return 0;
}
Mono.Unix.Native.Syscall.close(fd);
Console.WriteLine("Successfully closed loop-device\n");
return 1;
} // End Function del_loop
public static bool mount(string strDevice, string strMountPoint, string strFsType)
{
return mount(strDevice, strMountPoint, strFsType, MountFlags.MS_NOATIME);
}
public static bool mount(string strDevice, string strMountPoint, string strFsType, MountFlags mflags)
{
return mount(strDevice, strMountPoint, strFsType, mflags, IntPtr.Zero);
}
// http://cboard.cprogramming.com/c-programming/126630-using-sys-mount-h-mounting-usb-thumb-drive.html
// http://stackoverflow.com/questions/10458549/mount-usb-drive-in-linux-with-c
// mount("/dev/loop1", "/mnt/testdisk", "vfat");
public static bool mount(string strDevice, string strMountPoint, string strFsType, MountFlags mflags, IntPtr options)
{
// http://linux.die.net/man/2/mount
// MS_RDONLY
// MS_RELATIME (default for Linux >= 2.6.30)
// MS_STRICTATIME (default for Linux < 2.6.30)
if (UnsafeNativeMethods.mount(strDevice, strMountPoint, strFsType, mflags, options) != 0)
{
Mono.Unix.Native.Errno errno = Mono.Unix.Native.Syscall.GetLastError();
if (errno == Mono.Unix.Native.Errno.EBUSY)
{
Console.WriteLine("Mountpoint busy");
}
else
{
Console.WriteLine("Mount error: " + Mono.Unix.Native.Syscall.strerror(errno));
}
return false;
}
else
{
Console.WriteLine("Successfully mounted device !");
}
return true; ;
} // End Function mount
static class UnsafeNativeMethods
{
//string name = "Test";
//TypedReference tf = __makeref(name);
//int c = VarSum(2, __arglist(__makeref(name)));
// http://khason.net/blog/how-to-pinvoke-varargs-variable-arguments-in-c-or-hidden-junk-in-clr/
// //int rv = ioctl(2, 3, __arglist(5, 10));
[System.Runtime.InteropServices.DllImportAttribute("libc", EntryPoint = "ioctl",
CallingConvention = System.Runtime.InteropServices.CallingConvention.Cdecl)]
public static extern int ioctl(int descriptor, int request, __arglist);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, int data);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, ref int data);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, ref loop_info data);
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int ioctl(int d, int request, ref loop_info64 data);
//[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
//public static extern int open([System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPStr)]string pathname, OpenFlags flags);
//[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
//public static extern int close(int fd);
//[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
//public static extern IntPtr read(int fd, IntPtr buffer, UIntPtr count);
///////unsafe public static extern IntPtr read(int fd, void* buffer, UIntPtr count);
// http://linux.die.net/man/2/mount
// http://www.kernel.org/doc/man-pages/online/pages/man2/mount.2.html
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
private static extern int mount(string source, string target, string filesystemtype, UIntPtr mountflags, System.IntPtr data);
//int mount(const char *source, const char *target, const char *filesystemtype, ulong mountflags, const void *data);
public static int mount(string source, string target, string filesystemtype, MountFlags mountflags, System.IntPtr data)
{
System.UIntPtr p = new System.UIntPtr((ulong)mountflags);
return mount(source, target, filesystemtype, p, data);
} // End Function mount
[System.Runtime.InteropServices.DllImport("libc", SetLastError = true)]
public static extern int umount(string strMountPoint);
// extern int umount (__const char *__special_file);
} // End Class UnsafeNativeMethods
public static void TryMount()
{
const bool SUCCESS = true;
// int iReturnCode = Mono.Unix.Native.Syscall.system("mount -a");
// int iReturnCode = Mono.Unix.Native.Syscall.system("mount /volumes/jfs.dsk /mnt/jfs -t jfs -o loop");
// int iReturnCode = Mono.Unix.Native.Syscall.system("mkfs.jfs -O \"jfs.dsk\"");
string strLoopDeviceToUse = find_unused_loop_device();
string strMountPoint = "/mnt/testdisk";
int ro = 0;
set_loop(strLoopDeviceToUse, "/volumes/testdisk.dsk", 0, ref ro);
string strLoopDeviceToUse2 = find_unused_loop_device();
bool status = false;
int mountAttempts = 0;
do
{
//status = mount("/dev/sda1", "/media/usb0", "vfat", MS_MGC_VAL | MS_NOSUID, "");
status = mount(strLoopDeviceToUse, strMountPoint, "vfat", MountFlags.MS_NOATIME);
if (status != SUCCESS)
System.Threading.Thread.Sleep(1000);
mountAttempts++;
} while (status != SUCCESS && mountAttempts < 3);
} // End Sub TryMount
// In gcc or g++, to show all of the macros that are defined for a given platform:
// gcc -dM -E test.c
// or
// g++ -dM -E test.cpp
// http://manual.cream.org/index.cgi/gnu_dev_major.3
// http://www.gnu.org/software/gnulib/coverage/usr/include/sys/sysmacros.h.gcov.frameset.html
// http://en.wikipedia.org/wiki/C_data_types
protected static uint gnu_dev_major(System.UInt64 __dev)
{
return (uint)((uint)(((__dev >> 8) & 0xfff)) | ((uint)(__dev >> 32) & ~0xfff));
}
protected static uint gnu_dev_minor(System.UInt64 __dev)
{
return (uint)((uint)(__dev & 0xff) | ((uint)(__dev >> 12) & ~0xff));
}
public static string loopfile_from_sysfs(string device)
{
string res = null;
Mono.Unix.Native.Stat st;
System.IntPtr f;
//if (stat(device, &st) || !S_ISBLK(st.st_mode))
//if (System.Convert.ToBoolean(Mono.Unix.Native.Syscall.stat(device, out st)) || !S_ISBLK((int) st.st_mode))
// return null;
Mono.Unix.Native.Syscall.stat(device, out st);
const string _PATH_SYS_DEVBLOCK = "/sys/dev/block";
string strPath = string.Format("{0}/{1}:{2}/loop/backing_file", _PATH_SYS_DEVBLOCK, gnu_dev_major(st.st_rdev), gnu_dev_minor(st.st_rdev));
f = Mono.Unix.Native.Syscall.fopen(strPath, "r");
if (f == IntPtr.Zero)
return null;
Mono.Unix.Native.Syscall.fclose(f);
res = System.IO.File.ReadAllText(strPath);
strPath = null;
return res;
} // End Function loopfile_from_sysfs
public static string loopdev_get_loopfile(string device)
{
string res = loopfile_from_sysfs(device);
if (res == null)
{
loop_info lo = new loop_info();
loop_info64 lo64 = new loop_info64();
int fd;
if ((fd = Mono.Unix.Native.Syscall.open(device, Mono.Unix.Native.OpenFlags.O_RDONLY)) < 0)
return null;
if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS64, ref lo64) == 0)
{
//lo64.lo_file_name[LO_NAME_SIZE-2] = '*';
//lo64.lo_file_name[LO_NAME_SIZE-1] = 0;
//res = strdup((char *) lo64.lo_file_name);
res = lo64.lo_file_name;
Console.WriteLine("LOOP_GET_STATUS64");
}
else if (UnsafeNativeMethods.ioctl(fd, LOOP_GET_STATUS, ref lo) == 0)
{
//lo.lo_name[LO_NAME_SIZE-2] = '*';
//lo.lo_name[LO_NAME_SIZE-1] = 0;
//res = strdup((char *) lo.lo_name);
res = lo.lo_name;
Console.WriteLine("LOOP_GET_STATUS");
}
Mono.Unix.Native.Syscall.close(fd);
} // End if (res == null)
return res;
} // End Function loopdev_get_loopfile
public static void TryUnmount()
{
/*
string strMountPoint = "/mnt/testdisk";
umount(strMountPoint);
System.Threading.Thread.Sleep(1000);
del_loop("/dev/loop2");
*/
string xxx = loopdev_get_loopfile("/dev/loop0");
Console.WriteLine("xxx: " + xxx);
}
// kernel-support:
// grep hfs /proc/filesystems
// cat /proc/partitions
// apt-get install hfsprogs
// sudo modprobe hfsplus
// dd if=/dev/zero of=hfsplus.dsk bs=1048576 count=150
// mkfs.hfsplus /volumes/hfsplus.dsk
// mkfs.hfsplus hfsplus.dsk
// apt-get install jfsutils
// dd if=/dev/zero of=jfs.dsk bs=1048576 count=150
// mkfs.jfs -O jfs.dsk
// mkdir -p /mnt/jfs
// mount /volumes/jfs.dsk /mnt/jfs -t jfs -o loop
// umount /mnt/jfs/
// mkdir -p /mnt/hfsplus
// mount -t hfsplus /volumes/hfsplus.dsk /mnt/hfsplus/ -o loop
//
} // End Class Linux
} // End Namespace Syscalls
// http://ubuntuforums.org/showthread.php?t=135113
// http://stackoverflow.com/questions/7027151/call-expect-script-in-c-process
// http://linux.die.net/man/1/expect
// http://linux.die.net/man/3/libexpect
// http://linuxcommand.org/man_pages/losetup8.html
// losetup /dev/loop0 /file
// losetup -d /dev/loop0
// http://linux.about.com/library/cmd/blcmdl8_losetup.htm
To perma-mount it in fstab, you need to get the partition uuid (blkid)
getmntent can help you read /etc/fstab (and then use the mount function in the other answers).
I want to enumerate all dhcp reservation entries of our microsoft dhcp server with the microsoft dhcp server management api in c#.
This is my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Net;
using System.Windows.Forms;
using System.Collections;
namespace Sequence
{
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_CLIENT_INFO_ARRAY
{
public uint NumElements;
public IntPtr Clients;
}
[StructLayout(LayoutKind.Sequential)]
public struct DHCP_CLIENT_UID
{
public uint DataLength;
public IntPtr Data;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct DHCP_CLIENT_INFO
{
public uint ip;
public uint subnet;
public DHCP_CLIENT_UID mac;
[MarshalAs(UnmanagedType.LPWStr)]
public string ClientName;
[MarshalAs(UnmanagedType.LPWStr)]
public string ClientComment;
}
class CDHCP
{
public static ArrayList findDhcpClients(string server, string subnet)
{
ArrayList foundClients = new ArrayList();
uint parsedMask = StringIPAddressToUInt32(subnet);
uint resumeHandle = 0;
uint numClientsRead = 0;
uint totalClients = 0;
IntPtr info_array_ptr;
uint response = DhcpEnumSubnetClients(
server,
parsedMask,
ref resumeHandle,
65536,
out info_array_ptr,
ref numClientsRead,
ref totalClients
);
DHCP_CLIENT_INFO_ARRAY rawClients = (DHCP_CLIENT_INFO_ARRAY)Marshal.PtrToStructure(info_array_ptr, typeof(DHCP_CLIENT_INFO_ARRAY));
IntPtr current = rawClients.Clients;
for (int i = 0; i < (int)rawClients.NumElements; i++)
{
DHCP_CLIENT_INFO rawMachine = (DHCP_CLIENT_INFO)Marshal.PtrToStructure(Marshal.ReadIntPtr(current), typeof(DHCP_CLIENT_INFO));
CDHCPCLIENT thisClient = new CDHCPCLIENT();
thisClient.ip = UInt32IPAddressToString(rawMachine.ip);
thisClient.hostname = rawMachine.ClientName;
thisClient.mac = string.Format("{0:x2}-{1:x2}-{2:x2}-{3:x2}-{4:x2}-{5:x2}",
Marshal.ReadByte(rawMachine.mac.Data, 0),
Marshal.ReadByte(rawMachine.mac.Data, 1),
Marshal.ReadByte(rawMachine.mac.Data, 2),
Marshal.ReadByte(rawMachine.mac.Data, 3),
Marshal.ReadByte(rawMachine.mac.Data, 4),
Marshal.ReadByte(rawMachine.mac.Data, 5));
foundClients.Add(thisClient);
current = (IntPtr)((int)current + (int)Marshal.SizeOf(typeof(IntPtr)));
}
return foundClients;
}
public static uint StringIPAddressToUInt32(string ip)
{
IPAddress i = IPAddress.Parse(ip);
byte[] ipByteArray = i.GetAddressBytes();
uint ipUint = (uint)ipByteArray[0] << 24;
ipUint += (uint)ipByteArray[1] << 16;
ipUint += (uint)ipByteArray[2] << 8;
ipUint += (uint)ipByteArray[3];
return ipUint;
}
public static string UInt32IPAddressToString(uint ip)
{
IPAddress i = new IPAddress(ip);
string[] ipArray = i.ToString().Split('.');
return ipArray[3] + "." + ipArray[2] + "." + ipArray[1] + "." + ipArray[0];
}
[DllImport("C:\\Windows\\System32\\dhcpsapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern uint DhcpEnumSubnetClients(
string ServerIpAddress,
uint SubnetAddress,
ref uint ResumeHanle,
uint PreferredMaximum,
out IntPtr ClientInfo,
ref uint ElementsRead,
ref uint ElementsTotal);
}
}
Unfortunately I'm getting a NullReferenceException at this line:
DHCP_CLIENT_INFO_ARRAY rawClients = (DHCP_CLIENT_INFO_ARRAY)Marshal.PtrToStructure(info_array_ptr, typeof(DHCP_CLIENT_INFO_ARRAY));
I' calling the function like this:
public void GetDHCPReservation()
{
ArrayList clients = CDHCP.findDhcpClients("192.168.1.3", "192.168.1.5");
foreach (CDHCPCLIENT c in clients)
{
MessageBox.Show(string.Format("(0,-35}{1,-15}{2,-15}", c.hostname, c.ip, c.mac));
}
}
Can anybody help me?
Thanks in advance.
I think I've just solved my problem.
The reason why I was getting the NullReferenceException was because the IntPtr info_array_ptr always pointed to 0 (zero). That was because of not enough user rights on the dhcp server. After I added the user with which I'm running my application to the domain admins everything worked well.
I have this piece of code that has not been modified but all of a sudden it has stopped working... I could swear that it used to work but can't guarantee it. It throws an exception:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
static void Main(string[] args)
{
ErrorMsg(123);
}
[DllImport("kernel32.dll", EntryPoint = "FormatMessageW", CharSet = CharSet.Auto)]
static extern int FormatMessage(int dwFlags, IntPtr lpSource, long dwMessageId, int dwLanguageId, out IntPtr MsgBuffer, int nSize, IntPtr Arguments);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern int GetThreadLocale();
/// <summary>
/// Gets a Locale specific windows error
/// code specified.
/// </summary>
/// <param name="errorcode">The errorcode.</param>
public static string ErrorMsg(long errorcode)
{
try
{
if (errorcode == 0)
return "No Error";
IntPtr pMessageBuffer;
int dwBufferLength;
string sMsg;
int dwFormatFlags;
//FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
dwFormatFlags = 0x00000100 | 0x00000200 | 0x00001000;
dwBufferLength = FormatMessage(dwFormatFlags, IntPtr.Zero, errorcode, GetThreadLocale(), out pMessageBuffer, 0, IntPtr.Zero);
if (dwBufferLength == 0)
return "An Unknown Error Has occured.";
sMsg = Marshal.PtrToStringUni(pMessageBuffer);
Marshal.FreeHGlobal(pMessageBuffer);
return sMsg;
}
catch (Exception ex)
{
return "An Unknown Error Has occured.";
}
}
What am I doing wrong here, I can't seem to find anything? Thanks!
Your code worked fine when I tested it on my machine. By the way is there any reason you wouldn't prefer the following method which is a little shorter and achieves equivalent goal:
static void Main()
{
var ex = new Win32Exception(123);
Console.WriteLine(ex.Message);
}
Of course under the covers Win32Exception PInvokes into FormatMessage but at least it's the .NET framework that should worry about it, not us.
UPDATE:
Here's how the Win32Exception.GetErrorMessage method is implemented in .NET:
private static string GetErrorMessage(int error)
{
string result = "";
StringBuilder stringBuilder = new StringBuilder(256);
int num = SafeNativeMethods.FormatMessage(12800, NativeMethods.NullHandleRef, error, 0, stringBuilder, stringBuilder.Capacity + 1, IntPtr.Zero);
if (num != 0)
{
int i;
for (i = stringBuilder.Length; i > 0; i--)
{
char c = stringBuilder[i - 1];
if (c > ' ' && c != '.')
{
break;
}
}
result = stringBuilder.ToString(0, i);
}
else
{
result = "Unknown error (0x" + Convert.ToString(error, 16) + ")";
}
return result;
}
where FormatMessage is declared like this:
[DllImport("kernel32.dll", BestFitMapping = true, CharSet = CharSet.Auto, SetLastError = true)]
public static extern int FormatMessage(int dwFlags, HandleRef lpSource, int dwMessageId, int dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr arguments);
Try sMsg = Marshal.PtrToStringUni(pMessageBuffer, dwBufferLength);
Is there a way via .NET/C# to find out the number of CPU cores?
PS This is a straight code question, not a "Should I use multi-threading?" question! :-)
There are several different pieces of information relating to processors that you could get:
Number of physical processors
Number of cores
Number of logical processors.
These can all be different; in the case of a machine with 2 dual-core hyper-threading-enabled processors, there are 2 physical processors, 4 cores, and 8 logical processors.
The number of logical processors is available through the Environment class, but the other information is only available through WMI (and you may have to install some hotfixes or service packs to get it on some systems):
Make sure to add a reference in your project to System.Management.dll
In .NET Core, this is available (for Windows only) as a NuGet package.
Physical Processors:
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
Console.WriteLine("Number Of Physical Processors: {0} ", item["NumberOfProcessors"]);
}
Cores:
int coreCount = 0;
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
{
coreCount += int.Parse(item["NumberOfCores"].ToString());
}
Console.WriteLine("Number Of Cores: {0}", coreCount);
Logical Processors:
Console.WriteLine("Number Of Logical Processors: {0}", Environment.ProcessorCount);
OR
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
Console.WriteLine("Number Of Logical Processors: {0}", item["NumberOfLogicalProcessors"]);
}
Processors excluded from Windows:
You can also use Windows API calls in setupapi.dll to discover processors that have been excluded from Windows (e.g. through boot settings) and aren't detectable using the above means. The code below gives the total number of logical processors (I haven't been able to figure out how to differentiate physical from logical processors) that exist, including those that have been excluded from Windows:
static void Main(string[] args)
{
int deviceCount = 0;
IntPtr deviceList = IntPtr.Zero;
// GUID for processor classid
Guid processorGuid = new Guid("{50127dc3-0f36-415e-a6cc-4cb3be910b65}");
try
{
// get a list of all processor devices
deviceList = SetupDiGetClassDevs(ref processorGuid, "ACPI", IntPtr.Zero, (int)DIGCF.PRESENT);
// attempt to process each item in the list
for (int deviceNumber = 0; ; deviceNumber++)
{
SP_DEVINFO_DATA deviceInfo = new SP_DEVINFO_DATA();
deviceInfo.cbSize = Marshal.SizeOf(deviceInfo);
// attempt to read the device info from the list, if this fails, we're at the end of the list
if (!SetupDiEnumDeviceInfo(deviceList, deviceNumber, ref deviceInfo))
{
deviceCount = deviceNumber;
break;
}
}
}
finally
{
if (deviceList != IntPtr.Zero) { SetupDiDestroyDeviceInfoList(deviceList); }
}
Console.WriteLine("Number of cores: {0}", deviceCount);
}
[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
[MarshalAs(UnmanagedType.LPStr)]String enumerator,
IntPtr hwndParent,
Int32 Flags);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
Int32 MemberIndex,
ref SP_DEVINFO_DATA DeviceInterfaceData);
[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
public int cbSize;
public Guid ClassGuid;
public uint DevInst;
public IntPtr Reserved;
}
private enum DIGCF
{
DEFAULT = 0x1,
PRESENT = 0x2,
ALLCLASSES = 0x4,
PROFILE = 0x8,
DEVICEINTERFACE = 0x10,
}
Environment.ProcessorCount
[Documentation]
WMI queries are slow, so try to Select only the desired members instead of using Select *.
The following query takes 3.4s:
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
While this one takes 0.122s:
foreach (var item in new System.Management.ManagementObjectSearcher("Select NumberOfCores from Win32_Processor").Get())
Environment.ProcessorCount should give you the number of cores on the local machine.
The the easyest way = Environment.ProcessorCount
Exemple from Environment.ProcessorCount Property
using System;
class Sample
{
public static void Main()
{
Console.WriteLine("The number of processors " +
"on this computer is {0}.",
Environment.ProcessorCount);
}
}
It's rather interesting to see how .NET get this internally to say the least... It's as "simple" as below:
namespace System.Threading
{
using System;
using System.Runtime.CompilerServices;
internal static class PlatformHelper
{
private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 0x7530;
private static volatile int s_lastProcessorCountRefreshTicks;
private static volatile int s_processorCount;
internal static bool IsSingleProcessor
{
get
{
return (ProcessorCount == 1);
}
}
internal static int ProcessorCount
{
get
{
int tickCount = Environment.TickCount;
int num2 = s_processorCount;
if ((num2 == 0) || ((tickCount - s_lastProcessorCountRefreshTicks) >= 0x7530))
{
s_processorCount = num2 = Environment.ProcessorCount;
s_lastProcessorCountRefreshTicks = tickCount;
}
return num2;
}
}
}
}
From .NET Framework source
You can also get it with PInvoke on Kernel32.dll
The following code is coming more or less from SystemInfo.cs from System.Web source located here:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_INFO
{
public ushort wProcessorArchitecture;
public ushort wReserved;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public ushort wProcessorLevel;
public ushort wProcessorRevision;
}
internal static class SystemInfo
{
static int _trueNumberOfProcessors;
internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
internal static extern void GetSystemInfo(out SYSTEM_INFO si);
[DllImport("kernel32.dll")]
internal static extern int GetProcessAffinityMask(IntPtr handle, out IntPtr processAffinityMask, out IntPtr systemAffinityMask);
internal static int GetNumProcessCPUs()
{
if (SystemInfo._trueNumberOfProcessors == 0)
{
SYSTEM_INFO si;
GetSystemInfo(out si);
if ((int) si.dwNumberOfProcessors == 1)
{
SystemInfo._trueNumberOfProcessors = 1;
}
else
{
IntPtr processAffinityMask;
IntPtr systemAffinityMask;
if (GetProcessAffinityMask(INVALID_HANDLE_VALUE, out processAffinityMask, out systemAffinityMask) == 0)
{
SystemInfo._trueNumberOfProcessors = 1;
}
else
{
int num1 = 0;
if (IntPtr.Size == 4)
{
uint num2 = (uint) (int) processAffinityMask;
while ((int) num2 != 0)
{
if (((int) num2 & 1) == 1)
++num1;
num2 >>= 1;
}
}
else
{
ulong num2 = (ulong) (long) processAffinityMask;
while ((long) num2 != 0L)
{
if (((long) num2 & 1L) == 1L)
++num1;
num2 >>= 1;
}
}
SystemInfo._trueNumberOfProcessors = num1;
}
}
}
return SystemInfo._trueNumberOfProcessors;
}
}
There are many answers here already, but some have heavy upvotes and are incorrect.
The .NET Environment.ProcessorCount WILL return incorrect values and can fail critically if your system WMI is configured incorrectly.
If you want a RELIABLE way to count the cores, the only way is Win32 API.
Here is a C++ snippet:
#include <Windows.h>
#include <vector>
int num_physical_cores()
{
static int num_cores = []
{
DWORD bytes = 0;
GetLogicalProcessorInformation(nullptr, &bytes);
std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> coreInfo(bytes / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
GetLogicalProcessorInformation(coreInfo.data(), &bytes);
int cores = 0;
for (auto& info : coreInfo)
{
if (info.Relationship == RelationProcessorCore)
++cores;
}
return cores > 0 ? cores : 1;
}();
return num_cores;
}
And since this is a .NET C# Question, here's the ported version:
[StructLayout(LayoutKind.Sequential)]
struct CACHE_DESCRIPTOR
{
public byte Level;
public byte Associativity;
public ushort LineSize;
public uint Size;
public uint Type;
}
[StructLayout(LayoutKind.Explicit)]
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
{
[FieldOffset(0)] public byte ProcessorCore;
[FieldOffset(0)] public uint NumaNode;
[FieldOffset(0)] public CACHE_DESCRIPTOR Cache;
[FieldOffset(0)] private UInt64 Reserved1;
[FieldOffset(8)] private UInt64 Reserved2;
}
public enum LOGICAL_PROCESSOR_RELATIONSHIP
{
RelationProcessorCore,
RelationNumaNode,
RelationCache,
RelationProcessorPackage,
RelationGroup,
RelationAll = 0xffff
}
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
public UIntPtr ProcessorMask;
public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
}
[DllImport("kernel32.dll")]
static extern unsafe bool GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION* buffer, out int bufferSize);
static unsafe int GetProcessorCoreCount()
{
GetLogicalProcessorInformation(null, out int bufferSize);
int numEntries = bufferSize / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
var coreInfo = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[numEntries];
fixed (SYSTEM_LOGICAL_PROCESSOR_INFORMATION* pCoreInfo = coreInfo)
{
GetLogicalProcessorInformation(pCoreInfo, out bufferSize);
int cores = 0;
for (int i = 0; i < numEntries; ++i)
{
ref SYSTEM_LOGICAL_PROCESSOR_INFORMATION info = ref pCoreInfo[i];
if (info.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore)
++cores;
}
return cores > 0 ? cores : 1;
}
}
public static readonly int NumPhysicalCores = GetProcessorCoreCount();
One option would be to read the data from the registry.
MSDN Article On The Topic: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx)
The processors, I believe can be located here, HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor
private void determineNumberOfProcessCores()
{
RegistryKey rk = Registry.LocalMachine;
String[] subKeys = rk.OpenSubKey("HARDWARE").OpenSubKey("DESCRIPTION").OpenSubKey("System").OpenSubKey("CentralProcessor").GetSubKeyNames();
textBox1.Text = "Total number of cores:" + subKeys.Length.ToString();
}
I am reasonably sure the registry entry will be there on most systems.
Thought I would throw my $0.02 in.
You can use this class:
public static class CpuCores
{
private static int cores = 0;
public static int Number
{
get
{
if (cores > 0) return cores;
RegistryKey key = Registry.LocalMachine.OpenSubKey(#"SYSTEM\CurrentControlSet\Control\Class\" +
"{50127dc3-0f36-415e-a6cc-4cb3be910b65}");
if (key == null)
{
cores = Environment.ProcessorCount;
return cores;
}
string[] subkeys = key.GetSubKeyNames();
key.Close();
cores = 0;
if (subkeys != null && subkeys.Length > 0) foreach (string s in subkeys)
{
if (s.Length != 4) continue;
int n;
if (int.TryParse(s, out n) && ++n > cores) cores = n;
}
if (cores <= 0) cores = Environment.ProcessorCount;
return cores;
}
}
}
I was looking for the same thing but I don't want to install any nuget or servicepack, so I found this solution, it is pretty simple and straight forward,
using this discussion, I thought it would be so easy to run that WMIC command and get that value, here is the C# code. You only need to use System.Management namespace (and couple more standard namespaces for process and so on).
string fileName = Path.Combine(Environment.SystemDirectory, "wbem", "wmic.exe");
string arguments = #"cpu get NumberOfCores";
Process process = new Process
{
StartInfo =
{
FileName = fileName,
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
}
};
process.Start();
StreamReader output = process.StandardOutput;
Console.WriteLine(output.ReadToEnd());
process.WaitForExit();
int exitCode = process.ExitCode;
process.Close();
I have a progaram that can be ran both as a winform, or from command line. If it is invoked from a command line I call AttachConsole(-1) to attach to parent console.
However, after my program ends, the user must hit enter to get back the standard command prompt ("c:\>"). is there a way to avoid that need?
Thanks.
I could wrap it in a cmd file to avoid that issue, but I would like to do it from my exe.
Try adding this line just before your exe exits...
System.Windows.Forms.SendKeys.SendWait("{ENTER}");
Bit of a hack, but best I could find when I encountered that problem.
Here is the safest hack that solves the Enter key problem regardless of whether the console window is in the foreground, background, or minimized. You can even run it in multiple console windows.
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace WindowsAndConsoleApp
{
static class Program
{
const uint WM_CHAR = 0x0102;
const int VK_ENTER = 0x0D;
[DllImport("kernel32.dll")]
static extern bool AttachConsole(int dwProcessId);
private const int ATTACH_PARENT_PROCESS = -1;
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
[STAThread]
static void Main(string[] args)
{
if (args.Length > 0)
{
// Do this first.
AttachConsole(ATTACH_PARENT_PROCESS);
Console.Title = "Console Window - Enter Key Test";
Console.WriteLine("Getting the handle of the currently executing console window...");
IntPtr cw = GetConsoleWindow();
Console.WriteLine($"Console handle: {cw.ToInt32()}");
Console.WriteLine("\nPut some windows in from of this one...");
Thread.Sleep(5000);
Console.WriteLine("Take your time...");
Thread.Sleep(5000);
Console.WriteLine("Sending the Enter key now...");
// Send the Enter key to the console window no matter where it is.
SendMessage(cw, WM_CHAR, (IntPtr)VK_ENTER, IntPtr.Zero);
// Do this last.
FreeConsole();
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
}
}
Rob L's approach is somewhat dangerous as it will send an Enter to the active window. A better approach is to actual send the Enter to the correct process (console).
here is how
internal static class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool AllocConsole();
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern bool FreeConsole();
[DllImport("kernel32", SetLastError = true)]
internal static extern bool AttachConsole(int dwProcessId);
[DllImport("user32.dll")]
internal static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll", SetLastError = true)]
internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
[DllImport("User32.Dll", EntryPoint = "PostMessageA")]
internal static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
internal const int VK_RETURN = 0x0D;
internal const int WM_KEYDOWN = 0x100;
}
--snip--
bool attached = false;
// Get uppermost window process
IntPtr ptr = NativeMethods.GetForegroundWindow();
int u;
NativeMethods.GetWindowThreadProcessId(ptr, out u);
Process process = Process.GetProcessById(u);
if (string.Compare(process.ProcessName, "cmd", StringComparison.InvariantCultureIgnoreCase) == 0)
{
// attach to the current active console
NativeMethods.AttachConsole(process.Id);
attached = true;
}
else
{
// create new console
NativeMethods.AllocConsole();
}
Console.Write("your output");
NativeMethods.FreeConsole();
if (attached)
{
var hWnd = process.MainWindowHandle;
NativeMethods.PostMessage(hWnd, NativeMethods.WM_KEYDOWN, NativeMethods.VK_RETURN, 0);
}
This solution is build upon the code that is found here:
http://www.jankowskimichal.pl/en/2011/12/wpf-hybrid-application-with-parameters/
It's late to the party and there have been many suggestions over the years, but as I recently just solved this issue myself by stitching together a bunch of information from various posts, I thought I'd post the solution here since it has the most relevant title.
This solution works without using the Enter key or simulating a key press. The only thing I couldn't completely solve is intercepting the Enter from the parent console when your application starts. I think this is impossible because it happens before you get a chance to intercept it; however, there is a reasonable quasi-workaround.
Before diving into the code, here's the sequence of things we need to do:
Attach to the parent console
Capture the text of the current prompt output by the parent console
Clear the parent console's prompt by overwriting it with spaces (not sure it's possible to otherwise prevent this from happening)
Interact with the console as normal
Restore parent console's previous prompt by writing what we captured in #2
This is what it would look like in use:
using System;
using System.Windows.Forms;
public static void Main(string[] args)
{
if (args.Length > 0)
{
using (new ConsoleScope())
{
Console.WriteLine("I now own the console");
Console.WriteLine("MUA HA HA HA HA HA!!!");
}
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
... and now for the code. It's more than I'd like, but this is as succinct as I could make it for a post. May this help others attempting the same thing. Enjoy!
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
public sealed class ConsoleScope : IDisposable
{
const int ATTACH_PARENT_PROCESS = -1;
const int STD_OUTPUT_HANDLE = -11;
readonly bool createdNewConsole;
readonly string prompt;
bool disposed;
public ConsoleScope()
{
if (AttachParentConsole())
{
prompt = CaptureParentConsoleCurrentPrompt();
}
else
{
AllocConsole();
createdNewConsole = true;
}
}
~ConsoleScope() => CleanUp();
public void Dispose()
{
CleanUp();
GC.SuppressFinalize(this);
}
static string CaptureParentConsoleCurrentPrompt()
{
var line = (short)Console.CursorTop;
var length = (short)Console.CursorLeft;
var noPrompt = line == 0 && length == 0;
if (noPrompt)
{
return default;
}
return ReadCurrentLineFromParentConsoleBuffer(line, length);
}
static string ReadCurrentLineFromParentConsoleBuffer(short line, short length)
{
var itemSize = Marshal.SizeOf(typeof(CHAR_INFO));
var buffer = Marshal.AllocHGlobal(length * itemSize);
var encoding = Console.OutputEncoding;
var text = new StringBuilder(capacity: length + 1);
var coordinates = default(COORD);
var textRegion = new SMALL_RECT
{
Left = 0,
Top = line,
Right = (short)(length - 1),
Bottom = line,
};
var bufferSize = new COORD
{
X = length,
Y = 1,
};
try
{
if (!ReadConsoleOutput(GetStdOutputHandle(), buffer, bufferSize, coordinates, ref textRegion))
{
Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
}
var array = buffer;
for (var i = 0; i < length; i++)
{
var info = Marshal.PtrToStructure<CHAR_INFO>(array);
var chars = encoding.GetChars(info.CharData);
text.Append(chars[0]);
array += itemSize;
}
}
finally
{
Marshal.FreeHGlobal(buffer);
}
// now that we've captured the current prompt, overwrite it with spaces
// so that things start where the parent left off at
Console.SetCursorPosition(0, line);
Console.Write(new string(' ', length));
Console.SetCursorPosition(0, line - 1);
return text.ToString();
}
void CleanUp()
{
if (disposed)
{
return;
}
disposed = true;
RestoreParentConsolePrompt();
if (createdNewConsole)
{
FreeConsole();
}
}
void RestoreParentConsolePrompt()
{
var text = prompt;
if (!string.IsNullOrEmpty(text))
{
// this assumes the last output from your application used
// Console.WriteLine or otherwise output a CRLF. if it didn't,
// you may need to add an extra line here
Console.Write(text);
}
}
[StructLayout(LayoutKind.Sequential)]
struct CHAR_INFO
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public byte[] CharData;
public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
struct COORD
{
public short X;
public short Y;
}
[StructLayout(LayoutKind.Sequential)]
struct SMALL_RECT
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
// REF: https://learn.microsoft.com/en-us/windows/console/allocconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AllocConsole();
// REF: https://learn.microsoft.com/en-us/windows/console/attachconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(int dwProcessId);
// REF: https://learn.microsoft.com/en-us/windows/console/freeconsole
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FreeConsole();
static bool AttachParentConsole() => AttachConsole(ATTACH_PARENT_PROCESS);
// REF: https://learn.microsoft.com/en-us/windows/console/readconsoleoutput
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadConsoleOutput(IntPtr hConsoleOutput, IntPtr lpBuffer, COORD dwBufferSize, COORD dwBufferCoord, ref SMALL_RECT lpReadRegion);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
static IntPtr GetStdOutputHandle() => GetStdHandle(STD_OUTPUT_HANDLE);
}
Ok, I don't have the solution, but it seems to be because the cmd.exe is not waiting on the started process, whereas with a normal console application cmd.exe waits until the the application exits. I don't know what makes cmd.exe decide to wait or not on an application, normal Windows Forms applications are just started and cmd.exe doesn't wait for it to exit. Maybe this hint triggers somebody! I will dig a bit deeper in the mean while.
Try calling the FreeConsole function prior to exiting your executable.
This one has been the easiest solution for me:
myapp.exe [params] | ECHO.
I attempted my own Qt cpp version of Chris Martinez's C# answer:
https://github.com/NightVsKnight/QtGuiConsoleApp/blob/main/QtGuiConsoleApp/main.cpp
#include <QApplication>
#include <QMessageBox>
#ifdef Q_OS_WIN
// Solution posted to https://stackoverflow.com/a/73942013/252308
#define VC_EXTRALEAN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
QString consolePromptClear()
{
QString prompt = nullptr;
auto bSuccess = AttachConsole(ATTACH_PARENT_PROCESS);
if (bSuccess)
{
auto hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut != INVALID_HANDLE_VALUE)
{
CONSOLE_SCREEN_BUFFER_INFO csbi;
bSuccess = GetConsoleScreenBufferInfo(hStdOut, &csbi);
if (bSuccess)
{
auto dwConsoleColumnWidth = (DWORD)(csbi.srWindow.Right - csbi.srWindow.Left + 1);
auto xEnd = csbi.dwCursorPosition.X;
auto yEnd = csbi.dwCursorPosition.Y;
if (xEnd != 0 || yEnd != 0)
{
DWORD dwNumberOfChars;
SHORT yBegin = yEnd;
{
// Walk backwards to find first all blank line
auto pBuffer = (LPWSTR)LocalAlloc(LPTR, dwConsoleColumnWidth * sizeof(WCHAR));
while (yBegin)
{
COORD dwReadCoord = { 0, yBegin };
bSuccess = ReadConsoleOutputCharacterW(hStdOut, pBuffer, dwConsoleColumnWidth, dwReadCoord, &dwNumberOfChars);
if (!bSuccess) break;
DWORD i;
for (i=0; i < dwNumberOfChars; ++i)
{
WCHAR wchar = pBuffer[i];
if (wchar != L' ')
{
--yBegin;
break;
}
}
if (i == dwNumberOfChars)
{
// Found all blank line; we want the *next* [non-blank] line
yBegin++;
break;
}
}
LocalFree(pBuffer);
}
auto promptLength = (yEnd - yBegin) * dwConsoleColumnWidth + xEnd;
auto lpPromptBuffer = (LPWSTR)LocalAlloc(LPTR, promptLength * sizeof(WCHAR));
COORD dwPromptCoord = { 0, yBegin };
bSuccess = ReadConsoleOutputCharacterW(hStdOut, lpPromptBuffer, promptLength, dwPromptCoord, &dwNumberOfChars);
if (bSuccess)
{
Q_ASSERT(promptLength == dwNumberOfChars);
prompt = QString::fromWCharArray(lpPromptBuffer, dwNumberOfChars);
bSuccess = SetConsoleCursorPosition(hStdOut, dwPromptCoord);
if (bSuccess)
{
FillConsoleOutputCharacterW(hStdOut, L' ', promptLength, dwPromptCoord, &dwNumberOfChars);
}
}
LocalFree(lpPromptBuffer);
}
}
}
}
if (prompt.isEmpty())
{
FreeConsole();
return nullptr;
}
else
{
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
freopen_s((FILE**)stderr, "CONOUT$", "w", stderr);
freopen_s((FILE**)stdin, "CONIN$", "r", stdin);
return prompt;
}
}
void consolePromptRestore(const QString& prompt)
{
if (prompt.isEmpty()) return;
auto hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hStdOut == INVALID_HANDLE_VALUE) return;
CONSOLE_SCREEN_BUFFER_INFO csbi;
BOOL bSuccess = GetConsoleScreenBufferInfo(hStdOut, &csbi);
if (!bSuccess) return;
auto xEnd = csbi.dwCursorPosition.X;
auto yEnd = csbi.dwCursorPosition.Y;
if (xEnd == 0 && yEnd == 0) return;
auto buffer = prompt.toStdWString();
auto lpBuffer = buffer.data();
auto nLength = (DWORD)buffer.length();
COORD dwWriteCoord = { 0, (SHORT)(yEnd + 1) };
DWORD dwNumberOfCharsWritten;
WriteConsoleOutputCharacterW(hStdOut, lpBuffer, nLength, dwWriteCoord, &dwNumberOfCharsWritten);
dwWriteCoord = { (SHORT)dwNumberOfCharsWritten, (SHORT)(yEnd + 1) };
SetConsoleCursorPosition(hStdOut, dwWriteCoord);
}
#else
// Non-Windows impl...
#endif
int main(int argc, char *argv[])
{
// NOTE: Any console output before call to consolePromptClear() may get cleared.
// NOTE: Console vs GUI mode has **NOTHING** to do with being passed arguments; You can easily pass arguments to GUI apps.
int returnCode;
auto prompt = consolePromptClear();
if (prompt.isEmpty())
{
QApplication a(argc, argv);
a.setQuitOnLastWindowClosed(true);
QMessageBox msgBox(nullptr);
msgBox.setWindowTitle(a.applicationName());
msgBox.setTextFormat(Qt::RichText);
msgBox.setText("App is detected to be running as a GUI");
msgBox.setStandardButtons(QMessageBox::Ok);
msgBox.show();
returnCode = a.exec();
}
else
{
QCoreApplication a(argc, argv);
QTextStream qout(stdout);
qout << "App is detected to be running as a Console" << Qt::endl;
returnCode = 0;
consolePromptRestore(prompt);
}
return returnCode;
}