opc_da_client/opc_da/
com_utils.rs1use windows::{
7 Win32::System::Com::{CoTaskMemAlloc, CoTaskMemFree},
8 core::PWSTR,
9};
10
11#[derive(Debug, Clone, PartialEq)]
18pub struct RemoteArray<T: Sized> {
19 pointer: RemotePointer<T>,
20 len: u32,
21}
22
23impl<T: Sized> RemoteArray<T> {
24 #[inline(always)]
27 pub fn new(len: u32) -> Self {
28 Self {
29 pointer: RemotePointer::null(),
30 len,
31 }
32 }
33
34 #[inline(always)]
39 pub(crate) fn from_mut_ptr(pointer: *mut T, len: u32) -> Self {
40 Self {
41 pointer: RemotePointer::from_raw(pointer),
42 len,
43 }
44 }
45
46 #[inline(always)]
51 pub(crate) fn from_ptr(pointer: *const T, len: u32) -> Self {
52 Self {
53 pointer: RemotePointer::from_raw(pointer as *mut T),
54 len,
55 }
56 }
57
58 #[inline(always)]
60 pub fn empty() -> Self {
61 Self {
62 pointer: RemotePointer::null(),
63 len: 0,
64 }
65 }
66
67 #[inline(always)]
71 pub fn as_mut_ptr(&mut self) -> *mut *mut T {
72 self.pointer.as_mut_ptr()
73 }
74
75 #[inline(always)]
80 pub fn as_slice(&self) -> &[T] {
81 if self.pointer.inner.is_null() || self.len == 0 {
82 return &[];
83 }
84
85 let len = usize::try_from(self.len).unwrap_or(0);
86
87 unsafe { core::slice::from_raw_parts(self.pointer.inner, len) }
89 }
90
91 #[inline(always)]
96 pub fn as_mut_slice(&mut self) -> &mut [T] {
97 if self.pointer.inner.is_null() || self.len == 0 {
98 return &mut [];
99 }
100
101 let len = usize::try_from(self.len).unwrap_or(0);
102
103 unsafe { core::slice::from_raw_parts_mut(self.pointer.inner, len) }
105 }
106
107 #[inline(always)]
109 pub fn len(&self) -> u32 {
110 if self.pointer.inner.is_null() {
111 return 0;
112 }
113
114 self.len
115 }
116
117 #[inline(always)]
119 pub fn is_empty(&self) -> bool {
120 self.len == 0 || self.pointer.inner.is_null()
121 }
122
123 #[inline(always)]
127 pub fn as_mut_len_ptr(&mut self) -> *mut u32 {
128 &mut self.len
129 }
130
131 #[inline(always)]
136 pub(crate) unsafe fn set_len(&mut self, len: u32) {
137 self.len = len;
138 }
139
140 pub fn into_vec(self) -> Vec<RemotePointer<T>> {
141 self.as_slice()
142 .iter()
143 .map(|v| RemotePointer::from_raw(v as *const T as *mut T))
144 .collect()
145 }
146}
147
148impl<T: Sized> Default for RemoteArray<T> {
149 #[inline(always)]
151 fn default() -> Self {
152 Self::empty()
153 }
154}
155
156#[repr(transparent)]
161#[derive(Debug, Clone, PartialEq)]
162pub struct RemotePointer<T: Sized> {
163 inner: *mut T,
164}
165
166impl<T: Sized> RemotePointer<T> {
167 #[inline(always)]
169 pub fn null() -> Self {
170 Self {
171 inner: core::ptr::null_mut(),
172 }
173 }
174
175 #[inline(always)]
179 pub(crate) fn from_raw(pointer: *mut T) -> Self {
180 Self { inner: pointer }
181 }
182
183 pub(crate) fn copy_slice(value: &[T]) -> Self {
184 let pointer = unsafe { CoTaskMemAlloc(core::mem::size_of_val(value)) };
186 unsafe {
188 core::ptr::copy_nonoverlapping(value.as_ptr(), pointer as _, value.len());
189 }
190 Self {
191 inner: pointer as _,
192 }
193 }
194
195 #[inline(always)]
196 pub fn as_mut_ptr(&mut self) -> *mut *mut T {
197 &mut self.inner
198 }
199
200 #[inline(always)]
205 pub fn as_ref(&self) -> Option<&T> {
206 unsafe { self.inner.as_ref() }
208 }
209
210 #[inline(always)]
211 pub fn ok(&self) -> windows::core::Result<&T> {
212 unsafe { self.inner.as_ref() }.ok_or_else(|| {
214 windows::core::Error::new(windows::Win32::Foundation::E_POINTER, "Pointer is null")
215 })
216 }
217
218 #[inline(always)]
219 pub fn from_option<R: Into<RemotePointer<T>>>(value: Option<R>) -> Self {
220 match value {
221 Some(value) => value.into(),
222 None => Self::null(),
223 }
224 }
225}
226
227impl<T: Sized> Default for RemotePointer<T> {
228 #[inline(always)]
230 fn default() -> Self {
231 Self::null()
232 }
233}
234
235impl From<PWSTR> for RemotePointer<u16> {
236 #[inline(always)]
238 fn from(value: PWSTR) -> Self {
239 Self {
240 inner: value.as_ptr(),
241 }
242 }
243}
244
245impl From<&str> for RemotePointer<u16> {
246 #[inline(always)]
248 fn from(value: &str) -> Self {
249 Self::copy_slice(&value.encode_utf16().chain(Some(0)).collect::<Vec<u16>>())
250 }
251}
252
253impl TryFrom<RemotePointer<u16>> for String {
254 type Error = windows::core::Error;
255
256 #[inline(always)]
261 fn try_from(value: RemotePointer<u16>) -> Result<Self, Self::Error> {
262 if value.inner.is_null() {
263 return Err(windows::Win32::Foundation::E_POINTER.into());
264 }
265
266 Ok(unsafe { PWSTR(value.inner).to_string() }?)
268 }
269}
270
271impl TryFrom<RemotePointer<u16>> for Option<String> {
272 type Error = windows::core::Error;
273
274 #[inline(always)]
279 fn try_from(value: RemotePointer<u16>) -> Result<Self, Self::Error> {
280 if value.inner.is_null() {
281 return Ok(None);
282 }
283
284 Ok(Some(unsafe { PWSTR(value.inner).to_string() }?))
286 }
287}
288
289impl RemotePointer<u16> {
290 #[inline(always)]
292 pub fn as_mut_pwstr_ptr(&mut self) -> *mut PWSTR {
293 &mut self.inner as *mut *mut u16 as *mut PWSTR
294 }
295}
296
297impl<T: Sized> Drop for RemotePointer<T> {
298 #[inline(always)]
300 fn drop(&mut self) {
301 if !self.inner.is_null() {
302 unsafe {
304 CoTaskMemFree(Some(self.inner as _));
305 }
306 }
307 }
308}
309
310pub struct LocalPointer<T: Sized> {
314 inner: Option<Box<T>>,
315}
316
317impl<T: Sized> LocalPointer<T> {
318 #[inline(always)]
320 pub fn new(value: Option<T>) -> Self {
321 Self {
322 inner: value.map(Box::new),
323 }
324 }
325
326 #[inline(always)]
328 pub fn from_box(value: Box<T>) -> Self {
329 Self { inner: Some(value) }
330 }
331
332 #[inline(always)]
333 pub fn from_option<R: Into<LocalPointer<T>>>(value: Option<R>) -> Self {
334 match value {
335 Some(value) => value.into(),
336 None => Self::new(None),
337 }
338 }
339
340 #[inline(always)]
342 pub fn as_ptr(&self) -> *const T {
343 match &self.inner {
344 Some(value) => value.as_ref() as *const T,
345 None => std::ptr::null_mut(),
346 }
347 }
348
349 #[inline(always)]
351 pub fn as_mut_ptr(&mut self) -> *mut T {
352 match &mut self.inner {
353 Some(value) => value.as_mut() as *mut T,
354 None => std::ptr::null_mut(),
355 }
356 }
357
358 #[inline(always)]
360 pub fn into_inner(self) -> Option<T> {
361 self.inner.map(|v| *v)
362 }
363
364 #[inline(always)]
366 pub fn inner(&self) -> Option<&T> {
367 self.inner.as_ref().map(|v| v.as_ref())
368 }
369}
370
371impl<S: AsRef<str>> From<S> for LocalPointer<Vec<u16>> {
374 #[inline(always)]
376 fn from(s: S) -> Self {
377 Self::new(Some(s.as_ref().encode_utf16().chain(Some(0)).collect()))
378 }
379}
380
381impl From<&[String]> for LocalPointer<Vec<Vec<u16>>> {
382 #[inline(always)]
384 fn from(values: &[String]) -> Self {
385 Self::new(Some(
386 values
387 .iter()
388 .map(|s| s.encode_utf16().chain(Some(0)).collect())
389 .collect(),
390 ))
391 }
392}
393
394impl<T> LocalPointer<Vec<T>> {
395 #[inline(always)]
397 pub fn len(&self) -> usize {
398 match &self.inner {
399 Some(values) => values.len(),
400 None => 0,
401 }
402 }
403
404 #[inline(always)]
406 pub fn is_empty(&self) -> bool {
407 match &self.inner {
408 Some(values) => values.is_empty(),
409 None => true,
410 }
411 }
412
413 #[inline(always)]
415 pub fn as_array_ptr(&self) -> *const T {
416 match &self.inner {
417 Some(values) => values.as_ptr(),
418 None => std::ptr::null(),
419 }
420 }
421
422 #[inline(always)]
424 pub fn as_mut_array_ptr(&mut self) -> *mut T {
425 match &mut self.inner {
426 Some(values) => values.as_mut_ptr(),
427 None => std::ptr::null_mut(),
428 }
429 }
430}
431
432impl LocalPointer<Vec<Vec<u16>>> {
433 #[inline(always)]
435 pub fn as_pwstr_array(&self) -> Vec<windows::core::PWSTR> {
436 match &self.inner {
437 Some(values) => values
438 .iter()
439 .map(|value| windows::core::PWSTR(value.as_ptr() as _))
440 .collect(),
441 None => vec![windows::core::PWSTR::null()],
442 }
443 }
444
445 #[inline(always)]
447 pub fn as_pcwstr_array(&self) -> Vec<windows::core::PCWSTR> {
448 match &self.inner {
449 Some(values) => values
450 .iter()
451 .map(|value| windows::core::PCWSTR::from_raw(value.as_ptr() as _))
452 .collect(),
453 None => vec![windows::core::PCWSTR::null()],
454 }
455 }
456}
457
458impl LocalPointer<Vec<u16>> {
459 #[inline(always)]
461 pub fn as_pwstr(&self) -> windows::core::PWSTR {
462 match &self.inner {
463 Some(value) => windows::core::PWSTR(value.as_ptr() as _),
464 None => windows::core::PWSTR::null(),
465 }
466 }
467
468 #[inline(always)]
470 pub fn as_pcwstr(&self) -> windows::core::PCWSTR {
471 match &self.inner {
472 Some(value) => windows::core::PCWSTR::from_raw(value.as_ptr() as _),
473 None => windows::core::PCWSTR::null(),
474 }
475 }
476}
477
478pub(crate) trait IntoBridge<Bridge> {
481 fn into_bridge(self) -> Bridge;
482}
483
484pub(crate) trait ToNative<Native> {
485 fn to_native(&self) -> Native;
486}
487
488pub(crate) trait FromNative<Native> {
489 fn from_native(native: &Native) -> Self
490 where
491 Self: Sized;
492}
493
494pub(crate) trait TryToNative<Native> {
495 fn try_to_native(&self) -> windows::core::Result<Native>;
496}
497
498pub(crate) trait TryFromNative<Native> {
499 fn try_from_native(native: &Native) -> windows::core::Result<Self>
500 where
501 Self: Sized;
502}
503
504pub(crate) trait TryToLocal<Local> {
505 fn try_to_local(&self) -> windows::core::Result<Local>;
506}
507
508impl<Native, T: TryFromNative<Native>> TryToLocal<T> for Native {
509 fn try_to_local(&self) -> windows::core::Result<T> {
510 T::try_from_native(self)
511 }
512}
513
514impl<Native, T: FromNative<Native>> TryFromNative<Native> for T {
515 fn try_from_native(native: &Native) -> windows::core::Result<Self> {
516 Ok(Self::from_native(native))
517 }
518}
519
520impl<Native, T: ToNative<Native>> TryToNative<Native> for T {
521 fn try_to_native(&self) -> windows::core::Result<Native> {
522 Ok(self.to_native())
523 }
524}
525
526impl<Bridge, B: IntoBridge<Bridge>> IntoBridge<Vec<Bridge>> for Vec<B> {
527 fn into_bridge(self) -> Vec<Bridge> {
528 self.into_iter().map(IntoBridge::into_bridge).collect()
529 }
530}
531
532impl<Bridge, B: IntoBridge<Bridge> + Clone> IntoBridge<Vec<Bridge>> for &[B] {
533 fn into_bridge(self) -> Vec<Bridge> {
534 self.iter().cloned().map(IntoBridge::into_bridge).collect()
535 }
536}
537
538impl<Native, T: TryToNative<Native>> TryToNative<Vec<Native>> for Vec<T> {
539 fn try_to_native(&self) -> windows::core::Result<Vec<Native>> {
540 self.iter().map(TryToNative::try_to_native).collect()
541 }
542}
543
544impl TryFromNative<RemoteArray<windows::core::HRESULT>> for Vec<windows::core::Result<()>> {
545 fn try_from_native(
546 native: &RemoteArray<windows::core::HRESULT>,
547 ) -> windows::core::Result<Self> {
548 Ok(native.as_slice().iter().map(|v| (*v).ok()).collect())
549 }
550}
551
552impl<Native, T: TryFromNative<Native>> TryFromNative<RemoteArray<Native>> for Vec<T> {
553 fn try_from_native(native: &RemoteArray<Native>) -> windows::core::Result<Self> {
554 native.as_slice().iter().map(T::try_from_native).collect()
555 }
556}
557
558impl<Native, T: TryFromNative<Native>>
559 TryFromNative<(RemoteArray<Native>, RemoteArray<windows::core::HRESULT>)>
560 for Vec<windows::core::Result<T>>
561{
562 fn try_from_native(
563 native: &(RemoteArray<Native>, RemoteArray<windows::core::HRESULT>),
564 ) -> windows::core::Result<Self> {
565 let (results, errors) = native;
566 if results.len() != errors.len() {
567 return Err(windows::core::Error::new(
568 windows::Win32::Foundation::E_INVALIDARG,
569 "Results and errors arrays have different lengths",
570 ));
571 }
572
573 Ok(results
574 .as_slice()
575 .iter()
576 .zip(errors.as_slice())
577 .map(|(result, error)| {
578 if error.is_ok() {
579 T::try_from_native(result)
580 } else {
581 Err((*error).into())
582 }
583 })
584 .collect())
585 }
586}
587
588impl TryFromNative<windows::Win32::Foundation::FILETIME> for std::time::SystemTime {
589 fn try_from_native(
590 native: &windows::Win32::Foundation::FILETIME,
591 ) -> windows::core::Result<Self> {
592 let ft = ((native.dwHighDateTime as u64) << 32) | (u64::from(native.dwLowDateTime));
593 let duration_since_1601 = std::time::Duration::from_nanos(ft * 100);
594
595 let windows_to_unix_epoch_diff = std::time::Duration::from_secs(11_644_473_600);
596 let duration_since_unix_epoch = duration_since_1601
597 .checked_sub(windows_to_unix_epoch_diff)
598 .ok_or_else(|| {
599 windows::core::Error::new(
600 windows::Win32::Foundation::E_INVALIDARG,
601 "FILETIME is before UNIX_EPOCH",
602 )
603 })?;
604
605 Ok(std::time::UNIX_EPOCH + duration_since_unix_epoch)
606 }
607}
608
609#[macro_export]
610macro_rules! try_from_native {
612 ($native:expr) => {
613 $crate::opc_da::com_utils::TryFromNative::try_from_native($native)?
614 };
615}
616
617impl TryToNative<windows::Win32::Foundation::FILETIME> for std::time::SystemTime {
618 fn try_to_native(&self) -> windows::core::Result<windows::Win32::Foundation::FILETIME> {
619 let duration_since_unix_epoch =
620 self.duration_since(std::time::UNIX_EPOCH).map_err(|_| {
621 windows::core::Error::new(
622 windows::Win32::Foundation::E_INVALIDARG,
623 "SystemTime is before UNIX_EPOCH",
624 )
625 })?;
626
627 let duration_since_windows_epoch =
628 duration_since_unix_epoch + std::time::Duration::from_secs(11_644_473_600);
629
630 let ft = duration_since_windows_epoch.as_nanos() / 100;
631
632 Ok(windows::Win32::Foundation::FILETIME {
633 dwLowDateTime: ft as u32,
634 dwHighDateTime: (ft >> 32) as u32,
635 })
636 }
637}
638
639impl TryFromNative<windows::core::PWSTR> for String {
640 fn try_from_native(native: &windows::core::PWSTR) -> windows::core::Result<Self> {
641 RemotePointer::from(*native).try_into()
642 }
643}