How to properly use SetDisplayConfig with multiple monitors? - c++

I`m creating a small programm that will include all displays in desktop(extended mode) or disable all secondary displays (displays can be connected to gpus and integrated graphics).
This programm is for Windows 7, so relying on information from internet i decided to use CCD APIs, but encounted a problem with SetDisplayConfig() function.
For example this code to turn off all secondary displays works perfectly, as 'i' increments one of displays turns off:
UINT32 PathCount = 0; //path count
UINT32 ModeCount = 0; //mode count
HRESULT hr;
hr = GetDisplayConfigBufferSizes(QDC_ALL_PATHS, &PathCount, &ModeCount);
std::vector<DISPLAYCONFIG_PATH_INFO> pathArray(PathCount);
std::vector<DISPLAYCONFIG_MODE_INFO> modeArray(ModeCount);
hr = QueryDisplayConfig(QDC_ALL_PATHS, &PathCount, &pathArray[0], &ModeCount, &modeArray[0], NULL);
for (int i = 1; i < PathCount;i++)
{
if(pathArray[i].flags != 0)
{
pathArray[i].flags = 0;
hr = SetDisplayConfig(PathCount, &pathArray[0], ModeCount, &modeArray[0], SDC_APPLY | SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_ALLOW_CHANGES);
}
}
To extend a display i found this code:
SetDisplayConfig(0, NULL, 0, NULL, SDC_TOPOLOGY_EXTEND | SDC_APPLY);
This function with this specific parameters works, but it targeting only my second display which is conected to gpu as my primary display, but not the third display which is conected to motherboard ( only after i phisicly disconect my second display from gpu, this function works with display conected to motherboard).
I tried to use
for (int i = 1; i < PathCount;i++)
{
if(pathArray[i].flags != 1)
{
pathArray[i].flags = 1;
hr = SetDisplayConfig(PathCount, &pathArray[0], ModeCount, &modeArray[0], SDC_TOPOLOGY_EXTEND | SDC_APPLY | SDC_PATH_PERSIST_IF_REQUIRED);
}
}
but receiving ERROR_ADAP_HDW_ERR error
So i`m asking to help me. How to target specific display(or all displays at once) using SetDisplayConfig() finction with 'SDC_TOPOLOGY_EXTEND' flag, or there is another approach to resolve this problem ?

So i made it, it doesn't work with some exotic display setup(like multiple usb displays), but it works, and all displays are added to desktop(or desktop is streched to all displays)
How i done it ?
Microsoft detours helped me, by monitoring what system settings in windows 10 do when i add display to desktop, it just sets most important properties to default values.
commented parts works only on win 10.
HRESULT hr = S_OK;
UINT32 NumPathArrayElements = 0;
UINT32 NumModeInfoArrayElements = 0;
//LONG error = GetDisplayConfigBufferSizes((QDC_ALL_PATHS | QDC_VIRTUAL_MODE_AWARE), &NumPathArrayElements, &NumModeInfoArrayElements);
hr = GetDisplayConfigBufferSizes((QDC_ALL_PATHS), &NumPathArrayElements, &NumModeInfoArrayElements);
std::vector<DISPLAYCONFIG_PATH_INFO> PathInfoArray2(NumPathArrayElements);
std::vector<DISPLAYCONFIG_MODE_INFO> ModeInfoArray2(NumModeInfoArrayElements);
//error = QueryDisplayConfig((QDC_ALL_PATHS | QDC_VIRTUAL_MODE_AWARE), &NumPathArrayElements, &PathInfoArray2[0], &NumModeInfoArrayElements, &ModeInfoArray2[0], NULL);
hr = QueryDisplayConfig((QDC_ALL_PATHS), &NumPathArrayElements, &PathInfoArray2[0], &NumModeInfoArrayElements, &ModeInfoArray2[0], NULL);
struct displaySourcePair
{
std::wstring displayName;
UINT32 displayId;
};
std::vector<displaySourcePair> ocupiedDisplays;
if (hr == S_OK)
{
DISPLAYCONFIG_SOURCE_DEVICE_NAME SourceName = {};
SourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
SourceName.header.size = sizeof(SourceName);
DISPLAYCONFIG_TARGET_PREFERRED_MODE PreferedMode = {};
PreferedMode.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_PREFERRED_MODE;
PreferedMode.header.size = sizeof(PreferedMode);
int newId = 0;
for (UINT32 i = 0; i < NumPathArrayElements; i++)
{
bool match = false;
SourceName.header.adapterId = PathInfoArray2[i].sourceInfo.adapterId;
SourceName.header.id = PathInfoArray2[i].sourceInfo.id;
PreferedMode.header.adapterId = PathInfoArray2[i].targetInfo.adapterId;
PreferedMode.header.id = PathInfoArray2[i].targetInfo.id;
hr = HRESULT_FROM_WIN32(DisplayConfigGetDeviceInfo(&SourceName.header));
hr = HRESULT_FROM_WIN32(DisplayConfigGetDeviceInfo(&PreferedMode.header));
if (hr == S_OK)
{
if ((PathInfoArray2[i].flags & DISPLAYCONFIG_PATH_ACTIVE) == true)
{
std::wstring str = std::wstring(SourceName.viewGdiDeviceName);
displaySourcePair tmpStruct;
tmpStruct.displayId = PreferedMode.header.id;
tmpStruct.displayName = str;
ocupiedDisplays.push_back(tmpStruct);
}
for (int k = 0; k < ocupiedDisplays.size(); k++)
{
std::wstring str = std::wstring(SourceName.viewGdiDeviceName);
if (ocupiedDisplays[k].displayName == str || ocupiedDisplays[k].displayId == PreferedMode.header.id)
{
match = true;
}
}
if (match == false && PathInfoArray2[i].targetInfo.targetAvailable == 1)
{
PathInfoArray2[i].flags |= DISPLAYCONFIG_PATH_ACTIVE;
std::wstring str = std::wstring(SourceName.viewGdiDeviceName);
displaySourcePair tmpStruct;
tmpStruct.displayId = PreferedMode.header.id;
tmpStruct.displayName = str;
ocupiedDisplays.push_back(tmpStruct);
}
if (PathInfoArray2[i].targetInfo.targetAvailable == 1)
{
PathInfoArray2[i].sourceInfo.id = newId;
newId++;
}
if (PathInfoArray2[i].targetInfo.id != PreferedMode.header.id)
{
PathInfoArray2[i].targetInfo.id = PreferedMode.header.id;
}
PathInfoArray2[i].sourceInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
PathInfoArray2[i].targetInfo.modeInfoIdx = DISPLAYCONFIG_PATH_MODE_IDX_INVALID;
}
}
//hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_VALIDATE | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES | SDC_VIRTUAL_MODE_AWARE));
//hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_APPLY | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES | SDC_VIRTUAL_MODE_AWARE));
hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_VALIDATE | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES));
hr = SetDisplayConfig(NumPathArrayElements, &PathInfoArray2[0], 0, NULL, (SDC_APPLY | SDC_TOPOLOGY_SUPPLIED | SDC_ALLOW_PATH_ORDER_CHANGES));
}

Related

DxgiOuputDuplication texture frames to DirectX VideoProcessor

I was playing with this project DirectXVideoScreen because I am trying to find a way to convert RGBA to NV12 so I can make a video using the h264 transformation of my GPU. (You can only use the transformation when you have NV12 or YUV2 color format)
I have tried doing the color conversion using the Windows Media Foundation MFT (Video processor) but I am trying to go directly and use DirectX in order to have more control.
While you duplicate the screen the texture you get back is of type DXGI_FORMAT_B8G8R8A8_UNORM and I have to transform it to DXGI_FORMAT_NV12.
So I took the code from the project "D3D11VideoProcessor" and added the code from the "SimpleDesktopDuplication" project.
It is like this.
CD3D11VideoProcessor cD3D11VideoProcessor;
CSimpleDesktopDuplication cSimpleDesktopDuplication;
ID3D11Texture2D* output = NULL;
if (cSimpleDesktopDuplication.InitDesktopDuplication() == S_OK)
{
output = cSimpleDesktopDuplication.ProcessDesktopDuplication();
}
if(cD3D11VideoProcessor.InitDXVA2(2560,1440, DXGI_FORMAT_NV12) == S_OK)
{
cD3D11VideoProcessor.ProcessImageConversion(output, CONVERTED_IMAGE);
}
The problem (I think?) is that
m_pDXGIOutputDuplication->AcquireNextFrame(500, &FrameInfo, &pDXGIResource));
pDXGIResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&pScreenShotTexture2D)));
Returns a texture with the following flags.
Width: a00
Height: 5a0
MipLevels: 1
ArraySize: 1
Format: 57
Usage: 0
BindFlags: 28 // D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE
CPUAccessFlags: 0
MiscFlags: 2900
And here the code fails while executing
m_pD3D11VideoContext->VideoProcessorBlt(m_pD3D11VideoProcessor, pD3D11VideoProcessorOutputView, 0, 1, &StreamData)
HRESULT CD3D11VideoProcessor::ProcessImageConversion(ID3D11Texture2D* outputDuplicationTexture, LPCWSTR wszOutputImageFile)
{
HRESULT hr = S_OK;
ID3D11VideoProcessorInputView* pD3D11VideoProcessorInputViewIn = NULL;
ID3D11VideoProcessorOutputView* pD3D11VideoProcessorOutputView = NULL;
ID3D11VideoDevice* pD3D11VideoDevice = NULL;
ID3D11Texture2D* pOutTexture2D = NULL;
IF_FAILED_RETURN(m_pD3D11VideoContext == NULL ? E_UNEXPECTED : S_OK);
try
{
IF_FAILED_THROW(m_pD3D11Device->QueryInterface(__uuidof(ID3D11VideoDevice), reinterpret_cast<void**>(&pD3D11VideoDevice)));
D3D11_TEXTURE2D_DESC desc2D;
desc2D.Width = 2560;
desc2D.Height = 1440;
desc2D.MipLevels = 1;
desc2D.ArraySize = 1;
desc2D.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc2D.SampleDesc.Count = 1;
desc2D.SampleDesc.Quality = 0;
desc2D.Usage = D3D11_USAGE_DEFAULT;
desc2D.BindFlags = D3D11_BIND_RENDER_TARGET;
desc2D.CPUAccessFlags = 0;
desc2D.MiscFlags = 0;
D3D11_TEXTURE2D_DESC desc2Dtmp;
outputDuplicationTexture->GetDesc(&desc2Dtmp);
printf("Width: %x\n", desc2Dtmp.Width);
printf("Height: %x\n", desc2Dtmp.Height);
printf("MipLevels: %x\n", desc2Dtmp.MipLevels);
printf("ArraySize: %x\n", desc2Dtmp.ArraySize);
printf("Format: %x\n", desc2Dtmp.Format);
printf("Usage: %x\n", desc2Dtmp.Usage);
printf("BindFlags: %x\n", desc2Dtmp.BindFlags);
printf("CPUAccessFlags: %x\n", desc2Dtmp.CPUAccessFlags);
printf("MiscFlags: %x\n", desc2Dtmp.MiscFlags);
D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC pInDesc;
ZeroMemory(&pInDesc, sizeof(pInDesc));
pInDesc.FourCC = 0;
pInDesc.ViewDimension = D3D11_VPIV_DIMENSION_TEXTURE2D;
pInDesc.Texture2D.MipSlice = 0;
pInDesc.Texture2D.ArraySlice = 0;
IF_FAILED_THROW(pD3D11VideoDevice->CreateVideoProcessorInputView(outputDuplicationTexture, m_pD3D11VideoProcessorEnumerator, &pInDesc, &pD3D11VideoProcessorInputViewIn));
desc2D.Format = DXGI_FORMAT_NV12;
IF_FAILED_THROW(m_pD3D11Device->CreateTexture2D(&desc2D, NULL, &pOutTexture2D));
D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC pOutDesc;
ZeroMemory(&pOutDesc, sizeof(pOutDesc));
pOutDesc.ViewDimension = D3D11_VPOV_DIMENSION_TEXTURE2D;
pOutDesc.Texture2D.MipSlice = 0;
IF_FAILED_THROW(pD3D11VideoDevice->CreateVideoProcessorOutputView(pOutTexture2D, m_pD3D11VideoProcessorEnumerator, &pOutDesc, &pD3D11VideoProcessorOutputView));
D3D11_VIDEO_PROCESSOR_STREAM StreamData;
ZeroMemory(&StreamData, sizeof(StreamData));
StreamData.Enable = TRUE;
StreamData.OutputIndex = 0;
StreamData.InputFrameOrField = 0;
StreamData.PastFrames = 0;
StreamData.FutureFrames = 0;
StreamData.ppPastSurfaces = NULL;
StreamData.ppFutureSurfaces = NULL;
StreamData.pInputSurface = pD3D11VideoProcessorInputViewIn;
StreamData.ppPastSurfacesRight = NULL;
StreamData.ppFutureSurfacesRight = NULL;
/*Error is here !!!*/
IF_FAILED_THROW(m_pD3D11VideoContext->VideoProcessorBlt(m_pD3D11VideoProcessor, pD3D11VideoProcessorOutputView, 0, 1, &StreamData));
IF_FAILED_THROW(CreateBmpFileFromYUVSurface(pOutTexture2D, wszOutputImageFile));
}
catch(HRESULT){}
SAFE_RELEASE(pOutTexture2D);
SAFE_RELEASE(outputDuplicationTexture);
SAFE_RELEASE(pD3D11VideoProcessorOutputView);
SAFE_RELEASE(pD3D11VideoProcessorInputViewIn);
SAFE_RELEASE(pD3D11VideoDevice);
return hr;
}
The error code is 0x80070057 meaning E_INVALIDARG One or more arguments are invalid.. I can not find the invalid arg :/
Can you help me ? or guide me ? Maybe I have to somehow change the ID3D11Texture2D* outputDuplicationTexture to match a simpler version of the texture (change misc params) ?
Thank you!

scanning pages using WIA or TWAIN

Edit: Are there any tutorials on how to use WIA or TWAIN in c++, that explain how to scan pages, adjust settings (DPI, using automatic feeder etc.) and save them as PNG files?
I'd like to use WIA to scan pages and store them as png files. If the scanner supports automatic feeding I'd also like to use that feature. Currently I am following the steps of this tutorial and am stuck at the section Transferring Image Data in WIA 2.0.
So far my scanner has been found and I am able to create the device, and an IWiaItem2* has been created. How can use it to scan at 300dpi and store the result as png file?
The tutorial is not clear about how to start the scan process or how to set dpi for scanning, so I hope someone can help me with the code.
This is essentially the code for getting all local devices:
bool init(IWiaDevMgr2* devMgr)
{
//creating the device manager
*devMgr = 0;
CoCreateInstance( CLSID_WiaDevMgr2, 0, CLSCTX_LOCAL_SERVER, IID_IWiaDevMgr2, (void**)&devMgr);
//enumerating wia devices
IEnumWIA_DEV_INFO* enumDevInfo = 0;
HRESULT hr = devMgr->EnumDeviceInfo( WIA_DEVINFO_ENUM_LOCAL, &enumDevInfo);
if(SUCCEEDED(hr))
{
//loop until an error occurs or end of list
while(hr == S_OK)
{
IWiaPropertyStorage* storage = 0;
hr = enumDevInfo->Next( 1, &storage, 0);
if(hr == S_OK)
{
readProperties(storage);
storage->Release();
storage = 0;
}
}
//set hr to ok, so no error code is returned
if(hr == S_FALSE) hr = S_OK;
enumDevInfo->Release();
enumDevInfo = 0;
}
return SUCCEEDED(hr);
}
void readProperties(IWiaPropertyStorage* storage)
{
PROPSPEC propSpec[2] = {0};
PROPVARIANT propVar[2] = {0};
const ULONG propCount = sizeof(propSpec) / sizeof(propSpec[0]);
propSpec[0].ulKind = PRSPEC_PROPID;
propSpec[0].propid = WIA_DIP_DEV_ID;
propSpec[1].ulKind = PRSPEC_PROPID;
propSpec[1].propid = WIA_DIP_DEV_NAME;
HRESULT hr = storage->ReadMultiple(propCount, propSpec, propVar);
if(SUCCEEDED(hr))
{
Device* dev = new Device(propVar[0].bstrVal, propVar[1].bstrVal);
devices.push_back( dev );
FreePropVariantArray( propCount, propVar );
}
}
Afterwards a device is initialized like this:
bool createDevice(BSTR id, IWiaItem2** item)
{
*item = 0;
HRESULT hr = devMgr->CreateDevice( 0, deviceId, item);
return SUCCEEDED(hr);
}
Then the items are enumerated:
bool enumerateItems(IWiaItem2* item)
{
LONG itemType = 0;
HRESULT hr = item->GetItemType(&itemType);
if(SUCCEEDED(hr))
{
if(itemType & WiaItemTypeFolder || itemType & WiaItemTypeHasAttachments)
{
IEnumWiaItem2* enumItem = 0;
hr = item->EnumChildItems(0, &enumItem );
while(hr == S_OK)
{
IWiaItem2* child = 0;
hr = enumItem->Next( 1, &child, 0 );
if(hr == S_OK)
{
hr = enumerateItems( child );
child->Release();
child = 0;
}
}
if(hr == S_FALSE) hr = S_OK;
enumItem->Release();
enumItem = 0;
}
}
return SUCCEEDED(hr);
}
Now that everything has been initialized I'd like to implement a scan function. However, the code provided at the tutorial is for transferring files and folders and not for scanning images.
void scanAndSaveAsPNG(IWiaItem2* item, unsigned int dpi, std::string targetPath)
{
}
EDIT:
I installed the latest version available of the scanner driver (WIA and TWAIN) and after checking the supported commands using this code
void printCommands(IWiaItem2* i)
{
IEnumWIA_DEV_CAPS* caps = 0;
HRESULT h = item->EnumDeviceCapabilities(WIA_DEVICE_COMMANDS, &caps);
if(SUCCEEDED(h))
{
ULONG count = 0;
caps->GetCount(&count);
if(count > 0)
{
WIA_DEV_CAP* cap = new WIA_DEV_CAP[ count ];
ULONG fetched;
caps->Next(count, cap, &fetched);
for(int i = 0; i < fetched; i++)
{
std::cout << bstr_t( cap[i].bstrName ) << "\n";
}
}
caps->Release();
}
}
I noticed it only lists WIA Synchronize command. I am not sure if I didn't initialize the device correctly, or if the device doesn't support all WIA commands although the driver is installed.
So unless this problem is solved I am alternatively also looking for the same code based on TWAIN.
You want to use IWiaItem2::DeviceCommand which sends a command to the image capture device. The list of commands you can send are listed here.

Fetching column data using IRow::GetColumns (OLE DB)(MSSQL)

In my application I am trying to read data from a VARCHAR(4000) column. Since the column is 4000 bytes, I have an application buffer which is big enough to handle it. But at the moment I have only 10 bytes of data in the column. After doing IRow->GetColumns(), not sure how to copy only 10 bytes of data. I do the following, but get all 4000 bytes, so when the data is printed, it is 10 characters of actual data padded with 3990 whitespaces.
retcode = WideCharToMultiByte(CP_UTF8, 0, (WCHAR*)pDBColumnAccess[nCol].pData, -1, (char *)pReadBuf, pDBColumnAccess[nCol].cbDataLen, NULL, NULL);
I thought pDBColumnAccess.cbDataLen would have only 10, but it has the value 4000.
How to read the exact number of bytes from the column?
Thanks.
Figured out a way for this, not sure if this is the actual solution or just a workaround.
If ANSI_PADDING is set to OFF before the table is created, padding of whitespaces after the actual data will not happen. You can see the property value "TrimTrailingBlanks" in sp_help of the table.
Thanks.
You should be setting a DBBINDING that includes DBPART_LENGTH to get the length of the field. To illustrate this, I've created some VARCHAR data in SQL Server using the following DDL:
CREATE TABLE NEWS
(
ID INT NOT NULL,
ARTICLE NVARCHAR(4000)
);
INSERT INTO NEWS (1, 'Today is a sunny day.');
Then I use the following ATL based C++ code sample to read the ARTICLE field. If you put breakpoints in the code, you will see it will come back with
data.nArticleLength = 21
data.szArticle = "Today is a sunny day."
Here's the code sample sample:
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <oledb.h>
#include <atlbase.h>
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = S_OK;
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
// Connect to SQL Server.
CComPtr<IDBInitialize> spIDBInitialize;
hr = spIDBInitialize.CoCreateInstance(OLESTR("SQLNCLI"));
CComPtr<IDBProperties> spIDBProperties;
hr = spIDBInitialize->QueryInterface(IID_IDBProperties, (void**) &spIDBProperties);
CComVariant varDataSource(OLESTR("InsertYourSQLServer"));
CComVariant varCatalog(_T("InsertYourDatabase"));
CComVariant varUserID(_T("InsertYourUserName"));
CComVariant varPassword(_T("InsertYourPassword"));
DBPROP rgProps[4] =
{
{ DBPROP_INIT_DATASOURCE, DBPROPOPTIONS_REQUIRED, 0, DB_NULLID, varDataSource },
{ DBPROP_INIT_CATALOG, DBPROPOPTIONS_REQUIRED, 0, DB_NULLID, varCatalog },
{ DBPROP_AUTH_USERID, DBPROPOPTIONS_REQUIRED, 0, DB_NULLID, varUserID },
{ DBPROP_AUTH_PASSWORD, DBPROPOPTIONS_REQUIRED, 0, DB_NULLID, varPassword }
};
DBPROPSET propSet = {rgProps, 4, DBPROPSET_DBINIT};
hr = spIDBProperties->SetProperties(1, &propSet);
spIDBProperties = NULL;
hr = spIDBInitialize->Initialize();
// Execute the query.
CComPtr<IDBCreateSession> spIDBCreateSession;
hr = spIDBInitialize->QueryInterface(IID_IDBCreateSession, (void**) &spIDBCreateSession);
CComPtr<IDBCreateCommand> spIDBCreateCommand;
hr = spIDBCreateSession->CreateSession(NULL, IID_IDBCreateCommand, (IUnknown**) &spIDBCreateCommand);
spIDBCreateSession = NULL;
CComPtr<ICommandText> spICommandText;
hr = spIDBCreateCommand->CreateCommand(NULL, IID_ICommandText, (IUnknown**) &spICommandText);
spIDBCreateCommand = NULL;
hr = spICommandText->SetCommandText(DBGUID_SQL, OLESTR("SELECT ID, ARTICLE FROM NEWS"));
DBROWCOUNT cRowsAffected = 0;
CComPtr<IRowset> spIRowset;
hr = spICommandText->Execute(NULL, IID_IRowset, NULL, &cRowsAffected, (IUnknown**) &spIRowset);
spICommandText = NULL;
// Retrieve records.
HROW hRow = NULL;
HROW *rghRow = &hRow;
DBCOUNTITEM cRowsObtained = 0;
hr = spIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &rghRow);
while (hr == S_OK && cRowsObtained == 1)
{
// Fetch the ARTICLE field.
struct
{
DBSTATUS dbArticleStatus;
ULONG nArticleLength;
char szArticle[4000 + 4];
} data = {0};
DBBINDING Binding = {0};
Binding.iOrdinal = 2;
Binding.obValue = sizeof(DBSTATUS) + sizeof(ULONG);
Binding.obLength = sizeof(DBSTATUS);
Binding.obStatus = 0;
Binding.pTypeInfo = NULL;
Binding.pObject = NULL;
Binding.pBindExt = NULL;
Binding.dwPart = DBPART_STATUS | DBPART_LENGTH | DBPART_VALUE;
Binding.dwMemOwner = DBMEMOWNER_CLIENTOWNED;
Binding.eParamIO = DBPARAMIO_NOTPARAM;
Binding.cbMaxLen = 4000 + 1;
Binding.wType = DBTYPE_STR;
Binding.dwFlags = 0;
Binding.bPrecision = 0;
Binding.bScale = 0;
CComPtr<IAccessor> spIAccessor;
hr = spIRowset->QueryInterface(IID_IAccessor, (void**) &spIAccessor);
HACCESSOR hAccessor = NULL;
hr = spIAccessor->CreateAccessor(DBACCESSOR_ROWDATA, 1, &Binding, 0, &hAccessor, NULL);
hr = spIRowset->GetData(hRow, hAccessor, &data);
DBREFCOUNT cRefCount = 0;
hr = spIAccessor->ReleaseAccessor(hAccessor, &cRefCount);
spIAccessor = NULL;
// ##TODO: Do something with data.szArticle and data.nArticleLength
// ...
// Fetch next row of data.
hr = spIRowset->ReleaseRows(1, rghRow, NULL, NULL, NULL);
cRowsObtained = 0;
hr = spIRowset->GetNextRows(DB_NULL_HCHAPTER, 0, 1, &cRowsObtained, &rghRow);
}
// Release everything
spIRowset = NULL;
spIDBInitialize = NULL;
CoUninitialize();
return 0;
}

Creating VHD files with the VirtualDisk API

I'm trying to use a VSS snapshot as the source for CreateVirtualDisk(). Environment/tools are C++ VS2008SP1 and 7.1 SDK on W7x64Ultimate
[Edited]
This works on Windows 7 x64
BOOL CreateVHD_Fixed(PCWSTR pszVhdPath, ULONG sizeInMB)
{
BOOL bRet = FALSE;
HANDLE hvhd;
CREATE_VIRTUAL_DISK_PARAMETERS params;
VIRTUAL_DISK_ACCESS_MASK mask;
VIRTUAL_STORAGE_TYPE vst =
{
VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT
};
wprintf(L"CreateVHD_Fixed %s, size (MB) %d\n", pszVhdPath, sizeInMB);
params.Version1.UniqueId = GUID_NULL;
params.Version1.BlockSizeInBytes = 0;
params.Version1.MaximumSize = sizeInMB * 1024 * 1024;
params.Version1.ParentPath = NULL;
params.Version1.SourcePath = NULL;
params.Version1.SectorSizeInBytes = 512;
params.Version = CREATE_VIRTUAL_DISK_VERSION_1;
mask = VIRTUAL_DISK_ACCESS_CREATE;
DWORD ret = CreateVirtualDisk(&vst,
pszVhdPath,
mask,
NULL,
// To create a dynamic disk, use CREATE_VIRTUAL_DISK_FLAG_NONE instead.
CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION,
0,
&params,
NULL,
&hvhd);
if (ret == ERROR_SUCCESS)
{
bRet = TRUE;
}
else
{
bRet = FALSE;
printf("failed to create vdisk...err 0x%x\n", ret);
PrintErrorMessage(GetLastError());
}
if (INVALID_HANDLE_VALUE != hvhd)
{
CloseHandle(hvhd);
}
return bRet;
}
[Edited] - now failing in a different way with ERROR_INVALID_PARAMETER. Parameters are below with a root path of "\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy64"
VIRTUAL_STORAGE_TYPE storageType =
{
VIRTUAL_STORAGE_TYPE_DEVICE_VHD,
// do not use any other GUID else you get an unknown provider error
VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT // **critical!**
};
VIRTUAL_DISK_ACCESS_MASK vdam = (VIRTUAL_DISK_ACCESS_MASK)(VIRTUAL_DISK_ACCESS_CREATE); // |VIRTUAL_DISK_ACCESS_WRITABLE|VIRTUAL_DISK_ACCESS_READ|VIRTUAL_DISK_ACCESS_GET_INFO);
CREATE_VIRTUAL_DISK_FLAG flags = CREATE_VIRTUAL_DISK_FLAG_FULL_PHYSICAL_ALLOCATION; // CREATE_VIRTUAL_DISK_FLAG_NONE;
CREATE_VIRTUAL_DISK_PARAMETERS parameters;
//
parameters.Version = CREATE_VIRTUAL_DISK_VERSION_1;
parameters.Version1.UniqueId = GUID_NULL;
parameters.Version1.MaximumSize = 0;
parameters.Version1.BlockSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_BLOCK_SIZE;
parameters.Version1.ParentPath = 0;
parameters.Version1.SourcePath = root.c_str();
parameters.Version1.SectorSizeInBytes = CREATE_VIRTUAL_DISK_PARAMETERS_DEFAULT_SECTOR_SIZE;
ULONG ProviderSpecificFlags = 0;
HANDLE handle = 0;
dwRet = CreateVirtualDisk(&storageType,
_T("t:\\test.vhd"),
vdam,
NULL,
flags,
ProviderSpecificFlags,
&parameters,0,&handle);
Any ideas? The virtual disk API does not seem to have much example code.
Thx++
Jerry.
Jerry,
Use CreateVirtualDisk API to create a VHD or a differential VHD, make sure u send the right parameters. You have to specify the parent hard disk but not the source hard disk. Care must also be taken while using the flags.
Refer the links below:
"http://code.msdn.microsoft.com/windowshardware/CppVhdAPI-4412d182"
and
"http://code.msdn.microsoft.com/windowsdesktop/Virtual-hard-disk-03108ed3"

C++ LDAP Query to locate memberOf

I am currently trying to perform an LDAP query in c++ to get the memberOf attribute of a given user. I have a function written that successfully gets the attribute if they are only in one group. The problem is that when they are in more than one group it only returns the first one. When I look at the user in Active Directory browser I can see that it says they have 2 entries for memberOf, but when I use my function I only get the first. Is there a way to modify my function to pull back all the entries or am I completely on the wrong path?
bool FindADMembership(CStringArray* pUserArray,IDirectorySearch *pContainerToSearch, CString sAMAccountName){
if(pContainerToSearch==NULL||pUserArray==NULL)
return false;
CString strSearchFilter;
strSearchFilter.Format("(&(objectClass=user)(objectCategory=person)(sAMAccountName=%s))", sAMAccountName);
BSTR b = strSearchFilter.AllocSysString();
LPOLESTR pszSearchFilter = b;
ADS_SEARCHPREF_INFO SearchPrefs;
SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
SearchPrefs.vValue.Integer = ADS_SCOPE_SUBTREE;
DWORD dwNumPrefs = 1;
LPOLESTR pszColumn = NULL;
ADS_SEARCH_COLUMN col;
HRESULT hr;
IADs *pObj = NULL;
IADs * pIADs = NULL;
ADS_SEARCH_HANDLE hSearch = NULL;
hr = pContainerToSearch->SetSearchPreference( &SearchPrefs, dwNumPrefs);
if (FAILED(hr))
return false;
LPOLESTR pszNonVerboseList[] = {L"memberOf"};
LPOLESTR szName = new OLECHAR[MAX_PATH];
int iCount = 0;
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter, pszNonVerboseList,sizeof(pszNonVerboseList)/sizeof(LPOLESTR),&hSearch);
if ( SUCCEEDED(hr) )
{
hr = pContainerToSearch->GetFirstRow( hSearch);
if (SUCCEEDED(hr))
{
while( hr != S_ADS_NOMORE_ROWS )
{
iCount++;
while( pContainerToSearch->GetNextColumnName( hSearch, &pszColumn ) != S_ADS_NOMORE_COLUMNS )
{
hr = pContainerToSearch->GetColumn( hSearch, pszColumn, &col );
if ( SUCCEEDED(hr) )
{
if (0==wcscmp(L"memberOf", pszColumn))
{
wcscpy(szName,col.pADsValues->CaseIgnoreString);
pUserArray->Add(szName);
}
pContainerToSearch->FreeColumn( &col );
}
FreeADsMem( pszColumn );
}
hr = pContainerToSearch->GetNextRow( hSearch);
}
}
else
return false;
pContainerToSearch->CloseSearchHandle(hSearch);
}
else
return false;
return true; }
Edit:
After working on this here is the code to iterate through the results
if (0==wcscmp(L"memberOf", pszColumn)) {
for(int i = 0; i < col.dwNumValues; i++)
{
wcscpy(szName, col.pADsValues[i].CaseIgnoreString);
pUserArray->Add(szName);
}
}
You need to check the number of values and loop over them to get each in turn - pADSValues is an array that could contain > 1 string. The count is returned in dwNumValues.
if (0==wcscmp(L"memberOf", pszColumn))
{
// This code should actually be a loop for i = 0 to dwNumValues - 1
// with each loop checking the next array entry in the list pADsValues
wcscpy(szName,col.pADsValues->CaseIgnoreString);
pUserArray->Add(szName);
}
See docs here.