Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What is a `**ppEnum`?

79 views
Skip to first unread message

T

unread,
Nov 14, 2022, 3:39:38 AM11/14/22
to
Hi All,

I am trying to write an interface to wit Raku.
I do have a time understanding C++.


https://learn.microsoft.com/en-us/windows/win32/api/vds/nf-vds-ivdspack-queryvolumes

What is `[out] IEnumVdsObject **ppEnum`.

C++

HRESULT QueryVolumes(
[out] IEnumVdsObject **ppEnum
);


Is this an array of 32 bit Integers? Where is
this length?

Chris M. Thomasson

unread,
Nov 14, 2022, 4:29:25 AM11/14/22
to
Afaict, it means the function can store a resulting pointer in ppEnum,
perhaps depending on HRESULT, don't know off hand.

Öö Tiib

unread,
Nov 14, 2022, 4:39:08 AM11/14/22
to
On Monday, 14 November 2022 at 10:39:38 UTC+2, T wrote:
> Hi All,
>
> I am trying to write an interface to wit Raku.
> I do have a time understanding C++.
>
>
> https://learn.microsoft.com/en-us/windows/win32/api/vds/nf-vds-ivdspack-queryvolumes
>
> What is `[out] IEnumVdsObject **ppEnum`.

Not standard C++. The [out] is simply MS extension/notation that ppEnum is
pointer to output parameter that is pointer of interface. So pointer of pointer.

>
> C++
>
> HRESULT QueryVolumes(
> [out] IEnumVdsObject **ppEnum
> );
>
>
> Is this an array of 32 bit Integers? Where is
> this length?

No arrays there. Pointer of pointer to single interface.
It is typical MS COM interface calling concept so you use it like that:

IVdsPack* pVp = get_that_interface_from_somewhere();
IEnumVdsObject* pEvo;
HRESULT res = pVp->QueryVolumes(&pEvo);
// check if res was good and handle issues
// if good then use pEvo->methods documented there:
// <https://learn.microsoft.com/en-us/windows/win32/api/vdshwprv/nn-vdshwprv-ienumvdsobject>








T

unread,
Nov 14, 2022, 4:48:27 AM11/14/22
to
> perhaps depending on HRESULT, don't know off handHi Chris,


I get it now. **ppEnum is a C style pointer
to a structure defined by HSRESULT.

Now to figure out what HSRESULT is.

Thank you!

-T

T

unread,
Nov 14, 2022, 4:49:12 AM11/14/22
to
I am confused. :'(

Chris M. Thomasson

unread,
Nov 14, 2022, 4:52:27 AM11/14/22
to
take:

IEnumVdsObject **ppEnum

Well, that is pointer to a pointer.

So, think of: typing, please try to forgive any typos:

int a = 0;
int* p_a = &a;

int* p_b = nullptr;

int** foo = nullptr;

foo = &p_b;
*foo = &p_a;

Now p_a == p_b, the both point at a ala (&a)...

;^)

Chris M. Thomasson

unread,
Nov 14, 2022, 4:55:35 AM11/14/22
to
^^^^^^^^^^^^^^^

GOD DAMN IT!!

That should be *foo = p_a, shit. That's what I get wrt typing code into
a damn newsreader!

So sorry for that boneheaded mistake. ;^o

Chris M. Thomasson

unread,
Nov 14, 2022, 5:01:52 AM11/14/22
to
int a = 0;
int* p_a = &a;
int* p_b = nullptr;

int** pp = &p_b;
*pp = p_a;

p_b == p_a

I think I got it right typing directly in the newsreader. :^)

Öö Tiib

unread,
Nov 14, 2022, 5:30:21 AM11/14/22
to
No "HSRESULT" there. It is HRESULT. 32-bit signed integer encoding
long list of values ... for that method the potential values are mentioned
in the very page you gave. E_INVALIDARG, E_OUTOFMEMORY,
S_OK, VDS_E_PROVIDER_CACHE_CORRUPT.

Öö Tiib

unread,
Nov 14, 2022, 5:33:13 AM11/14/22
to
I am not psychic. Elaborate what you understand, what you do
not. Default is nothing ... but that takes about half a year of
teaching to compensate ... that we can't do in Usenet thread.

T

unread,
Nov 14, 2022, 5:39:50 AM11/14/22
to
This is their list of return codes:

https://learn.microsoft.com/en-us/windows/win32/vds/virtual-disk-service-common-return-codes

I am not wrapping my head around how one crams all
those codes into one 32 bit integer.

By "32-bit signed integer", do you mean it can
have a negative value (as opposed to a cardinal)?


Öö Tiib

unread,
Nov 14, 2022, 5:48:45 AM11/14/22
to
Those are special values for whole subsystem. The methods return
still usually S_OK for when all is good.

> I am not wrapping my head around how one crams all
> those codes into one 32 bit integer.

One at time. It is return value of method. So each method
call produces only one value of HRESULT.

> By "32-bit signed integer", do you mean it can
> have a negative value (as opposed to a cardinal)?

Yes. Basically zero is full success named S_OK, positive values are
indicating generally positive outcome and negative values are errors.
Maybe that is simpler explanation:
<https://en.wikipedia.org/wiki/HRESULT>

T

unread,
Nov 14, 2022, 6:00:13 AM11/14/22
to
This is what I think I understand and do not.

`*` is a C pointer

`**` is a C pointer that points to another
C pointer (AAAHHH!)

`HRESULT QueryVolume` is not `HRESULT = QueryVolume(...)`.
It is not a return value from a function.

`HRESULT` is telling me that is the structure of what
the pointer to a pointer (**ppEnum) points to.

I have no idea what `HRESULT` is. I am not sure
how this applies:
https://en.wikipedia.org/wiki/HRESULT#Data_Structure

I am after both
VDS_E_VOLUME_NOT_HEALTHY (0x8004243EL), and
VDS_E_VOLUME_NOT_A_MIRROR (0x80042445L)

Does the "L" stand from 32 bit integer? (To me,
64 bits is "Long".)

And is bit 31 is used for "Severity" it look a
lot and unsigned integer (cardinal) to me.



Öö Tiib

unread,
Nov 14, 2022, 6:34:00 AM11/14/22
to
Correct.

>
> `HRESULT QueryVolume` is not `HRESULT = QueryVolume(...)`.
> It is not a return value from a function.

It is, QueryVolume is method of IVdsPack interface.

>
> `HRESULT` is telling me that is the structure of what
> the pointer to a pointer (**ppEnum) points to.

No.
You get the value like I posted:

HRESULT res = pVp->QueryVolumes(&pEvo);

Where pVp is pointer to IVdsPack interface.

>
> I have no idea what `HRESULT` is.

It is 32 bit signed integer.

> I am not sure
> how this applies:
> https://en.wikipedia.org/wiki/HRESULT#Data_Structure

It is just meaning of individual bits in HRESULT value.
Those bits do not generally matter unless you want to
make some kind of very generic handling of HRESULT
value space.

>
> I am after both
> VDS_E_VOLUME_NOT_HEALTHY (0x8004243EL), and
> VDS_E_VOLUME_NOT_A_MIRROR (0x80042445L)
>
> Does the "L" stand from 32 bit integer? (To me,
> 64 bits is "Long".)

The L means signed long. MS is not caring what rest of
the world thinks, as it is very rich and powerful company.
For MS signed long is 32 bits until MS decides otherwise,
and the C++ standard allows that freedom.

> And is bit 31 is used for "Severity" it look a
> lot and unsigned integer (cardinal) to me.

As result of QueryVolumes you should perhaps just check
that if res == S_OK then you use gained IEnumVdsObject
interface, if not then refuse to continue with error
message.

Alf P. Steinbach

unread,
Nov 14, 2022, 8:34:30 AM11/14/22
to
On 14 Nov 2022 09:39, T wrote:
> Hi All,
>
> I am trying to write an interface to wit Raku.
> I do have a time understanding C++.
>
>
> https://learn.microsoft.com/en-us/windows/win32/api/vds/nf-vds-ivdspack-queryvolumes
>
> What is `[out] IEnumVdsObject **ppEnum`.
>
> C++
>
> HRESULT QueryVolumes(
>   [out] IEnumVdsObject **ppEnum
> );

What does a deprecated Microsoft interface for disk management have to
do with writing an "interface to wit Raku"?

(As I understand it Raku is sort of Perl 2, <url:
https://en.wikipedia.org/wiki/Raku_(programming_language)>).

For that matter what does "wit" stand for, and what exactly do you mean
by that?

Are you trying to write some Windows-specific library to be called from
Raku?

If so what?


> Is this an array of 32 bit Integers?  Where is this length?

As an ordinary C++ function you would have had something like
(considering that this is a member function of IVdsPack)

auto create_volumes_iterator() const
-> unique_ptr<IEnumVdsObject>;

But while the COM technology is /based/ on early C++, all the COM stuff
offered by the Windows API must be C compatible. They solved the problem
of calling C++ member functions from C, by specifying a binary level
memory layout (ABI) for COM objects, which is what COM is most about: a
known ABI. But C can't consume things like smart pointers, and though C
has `const` it isn't comfortable with C++ style `const`, so for C
compatibility one would have to express the function like this:

auto create_volumes_iterator() -> IEnumVdsObject* ;

Or with old C syntax:

IEnumVdsObject* create_volumes_iterator();

But this still has a hidden C++ specific dependency, namely that any
error is communicated via exception throwing. That won't do for COM.
It's not only that C lacks exceptions: COM was designed for use via any
programming language, and these languages don't do C++ exceptions.

So, COM communicates failure via the function result type, which must
always be an `HRESULT`, a structured 32-bit failure reporting value sort
of like the one used (as I recall) in VMS:

HRESULT create_volumes_iterator( IEnumVdsObject*& p_iterator );

But oh I forgot, C has no references to use for out parameters. So you
need to express that as a pointer to a pointer:

HRESULT create_volumes_iterator( IEnumVdsObject** pp_iterator );

And, I also forgot, to adhere to the COM spirit you need to Microsoftify
the function name:

HRESULT QueryVolumes( IEnumVdsObject** pp_iterator );

Now instead of the name signifying what this function does, it signifies
what you intend to do with the result, or perhaps a part of what the
function does on the inside (e.g. a side effect such as perhaps waiting
ten to fifteen seconds for the spinning up of disks, expressed subtly
via the name instead of documented). Smart. If one is into Microsoft-ish
obfuscation of things, e.g., if one works for Microsoft.

Anyway, an HRESULT is a not a simple error code, in particular 0 denotes
success not failure (it's the value S_OK), so in order to check the
HRESULT for success or failure you can and should preferentially use
macros like SUCCEEDED and FAILED, so a call can look like this:

void foo( IVdsPack* p_vds_pack )
{
IEnumVdsObject* p_iterator;
const HRESULT hr = p_vds_pack->QueryVolumes( &p_iterator );
if( not SUCCEEDED( hr ) ) {
throw "Alabama!"; // Ref. movie "Crimson Tide", 1995.
}
// Use p_iterator to iterate over volumes. Then:
p_iterator->Release();
}

However, throwing exceptions is not very compatible with manual release
calls like the last statement above; you risk memory and resource
leakage. Instead of such risky raw interface pointer and manual release
calls you can use a COM /smart pointer/ that guarantees proper release
calls. There is an abundance of such smart pointer classes, including
one supplied with Visual C++, <url:
https://learn.microsoft.com/en-us/cpp/cpp/com-ptr-t-class?view=msvc-170>.

All this said, do heed the statements in the documentation that say that
something (e.g. this disk management functionality) is deprecated and
directs you to newer stuff; use the newer stuff instead.

- Alf

Alf P. Steinbach

unread,
Nov 14, 2022, 8:44:39 AM11/14/22
to
On 14 Nov 2022 14:34, Alf P. Steinbach wrote:
> in particular 0 denotes success not failure (it's the value S_OK)

I /meant/ to write, ❝in particular 1 denotes success not failure (it's
the value S_FALSE)❞

It's like the most common typo, namely transposition of characters, just
that this is the most common thinko, transposition of facts/thoughts.

Hm!

- Alf (possibly unclear in ze head b/c can't take my diabetes medicine
due to upcoming invasive heart/blood vessel check).

Keith Thompson

unread,
Nov 14, 2022, 12:13:12 PM11/14/22
to
"Alf P. Steinbach" <alf.p.s...@gmail.com> writes:
[...]
> (As I understand it Raku is sort of Perl 2, <url:
> https://en.wikipedia.org/wiki/Raku_(programming_language)>).
[...]

The current version of Perl is Perl 5 (the latest release is 5.36).

The language now called Raku was originally called Perl 6. It was
intended to be the next version of Perl, but it diverged enough that it
was decided to rename it. (There's more to the history, but this isn't
the place to discuss it.)

(There was a Perl 2, released in 1988.)

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */

T

unread,
Nov 14, 2022, 2:02:34 PM11/14/22
to
On 11/14/22 05:34, Alf P. Steinbach wrote:
> What does a deprecated Microsoft interface for disk management have to
> do with writing an "interface to wit Raku"?
>
> (As I understand it Raku is sort of Perl 2, <url:
> https://en.wikipedia.org/wiki/Raku_(programming_language)>).

Perl 6 was renamed Raku

> For that matter what does "wit" stand for, and what exactly do you mean
> by that?

"Wit" was a typo. I should have been "with"

> Are you trying to write some Windows-specific library to be called from
> Raku?

Yes. I am using the nightmare "Native Call".
They could not have made it more difficult, but I
usually make it work.

https://docs.perl6.org/language/nativecall

I am trying to avoid having to run as administrator
when calling Diskpart --> list volume

"Listing" should not require administrator privileges,
but Windows is not as smart as Linux ("dnf" lets
query without being root, etc.).


> If so what?


https://learn.microsoft.com/en-us/windows/win32/api/vds/nf-vds-ivdspack-queryvolumes

And I just found out that I have to also
be administrator to call IEnumVdsObject,
so poop (not my exact word). Meaning that
I am going to have to run as administrator
anyway, so I do not need to have this
question answered anymore, other that
for leaning for the future.

So everyone can stop trying to teach me
now. And thank you for all your gracious
effort!

A pointer to a pointer. Geez! I am going
to write that up in my C notes that I use
with native call.


T

unread,
Nov 14, 2022, 2:11:10 PM11/14/22
to
I just found out that `IVdsPack` also has
to be run as an administrator. I was trying
to get Diskpart --> list volume without
being an administrator. (Diskpart is
the only call that will give you a mirrored
volume's status.)

So I will have to run as administrator
anyway and I do not need this question
answered anymore. I will just call Diskpart.
Poop!

Thank you all for your gracious assistance!

-T

Queries should not have to be run as administrator.
Windows is what it is. I would not have a job
without with poor quality.

Ralf Fassel

unread,
Nov 15, 2022, 4:18:10 AM11/15/22
to
* T <T...@invalid.invalid>
| A pointer to a pointer. Geez! I am going to write that up in my C
| notes that I use with native call.

In c++, you have references to change a variable passed 'down' to a
function. But in C it is either return-value (which in your case is
already taken by the HRESULT success/failure indication), globals or
pointers. So if you want to change a pointer, you need to pass the
address of that pointer, i.e. pointer-to-pointer.

R'

Öö Tiib

unread,
Nov 15, 2022, 7:24:13 AM11/15/22
to
Yes but note that the question was about usage of Windows API COM
interface subsystem in Raku. That interface is designed to be
compatible with C to ensure it is possible to integrate with any language
that can interoperate with C. The result is not outright "three star
interface" but pointers to pointers are common enough. The MS ATL
libraries provide some C++ wrappers to abstract those pointers away
but I'm unsure how compatible Perl or Raku is with C++ templates
from ATL (likely not at all).


Alf P. Steinbach

unread,
Nov 15, 2022, 8:04:58 AM11/15/22
to
On 14 Nov 2022 20:02, T wrote:
> On 11/14/22 05:34, Alf P. Steinbach wrote:
>> What does a deprecated Microsoft interface for disk management have to
>> do with writing an "interface to wit Raku"?
>>
>> (As I understand it Raku is sort of Perl 2, <url:
>> https://en.wikipedia.org/wiki/Raku_(programming_language)>).
>
> Perl 6 was renamed Raku
>
>> For that matter what does "wit" stand for, and what exactly do you
>> mean by that?
>
> "Wit" was a typo.  I should have been "with"
>
>> Are you trying to write some Windows-specific library to be called
>> from Raku?
>
> Yes.  I am using the nightmare "Native Call".
> They could not have made it more difficult, but I
> usually make it work.
>
> https://docs.perl6.org/language/nativecall
>
> I am trying to avoid having to run as administrator
> when calling  Diskpart --> list volume
>
> "Listing" should not require administrator privileges,
> but Windows is not as smart as Linux ("dnf" lets
> query without being root, etc.).

Try command

> wmic volume list brief

There are also others, as I recall.


>> If so what?
>
>
> https://learn.microsoft.com/en-us/windows/win32/api/vds/nf-vds-ivdspack-queryvolumes
>
> And I just found out that I have to also
> be administrator to call IEnumVdsObject,
> so poop (not my exact word).  Meaning that
> I am going to have to run as administrator
> anyway,

No you don't. See above.


> so I do not need to have this
> question answered anymore, other that
> for leaning for the future.
>
> So everyone can stop trying to teach me now.

Hm!


>  And thank you for all your gracious effort!

Well, thanks. :)


> A pointer to a pointer.  Geez! I am going
> to write that up in my C notes that I use
> with native call.

Yeah. The nice thing about C++ is that you /can/ wrap such things in far
more convenient, well, wrappers. And if you're lucky Someone Else(tm)
will have done that for you already, so you can just use the stuff.

The flip side is that especially reusable (as opposed to /ad hoc/) more
convenient wrappers can be a heck of a lot of code.

But.


- Alf

T

unread,
Nov 15, 2022, 11:30:18 AM11/15/22
to
On 11/15/22 05:04, Alf P. Steinbach wrote:
> wmic volume list brief

Hi Alf,

Diskpart (as administrator) is the only utility
that will list mirror volumes. Note the
differences between the report for J: below


-T

C>diskpart

Microsoft DiskPart version 10.0.22621.1

Copyright (C) Microsoft Corporation.
On computer: KVM-W11

DISKPART> list volume

Volume ### Ltr Label Fs Type Size Status Info
---------- --- ----------- ----- ---------- ------- ---------
--------
Volume 0 J BACKUP NTFS Mirror 69 MB Healthy
...



>wmic volume list brief
Capacity DriveType FileSystem FreeSpace Label
Name
73396224 3 NTFS 58589184 BACKUP F:\

63637024768 3 NTFS 33665376256 C:\

661647360 3 NTFS 87003136
\\?\Volume{87e27543-bc3d-4f97-b7bc-321d8451c994}\
100663296 3 FAT32 67761152
\\?\Volume{d8fe8f8c-3c0b-4164-b282-da7270e7e411}\
72347648 3 NTFS 57581568 BACKUP J:\

4335380480 5 UDF 0 22000.1_X64_EN-US D:\

532060160 5 CDFS 0 virtio-win-0.1.225 E:\

1694498816 5 CDFS 0 Fedora-Xfce-Live G:\

Juha Nieminen

unread,
Nov 16, 2022, 4:47:31 AM11/16/22
to
T <T...@invalid.invalid> wrote:
> `*` is a C pointer
>
> `**` is a C pointer that points to another
> C pointer (AAAHHH!)

Since pointers in C (and thus C++) can point to a single value or an
entire array of values (well, technically speaking a single value
pretty much behaves like it were an array of size 1, so one could
think that pointers *always* point to an array, even if that "array"
is just one single non-array variable), it can become confusing
quite fast when mixing these two uses. Sometimes you may have a
pointer pointing to an array of pointers. Or sometimes you may have
a pointer pointing to another pointer (which in itself might be
pointing to a single value or an array of values).

One practical example of a double pointer is the second parameter to the
strtol() function. The reason why it takes a pointer-to-a-pointer is
because it needs to be able to modify the original pointer to point
somewhere else (ie. it needs to be able to assign a new value to that
original pointer variable).

(A better solution to this would have been to make strtol() return
a struct with two values: The evaluated integer and the new end pointer.
But this design was rarely if ever used in the early days of C. There
may also be some "you don't have to pay for what you don't use" design
in play here, because if you don't need the new end pointer you can
just pass NULL as the parameter.)

T

unread,
Nov 16, 2022, 6:55:45 PM11/16/22
to
The only thing I am not following, is how do I know the
length of each item in the array and how many items are
in the array.

Scott Lurndal

unread,
Nov 16, 2022, 7:20:07 PM11/16/22
to
You keep track of how many elements you allocated when
you create the array. As for the "size of" each
element, the 'sizeof' keyword applied to the element
will return the size, in bytes.

Mike Terry

unread,
Nov 16, 2022, 8:21:21 PM11/16/22
to
If you are still talking about

C++

HRESULT QueryVolumes(
[out] IEnumVdsObject **ppEnum
);

then you would seem to have misunderstood Öö Tiib's point - there are no arrays anywhere in there.

I'll respond to your earlier post about which bits you understand and don't understand... (but in
another thread I suggested that trying to use COM interfaces would require you to learn a load of
stuff your might rather avoid, and that's exactly what you're trying to use here!)

Regards,
Mike.

T

unread,
Nov 16, 2022, 9:02:09 PM11/16/22
to
On 11/16/22 17:21, Mike Terry wrote:
> C++
>
> HRESULT QueryVolumes(
>   [out] IEnumVdsObject **ppEnum
> );

My (mis) understanding is that **ppEnum points to a
pointer that points to an array of HRESULT, which is
a 32 bit word. Seems I don't understand.

I am expecting the result to give me a long table
of volumes. Again, I think I misunderstand.


Keith Thompson

unread,
Nov 16, 2022, 9:41:07 PM11/16/22
to
A (valid non-null) pointer value of type `foo*` always points to a
single object of type `foo`. (Handwave: Ignore for now the fact that a
valid pointer can point just past the end of an array object.)

That single object may or may not be an element of an array of `foo`.
If it's just a single object, it can be treated as the lone element of a
single-element array.

In the example above, ppEnum *might* be intended to be used to
manipulate an array of pointers. The declaration isn't enough to tell
us that.

Given a pointer value that points to an element of an array object, you
can access the other elements of that array object by performing pointer
arithmetic on the pointer value. (The [] operator is syntactic sugar
for this; x[y] means *(x+y).) In C, most array manipulation is done via
pointers to the elements of the array.

It's less common to have to deal with this in C++ than in C, since C++
defines container classes like std::vector that hide the raw pointer
operations. But C++ still inherits just about all of C's (often
counterintuitive) pointer and array semantics.

Recommended reading:
Section 6 of the comp.lang.c FAQ <https://www.c-faq.com/>.

Mike Terry

unread,
Nov 16, 2022, 10:37:08 PM11/16/22
to
Yes

>
> `**` is a C pointer that points to another
> C pointer  (AAAHHH!)
>
> `HRESULT QueryVolume` is not `HRESULT = QueryVolume(...)`.
> It is not a return value from a function.

QueryVolume is a COM "method" belonging to the IVdsPack interface. OK, so to use COM you should
understand COM basics:

- what is COM
- basic undertanding of COM architecture: COM objects, interfaces and methods
Note: COM does not supply direct C pointers to COM objects - it always provides
pointers to INTERFACES, which have METHODS you call to access the associated
object.
- how interfaces are aquired and released, and at least understand
the IUnknown interface, and how interfaces are resource-counted.
(Otherwise you will leak resources all over the place.)
- how COM reports errors via HRESULT.

For disk-management, you are not forced to use COM. There are surely non-COM APIs that would do
what you are want? Perhaps using those might be easier...

So, you are looking at some documentation at the web link you provided...

HRESULT QueryVolumes(
[out] IEnumVdsObject **ppEnum
);

Best to think of this as a function prototype, descibing/documenting how to use the QueryVolumes
method. So it is not an assignment statement. (Like you said.)

HMMM - have you spotted that the whole VDS COM apis have been superseded by thw

HRESULT is the standard return type used by all COM methods. It is a 32-bit structured value, with
some values indicating success, and some failure. See
<https://learn.microsoft.com/en-us/windows/win32/com/structure-of-com-error-codes>. So, when you
call QueryVolumes it returns an HRESULT to indicate success/failure. You would of course test this
following any call.

The [out] you can consider to be just documentation, indicating that ppEnum is a pointer to
something that is given back to you by the QueryVolumes function (method), i.e. it's an OUT
parameter for the call.

For the QueryVolumes method, ppEnum points to something of type (IEnumVdsObject *) - this is a new
COM interface pointer, pointing to a COM object implementing the IEnumVdsObject interface. You can
use that pointer to invoke other methods belonging to the IEnumVdsObject interface.

Note: QueryVolumes does not return an array of anything - it returns a new interface you can call
to access the various objects to be enumerated. E.g. it has a Next() method that you would call.
THAT method can indeed fill an array, but (of course) you need to read and understand the
documentation provided for that method...

>
> `HRESULT` is telling me that is the structure of what
> the pointer to a pointer (**ppEnum) points to.

No, it's the (type of the ) return code from calling the QueryVolumes method. The value indicates
primarily whether the method call worked or failed.

>
> I have no idea what `HRESULT` is. I am not sure
> how this applies:
> https://en.wikipedia.org/wiki/HRESULT#Data_Structure

HRESULT is the type that all COM methods return. It is a 32 bit value and in particular the high
order bit (bit 31) is the S bit indicating success/failure. Normally you would test this bit by
with the SUCCESS or FAILED macros. Something like

// iVdsPac is obtained elsewhere
HRESULT hr;
IEnumVdsObject* pIEnum = NULL;
hr = iVdsPac->QueryVolumes (&pIEnum);
if (SUCCEEDED(hr))
{
// call succeeded, so pIEnum is a new returned interface pointer.
// Use the interface, then RELEASE it!
// Documentation for the interface (the methods it supports) is at
// <https://learn.microsoft.com/en-us/windows/win32/api/vdshwprv/nn-vdshwprv-ienumvdsobject>
IUnknown* pIUnknown = NULL;
ULONG cFetched = 0;
hr = pIEnum->Next (1, &pIUnknown, &cFetched);
if (SUCCEEDED(hr))
{
// use AND RELEASE pIUnknown, e.g. call QueryInteface to get required interface type etc.
// Actually using the interface is omitted from this listing!
pIUnknown->Release (); // finished with interface so release it
}
pIEnum->Release ();
)

>
> I am after both
>     VDS_E_VOLUME_NOT_HEALTHY  (0x8004243EL), and
>     VDS_E_VOLUME_NOT_A_MIRROR (0x80042445L)
>

No, those are HRESULT failure return codes, as conventionally indicated by the _E_ in the name, and
also as indicated by bit 31 of the value being 1. You presumably don't want to fail!

What I think you think you want to do is use the IEnumVdsObject interface to step through the
objects it enumerates. You should probably read
<https://learn.microsoft.com/en-us/windows/win32/vds/working-with-enumeration-objects>
and of course before starting on any of this, you need to properly understand the underlying object
model for the system (disks?) being modelled, so hopefully you have already read
<https://learn.microsoft.com/en-us/windows/win32/vds/vds-object-model>

...except, you probably shouldn't be doing any of this without understanding what you're doing.
That's why you should go to a suitable Microsoft development forum, explain what you're trying to
do, and follow advice. Or maybe, find an example doing exactly what you want, or get someone to
write the code for you! I don't think you have enough background to get going on whatever you're
trying to achieve with the route you seem to be taking... [Sorry, I don't have suggestions for
specifics for forums etc..]

And this whole API is superseded by newer APIs, so you shouldn't be using any of it! :)

> Does the "L" stand from 32 bit integer?  (To me,
> 64 bits is "Long".)

Yes, L defines a "long" integer, that's regular C/C++. In MSVC, long integers are 32 bits. 64 bit
integers are "long long".

>
> And is bit 31 is used for "Severity" it look a
> lot and unsigned integer (cardinal) to me.

Yes, bit 31 is severity as shown in the documentation. HRESULT values are unsigned, BUT that hardly
matters, as they're not manipulated as integer values - you compare them sometimes if you are
checking for one specific error, or you use macros like SUCCEEDED/FAILED to test the bits you need.

Regards,
Mike.

Mike Terry

unread,
Nov 16, 2022, 11:19:13 PM11/16/22
to
Yeah, COM normally returns /interfaces/ on objects and you call those interfaces. You don't access
the objects "directly". Enumeration for multiple objects is normally done by getting some kind of
enumeration interface which you call to step through whatever is being enumerated. That's how this
API works. (That's a COM paradigm, nothing to do with C++ which is what this newsgroup covers...)

Viewing the expression like this might help:

HRESULT QueryVolumes ( (IEnumVdsObject*) *ppEnum );

ppEnum needs to be a pointer to a single (IEnumVdsObject*) value, which you typically declare on the
stack. My other post had fuller illustration, but something like:

...
IEnumVdsObject* pIEnum;
HRESULT hr = pIface->QueryVolumes ( &pIEnum );

Note the & "address of" C++ operator. (If you need this to be explained, all is lost! :) )
pIEnum has type (IEnumVdsObject*)
&pIEnum has type (IEnumVdsObject**)

The QueryVolumes call will set the value of pIEnum, assuming it succeeds... pIEnum would be your
new interface to call to enumerate [whatever].
The call also sets hr to the return code from the call, which you should test before proceeding -
typically it should be tested with the SUCCEEDS macro:

if (SUCCEEDED(hr)) ....; // (tests bit 31, but you don't need to know that...)

You would call methods on the returned interface to do any stepping through objects etc.. But next
you might ask what to do with the IUnknowns that Next() returns and so it will go on. [That's all
off topic for this newsgroup, and you shouldn't be doing any of this in the first place, so I won't
go any further.]

Mike.

T

unread,
Nov 16, 2022, 11:58:56 PM11/16/22
to
Wow! Excellent explanation. Thank you!

Juha Nieminen

unread,
Nov 17, 2022, 3:25:55 AM11/17/22
to
T <T...@invalid.invalid> wrote:
> The only thing I am not following, is how do I know the
> length of each item in the array and how many items are
> in the array.

The size (in bytes, if that's what you mean) of each element in the
array is just the sizeof the element type. If you have a 'Type*' pointer,
then the size of each element in the array is sizeof(Type).

A pointer does not carry with itself any information about what it's
pointing to (other than the type of that thing). In other words, you
cannot know, from the pointer alone, whether it's pointing to a single
value or to an array, or what the size of that array is.

So to answer your question about how you know how many items there are
in the array: You don't.

You need to pass the number of elements in the array as a separate value
alongside the pointer.

(To make this easier you could eg. create a struct that has the pointer
and a 'std::size_t' member variable telling how many elements there
are in the array pointed to by the pointer.)

T

unread,
Nov 17, 2022, 3:31:55 AM11/17/22
to
I had mistaken HRESULT as the type that the pointer
of the pointer pointed to. HRESULT is just M$'s
way of saying it failed or passed.

Ben Bacarisse

unread,
Nov 17, 2022, 8:03:25 AM11/17/22
to
T <T...@invalid.invalid> writes:

> The only thing I am not following, is how do I know the
> length of each item in the array and how many items are
> in the array.

I typed this up before seeing Mike's reply. It seems this has been
covered more than once, but since I've typed it already...

Do you understand the concept of using a pointer to "return" a value
from a function? For example

int get_some_fancy_hardware_value(long *res);

This interface return success (0) or some negative value indicating an
error so the return value can't be used to get the fancy value out of the
hardware. Instead, it's placed in the long pointed to by the long *
argument. You'd write:

long hw_val;
if (get_some_fancy_hardware_value(&hw_val) >= 0)
printf("value=%ld\n", hw_val);
else fprintf(stderr, "Could not get hardware value.\n");

Sometimes the value you want to get out is itself a pointer. In these
cases that argument will be a pointer to a pointer, as in your example.

--
Ben.

Alf P. Steinbach

unread,
Nov 17, 2022, 10:21:59 AM11/17/22
to
Oh. Well I don't have any mirrored volumes to test this with, but the
docs say that `fsutil volume list` lists /all/ volumes on the system.

<url:
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/fsutil-volume>

"list Lists all of the volumes on the system."


---------------------------------------------------------------------
[C:\root\temp]
> fsutil volume list
Possible volumes and current mount points are:

\\?\Volume{8cd6348d-459b-4886-8356-86fd8fc094db}\
C:\

\\?\Volume{a4069431-b4bd-4e1d-af34-e433ebccf585}\

\\?\Volume{fe18d3d2-8a44-4b3a-86cf-ce555ee308dd}\
D:\

\\?\Volume{511a54e0-b6d4-49b9-8000-e202ce217be7}\


[C:\root\temp]
> wmic volume list brief
Capacity DriveType FileSystem FreeSpace Label
Name
510770802688 3 NTFS 108444856320
C:\
1043329024 3 NTFS 317726720 WinRE_DRV
\\?\Volume{a4069431-b4bd-4e1d-af34-e433ebccf585}\
8001427599360 3 NTFS 3994988654592 Seagate Expansion
Drive D:\
268435456 3 FAT32 231571456 SYSTEM
\\?\Volume{511a54e0-b6d4-49b9-8000-e202ce217be7}\
---------------------------------------------------------------------


Disclaimer: I only tested that that sub-command doesn't require elevated
access in Windows 11. I remember that fsutil was one of the things
totally "security"-fouled up in Windows Vista. It's hard to keep track
of Microsoft's endless bug introductions, partial bug fixes and willy
nilly changes of things, and I don't even try that any more, so.


- Alf

Keith Thompson

unread,
Nov 17, 2022, 1:18:00 PM11/17/22
to
Juha Nieminen <nos...@thanks.invalid> writes:
> T <T...@invalid.invalid> wrote:
>> The only thing I am not following, is how do I know the
>> length of each item in the array and how many items are
>> in the array.
>
> The size (in bytes, if that's what you mean) of each element in the
> array is just the sizeof the element type. If you have a 'Type*' pointer,
> then the size of each element in the array is sizeof(Type).
>
> A pointer does not carry with itself any information about what it's
> pointing to (other than the type of that thing). In other words, you
> cannot know, from the pointer alone, whether it's pointing to a single
> value or to an array, or what the size of that array is.

A (valid non-null) pointer to FOO points to a single object (not value)
of type FOO. It never points to an array, unless FOO happens to be an
array type. The single FOO object that it points to may or may not be
an element of an array of FOO (and if it's really just a single object,
it can be treated as the sole element of a 1-element array of FOO).

The think you cannot know is the size of the array of which the
pointed-to FOO object may be an element.

It's not uncommon to refer informally to a pointer to the initial
element of an array as a pointer to the array, but pointers to arrays
are a distinct thing.

> So to answer your question about how you know how many items there are
> in the array: You don't.
>
> You need to pass the number of elements in the array as a separate value
> alongside the pointer.
>
> (To make this easier you could eg. create a struct that has the pointer
> and a 'std::size_t' member variable telling how many elements there
> are in the array pointed to by the pointer.)

T

unread,
Nov 17, 2022, 1:29:08 PM11/17/22
to
I found this example that follows what you guys have been
trying to get through my thick skull:

if (!WTSEnumerateSessions(WTS_CURRENT_SERVER, 0, 1, &pwsi, &dwCount))
{
Report(_T("WTSEnumerateSessions failed with error code %d\n"),
GetLastError());
return 0;
}

for (DWORD i = 0; i < dwCount; i++)
{
if (pwsi[i].State == WTSActive)
{
dwSession = pwsi[i].SessionId;
break;
}
}

WTSFreeMemory(pwsi);




T

unread,
Nov 17, 2022, 7:39:45 PM11/17/22
to
wmic won't tell you mirrored status. Only Diskpart.

Alf P. Steinbach

unread,
Nov 19, 2022, 8:16:36 AM11/19/22
to
On 18 Nov 2022 01:39, T wrote:
>
>
> wmic won't tell you mirrored status.  Only Diskpart.

This is now totally off topic, but you're mindlessly repeating your
earlier assertion and failing to notice what was written in the posting
you responded to.

- Alf

0 new messages