Multi-monitor Screenshots Using WinAPI

void GdiPlusScreenCapture(MONITORINFOEX info, IStream* istream)
{
    HDC DevC = CreateDC(nullptr, info.szDevice, nullptr, nullptr);
    DWORD Width = info.rcMonitor.right - info.rcMonitor.left;
    DWORD Height = info.rcMonitor.bottom - info.rcMonitor.top;

    HDC CaptureDC = CreateCompatibleDC(DevC);
    HBITMAP CaptureBitmap = CreateCompatibleBitmap(DevC, Width, Height);
    HGDIOBJ old_obj = SelectObject(CaptureDC, CaptureBitmap);
    BitBlt(CaptureDC, 0, 0, Width, Height, DevC, 0, 0, SRCCOPY | CAPTUREBLT);

    Gdiplus::Bitmap bitmap(CaptureBitmap, nullptr);

    CLSID pngClsid;
    CLSIDFromString(L"{557cf401-1a04-11d3-9a73-0000f81ef32e}", &pngClsid);

    bitmap.Save(istream, &pngClsid, nullptr);

    SelectObject(CaptureDC, old_obj);
    DeleteDC(CaptureDC);
    ReleaseDC(nullptr, DevC);
    DeleteObject(CaptureBitmap);
    DeleteDC(DevC);
}

void SaveScreenCapture()
{
    std::vector<MONITORINFOEX> monitorInfos;
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, nullptr);

    // get the bitmap handle to the bitmap screenshot
    HDC hdc = GetDC(nullptr);
    EnumDisplayMonitors(hdc, nullptr, [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL {
    std::vector<MONITORINFOEX>* pMonData = reinterpret_cast<std::vector<MONITORINFOEX>*>(dwData);
		MONITORINFOEX info;
		info.cbSize = sizeof(MONITORINFOEX);
		GetMonitorInfo(hMonitor, &info);
		pMonData->push_back(info);
		return true;
	}, reinterpret_cast<LPARAM>(&monitorInfos));
    
    int index = 1;
    for (auto& monitorInfo : monitorInfos)
    {	//Create an empty IStream:
	IStream* pIStream = nullptr;
	if (CreateStreamOnHGlobal(nullptr, true, &pIStream) == S_OK)
        {

            GdiPlusScreenCapture(monitorInfo, pIStream);
	    HGLOBAL hg = nullptr;
	    GetHGlobalFromStream(pIStream, &hg);
	    SIZE_T bufsize = GlobalSize(hg);
	    LPVOID ptr = GlobalLock(hg);
            TCHAR jpegFile[MAX_PATH + 1] = { 0 };
	    _stprintf_s(jpegFile, MAX_PATH, "%d.jpg", index);
	    //write from memory to file for testing:
	    std::ofstream fout(jpegFile, std::ios::binary);
	    fout.write((char*)ptr, bufsize);
	    fout.close();
	    pIStream->Release();
	    GlobalUnlock(hg);
        }
    }

    ReleaseDC(nullptr, hdc);
    GdiplusShutdown(gdiplusToken);
}

How to use SharpExt4 to access Raspberry Pi SD Card Linux partition

In my previous post, I introduced the SharpExt4 .Net library. In this post, I will show how to use SharpExt4 to access the Raspberry Pi SD card from Windows OS.

  • Take out the Raspberry Pi SD card and insert it into a USB card reader
  • Run “diskpart” from Windows command prompt and find out the SD card disk number and partition number. In my case, the disk number is 3 and the partition is 2.
  • Clone the SharpExt4 from GitHub, and open Visual Studio as Admin

Note: If you want to access physical drive, you must run application in admin permission

  • Open SharpExt4 and edit the Program.cs from the Sample project
static void Main(string[] args)
{
    //Open Raspberry Pi SD card, see diskpart disk number
    var disk = ExtDisk.Open(3);
    //Get the file system, see diskpart partition number
    var fs = ExtFileSystem.Open(disk.Parititions[1]);
    //List all directories in root folder
    foreach (var file in fs.GetDirectories("/", "*", SearchOption.TopDirectoryOnly))
    {
        Console.WriteLine(file);
    }
}

Run the Sample project and see the result to list all the folders in Raspberry Pi root folder.

SharpExt4, a .Net library, provides read/write Linux ext2/3/4 file system from Windows application (continue)

Follow my previous post: https://www.nickdu.com/?p=896

Open Ext4 file system.

 //Get the file system
 var fs = ExtFileSystem.Open(disk.Parititions[0]);

Sample code to open a file for read

//Open a file for read
var file = fs.OpenFile("/etc/shells", FileMode.Open, FileAccess.Read);
//Check the file length
var filelen = file.Length;
var buf = new byte[filelen];
//Read the file content
var count = file.Read(buf, 0, (int)filelen);
file.Close();
var content = Encoding.Default.GetString(buf);
Console.WriteLine(content);

Sample code for listing all files in a folder

//List all files in /etc folder
foreach (var file in fs.GetFiles("/etc", "*", SearchOption.AllDirectories))
{
    Console.WriteLine(file);
}

Sample code for file creation

//Open a file for write
var file = fs.OpenFile("/etc/test", FileMode.Create, FileAccess.Write);
var hello = "Hello World";
var buf = Encoding.ASCII.GetBytes(hello);
//Write to file
file.Write(buf, 0, buf.Length);
file.Close();

Full ExtDisk APIs

public sealed class ExtDisk : IDisposable
{
    public IList<Partition> Parititions { get; }
    public Geometry Geometry { get; }
    public ulong Capacity { get; }

    public static ExtDisk Open(string imagePath);
    public static ExtDisk Open(int DiskNumber);
    public sealed override void Dispose();
    public byte[] GetMasterBootRecord();

}

Full ExtFileSystem APIs

public sealed class ExtFileSystem : IDisposable
{
    public static string MountPoint { get; }
    public string Name { get; }
    public string VolumeLabel { get; }
    public bool CanWrite { get; }
    public string Description { get; }

    public static ExtFileSystem Open(Partition partition);
    public void CopyFile(string sourceFile, string destinationFile, bool overwrite);
    public void CreateDirectory(string path);
    public void CreateHardLink(string target, string path);
    public void CreateSymLink(string target, string path);
    public void DeleteDirectory(string path);
    public void DeleteFile(string path);
    public bool DirectoryExists(string path);
    public sealed override void Dispose();
    public bool FileExists(string path);
    public ValueType GetCreationTime(string path);
    public string[] GetDirectories(string path, string searchPattern, SearchOption searchOption);
    public ulong GetFileLength(string path);
    public string[] GetFiles(string path, string searchPattern, SearchOption searchOption);
    public DateTime GetLastAccessTime(string path);
    public DateTime GetLastWriteTime(string path);
    public uint GetMode(string path);
    public Tuple<uint, uint> GetOwner(string path);
    public void MoveDirectory(string sourceDirectoryName, string destinationDirectoryName);
    public ExtFileStream OpenFile(string path, FileMode mode, FileAccess access);
    public string ReadSymLink(string path);
    public void RenameFile(string sourceFileName, string destFileName);
    public void SetCreationTime(string path, DateTime newTime);
    public void SetLastAccessTime(string path, DateTime newTime);
    public void SetLastWriteTime(string path, DateTime newTime);
    public void SetMode(string path, uint mode);
    public void SetOwner(string path, uint uid, uint gid);
    public sealed override string ToString();
    public void Truncate(string path, ulong size);
}

Full ExtFileStream APIs

public class ExtFileStream : Stream
{
    public ExtFileStream(ExtFileSystem fs, string path, FileMode mode, FileAccess access);

    public override long Position { get; set; }
    public override long Length { get; }
    public override bool CanWrite { get; }
    public override bool CanRead { get; }
    public override bool CanSeek { get; }

    public override void Close();
    public override void Flush();
    public override int Read(byte[] array, int offset, int count);
    public override long Seek(long offset, SeekOrigin origin);
    public override void SetLength(long value);
    public override void Write(byte[] array, int offset, int count);

}

The full SharpExt4 library can be found at my GitHub.

https://github.com/nickdu088/SharpExt4

SharpExt4, a .Net library, provides read/write Linux ext2/3/4 file system from Windows application (continue)

Follow my last post SharpExt4, a .Net library, provides read/write Linux ext2/3/4 file system from Windows application.

The lwext4 is a great start point for me (thanks the author of lwext4), and it provides the core implementation of Linux ext2/3/4 filesystem.

What I need to do:

  1. To port the entire lwext4 project over to Visual Studio C/C++ (MSVC) environment, and compile it as a static library.
  2. To create a clr wrapper around the lwext4 static library and compiled as a .Net assembly DLL.
  3. To provide a .Net friendly interface for .Net Application to use.

When creating this .Net library, I would like to access not only physical Linux disk directly, but also Linux disk image file.

SharpExt4 provides two open disk APIs:

//Open physical Linux disk
ExtDisk SharpExt4.ExtDisk.Open(int DiskNumber);
//Open Linux disk image file
ExtDisk SharpExt4.ExtDisk.Open(String imagePath);

Open Linux disk image allows developer to directly manipulate the saved Linux disk image file, e.g. Raspberry Pi OS image or Debian OS image. The saved Linux disk image must be raw format. This API doesn’t support Virtual Machine disk files (VHD, VDI, XVA, VMDK, etc).

This API also support open USB/Hard drive as physical disk by giving disk number. If you have a USB disk or hard drive formatted as ext2/3/4 file system, this API allows the developer to read/write directly.

To be continue…

Next post: https://www.nickdu.com/?p=912

SharpExt4, a .Net library, provides read/write Linux ext2/3/4 file system from Windows application

As a day to day Windows user, it’s not easy to access Linux file system. Windows doesn’t natively supports Linux Extended file system access.

I have been working on Linux ARM IoT device, and it’s so annoying to be back and forth between Windows working PC and Linux development PC. I regularly need to burn/flash SD card, read/write file, remove/open directory in Linux device image.

These are the findings so far:

  1. DiscUtils, is a .NET library to read and write ISO files and Virtual Machine disk files (VHD, VDI, XVA, VMDK, etc). DiscUtils also provides limited access to ext2/3/4 filesystem.
  2. Ext2Fsd is another Windows file system driver for the Ext2, Ext3, and Ext4 file systems. It allows Windows to read Linux file systems natively, providing access to the file system via a drive letter that any program can access. But it stops developing since 2017.
  3. DiskInternals Linux Reader is a freeware application from DiskInternals, developers of data recovery software. But it doesn’t have API for .Net Framework.
  4. Ext2explore is an open-source application that works similarly to DiskInternals Linux Reader—but only for Ext4, Ext3, and Ext2 partitions. It stops developing for a long time and is read only.

I decided to implement my own library. Finally, I found this, lwext4, a C library to provide ext2/3/4 filesystem for microcontrollers. According to the author, the library has some cool and unique features in microcontrollers world:

  • directory indexing – fast file find and list operations
  • extents – fast big file truncate
  • journaling transactions & recovery – power loss resistance

Lwext4 is an excellent choice for SD/MMC card, USB flash drive or any other wear leveled memory types. However it is not good for raw flash devices.

To be continue.

Next post https://www.nickdu.com/?p=896

Get Processor Id (CPU) in C++

This piece of C++ code helps to get CPU (Processor) Id

#include <array>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include<conio.h>
std::string GetProcessorId() {
	std::array<int, 4> cpuInfo;
	__cpuid(cpuInfo.data(), 1);
	std::ostringstream buffer;
	buffer
		<< std::uppercase << std::hex << std::setfill('0')
		<< std::setw(8) << cpuInfo.at(3)
		<< std::setw(8) << cpuInfo.at(0);
	return buffer.str();
}

int main(void) {
	std::cout << "Processor Serial number is:  ";
		std::cout<<GetProcessorId() << std::endl;
	_getch();
	return 0;
}

Learning OpenCL programming

This post is to record the steps how I run my first “Hello World” OpenCL C++ program.

  1. To make things easier, I created this, OpenCL.zip, OpenCL library and C/C++ header files.
  2. Create a Visual Studio C++ project
  3. The following code is to add two 2^25 array altogether using GPU:
#include <iostream>
#include <vector>
#include <string>
using namespace std;

#define __CL_ENABLE_EXCEPTIONS
#include <CL\cl.hpp>

// Compute c = a + b.
static const char source[] =
"kernel void add(\n"
"       ulong n,\n"
"       global const float *a,\n"
"       global const float *b,\n"
"       global float *c\n"
"       )\n"
"{\n"
"    size_t i = get_global_id(0);\n"
"    if (i < n) {\n"
"       c[i] = a[i] + b[i];\n"
"    }\n"
"}\n";

int main() {

	const size_t N = 1 << 25;

	try {
		// Get list of OpenCL platforms.
		std::vector platform;
		cl::Platform::get(&platform);

		if (platform.empty()) {
			std::cerr << "OpenCL platforms not found." << std::endl;
			return 1;
		}

		// Get first available GPU device.
		cl::Context context;
		std::vector device;
		for (auto p = platform.begin(); device.empty() && p != platform.end(); p++) {
			std::vector pldev;

			try {
				p->getDevices(CL_DEVICE_TYPE_DEFAULT, &pldev);

				for (auto d = pldev.begin(); device.empty() && d != pldev.end(); d++) {
					if (!d->getInfo()) continue;

					std::string ext = d->getInfo();

					device.push_back(*d);
					context = cl::Context(device);
				}
			}
			catch (...) {
				device.clear();
			}
		}

		if (device.empty()) {
			std::cerr << "GPUs device not found." << std::endl;
			return 1;
		}

		std::cout << device[0].getInfo() << std::endl;

		// Create command queue.
		cl::CommandQueue queue(context, device[0]);

		// Compile OpenCL program for found device.
		cl::Program program(context, cl::Program::Sources(
			1, std::make_pair(source, strlen(source))
			));

		try {
			program.build(device);
		}
		catch (const cl::Error&) {
			std::cerr
				<< "OpenCL compilation error" << std::endl
				<< program.getBuildInfo(device[0])
				<< std::endl;
			return 1;
		}

		cl::Kernel add(program, "add");

		// Prepare input data.
		std::vector a(N, 1);
		std::vector b(N, 2);
		std::vector c(N);

		// Allocate device buffers and transfer input data to device.
		cl::Buffer A(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			a.size() * sizeof(float), a.data());

		cl::Buffer B(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR,
			b.size() * sizeof(float), b.data());

		cl::Buffer C(context, CL_MEM_READ_WRITE,
			c.size() * sizeof(float));

		// Set kernel parameters.
		add.setArg(0, static_cast(N));
		add.setArg(1, A);
		add.setArg(2, B);
		add.setArg(3, C);

		// Launch kernel on the compute device.
		queue.enqueueNDRangeKernel(add, cl::NullRange, N, cl::NullRange);

		// Get result back to host.
		queue.enqueueReadBuffer(C, CL_TRUE, 0, c.size() * sizeof(float), c.data());

		// Should get '3' here.
		std::cout << c[42] << std::endl;
	}
	catch (const cl::Error &err) {
		std::cerr
			<< "OpenCL error: "
			<< err.what() << "(" << err.err() << ")"
			<< std::endl;
		return 1;
	}
}

Generate large prime numbers using boost library

Recently I am working on a RSA like algorithm project, which needs to generate large secret prime numbers, e.g. 512 bits, or 1024 bits. Luckily we are using boost library, which is inbuilt lots of functions to assist this generation.

#include <boost/multiprecision/miller_rabin.hpp>
#include <boost/random/mersenne_twister.hpp>

using namespace boost::multiprecision;
using namespace boost::random;

...
//Generate a secret RSA-like 512 bits primes p
cpp_int p = GetPrime();
...
cpp_int GetPrime()
{
    mt11213b base_gen(clock());
    independent_bits_engine<mt11213b, 512, cpp_int> gen(base_gen);

    // Generate some large random primes
    // Note 25 trials of Miller-Rabin 
    // likelihood that number is prime
    cpp_int n;
    do
    {
        n = gen();
    }while (!miller_rabin_test(n, 25));
    return n;
}

Change IP address & Mask address (C++) Windows

Introduction

This is one way of changing IP address & mask address in C++ using Iphlpapi.h. It’s a simple program, no UI for you, sorry…

  • Use GetAdaptersInfo to get adapters information.
  • Then use GetPerAdapterInfo to get information for every ethernet network card. The information contains IP address.
  • Use DeleteIPAddress remove the old IP address.
  • Use AddIPAddress to add new IP&Mask address.
// ChangeIPAddress("192.168.0.123", "192.168.0.44","255,255,255,0");
bool ChangeIPAddress(char oldIPAddress[], char newIPAddress[], char newMaskAddress[])
{
    DWORD dwRetVal = 0;
    PIP_ADAPTER_INFO pAdapter = NULL;

    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
    PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(sizeof(IP_ADAPTER_INFO));
    if (pAdapterInfo == NULL)
    {
        return false;
    }

    if (GetAdaptersInfo(pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
    {
        free(pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO *)malloc(ulOutBufLen);
        if (pAdapterInfo == NULL)
        {
            return false;
        }
    }

    if ((dwRetVal = GetAdaptersInfo(pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
    {
        pAdapter = pAdapterInfo;
        while (pAdapter)
        {
            if (strcmp(oldIPAddress, pAdapter->IpAddressList.IpAddress.String) == 0)
            {
                IPAddr addr = inet_addr(newIPAddress);
                IPMask mask = inet_addr(newMaskAddress);
                ULONG context, instance;

                if (DeleteIPAddress(pAdapter->IpAddressList.Context) != NO_ERROR
                    || AddIPAddress(addr, mask, pAdapter->Index, &context, &instance) != NO_ERROR)
                {
                    return false;
                }
                return true;
            }
            pAdapter = pAdapter->Next;
        }
        return false;
    }
}

Windows physical disk image writer in C

My recent project is to write a raw image file into CFast card, and I was looking for a physical disk writer on google. Finally I found this http://m0n0.ch/wall/physdiskwrite.php, but it doesn’t work properly.

  1. GUI is not Englisht
  2. When image size is too big, it doesn’t show the correct size.

Then I decide to make my own physical disk image writer in C.

Be extremely careful to use this tool, incorrect disk number can completely corrupt your disk data! I am not responsible for this.

Download

PhyDiskWrite.zip (4k)

#include <windows.h>

void PrintHelp()
{
    printf("Usage: phydiskwrite [imagefile]\r\n\r\n");
}

HANDLE OpenDisk(int deviceID)
{
    HANDLE hDisk;
    TCHAR diskName[MAX_PATH];
    _stprintf_s(diskName, _T("\\\\.\\PhysicalDrive%d"), deviceID);
    hDisk = CreateFile(diskName, 
        GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
    return hDisk;
}

int PrintDisks()
{
    printf("Searching for physical drives...\r\n\r\n");
    DWORD bytes;
    TCHAR diskName[MAX_PATH];
    HANDLE hDisk;
    int selectedDisk = -1;
    for (int i = 0; i < 255; i++)
    {
        hDisk = OpenDisk(i);
        if (hDisk == INVALID_HANDLE_VALUE)
            continue;
        printf("Information for Device %d\r\n", i);

        DISK_GEOMETRY pdg = { 0 };
        if (!DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &pdg, sizeof(pdg), &bytes, NULL))
        {
            printf("error: Get disk info failed %d\r\n", GetLastError());
            continue;
        }
        ULONGLONG DiskSize = pdg.Cylinders.QuadPart 
            * (ULONG)pdg.TracksPerCylinder 
            * (ULONG)pdg.SectorsPerTrack 
            * (ULONG)pdg.BytesPerSector;
        CloseHandle(hDisk);
        printf("\tByte/Sec %d, Cylinders: %d, Sector/Track %d, Track/Cylinder %d\r\n", 
            pdg.BytesPerSector, pdg.Cylinders, pdg.SectorsPerTrack, pdg.TracksPerCylinder);
        printf("\tDevice:%d, Media Type:%d Disk Size: %.2f (Gb)\r\n\r\n", 
            i, pdg.MediaType, (double)DiskSize / (1024.0f * 1024.0f * 1024.0f));
    }
    printf("Which disk do you want to write?\r\n");
    printf("(Be careful, wrong disk number could corrupt your entire disk!)\r\n");
    scanf("%d", &selectedDisk);
    return selectedDisk;
}

int _tmain(int argc, _TCHAR* argv[])
{   
    if (argc != 2)
    {
        PrintHelp();
        return 0;
    }
    
    int devID = PrintDisks();
    if (devID != -1)
    {
        HANDLE hDisk = OpenDisk(devID);
        BYTE buffer[65536];
        DWORD byteRead, byteWrite, status;
        ULONGLONG totalWrite = 0;
        LARGE_INTEGER fileSize;
        HANDLE hImage = CreateFile(argv[1], 
            GENERIC_READ, FILE_SHARE_READ, NULL, 
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (hImage == INVALID_HANDLE_VALUE)
        {
            printf("error: Open file failed %d", GetLastError());
            return -1;
        }

        GetFileSizeEx(hImage, &fileSize);
        while (ReadFile(hImage, buffer, 65536, &byteRead, NULL) && byteRead > 0)
        {
            if (!WriteFile(hDisk, buffer, byteRead, &byteWrite, NULL))
            {
                printf("error: Write to disk failed %d", GetLastError());
                break;
            }
            else
            {
                printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
                totalWrite += byteRead;
                printf("%f%%", totalWrite * 100.0f / fileSize.QuadPart);
            }
        }
        CloseHandle(hImage);
        CloseHandle(hDisk);
    }
    return 0;
}