C/C++/C#: Howto Do "Mount -A"

C / C++ / C#: Howto do mount -a

getmntent can help you read /etc/fstab (and then use the mount function in the other answers).

Mounting a hidden partition in windows using C# or C++ program

CreateSymbolicLink did the trick. You can pass path of a volume to it. It lets you create a symbolic link to hidden partitions. You can create sym links to specific files as well as directories with it.
The path of hidden partition should be passed like "\?\GLOBALROOT\device\harddisk0\partition1\dirA\dirB".

Programmatically mount a Microsoft Virtual Hard Drive (VHD)

This is an old question but it still has no answer so I'll provide one in case someone stumble upon it like I did.

Attaching the VHD

For the complete Reference on MSDN [VHD Reference]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd323700(v=vs.85).aspx

OPEN_VIRTUAL_DISK_PARAMETERS openParameters;
openParameters.Version = OPEN_VIRTUAL_DISK_VERSION_1;
openParameters.Version1.RWDepth = OPEN_VIRTUAL_DISK_RW_DEPTH_DEFAULT;

VIRTUAL_STORAGE_TYPE storageType;
storageType.DeviceID = VIRTUAL_STORAGE_TYPE_DEVICE_VHD;
storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT;

ATTACH_VIRTUAL_DISK_PARAMETERS attachParameters;
attachParameters.Version = ATTACH_VIRTUAL_DISK_VERSION_1;

HANDLE vhdHandle;

if (OpenVirtualDisk(&openStorageType, "{VHD PATH GOES HERE}",
VIRTUAL_DISK_ACCESS_ALL, OPEN_VIRTUAL_DISK_FLAG_NONE,
&openParameters, &vhdHandle) != ERROR_SUCCESS) {
// If return value of OpenVirtualDisk isn't ERROR_SUCCESS, there was a problem opening the VHD
}

// Warning: AttachVirtualDisk requires elevation
if (AttachVirtualDisk(vhdHandle, 0, ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME,
0, &attachParameters, 0) != ERROR_SUCCESS) {
// If return value of AttachVirtualDisk isn't ERROR_SUCCESS, there was a problem attach the disk
}

VHD successfully attached, now it'll show up like any other physical disks and a drive letter will automatically be assigned to the volume(s) contained in the VHD. If you'd like to choose what drive letter is used to mount it, keep reading.

Assigning a drive letter

First, add the ATTACH_VIRTUAL_DISK_FLAG_NO_DRIVE_LETTER flag to your AttachVirtualDisk call so it won't do this automatic letter assigning. Next, you'll have to find the volume path of the VHD volumes [it has this format: \\?\Volume{GUID}]:

wchar_t physicalDrive[MAX_PATH];
ULONG bufferSize = sizeof(physicalDrive);
GetVirtualDiskPhysicalPath(vhdHandle, &bufferSize, physicalDrive);

Now you'll have the physical path of your attached VHD in physical drive in the following format: \\.\PhysicalDrive# where # is the drive number you'll need to find your VHD volumes with FindFirstVolume/FindNextVolume. Extract the number and convert it to an integer and you'll be ready for the next piece of code:

char volumeName[MAX_PATH];
DWORD bytesReturned;
VOLUME_DISK_EXTENTS diskExtents;
HANDLE hFVol = FindFirstVolume(volumeName, sizeof(volumeName));
bool hadTrailingBackslash = false;

do {
// I had a problem where CreateFile complained about the trailing \ and
// SetVolumeMountPoint desperately wanted the backslash there. I ended up
// doing this to get it working but I'm not a fan and I'd greatly
// appreciate it if someone has any further info on this matter
int backslashPos = strlen(volumeName) - 1;
if (hadTrailingBackslash = volumeName[backslashPos] == '\\') {
volumeName[backslashPos] = 0;
}

HANDLE hVol = CreateFile(volumeName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hVol == INVALID_HANDLE_VALUE) {
return;
}

DeviceIoControl(hVol, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL,
0, &diskExtents, sizeof(diskExtents), &bytesReturned, NULL);

// If the volume were to span across multiple physical disks, you'd find
// more than one Extents here but we don't have to worry about that with VHD
// Note that 'driveNumber' would be the integer you extracted out of
// 'physicalDrive' in the previous snippet
if (diskExtents.Extents[0].DiskNumber == driveNumber) {
if (hadTrailingBackslash) {
volumeName[backslashPos] = '\\';
}

// Found volume that's on the VHD, let's mount it with a letter of our choosing.
// Warning: SetVolumeMountPoint requires elevation
SetVolumeMountPoint("H:\\", volumeName);
}
} while (FindNextVolume(hFVol, volumeName, sizeof(volumeName)));
FindVolumeClose(hFVol);

Don't forget these includes and link to this library:

#define WINVER _WIN32_WINNT_WIN7
#include <windows.h>
#include <winioctl.h>
#include <virtdisk.h>

#pragma comment(lib, "virtdisk.lib")

Disclaimer: This is something I was doing in a C# codebase, I translated the code to C/C++ because of the question but haven't tried to actually compile it. If you find errors in the code, please edit it or let me know so I can do it.

Edits: Typos, includes and lib, forgot FindVolumeClose, elevation warnings

Add /MT compile flag to project

It is a setting that only applies to C or C++ projects. It selects the kind of C runtime library that will be used. There are two basic flavors, /MT vs /MD, multiplied by two for Debug and Release builds, /MTd and /MDd. With /MT, the runtime library will get linked into the EXE program itself. With /MD, the runtime library is in a DLL that can be shared by all modules in the process. /MD is very important when you partition your program into multiple modules, like an EXE and one or more DLLs.

A vendor will tell you to use /MT only when they supplied you with a static link library, a .lib file. And gave you only one flavor of it, in general a very questionable practice that you should not put up with since it gives you many less options to build a program that uses the library. Unstated, but very important, is that he also needs to tell you what version of Visual Studio to use.

The .lib file he gave you is not useable from C# at all. You'll have to link it to C or C++ code that uses the library. To interop with C#, you'd typically want to use C++/CLI to write wrapper classes that give you access to the library functions. But you can't do that, C++/CLI only supports building with /MD. All that's left is creating a DLL that exports the functions with a .def file. You have to use pinvoke in your C# code to call them.

Clearly there's a rather major communication failure. You need to follow up and talk to the vendor to get this straightened out. Pursue this aggressively, getting stuck with a wonky library can be an enormous pita for many years to come, increasingly getting worse as tooling evolves.

Creating a filesystem file in C++

To really "mount" the filesystem (ie. to be able to just "fopen" something inside that filesystem) you need support from the kernel. Either you let your operating system take care of it completely (e.g the VHD commands on windows; example use on stackoverflow). Alternatively can use libfuse/winfsp to interact with the kernel more directly, but afaik that requires a custom kernel driver.

If you have an image file with some filesystem inside and you just want to look whats inside, but don't need the C/C++ standard library commands (fopen, ifstream, etc.) to work, then something that can specifically read/write that combination of image file format and filesystem would also suffice.

For C# there would be the DiscUtils library which might do what you want, but I'm not aware of a similar c/c++ library with NTFS support.

How to get array from C++/C to C# using DLL

There are multiple ways to return arrays, but in all cases you must solve two problems:

  • be able to tell .NET the count of items in the array;
  • use a common memory allocator for the native and .NET Size. The allocator of choice is the COM allocator as it's known to .NET;

Here are two examples using .NET safe code:

Native code 1:

extern __declspec(dllexport) SAFEARRAY * sumSafeArray()
{
int a = 50, b = 80, sum, neg;
sum = a + b;
neg = b - a;
int arr[2] = { sum, neg };

// SAFEARRAY is a OLE/COM automation type well known to Windows and .NET
// (note it implicitly uses the COM allocator too)
SAFEARRAY* psa = SafeArrayCreateVector(VT_I4, 0, 2);
void* data;
SafeArrayAccessData(psa, &data);
CopyMemory(data, arr, sizeof(arr));
SafeArrayUnaccessData(psa);
return psa;
}

Managed code 1:

[DllImport("mydll")]
[return: MarshalAs(UnmanagedType.SafeArray)]
private static extern int[] sumSafeArray();

foreach (var i in sumSafeArray())
{
Console.WriteLine(i);
}

Native code 2:

extern __declspec(dllexport) void sumArray(int** out, int* count)
{
int a = 50, b = 80, sum, neg;
sum = a + b;
neg = b - a;
int arr[2] = { sum, neg };

// always use COM allocator
*out = (int*)CoTaskMemAlloc(sizeof(arr));
CopyMemory(*out, arr, sizeof(arr));
*count = sizeof(arr) / sizeof(int);
}

Managed code 2:

[DllImport("mydll")]
private static extern void sumArray(out IntPtr array, out int size);

sumArray(out var ptr, out var size);
var ints = new int[size];
Marshal.Copy(ptr, ints, 0, size);
Marshal.FreeCoTaskMem(ptr);
foreach (var i in ints)
{
Console.WriteLine(i);
}

And one example using .NET unsafe code:

Native code:

extern __declspec(dllexport) int* sum(int* count)
{
int a = 50, b = 80, sum, neg;
sum = a + b;
neg = b - a;
int arr[2] = { sum, neg };

// always use COM allocator
int* out = (int*)CoTaskMemAlloc(sizeof(arr));
CopyMemory(out, arr, sizeof(arr));
*count = sizeof(arr) / sizeof(int);
return out;
}

Managed code:

[DllImport("mydll")]
private unsafe static extern int* sum(out int count);

var ints = sum(out var count);
for (var i = 0; i < count; i++)
{
Console.WriteLine(*ints);
ints++;
}

You could also use another allocator (new/malloc/custom, etc.) but in this case you would need to expose/export to .NET the corresponding "free" method and do the free yourself when needed.

How Do I Get the Assigned Drive Letter When Mounting an ISO with WMI in C#?

I found a solution in a C++ project created by Jim Dale MountISO.wmi on GitHub

Here is my final C# class for automated mounting and unmounting iso images in Windows 8+ and retrieving the auto-mounted drive letter. It is dramatically faster and more reliable than the typical PowerShell method.

using System.Management;
using System.Threading;

namespace IsoTools
{
public static class IsoMounter
{
private const string WmiScope = @"root\Microsoft\Windows\Storage";

/// <summary>Mounts an ISO disc image file.</summary>
/// <param name="isoPath">
/// The full path of the ISO to be mounted.
/// </param>
/// <returns>
/// A System.Char representing a drive volume letter.
/// </returns>
public static char Mount(string isoPath)
{
string isoObjectPath = BuildIsoObjectPath(isoPath);
using (var isoObject =
new ManagementObject(WmiScope, isoObjectPath, null))
{
using (ManagementBaseObject inParams =
isoObject.GetMethodParameters("Mount"))
{
isoObject.InvokeMethod("Mount", inParams, null);
}
}

// The query used to retrieve the volume letter for an image.
string volumeQuery = "ASSOCIATORS OF {" + isoObjectPath + "}" +
"WHERE AssocClass = MSFT_DiskImageToVolume " +
"ResultClass = MSFT_Volume";

char mountLetter = '\0';
using (var query =
new ManagementObjectSearcher(WmiScope, volumeQuery))
{
// Run query until drive is mounted
while (mountLetter < 65)
{
Thread.Sleep(50);
using (ManagementObjectCollection queryCollection =
query.Get())
{
foreach (ManagementBaseObject item in queryCollection)
{
mountLetter = item["DriveLetter"].ToString()[0];
}
}
}
}

return mountLetter;
}

/// <summary>Dismount an ISO disc image file.</summary>
/// <param name="isoPath">
/// The full path of the ISO to be mounted.
/// </param>
public static void Dismount(string isoPath)
{
using (var isoObject = new ManagementObject(
WmiScope,
BuildIsoObjectPath(isoPath),
null))
{
using (ManagementBaseObject inParams =
isoObject.GetMethodParameters("Dismount"))
{
isoObject.InvokeMethod("Dismount", inParams, null);
}
}
}

/// <summary>Creates the WMI pathstring for an ISO image.</summary>
/// <param name="isoPath">
/// The full path of the ISO to be mounted.
/// </param>
/// <returns>A System.String representing a WMI pathstring.</returns>
private static string BuildIsoObjectPath(string isoPath)
{
// Single quoted paths do not allow escaping of single quotes
// within the ImagePath. Use double quotes and escape backslashes.
return "MSFT_DiskImage.ImagePath=\"" +
isoPath.Replace("\\", "\\\\") +
"\",StorageType=1";
}
}
}

How to pass an interface from C# to C++

If you mean unmanaged c++, then you can create a COM interface from a C# interface by using the COMVisible attribute i.e.

[Guid("<a guid>")]
[ComVisible(true)]
interface IMessage
{
DoSomething();
}


Related Topics



Leave a reply



Submit