mirror of
https://github.com/nomi-san/parsec-vdd.git
synced 2026-08-07 13:50:45 +00:00
add core api
This commit is contained in:
@@ -1,214 +0,0 @@
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <tchar.h>
|
||||
#include <strsafe.h>
|
||||
#include <Windows.h>
|
||||
#include <SetupAPI.h>
|
||||
|
||||
#pragma comment(lib, "Setupapi.lib")
|
||||
|
||||
BOOLEAN GetDevicePath2(
|
||||
_In_ LPCGUID InterfaceGuid,
|
||||
_Out_writes_(BufLen) PTCHAR DevicePath,
|
||||
_In_ size_t BufLen
|
||||
)
|
||||
{
|
||||
HANDLE hDevice = INVALID_HANDLE_VALUE;
|
||||
PSP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData = NULL;
|
||||
ULONG predictedLength = 0;
|
||||
ULONG requiredLength = 0;
|
||||
HDEVINFO hardwareDeviceInfo;
|
||||
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
|
||||
BOOLEAN status = FALSE;
|
||||
HRESULT hr;
|
||||
|
||||
hardwareDeviceInfo = SetupDiGetClassDevs(
|
||||
InterfaceGuid,
|
||||
NULL, // Define no enumerator (global)
|
||||
NULL, // Define no
|
||||
(DIGCF_PRESENT | // Only Devices present
|
||||
DIGCF_DEVICEINTERFACE)); // Function class devices.
|
||||
if (INVALID_HANDLE_VALUE == hardwareDeviceInfo)
|
||||
{
|
||||
printf("Idd device: SetupDiGetClassDevs failed, last error 0x%x\n", GetLastError());
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
deviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
if (!SetupDiEnumDeviceInterfaces(hardwareDeviceInfo,
|
||||
0, // No care about specific PDOs
|
||||
InterfaceGuid,
|
||||
0, //
|
||||
&deviceInterfaceData))
|
||||
{
|
||||
printf("Idd device: SetupDiEnumDeviceInterfaces failed, last error 0x%x\n", GetLastError());
|
||||
goto Clean0;
|
||||
}
|
||||
|
||||
//
|
||||
// Allocate a function class device data structure to receive the
|
||||
// information about this particular device.
|
||||
//
|
||||
SetupDiGetDeviceInterfaceDetail(
|
||||
hardwareDeviceInfo,
|
||||
&deviceInterfaceData,
|
||||
NULL, // probing so no output buffer yet
|
||||
0, // probing so output buffer length of zero
|
||||
&requiredLength,
|
||||
NULL);//not interested in the specific dev-node
|
||||
|
||||
if (ERROR_INSUFFICIENT_BUFFER != GetLastError())
|
||||
{
|
||||
printf("Idd device: SetupDiGetDeviceInterfaceDetail failed, last error 0x%x\n", GetLastError());
|
||||
goto Clean0;
|
||||
}
|
||||
|
||||
predictedLength = requiredLength;
|
||||
deviceInterfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)HeapAlloc(
|
||||
GetProcessHeap(),
|
||||
HEAP_ZERO_MEMORY,
|
||||
predictedLength
|
||||
);
|
||||
|
||||
if (deviceInterfaceDetailData)
|
||||
{
|
||||
deviceInterfaceDetailData->cbSize =
|
||||
sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Idd device: HeapAlloc failed, last error 0x%x\n", GetLastError());
|
||||
goto Clean0;
|
||||
}
|
||||
|
||||
if (!SetupDiGetDeviceInterfaceDetail(
|
||||
hardwareDeviceInfo,
|
||||
&deviceInterfaceData,
|
||||
deviceInterfaceDetailData,
|
||||
predictedLength,
|
||||
&requiredLength,
|
||||
NULL))
|
||||
{
|
||||
printf("Idd device: SetupDiGetDeviceInterfaceDetail failed, last error 0x%x\n", GetLastError());
|
||||
goto Clean1;
|
||||
}
|
||||
|
||||
hr = StringCchCopy(DevicePath, BufLen, deviceInterfaceDetailData->DevicePath);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
printf("Error: StringCchCopy failed with HRESULT 0x%x", hr);
|
||||
status = FALSE;
|
||||
goto Clean1;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = TRUE;
|
||||
}
|
||||
|
||||
Clean1:
|
||||
(VOID)HeapFree(GetProcessHeap(), 0, deviceInterfaceDetailData);
|
||||
Clean0:
|
||||
(VOID)SetupDiDestroyDeviceInfoList(hardwareDeviceInfo);
|
||||
return status;
|
||||
}
|
||||
|
||||
HANDLE DeviceOpenHandle(const GUID &devGuid)
|
||||
{
|
||||
// const int maxDevPathLen = 256;
|
||||
TCHAR devicePath[256] = { 0 };
|
||||
HANDLE hDevice = INVALID_HANDLE_VALUE;
|
||||
do
|
||||
{
|
||||
if (FALSE == GetDevicePath2(
|
||||
&devGuid,
|
||||
devicePath,
|
||||
sizeof(devicePath) / sizeof(devicePath[0])))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (_tcslen(devicePath) == 0)
|
||||
{
|
||||
printf("GetDevicePath got empty device path\n");
|
||||
break;
|
||||
}
|
||||
|
||||
_tprintf(_T("Idd device: try open %s\n"), devicePath);
|
||||
hDevice = CreateFile(
|
||||
devicePath,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
// FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
0,
|
||||
NULL, // no SECURITY_ATTRIBUTES structure
|
||||
OPEN_EXISTING, // No special create flags
|
||||
0, // No special attributes
|
||||
NULL
|
||||
);
|
||||
if (hDevice == INVALID_HANDLE_VALUE || hDevice == NULL)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
printf("CreateFile failed 0x%lx\n", error);
|
||||
}
|
||||
} while (0);
|
||||
|
||||
return hDevice;
|
||||
}
|
||||
|
||||
enum VddCtlCode
|
||||
{
|
||||
IOCTL_VDD_CONNECT = 0x22A008,
|
||||
IOCTL_VDD_ADD = 0x22E004,
|
||||
IOCTL_VDD_UPDATE = 0x22A00C,
|
||||
};
|
||||
|
||||
void VddIoCtl(HANDLE vdd, VddCtlCode code)
|
||||
{
|
||||
BYTE InBuffer[32]{};
|
||||
int OutBuffer = 0;
|
||||
OVERLAPPED Overlapped{};
|
||||
DWORD NumberOfBytesTransferred;
|
||||
|
||||
Overlapped.hEvent = CreateEventW(NULL, NULL, NULL, NULL);
|
||||
DeviceIoControl(vdd, code, InBuffer, _countof(InBuffer), &OutBuffer, sizeof(OutBuffer), NULL, &Overlapped);
|
||||
GetOverlappedResult(vdd, &Overlapped, &NumberOfBytesTransferred, TRUE);
|
||||
|
||||
if (Overlapped.hEvent && Overlapped.hEvent != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(Overlapped.hEvent);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const GUID PARSEC_VDD_DEVINTERFACE = \
|
||||
{ 0x00b41627, 0x04c4, 0x429e, { 0xa2, 0x6e, 0x02, 0x65, 0xcf, 0x50, 0xc8, 0xfa } };
|
||||
|
||||
// try to get device handle with GUID
|
||||
HANDLE vdd = DeviceOpenHandle(PARSEC_VDD_DEVINTERFACE);
|
||||
if (!vdd || vdd == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
printf("failed to get ParsecVDD device handle.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// connect & plug in
|
||||
VddIoCtl(vdd, IOCTL_VDD_CONNECT);
|
||||
VddIoCtl(vdd, IOCTL_VDD_UPDATE);
|
||||
VddIoCtl(vdd, IOCTL_VDD_ADD);
|
||||
VddIoCtl(vdd, IOCTL_VDD_UPDATE);
|
||||
|
||||
// work for 5s
|
||||
const int kDuration = 5000;
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
while (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - startTime).count() < kDuration)
|
||||
{
|
||||
// update each 100ms
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
VddIoCtl(vdd, IOCTL_VDD_UPDATE);
|
||||
}
|
||||
|
||||
// disconnect
|
||||
VddIoCtl(vdd, IOCTL_VDD_CONNECT);
|
||||
CloseHandle(vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include "parsec-vdd.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace parsec_vdd;
|
||||
|
||||
int main()
|
||||
{
|
||||
// Check driver status.
|
||||
DeviceStatus status = QueryDeviceStatus(&VDD_CLASS_GUID, VDD_HARDWARE_ID);
|
||||
if (status != DEVICE_OK)
|
||||
{
|
||||
printf("Parsec VDD device is not OK, got status %d.\n", status);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Obtain device handle.
|
||||
HANDLE vdd = OpenDeviceHandle(&VDD_ADAPTER_GUID);
|
||||
if (vdd == NULL || vdd == INVALID_HANDLE_VALUE) {
|
||||
printf("Failed to obtain the device handle.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool running = true;
|
||||
std::vector<int> displays;
|
||||
|
||||
// Side thread for updating vdd.
|
||||
std::thread updater([&running, vdd] {
|
||||
while (running) {
|
||||
VddUpdate(vdd);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
});
|
||||
|
||||
updater.detach();
|
||||
|
||||
// Print out guide.
|
||||
printf("Press A to add a virtual display.\n");
|
||||
printf("Press R to remove the last added.\n");
|
||||
printf("Press Q to quit (then unplug all).\n\n");
|
||||
|
||||
while (running) {
|
||||
switch (_getch()) {
|
||||
// quit
|
||||
case 'q':
|
||||
running = false;
|
||||
break;
|
||||
// add display
|
||||
case 'a':
|
||||
if (displays.size() < VDD_MAX_DISPLAYS) {
|
||||
int index = VddAddDisplay(vdd);
|
||||
displays.push_back(index);
|
||||
printf("Added a new virtual display, index: %d.\n", index);
|
||||
}
|
||||
else {
|
||||
printf("Limit exceeded (%d), could not add more virtual displays.\n", VDD_MAX_DISPLAYS);
|
||||
}
|
||||
break;
|
||||
// remove display
|
||||
case 'r':
|
||||
if (displays.size() > 0) {
|
||||
int index = displays.back();
|
||||
VddRemoveDisplay(vdd, index);
|
||||
displays.pop_back();
|
||||
printf("Removed the last virtual display, index: %d.\n", index);
|
||||
}
|
||||
else {
|
||||
printf("No added virtual displays.\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all before exiting.
|
||||
for (int index : displays) {
|
||||
VddRemoveDisplay(vdd, index);
|
||||
}
|
||||
|
||||
if (updater.joinable()) {
|
||||
updater.join();
|
||||
}
|
||||
|
||||
// Close the device handle.
|
||||
CloseDeviceHandle(vdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
+350
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Nguyen Duy <wuuyi123@gmail.com> All rights reserved.
|
||||
* GitHub repo: https://github.com/nomi-san/parsec-vdd/
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PARSEC_VDD_H
|
||||
#define __PARSEC_VDD_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <setupapi.h>
|
||||
#include <cfgmgr32.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "cfgmgr32.lib")
|
||||
#pragma comment(lib, "setupapi.lib")
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace parsec_vdd
|
||||
{
|
||||
#endif
|
||||
|
||||
// Device helper.
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
typedef enum {
|
||||
DEVICE_OK = 0, // Ready to use
|
||||
DEVICE_INACCESSIBLE, // Inaccessible
|
||||
DEVICE_UNKNOW, // Unknow status
|
||||
DEVICE_UNKNOW_PROBLEM, // Unknow problem
|
||||
DEVICE_DISABLED, // Device is disabled
|
||||
DEVICE_DRIVER_ERROR, // Device encountered error
|
||||
DEVICE_RESTART_REQUIRED, // Must restart PC to use (could ignore but would have issue)
|
||||
DEVICE_DISABLED_SERVICE, // Service is disabled
|
||||
DEVICE_NOT_INSTALLED // Driver is not installed
|
||||
} DeviceStatus;
|
||||
|
||||
/**
|
||||
* Query the driver status.
|
||||
*
|
||||
* @param classGuid The GUID of the class.
|
||||
* @param deviceId The device/hardware ID of the driver.
|
||||
* @return DeviceStatus
|
||||
*/
|
||||
static DeviceStatus QueryDeviceStatus(const GUID *classGuid, const char *deviceId)
|
||||
{
|
||||
DeviceStatus status = DEVICE_INACCESSIBLE;
|
||||
|
||||
SP_DEVINFO_DATA devInfoData;
|
||||
ZeroMemory(&devInfoData, sizeof(SP_DEVINFO_DATA));
|
||||
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||
|
||||
HDEVINFO devInfo = SetupDiGetClassDevsA(classGuid, NULL, NULL, DIGCF_PRESENT);
|
||||
|
||||
if (devInfo != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
BOOL foundProp = FALSE;
|
||||
UINT deviceIndex = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (!SetupDiEnumDeviceInfo(devInfo, deviceIndex, &devInfoData))
|
||||
break;
|
||||
|
||||
DWORD requiredSize = 0;
|
||||
SetupDiGetDeviceRegistryPropertyA(devInfo, &devInfoData,
|
||||
SPDRP_HARDWAREID, NULL, NULL, 0, &requiredSize);
|
||||
|
||||
if (requiredSize > 0)
|
||||
{
|
||||
DWORD regDataType = 0;
|
||||
LPBYTE propBuffer = (LPBYTE)calloc(1, requiredSize);
|
||||
|
||||
if (SetupDiGetDeviceRegistryPropertyA(
|
||||
devInfo,
|
||||
&devInfoData,
|
||||
SPDRP_HARDWAREID,
|
||||
®DataType,
|
||||
propBuffer,
|
||||
requiredSize,
|
||||
&requiredSize))
|
||||
{
|
||||
if (regDataType == REG_SZ || regDataType == REG_MULTI_SZ)
|
||||
{
|
||||
for (LPCSTR cp = (LPCSTR)propBuffer; ; cp += lstrlenA(cp) + 1)
|
||||
{
|
||||
if (!cp || *cp == 0 || cp >= (LPCSTR)(propBuffer + requiredSize))
|
||||
{
|
||||
status = DEVICE_NOT_INSTALLED;
|
||||
goto except;
|
||||
}
|
||||
|
||||
if (lstrcmpA(deviceId, cp) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
foundProp = TRUE;
|
||||
ULONG devStatus, devProblemNum;
|
||||
|
||||
if (CM_Get_DevNode_Status(&devStatus, &devProblemNum, devInfoData.DevInst, 0) != CR_SUCCESS)
|
||||
{
|
||||
status = DEVICE_NOT_INSTALLED;
|
||||
goto except;
|
||||
}
|
||||
|
||||
if ((devStatus & (DN_DRIVER_LOADED | DN_STARTED)) != 0)
|
||||
{
|
||||
status = DEVICE_OK;
|
||||
}
|
||||
else if ((devStatus & DN_HAS_PROBLEM) != 0)
|
||||
{
|
||||
switch (devProblemNum)
|
||||
{
|
||||
case CM_PROB_NEED_RESTART:
|
||||
status = DEVICE_RESTART_REQUIRED;
|
||||
break;
|
||||
case CM_PROB_DISABLED:
|
||||
case CM_PROB_HARDWARE_DISABLED:
|
||||
status = DEVICE_DISABLED;
|
||||
break;
|
||||
case CM_PROB_DISABLED_SERVICE:
|
||||
status = DEVICE_DISABLED_SERVICE;
|
||||
break;
|
||||
default:
|
||||
if (devProblemNum == CM_PROB_FAILED_POST_START)
|
||||
status = DEVICE_DRIVER_ERROR;
|
||||
else
|
||||
status = DEVICE_UNKNOW_PROBLEM;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status = DEVICE_UNKNOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
except:
|
||||
free(propBuffer);
|
||||
}
|
||||
|
||||
++deviceIndex;
|
||||
} while (!foundProp);
|
||||
|
||||
if (!foundProp && GetLastError() != 0)
|
||||
status = DEVICE_NOT_INSTALLED;
|
||||
|
||||
SetupDiDestroyDeviceInfoList(devInfo);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain the device handle.
|
||||
* Returns NULL or INVALID_HANDLE_VALUE if fails, otherwise a valid handle.
|
||||
* Should call CloseDeviceHandle to close this handle after use.
|
||||
*
|
||||
* @param interfaceGuid The adapter/interface GUID of the target device.
|
||||
* @return HANDLE
|
||||
*/
|
||||
static HANDLE OpenDeviceHandle(const GUID *interfaceGuid)
|
||||
{
|
||||
HANDLE handle = INVALID_HANDLE_VALUE;
|
||||
HDEVINFO devInfo = SetupDiGetClassDevsA(interfaceGuid,
|
||||
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||
|
||||
if (devInfo != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
SP_DEVICE_INTERFACE_DATA devInterface;
|
||||
ZeroMemory(&devInterface, sizeof(SP_DEVICE_INTERFACE_DATA));
|
||||
devInterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||
|
||||
for (DWORD i = 0; SetupDiEnumDeviceInterfaces(devInfo, NULL, interfaceGuid, i, &devInterface); ++i)
|
||||
{
|
||||
DWORD detailSize = 0;
|
||||
SetupDiGetDeviceInterfaceDetailA(devInfo, &devInterface, NULL, 0, &detailSize, NULL);
|
||||
|
||||
SP_DEVICE_INTERFACE_DETAIL_DATA_A *detail = (SP_DEVICE_INTERFACE_DETAIL_DATA_A *)calloc(1, detailSize);
|
||||
detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
|
||||
|
||||
if (SetupDiGetDeviceInterfaceDetailA(devInfo, &devInterface, detail, detailSize, &detailSize, NULL))
|
||||
{
|
||||
handle = CreateFileA(detail->DevicePath,
|
||||
GENERIC_READ | GENERIC_WRITE,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED | FILE_FLAG_WRITE_THROUGH,
|
||||
NULL);
|
||||
|
||||
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
}
|
||||
|
||||
free(detail);
|
||||
}
|
||||
|
||||
SetupDiDestroyDeviceInfoList(devInfo);
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
/* Release the device handle */
|
||||
static void CloseDeviceHandle(HANDLE handle)
|
||||
{
|
||||
if (handle != NULL && handle != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
||||
// Parsec VDD core.
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
// Display name info.
|
||||
static const char *VDD_DISPLAY_ID = "PSCCDD0"; // You will see it in registry (HKLM\SYSTEM\CurrentControlSet\Enum\DISPLAY)
|
||||
static const char *VDD_DISPLAY_NAME = "ParsecVDA"; // You will see it in the [Advanced display settings] tab.
|
||||
|
||||
// Apdater GUID to obtain the device handle.
|
||||
// {00b41627-04c4-429e-a26e-0265cf50c8fa}
|
||||
static const GUID VDD_ADAPTER_GUID = { 0x00b41627, 0x04c4, 0x429e, { 0xa2, 0x6e, 0x02, 0x65, 0xcf, 0x50, 0xc8, 0xfa } };
|
||||
static const char *VDD_ADAPTER_NAME = "Parsec Virtual Display Adapter";
|
||||
|
||||
// Class and hwid to query device status.
|
||||
// {4d36e968-e325-11ce-bfc1-08002be10318}
|
||||
static const GUID VDD_CLASS_GUID = { 0x4d36e968, 0xe325, 0x11ce, { 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } };
|
||||
static const char *VDD_HARDWARE_ID = "Root\\Parsec\\VDA";
|
||||
|
||||
// Actually up to 16 devices could be created per adapter
|
||||
// so just use a half to avoid plugging lag.
|
||||
static const int VDD_MAX_DISPLAYS = 8;
|
||||
|
||||
// Core IoControl codes, see usage below.
|
||||
typedef enum {
|
||||
VDD_IOCTL_ADD = 0x0022e004,
|
||||
VDD_IOCTL_REMOVE = 0x0022a008,
|
||||
VDD_IOCTL_UPDATE = 0x0022a00c,
|
||||
VDD_IOCTL_VERSION = 0x0022e010,
|
||||
} VddCtlCode;
|
||||
|
||||
// Generic DeviceIoControl for all IoControl codes.
|
||||
static DWORD VddIoControl(HANDLE vdd, VddCtlCode code, const void *data, size_t size)
|
||||
{
|
||||
if (vdd == NULL || vdd == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
BYTE InBuffer[32];
|
||||
ZeroMemory(InBuffer, sizeof(InBuffer));
|
||||
|
||||
OVERLAPPED Overlapped;
|
||||
ZeroMemory(&Overlapped, sizeof(OVERLAPPED));
|
||||
|
||||
DWORD OutBuffer = 0;
|
||||
DWORD NumberOfBytesTransferred;
|
||||
|
||||
if (data != NULL && size > 0)
|
||||
memcpy(InBuffer, data, (size < sizeof(InBuffer)) ? size : sizeof(InBuffer));
|
||||
|
||||
Overlapped.hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
DeviceIoControl(vdd, (DWORD)code, InBuffer, sizeof(InBuffer), &OutBuffer, sizeof(DWORD), NULL, &Overlapped);
|
||||
|
||||
GetOverlappedResult(vdd, &Overlapped, &NumberOfBytesTransferred, TRUE);
|
||||
|
||||
if (Overlapped.hEvent != NULL)
|
||||
CloseHandle(Overlapped.hEvent);
|
||||
|
||||
return OutBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query VDD minor version.
|
||||
*
|
||||
* @param vdd The device handle of VDD.
|
||||
* @return The number of minor version.
|
||||
*/
|
||||
static int VddVersion(HANDLE vdd)
|
||||
{
|
||||
int minor = VddIoControl(vdd, VDD_IOCTL_VERSION, NULL, 0);
|
||||
return minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update/ping to VDD.
|
||||
* Should call this function in a side thread for each
|
||||
* less than 100ms to keep all added virtual displays alive.
|
||||
*
|
||||
* @param vdd The device handle of VDD.
|
||||
*/
|
||||
static void VddUpdate(HANDLE vdd)
|
||||
{
|
||||
VddIoControl(vdd, VDD_IOCTL_UPDATE, NULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add/plug a virtual display.
|
||||
*
|
||||
* @param vdd The device handle of VDD.
|
||||
* @return The index of the added display.
|
||||
*/
|
||||
static int VddAddDisplay(HANDLE vdd)
|
||||
{
|
||||
int idx = VddIoControl(vdd, VDD_IOCTL_ADD, NULL, 0);
|
||||
VddUpdate(vdd);
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove/unplug a virtual display.
|
||||
*
|
||||
* @param vdd The device handle of VDD.
|
||||
* @param index The index of the display will be removed.
|
||||
*/
|
||||
static void VddRemoveDisplay(HANDLE vdd, int index)
|
||||
{
|
||||
// 16-bit BE index
|
||||
UINT16 indexData = ((index & 0xFF) << 8) | ((index >> 8) & 0xFF);
|
||||
|
||||
VddIoControl(vdd, VDD_IOCTL_REMOVE, &indexData, sizeof(indexData));
|
||||
VddUpdate(vdd);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user