1use crate::{PropTag, sys};
7use core::{ffi, ptr, slice};
8use windows::Win32::{
9 Foundation::{E_INVALIDARG, E_POINTER, FILETIME},
10 System::Com::CY,
11};
12use windows_core::*;
13
14pub struct PropValue<'a> {
16 pub tag: PropTag,
17 pub value: PropValueData<'a>,
18}
19
20pub enum PropValueData<'a> {
22 Null,
24
25 Short(i16),
27
28 Long(i32),
30
31 Pointer(*mut ffi::c_void),
33
34 Float(f32),
36
37 Double(f64),
39
40 Boolean(u16),
42
43 Currency(i64),
45
46 AppTime(f64),
48
49 FileTime(FILETIME),
51
52 AnsiString(PCSTR),
54
55 Binary(&'a [u8]),
57
58 Unicode(Vec<u16>),
60
61 Guid(GUID),
63
64 LargeInteger(i64),
66
67 ShortArray(&'a [i16]),
69
70 LongArray(&'a [i32]),
72
73 FloatArray(&'a [f32]),
75
76 DoubleArray(Vec<f64>),
78
79 CurrencyArray(Vec<CY>),
81
82 AppTimeArray(Vec<f64>),
84
85 FileTimeArray(Vec<FILETIME>),
87
88 BinaryArray(Vec<sys::SBinary>),
90
91 AnsiStringArray(Vec<PCSTR>),
93
94 UnicodeArray(Vec<PCWSTR>),
96
97 GuidArray(Vec<GUID>),
99
100 LargeIntegerArray(Vec<i64>),
102
103 Error(HRESULT),
105
106 Object(i32),
108}
109
110impl<'a> From<&'a sys::SPropValue> for PropValue<'a> {
111 fn from(value: &sys::SPropValue) -> Self {
114 let tag = PropTag(value.ulPropTag);
115 let prop_type = tag.prop_type().remove_flags(sys::MV_INSTANCE).into();
116 let data = unsafe {
117 match prop_type {
118 sys::PT_NULL => PropValueData::Null,
119 sys::PT_SHORT => PropValueData::Short(value.Value.i),
120 sys::PT_LONG => PropValueData::Long(value.Value.l),
121 sys::PT_PTR => PropValueData::Pointer(value.Value.lpv),
122 sys::PT_FLOAT => PropValueData::Float(value.Value.flt),
123 sys::PT_DOUBLE => PropValueData::Double(value.Value.dbl),
124 sys::PT_BOOLEAN => PropValueData::Boolean(value.Value.b),
125 sys::PT_CURRENCY => PropValueData::Currency(value.Value.cur.int64),
126 sys::PT_APPTIME => PropValueData::AppTime(value.Value.at),
127 sys::PT_SYSTIME => PropValueData::FileTime(value.Value.ft),
128 sys::PT_STRING8 => {
129 if value.Value.lpszA.is_null() {
130 PropValueData::Error(E_POINTER)
131 } else {
132 PropValueData::AnsiString(PCSTR::from_raw(value.Value.lpszA.as_ptr()))
133 }
134 }
135 sys::PT_BINARY => {
136 if value.Value.bin.lpb.is_null() {
137 PropValueData::Error(E_POINTER)
138 } else {
139 PropValueData::Binary(slice::from_raw_parts(
140 value.Value.bin.lpb,
141 value.Value.bin.cb as usize,
142 ))
143 }
144 }
145 sys::PT_UNICODE => {
146 if value.Value.lpszW.is_null() {
147 PropValueData::Error(E_POINTER)
148 } else {
149 let mut aligned = Vec::new();
150 let mut raw = value.Value.lpszW.as_ptr();
151 loop {
152 let next = ptr::read_unaligned(raw);
153 raw = raw.offset(1);
154 aligned.push(next);
155 if next == 0 {
156 break;
157 }
158 }
159 PropValueData::Unicode(aligned)
160 }
161 }
162 sys::PT_CLSID => {
163 if value.Value.lpguid.is_null() {
164 PropValueData::Error(E_POINTER)
165 } else {
166 PropValueData::Guid(ptr::read_unaligned(value.Value.lpguid))
167 }
168 }
169 sys::PT_LONGLONG => PropValueData::LargeInteger(value.Value.li),
170 sys::PT_MV_SHORT => {
171 if value.Value.MVi.lpi.is_null() {
172 PropValueData::Error(E_POINTER)
173 } else {
174 PropValueData::ShortArray(slice::from_raw_parts(
175 value.Value.MVi.lpi,
176 value.Value.MVi.cValues as usize,
177 ))
178 }
179 }
180 sys::PT_MV_LONG => {
181 if value.Value.MVl.lpl.is_null() {
182 PropValueData::Error(E_POINTER)
183 } else {
184 PropValueData::LongArray(slice::from_raw_parts(
185 value.Value.MVl.lpl,
186 value.Value.MVl.cValues as usize,
187 ))
188 }
189 }
190 sys::PT_MV_FLOAT => {
191 if value.Value.MVflt.lpflt.is_null() {
192 PropValueData::Error(E_POINTER)
193 } else {
194 PropValueData::FloatArray(slice::from_raw_parts(
195 value.Value.MVflt.lpflt,
196 value.Value.MVflt.cValues as usize,
197 ))
198 }
199 }
200 sys::PT_MV_DOUBLE => {
201 if value.Value.MVdbl.lpdbl.is_null() {
202 PropValueData::Error(E_POINTER)
203 } else {
204 let count = value.Value.MVdbl.cValues as usize;
205 let first = value.Value.MVdbl.lpdbl;
206 let mut values = Vec::with_capacity(count);
207 for idx in 0..count {
208 values.push(ptr::read_unaligned(first.add(idx)))
209 }
210 PropValueData::DoubleArray(values)
211 }
212 }
213 sys::PT_MV_CURRENCY => {
214 if value.Value.MVcur.lpcur.is_null() {
215 PropValueData::Error(E_POINTER)
216 } else {
217 let count = value.Value.MVcur.cValues as usize;
218 let first = value.Value.MVcur.lpcur;
219 let mut values = Vec::with_capacity(count);
220 for idx in 0..count {
221 values.push(ptr::read_unaligned(first.add(idx)))
222 }
223 PropValueData::CurrencyArray(values)
224 }
225 }
226 sys::PT_MV_APPTIME => {
227 if value.Value.MVat.lpat.is_null() {
228 PropValueData::Error(E_POINTER)
229 } else {
230 let count = value.Value.MVat.cValues as usize;
231 let first = value.Value.MVat.lpat;
232 let mut values = Vec::with_capacity(count);
233 for idx in 0..count {
234 values.push(ptr::read_unaligned(first.add(idx)))
235 }
236 PropValueData::AppTimeArray(values)
237 }
238 }
239 sys::PT_MV_SYSTIME => {
240 if value.Value.MVft.lpft.is_null() {
241 PropValueData::Error(E_POINTER)
242 } else {
243 let count = value.Value.MVft.cValues as usize;
244 let first = value.Value.MVft.lpft;
245 let mut values = Vec::with_capacity(count);
246 for idx in 0..count {
247 values.push(ptr::read_unaligned(first.add(idx)))
248 }
249 PropValueData::FileTimeArray(values)
250 }
251 }
252 sys::PT_MV_BINARY => {
253 if value.Value.MVbin.lpbin.is_null() {
254 PropValueData::Error(E_POINTER)
255 } else {
256 let count = value.Value.MVbin.cValues as usize;
257 let first = value.Value.MVbin.lpbin;
258 let mut values = Vec::with_capacity(count);
259 for idx in 0..count {
260 values.push(ptr::read_unaligned(first.add(idx)))
261 }
262 PropValueData::BinaryArray(values)
263 }
264 }
265 sys::PT_MV_STRING8 => {
266 if value.Value.MVszA.lppszA.is_null() {
267 PropValueData::Error(E_POINTER)
268 } else {
269 let count = value.Value.MVszA.cValues as usize;
270 let first = value.Value.MVszA.lppszA;
271 let mut values = Vec::with_capacity(count);
272 for idx in 0..count {
273 values.push(PCSTR(ptr::read_unaligned(first.add(idx)).0))
274 }
275 PropValueData::AnsiStringArray(values)
276 }
277 }
278 sys::PT_MV_UNICODE => {
279 if value.Value.MVszW.lppszW.is_null() {
280 PropValueData::Error(E_POINTER)
281 } else {
282 let count = value.Value.MVszW.cValues as usize;
283 let first = value.Value.MVszW.lppszW;
284 let mut values = Vec::with_capacity(count);
285 for idx in 0..count {
286 values.push(PCWSTR(ptr::read_unaligned(first.add(idx)).0))
287 }
288 PropValueData::UnicodeArray(values)
289 }
290 }
291 sys::PT_MV_CLSID => {
292 if value.Value.MVguid.lpguid.is_null() {
293 PropValueData::Error(E_POINTER)
294 } else {
295 let count = value.Value.MVguid.cValues as usize;
296 let first = value.Value.MVguid.lpguid;
297 let mut values = Vec::with_capacity(count);
298 for idx in 0..count {
299 values.push(ptr::read_unaligned(first.add(idx)))
300 }
301 PropValueData::GuidArray(values)
302 }
303 }
304 sys::PT_MV_LONGLONG => {
305 if value.Value.MVli.lpli.is_null() {
306 PropValueData::Error(E_POINTER)
307 } else {
308 let count = value.Value.MVli.cValues as usize;
309 let first = value.Value.MVli.lpli;
310 let mut values = Vec::with_capacity(count);
311 for idx in 0..count {
312 values.push(ptr::read_unaligned(first.add(idx)))
313 }
314 PropValueData::LargeIntegerArray(values)
315 }
316 }
317 sys::PT_ERROR => PropValueData::Error(HRESULT(value.Value.err)),
318 sys::PT_OBJECT => PropValueData::Object(value.Value.x),
319 _ => PropValueData::Error(E_INVALIDARG),
320 }
321 };
322 PropValue { tag, value: data }
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use super::*;
329
330 use crate::{PropTag, PropType, sys};
331 use core::{iter, mem, ptr};
332 use windows_core::{s, w};
333
334 #[test]
335 fn test_null() {
336 let value = sys::SPropValue {
337 ulPropTag: sys::PR_NULL,
338 ..Default::default()
339 };
340 let value = PropValue::from(&value);
341 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_NULL);
342 }
343
344 #[test]
345 fn test_short() {
346 let mut value = sys::SPropValue {
347 ulPropTag: u32::from(
348 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_I2 as u16)),
349 ),
350 ..Default::default()
351 };
352 value.Value.i = 1;
353 let value = PropValue::from(&value);
354 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_I2);
355 assert!(matches!(value.value, PropValueData::Short(1)));
356 }
357
358 #[test]
359 fn test_long() {
360 let mut value = sys::SPropValue {
361 ulPropTag: u32::from(
362 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_I4 as u16)),
363 ),
364 ..Default::default()
365 };
366 value.Value.l = 2;
367 let value = PropValue::from(&value);
368 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_I4);
369 assert!(matches!(value.value, PropValueData::Long(2)));
370 }
371
372 #[test]
373 fn test_pointer() {
374 let mut value = sys::SPropValue {
375 ulPropTag: u32::from(
376 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_PTR as u16)),
377 ),
378 ..Default::default()
379 };
380 value.Value.lpv = ptr::null_mut();
381 let value = PropValue::from(&value);
382 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_PTR);
383 assert!(matches!(
384 value.value,
385 PropValueData::Pointer(ptr) if ptr.is_null()
386 ));
387 }
388
389 #[test]
390 fn test_float() {
391 let mut value = sys::SPropValue {
392 ulPropTag: u32::from(
393 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_R4 as u16)),
394 ),
395 ..Default::default()
396 };
397 value.Value.flt = 3.0;
398 let value = PropValue::from(&value);
399 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_R4);
400 assert!(matches!(value.value, PropValueData::Float(3.0)));
401 }
402
403 #[test]
404 fn test_double() {
405 let mut value = sys::SPropValue {
406 ulPropTag: u32::from(
407 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_R8 as u16)),
408 ),
409 ..Default::default()
410 };
411 value.Value.dbl = 4.0;
412 let value = PropValue::from(&value);
413 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_R8);
414 assert!(matches!(value.value, PropValueData::Double(4.0)));
415 }
416
417 #[test]
418 fn test_boolean() {
419 let mut value = sys::SPropValue {
420 ulPropTag: u32::from(
421 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_BOOLEAN as u16)),
422 ),
423 ..Default::default()
424 };
425 value.Value.b = 5;
426 let value = PropValue::from(&value);
427 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_BOOLEAN);
428 assert!(matches!(value.value, PropValueData::Boolean(5)));
429 }
430
431 #[test]
432 fn test_currency() {
433 let mut value = sys::SPropValue {
434 ulPropTag: u32::from(
435 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_CURRENCY as u16)),
436 ),
437 ..Default::default()
438 };
439 value.Value.cur.int64 = 6;
440 let value = PropValue::from(&value);
441 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_CURRENCY);
442 assert!(matches!(value.value, PropValueData::Currency(6)));
443 }
444
445 #[test]
446 fn test_app_time() {
447 let mut value = sys::SPropValue {
448 ulPropTag: u32::from(
449 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_APPTIME as u16)),
450 ),
451 ..Default::default()
452 };
453 value.Value.at = 7.0;
454 let value = PropValue::from(&value);
455 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_APPTIME);
456 assert!(matches!(value.value, PropValueData::AppTime(7.0)));
457 }
458
459 #[test]
460 fn test_file_time() {
461 let mut value = sys::SPropValue {
462 ulPropTag: u32::from(
463 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_SYSTIME as u16)),
464 ),
465 ..Default::default()
466 };
467 value.Value.ft.dwLowDateTime = 8;
468 let value = PropValue::from(&value);
469 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_SYSTIME);
470 assert!(matches!(
471 value.value,
472 PropValueData::FileTime(FILETIME {
473 dwHighDateTime: 0,
474 dwLowDateTime: 8
475 })
476 ));
477 }
478
479 #[test]
480 fn test_ansi_string() {
481 let expected = s!("nine");
482 let mut value = sys::SPropValue {
483 ulPropTag: u32::from(
484 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_STRING8 as u16)),
485 ),
486 ..Default::default()
487 };
488 value.Value.lpszA.0 = expected.0 as *mut _;
489 let value = PropValue::from(&value);
490 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_STRING8);
491 assert!(matches!(
492 value.value,
493 PropValueData::AnsiString(actual) if actual.0 == expected.0
494 ));
495 }
496
497 #[test]
498 fn test_binary() {
499 let expected = 10;
500 let mut value = sys::SPropValue {
501 ulPropTag: u32::from(
502 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_BINARY as u16)),
503 ),
504 ..Default::default()
505 };
506 value.Value.bin.cb = mem::size_of_val(&expected) as u32;
507 value.Value.bin.lpb = &expected as *const i32 as *mut i32 as *mut _;
508 let value = PropValue::from(&value);
509 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_BINARY);
510 assert!(matches!(
511 value.value,
512 PropValueData::Binary(actual)
513 if actual.len() == mem::size_of_val(&expected)
514 && actual.as_ptr() as *const i32 == &expected as *const i32
515 ));
516 }
517
518 #[test]
519 fn test_unicode() {
520 let expected = w!("eleven");
521 let mut value = sys::SPropValue {
522 ulPropTag: u32::from(
523 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_UNICODE as u16)),
524 ),
525 ..Default::default()
526 };
527 value.Value.lpszW.0 = expected.0 as *mut _;
528 let value = PropValue::from(&value);
529 let PropValueData::Unicode(actual) = value.value else {
530 panic!("wrong type");
531 };
532 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_UNICODE);
533 let expected = unsafe { expected.as_wide() };
534 let expected: Vec<_> = expected.iter().copied().chain(iter::once(0)).collect();
535 assert_eq!(actual, expected);
536 }
537
538 #[test]
539 fn test_guid() {
540 let expected = GUID {
541 data1: 12,
542 ..Default::default()
543 };
544 let mut value = sys::SPropValue {
545 ulPropTag: u32::from(
546 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_CLSID as u16)),
547 ),
548 ..Default::default()
549 };
550 value.Value.lpguid = &expected as *const _ as *mut _;
551 let value = PropValue::from(&value);
552 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_CLSID);
553 assert!(matches!(
554 value.value,
555 PropValueData::Guid(GUID { data1: 12, .. })
556 ));
557 }
558
559 #[test]
560 fn test_large_integer() {
561 let mut value = sys::SPropValue {
562 ulPropTag: u32::from(
563 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_I8 as u16)),
564 ),
565 ..Default::default()
566 };
567 value.Value.li = 13;
568 let value = PropValue::from(&value);
569 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_I8);
570 assert!(matches!(value.value, PropValueData::LargeInteger(13)));
571 }
572
573 #[test]
574 fn test_short_array() {
575 let expected = [14_i16, 15];
576 let mut value = sys::SPropValue {
577 ulPropTag: u32::from(
578 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_SHORT as u16)),
579 ),
580 ..Default::default()
581 };
582 value.Value.MVi.cValues = expected.len() as u32;
583 value.Value.MVi.lpi = expected.as_ptr() as *mut _;
584 let value = PropValue::from(&value);
585 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_SHORT);
586 assert!(matches!(value.value, PropValueData::ShortArray([14, 15])));
587 }
588
589 #[test]
590 fn test_long_array() {
591 let expected = [15_i32, 16];
592 let mut value = sys::SPropValue {
593 ulPropTag: u32::from(
594 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_LONG as u16)),
595 ),
596 ..Default::default()
597 };
598 value.Value.MVl.cValues = expected.len() as u32;
599 value.Value.MVl.lpl = expected.as_ptr() as *mut _;
600 let value = PropValue::from(&value);
601 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_LONG);
602 assert!(matches!(value.value, PropValueData::LongArray([15, 16])));
603 }
604
605 #[test]
606 fn test_float_array() {
607 let expected = [16.0_f32, 17.0];
608 let mut value = sys::SPropValue {
609 ulPropTag: u32::from(
610 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_FLOAT as u16)),
611 ),
612 ..Default::default()
613 };
614 value.Value.MVflt.cValues = expected.len() as u32;
615 value.Value.MVflt.lpflt = expected.as_ptr() as *mut _;
616 let value = PropValue::from(&value);
617 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_FLOAT);
618 assert!(matches!(
619 value.value,
620 PropValueData::FloatArray([16.0, 17.0])
621 ));
622 }
623
624 #[test]
625 fn test_double_array() {
626 let expected = [17.0_f64, 18.0];
627 let mut value = sys::SPropValue {
628 ulPropTag: u32::from(
629 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_DOUBLE as u16)),
630 ),
631 ..Default::default()
632 };
633 value.Value.MVdbl.cValues = expected.len() as u32;
634 value.Value.MVdbl.lpdbl = expected.as_ptr() as *mut _;
635 let value = PropValue::from(&value);
636 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_DOUBLE);
637 let PropValueData::DoubleArray(values) = value.value else {
638 panic!("wrong type")
639 };
640 assert!(matches!(values.as_slice(), [17.0, 18.0]));
641 }
642
643 #[test]
644 fn test_currency_array() {
645 let expected = [CY { int64: 18 }, CY { int64: 19 }];
646 let mut value = sys::SPropValue {
647 ulPropTag: u32::from(
648 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_CURRENCY as u16)),
649 ),
650 ..Default::default()
651 };
652 value.Value.MVcur.cValues = expected.len() as u32;
653 value.Value.MVcur.lpcur = expected.as_ptr() as *mut _;
654 let value = PropValue::from(&value);
655 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_CURRENCY);
656 let PropValueData::CurrencyArray(values) = value.value else {
657 panic!("wrong type")
658 };
659 unsafe {
660 assert!(matches!(
661 values.as_slice(),
662 [CY { int64: 18 }, CY { int64: 19 }]
663 ));
664 }
665 }
666
667 #[test]
668 fn test_app_time_array() {
669 let expected = [19.0_f64, 20.0];
670 let mut value = sys::SPropValue {
671 ulPropTag: u32::from(
672 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_APPTIME as u16)),
673 ),
674 ..Default::default()
675 };
676 value.Value.MVat.cValues = expected.len() as u32;
677 value.Value.MVat.lpat = expected.as_ptr() as *mut _;
678 let value = PropValue::from(&value);
679 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_APPTIME);
680 let PropValueData::AppTimeArray(values) = value.value else {
681 panic!("wrong type")
682 };
683 assert!(matches!(values.as_slice(), [19.0, 20.0]));
684 }
685
686 #[test]
687 fn test_file_time_array() {
688 let expected = [
689 FILETIME {
690 dwHighDateTime: 20,
691 dwLowDateTime: 21,
692 },
693 FILETIME {
694 dwHighDateTime: 22,
695 dwLowDateTime: 23,
696 },
697 ];
698 let mut value = sys::SPropValue {
699 ulPropTag: u32::from(
700 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_SYSTIME as u16)),
701 ),
702 ..Default::default()
703 };
704 value.Value.MVft.cValues = expected.len() as u32;
705 value.Value.MVft.lpft = expected.as_ptr() as *mut _;
706 let value = PropValue::from(&value);
707 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_SYSTIME);
708 let PropValueData::FileTimeArray(values) = value.value else {
709 panic!("wrong type")
710 };
711 assert!(matches!(
712 values.as_slice(),
713 [
714 FILETIME {
715 dwHighDateTime: 20,
716 dwLowDateTime: 21,
717 },
718 FILETIME {
719 dwHighDateTime: 22,
720 dwLowDateTime: 23,
721 }
722 ]
723 ));
724 }
725
726 #[test]
727 fn test_binary_array() {
728 let expected1 = [24_u8, 25_u8];
729 let expected1 = sys::SBinary {
730 cb: expected1.len() as u32,
731 lpb: expected1.as_ptr() as *mut _,
732 };
733 let expected2 = [26_u8, 27_u8];
734 let expected2 = sys::SBinary {
735 cb: expected2.len() as u32,
736 lpb: expected2.as_ptr() as *mut _,
737 };
738 let expected = [expected1, expected2];
739 let mut value = sys::SPropValue {
740 ulPropTag: u32::from(
741 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_BINARY as u16)),
742 ),
743 ..Default::default()
744 };
745 value.Value.MVbin.cValues = expected.len() as u32;
746 value.Value.MVbin.lpbin = expected.as_ptr() as *mut _;
747 let value = PropValue::from(&value);
748 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_BINARY);
749 let PropValueData::BinaryArray(values) = value.value else {
750 panic!("wrong type")
751 };
752 assert!(matches!(
753 values.as_slice(),
754 [actual1, actual2]
755 if actual1.cb == expected[0].cb && actual1.lpb == expected[0].lpb
756 && actual2.cb == expected[1].cb && actual2.lpb == expected[1].lpb
757 ));
758 }
759
760 #[test]
761 fn test_ansi_string_array() {
762 let expected = [s!("twenty-eight"), s!("twenty-nine")];
763 let mut value = sys::SPropValue {
764 ulPropTag: u32::from(
765 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_STRING8 as u16)),
766 ),
767 ..Default::default()
768 };
769 value.Value.MVszA.cValues = expected.len() as u32;
770 value.Value.MVszA.lppszA = expected.as_ptr() as *mut _;
771 let value = PropValue::from(&value);
772 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_STRING8);
773 let PropValueData::AnsiStringArray(values) = value.value else {
774 panic!("wrong type")
775 };
776 assert!(matches!(
777 values.as_slice(),
778 [actual1, actual2]
779 if actual1.0 == expected[0].0 && actual2.0 == expected[1].0
780 ));
781 }
782
783 #[test]
784 fn test_unicode_string_array() {
785 let expected = [w!("thirty"), w!("thirty-one")];
786 let mut value = sys::SPropValue {
787 ulPropTag: u32::from(
788 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_UNICODE as u16)),
789 ),
790 ..Default::default()
791 };
792 value.Value.MVszW.cValues = expected.len() as u32;
793 value.Value.MVszW.lppszW = expected.as_ptr() as *mut _;
794 let value = PropValue::from(&value);
795 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_UNICODE);
796 let PropValueData::UnicodeArray(values) = value.value else {
797 panic!("wrong type")
798 };
799 assert!(matches!(
800 values.as_slice(),
801 [actual1, actual2]
802 if actual1.0 == expected[0].0 && actual2.0 == expected[1].0
803 ));
804 }
805
806 #[test]
807 fn test_guid_array() {
808 let expected = [
809 GUID {
810 data1: 32,
811 ..Default::default()
812 },
813 GUID {
814 data2: 33,
815 ..Default::default()
816 },
817 GUID {
818 data3: 34,
819 ..Default::default()
820 },
821 GUID {
822 data4: [35, 0, 0, 0, 0, 0, 0, 0],
823 ..Default::default()
824 },
825 ];
826 let mut value = sys::SPropValue {
827 ulPropTag: u32::from(
828 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_CLSID as u16)),
829 ),
830 ..Default::default()
831 };
832 value.Value.MVguid.cValues = expected.len() as u32;
833 value.Value.MVguid.lpguid = expected.as_ptr() as *mut _;
834 let value = PropValue::from(&value);
835 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_CLSID);
836 let PropValueData::GuidArray(values) = value.value else {
837 panic!("wrong type")
838 };
839 assert!(matches!(
840 values.as_slice(),
841 [
842 GUID { data1: 32, .. },
843 GUID { data2: 33, .. },
844 GUID { data3: 34, .. },
845 GUID {
846 data4: [35, ..],
847 ..
848 }
849 ]
850 ));
851 }
852
853 #[test]
854 fn test_large_integer_array() {
855 let expected = [36_i64, 37];
856 let mut value = sys::SPropValue {
857 ulPropTag: u32::from(
858 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_MV_LONGLONG as u16)),
859 ),
860 ..Default::default()
861 };
862 value.Value.MVli.cValues = expected.len() as u32;
863 value.Value.MVli.lpli = expected.as_ptr() as *mut _;
864 let value = PropValue::from(&value);
865 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_MV_LONGLONG);
866 let PropValueData::LargeIntegerArray(values) = value.value else {
867 panic!("wrong type")
868 };
869 assert!(matches!(values.as_slice(), [36, 37]));
870 }
871
872 #[test]
873 fn test_error() {
874 let mut value = sys::SPropValue {
875 ulPropTag: u32::from(
876 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_ERROR as u16)),
877 ),
878 ..Default::default()
879 };
880 value.Value.err = 38;
881 let value = PropValue::from(&value);
882 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_ERROR);
883 assert!(matches!(value.value, PropValueData::Error(HRESULT(38))));
884 }
885
886 #[test]
887 fn test_object() {
888 let mut value = sys::SPropValue {
889 ulPropTag: u32::from(
890 PropTag(sys::PR_NULL).change_prop_type(PropType::new(sys::PT_OBJECT as u16)),
891 ),
892 ..Default::default()
893 };
894 value.Value.x = 39;
895 let value = PropValue::from(&value);
896 assert_eq!(u32::from(value.tag.prop_type()), sys::PT_OBJECT);
897 assert!(matches!(value.value, PropValueData::Object(39)));
898 }
899}