wsl_com_api_sys/
lib.rs

1#![allow(non_snake_case)]
2#![allow(non_camel_case_types)]
3
4use windows::core::{IUnknown, Interface, Result, GUID, HRESULT, PCSTR, PCWSTR, PWSTR};
5use windows::Win32::{
6    Foundation::HANDLE,
7    System::Com::{CoCreateInstance, CLSCTX_LOCAL_SERVER},
8};
9
10use std::mem::MaybeUninit;
11
12pub mod constants;
13pub mod error;
14pub mod interop;
15
16const CLSID_LXSSUSERSESSION: GUID = GUID::from_u128(0xa9b7a1b9_0671_405c_95f1_e0612cb4ce7e);
17const IID_ILXSSUSERSESSION: GUID = GUID::from_u128(0x38541bdc_f54f_4ceb_85d0_37f0f3d2617e);
18
19pub type LxssError = (HRESULT, LXSS_ERROR_INFO);
20pub type LxssResult<T> = std::result::Result<T, LxssError>;
21
22#[repr(C)]
23pub struct LXSS_ERROR_INFO {
24    pub Flags: u32,
25    pub Context: u64,
26    pub Message: PWSTR,
27    pub Warnings: PWSTR,
28    pub WarningsPipe: u32,
29}
30
31unsafe impl Send for LXSS_ERROR_INFO {}
32
33impl std::fmt::Debug for LXSS_ERROR_INFO {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        let mut s = f.debug_struct("LXSS_ERROR_INFO");
36        s.field("Flags", &self.Flags)
37            .field("Context", &self.Context)
38            .field("WarningsPipe", &self.WarningsPipe);
39
40        if !self.Message.is_null() {
41            s.field("Message", &unsafe { self.Message.to_string() });
42        }
43
44        if !self.Warnings.is_null() {
45            s.field("Warnings", &unsafe { self.Warnings.to_string() });
46        }
47
48        s.finish()
49    }
50}
51
52#[repr(C)]
53pub struct LXSS_ENUMERATE_INFO {
54    pub DistroGuid: GUID,
55    pub State: u32,
56    pub Version: u32,
57    pub Flags: u32,
58    pub DistroName: [u16; 257],
59}
60
61#[repr(C)]
62pub struct LXSS_HANDLE {
63    pub Handle: u32,
64    pub HandleType: LxssHandleType,
65}
66
67#[repr(u32)]
68pub enum LxssHandleType {
69    LxssHandleConsole = 0,
70    LxssHandleInput,
71    LxssHandleOutput,
72}
73
74#[repr(C)]
75pub struct LXSS_STD_HANDLES {
76    pub StdIn: LXSS_HANDLE,
77    pub StdOut: LXSS_HANDLE,
78    pub StdErr: LXSS_HANDLE,
79}
80
81#[repr(transparent)]
82#[derive(Clone)]
83pub struct ILxssUserSession(pub IUnknown);
84
85unsafe impl Interface for ILxssUserSession {
86    type Vtable = ILxssUserSession_Vtbl;
87    const IID: GUID = IID_ILXSSUSERSESSION;
88}
89
90#[repr(C)]
91pub struct ILxssUserSession_Vtbl {
92    pub base__: windows::core::IUnknown_Vtbl,
93
94    pub CreateInstance: unsafe extern "system" fn(
95        this: *mut ::core::ffi::c_void,
96        distro_guid: *const GUID,
97        flags: u32,
98        error: *mut LXSS_ERROR_INFO,
99    ) -> HRESULT,
100
101    pub RegisterDistribution: unsafe extern "system" fn(
102        this: *mut ::core::ffi::c_void,
103        name: PCWSTR,
104        version: u32,
105        file_handle: HANDLE,
106        stderr_handle: HANDLE,
107        target_directory: PCWSTR,
108        flags: u32,
109        vhd_size: u64,
110        package_family_name: PCWSTR,
111        installed_name: *mut PWSTR,
112        error: *mut LXSS_ERROR_INFO,
113        out_guid: *mut GUID,
114    ) -> HRESULT,
115
116    pub RegisterDistributionPipe: unsafe extern "system" fn(
117        this: *mut ::core::ffi::c_void,
118        name: PCWSTR,
119        version: u32,
120        pipe_handle: HANDLE,
121        stderr_handle: HANDLE,
122        target_directory: PCWSTR,
123        flags: u32,
124        vhd_size: u64,
125        package_family_name: PCWSTR,
126        installed_name: *mut PWSTR,
127        error: *mut LXSS_ERROR_INFO,
128        out_guid: *mut GUID,
129    ) -> HRESULT,
130
131    pub GetDistributionId: unsafe extern "system" fn(
132        this: *mut ::core::ffi::c_void,
133        name: PCWSTR,
134        flags: u32,
135        error: *mut LXSS_ERROR_INFO,
136        out_guid: *mut GUID,
137    ) -> HRESULT,
138
139    pub TerminateDistribution: unsafe extern "system" fn(
140        this: *mut ::core::ffi::c_void,
141        distro_guid: *const GUID,
142        error: *mut LXSS_ERROR_INFO,
143    ) -> HRESULT,
144
145    pub UnregisterDistribution: unsafe extern "system" fn(
146        this: *mut ::core::ffi::c_void,
147        distro_guid: *const GUID,
148        error: *mut LXSS_ERROR_INFO,
149    ) -> HRESULT,
150
151    pub ConfigureDistribution: unsafe extern "system" fn(
152        this: *mut ::core::ffi::c_void,
153        distro_guid: *const GUID,
154        default_uid: u32,
155        flags: u32,
156        error: *mut LXSS_ERROR_INFO,
157    ) -> HRESULT,
158
159    pub GetDistributionConfiguration: unsafe extern "system" fn(
160        this: *mut ::core::ffi::c_void,
161        distro_guid: *const GUID,
162        distribution_name: *mut PWSTR,
163        version: *mut u32,
164        default_uid: *mut u32,
165        env_count: *mut u32,
166        default_environment: *mut *mut PCSTR,
167        flags: *mut u32,
168        error: *mut LXSS_ERROR_INFO,
169    ) -> HRESULT,
170
171    pub GetDefaultDistribution: unsafe extern "system" fn(
172        this: *mut ::core::ffi::c_void,
173        error: *mut LXSS_ERROR_INFO,
174        out_guid: *mut GUID,
175    ) -> HRESULT,
176
177    pub ResizeDistribution: unsafe extern "system" fn(
178        this: *mut ::core::ffi::c_void,
179        distro_guid: *const GUID,
180        output_handle: HANDLE,
181        new_size: u64,
182        error: *mut LXSS_ERROR_INFO,
183    ) -> HRESULT,
184
185    pub SetDefaultDistribution: unsafe extern "system" fn(
186        this: *mut ::core::ffi::c_void,
187        distro_guid: *const GUID,
188        error: *mut LXSS_ERROR_INFO,
189    ) -> HRESULT,
190
191    pub SetSparse: unsafe extern "system" fn(
192        this: *mut ::core::ffi::c_void,
193        distro_guid: *const GUID,
194        sparse: i32,
195        allow_unsafe: i32,
196        error: *mut LXSS_ERROR_INFO,
197    ) -> HRESULT,
198
199    pub EnumerateDistributions: unsafe extern "system" fn(
200        this: *mut ::core::ffi::c_void,
201        count: *mut u32,
202        distros: *mut *const LXSS_ENUMERATE_INFO,
203        error: *mut LXSS_ERROR_INFO,
204    ) -> HRESULT,
205
206    pub CreateLxProcess: unsafe extern "system" fn(
207        this: *mut ::core::ffi::c_void,
208        distro_guid: *const GUID,
209        filename: PCSTR,
210        command_line_count: u32,
211        command_line: *const PCSTR,
212        cwd: PCWSTR,
213        nt_path: PCWSTR,
214        nt_env: *mut u16,
215        nt_env_len: u32,
216        username: PCWSTR,
217        columns: i16,
218        rows: i16,
219        console_handle: u32,
220        std_handles: *const LXSS_STD_HANDLES,
221        flags: u32,
222        out_distribution_id: *mut GUID,
223        out_instance_id: *mut GUID,
224        process_handle: *mut HANDLE,
225        server_handle: *mut HANDLE,
226        stdin: *mut HANDLE,
227        stdout: *mut HANDLE,
228        stderr: *mut HANDLE,
229        comm_channel: *mut HANDLE,
230        interop_socket: *mut HANDLE,
231        error: *mut LXSS_ERROR_INFO,
232    ) -> HRESULT,
233
234    pub SetVersion: unsafe extern "system" fn(
235        this: *mut ::core::ffi::c_void,
236        distro_guid: *const GUID,
237        version: u32,
238        stderr_handle: HANDLE,
239        error: *mut LXSS_ERROR_INFO,
240    ) -> HRESULT,
241
242    pub ExportDistribution: unsafe extern "system" fn(
243        this: *mut ::core::ffi::c_void,
244        distro_guid: *const GUID,
245        file_handle: HANDLE,
246        stderr_handle: HANDLE,
247        flags: u32,
248        error: *mut LXSS_ERROR_INFO,
249    ) -> HRESULT,
250
251    pub ExportDistributionPipe: unsafe extern "system" fn(
252        this: *mut ::core::ffi::c_void,
253        distro_guid: *const GUID,
254        pipe_handle: HANDLE,
255        stderr_handle: HANDLE,
256        flags: u32,
257        error: *mut LXSS_ERROR_INFO,
258    ) -> HRESULT,
259
260    pub AttachDisk: unsafe extern "system" fn(
261        this: *mut ::core::ffi::c_void,
262        disk: PCWSTR,
263        flags: u32,
264        error: *mut LXSS_ERROR_INFO,
265    ) -> HRESULT,
266
267    pub DetachDisk: unsafe extern "system" fn(
268        this: *mut ::core::ffi::c_void,
269        disk: PCWSTR,
270        result: *mut i32,
271        step: *mut i32,
272        error: *mut LXSS_ERROR_INFO,
273    ) -> HRESULT,
274
275    pub MountDisk: unsafe extern "system" fn(
276        this: *mut ::core::ffi::c_void,
277        disk: PCWSTR,
278        flags: u32,
279        partition_index: u32,
280        name: PCWSTR,
281        ttype: PCWSTR,
282        options: PCWSTR,
283        result: *mut i32,
284        step: *mut i32,
285        mount_name: *mut PWSTR,
286        error: *mut LXSS_ERROR_INFO,
287    ) -> HRESULT,
288
289    pub Shutdown: unsafe extern "system" fn(this: *mut ::core::ffi::c_void, force: i32) -> HRESULT,
290
291    pub ImportDistributionInplace: unsafe extern "system" fn(
292        this: *mut ::core::ffi::c_void,
293        name: PCWSTR,
294        vhd_path: PCWSTR,
295        error: *mut LXSS_ERROR_INFO,
296        out_guid: *mut GUID,
297    ) -> HRESULT,
298
299    pub MoveDistribution: unsafe extern "system" fn(
300        this: *mut ::core::ffi::c_void,
301        distro_guid: *const GUID,
302        name: PCWSTR,
303        error: *mut LXSS_ERROR_INFO,
304    ) -> HRESULT,
305}
306
307pub unsafe fn get_lxss_user_session() -> windows::core::Result<ILxssUserSession> {
308    let session: ILxssUserSession =
309        unsafe { CoCreateInstance(&CLSID_LXSSUSERSESSION, None, CLSCTX_LOCAL_SERVER)? };
310
311    Ok(session)
312}
313
314impl ILxssUserSession {
315    pub unsafe fn CreateInstance(&self, distro_guid: GUID, flags: u32) -> LxssResult<()> {
316        unsafe {
317            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
318            let mut error_info = std::mem::zeroed();
319            let result = ((*vtable).CreateInstance)(
320                self.0.as_raw(),
321                std::ptr::from_ref(&distro_guid),
322                flags,
323                std::ptr::from_mut(&mut error_info),
324            );
325            if result.is_ok() {
326                Ok(())
327            } else {
328                Err((result, error_info))
329            }
330        }
331    }
332
333    pub unsafe fn GetDefaultDistribution(&self) -> LxssResult<GUID> {
334        unsafe {
335            let vtable = self.vtable();
336            let mut error_info: LXSS_ERROR_INFO = std::mem::zeroed();
337            let mut guid = MaybeUninit::uninit();
338            let result = ((*vtable).GetDefaultDistribution)(
339                self.0.as_raw(),
340                std::ptr::from_mut(&mut error_info),
341                guid.as_mut_ptr(),
342            );
343            if result.is_ok() {
344                Ok(guid.assume_init())
345            } else {
346                Err((result, error_info))
347            }
348        }
349    }
350
351    pub unsafe fn UnregisterDistribution(&self, distro_guid: GUID) -> LxssResult<()> {
352        unsafe {
353            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
354            let mut error_info = std::mem::zeroed();
355            let result = ((*vtable).UnregisterDistribution)(
356                self.0.as_raw(),
357                std::ptr::from_ref(&distro_guid),
358                std::ptr::from_mut(&mut error_info),
359            );
360            if result.is_ok() {
361                Ok(())
362            } else {
363                Err((result, error_info))
364            }
365        }
366    }
367
368    pub unsafe fn TerminateDistribution(&self, distro_guid: GUID) -> LxssResult<()> {
369        unsafe {
370            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
371            let mut error_info = std::mem::zeroed();
372            let result = ((*vtable).TerminateDistribution)(
373                self.0.as_raw(),
374                std::ptr::from_ref(&distro_guid),
375                std::ptr::from_mut(&mut error_info),
376            );
377            if result.is_ok() {
378                Ok(())
379            } else {
380                Err((result, error_info))
381            }
382        }
383    }
384
385    pub unsafe fn ConfigureDistribution(
386        &self,
387        distro_guid: GUID,
388        default_uid: u32,
389        flags: u32,
390    ) -> LxssResult<()> {
391        unsafe {
392            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
393            let mut error_info = std::mem::zeroed();
394            let result = ((*vtable).ConfigureDistribution)(
395                self.0.as_raw(),
396                std::ptr::from_ref(&distro_guid),
397                default_uid,
398                flags,
399                std::ptr::from_mut(&mut error_info),
400            );
401            if result.is_ok() {
402                Ok(())
403            } else {
404                Err((result, error_info))
405            }
406        }
407    }
408
409    pub unsafe fn EnumerateDistributions(&self) -> LxssResult<(u32, *const LXSS_ENUMERATE_INFO)> {
410        unsafe {
411            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
412            let mut error_info = std::mem::zeroed();
413            let mut distros = std::ptr::null();
414            let mut count = 0;
415            let result = ((*vtable).EnumerateDistributions)(
416                self.0.as_raw(),
417                std::ptr::from_mut(&mut count),
418                std::ptr::from_mut(&mut distros),
419                std::ptr::from_mut(&mut error_info),
420            );
421            if result.is_ok() {
422                Ok((count, distros))
423            } else {
424                Err((result, error_info))
425            }
426        }
427    }
428
429    pub unsafe fn SetVersion(
430        &self,
431        distro_guid: GUID,
432        version: u32,
433        stderr_handle: HANDLE,
434    ) -> LxssResult<()> {
435        unsafe {
436            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
437            let mut error_info = std::mem::zeroed();
438            let result = ((*vtable).SetVersion)(
439                self.0.as_raw(),
440                std::ptr::from_ref(&distro_guid),
441                version,
442                stderr_handle,
443                std::ptr::from_mut(&mut error_info),
444            );
445            if result.is_ok() {
446                Ok(())
447            } else {
448                Err((result, error_info))
449            }
450        }
451    }
452
453    pub unsafe fn RegisterDistribution(
454        &self,
455        name: PCWSTR,
456        version: u32,
457        file_handle: HANDLE,
458        stderr_handle: HANDLE,
459        target_directory: PCWSTR,
460        flags: u32,
461        vhd_size: u64, // zero = default size
462        package_family_name: PCWSTR,
463    ) -> LxssResult<RegisterDistributionResult> {
464        unsafe {
465            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
466            let mut error_info = std::mem::zeroed();
467            let mut installed_name = PWSTR::null();
468            let mut guid = MaybeUninit::uninit();
469            let result = ((*vtable).RegisterDistribution)(
470                self.0.as_raw(),
471                name,
472                version,
473                file_handle,
474                stderr_handle,
475                target_directory,
476                flags,
477                vhd_size,
478                package_family_name,
479                std::ptr::from_mut(&mut installed_name),
480                std::ptr::from_mut(&mut error_info),
481                guid.as_mut_ptr(),
482            );
483            if result.is_ok() {
484                Ok(RegisterDistributionResult {
485                    Guid: guid.assume_init(),
486                    InstalledName: installed_name,
487                })
488            } else {
489                Err((result, error_info))
490            }
491        }
492    }
493
494    pub unsafe fn ExportDistribution(
495        &self,
496        distro_guid: GUID,
497        file_handle: HANDLE,
498        stderr_handle: HANDLE,
499        flags: u32,
500    ) -> LxssResult<()> {
501        unsafe {
502            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
503            let mut error_info = std::mem::zeroed();
504            let result = ((*vtable).ExportDistribution)(
505                self.0.as_raw(),
506                std::ptr::from_ref(&distro_guid),
507                file_handle,
508                stderr_handle,
509                flags,
510                std::ptr::from_mut(&mut error_info),
511            );
512            if result.is_ok() {
513                Ok(())
514            } else {
515                Err((result, error_info))
516            }
517        }
518    }
519
520    pub unsafe fn CreateLxProcess(
521        &self,
522        distro_guid: GUID,
523        filename: PCSTR,
524        command_line_count: u32,
525        command_line: *const PCSTR,
526        cwd: PCWSTR,      // optional
527        nt_path: PCWSTR,  // optional
528        nt_env: *mut u16, // optional
529        nt_env_len: u32,  // optional
530        username: PCWSTR, // optional
531        columns: i16,
532        rows: i16,
533        console_handle: u32, // optional
534        std_handles: *const LXSS_STD_HANDLES,
535        flags: u32,
536    ) -> LxssResult<CreateLxProcessResult> {
537        unsafe {
538            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
539            let mut error_info = std::mem::zeroed();
540            let mut lx_process_result = CreateLxProcessResult {
541                DistributionId: GUID::default(),
542                InstanceId: GUID::default(),
543                ProcessHandle: HANDLE::default(),
544                ServerHandle: HANDLE::default(),
545                StandardIn: HANDLE::default(),
546                StandardOut: HANDLE::default(),
547                StandardErr: HANDLE::default(),
548                CommunicationChannel: HANDLE::default(),
549                InteropSocket: HANDLE::default(),
550            };
551            let result = ((*vtable).CreateLxProcess)(
552                self.0.as_raw(),
553                std::ptr::from_ref(&distro_guid),
554                filename,
555                command_line_count,
556                command_line,
557                cwd,
558                nt_path,
559                nt_env,
560                nt_env_len,
561                username,
562                columns,
563                rows,
564                console_handle,
565                std_handles,
566                flags,
567                std::ptr::from_mut(&mut lx_process_result.DistributionId),
568                std::ptr::from_mut(&mut lx_process_result.InstanceId),
569                std::ptr::from_mut(&mut lx_process_result.ProcessHandle),
570                std::ptr::from_mut(&mut lx_process_result.ServerHandle),
571                std::ptr::from_mut(&mut lx_process_result.StandardIn),
572                std::ptr::from_mut(&mut lx_process_result.StandardOut),
573                std::ptr::from_mut(&mut lx_process_result.StandardErr),
574                std::ptr::from_mut(&mut lx_process_result.CommunicationChannel),
575                std::ptr::from_mut(&mut lx_process_result.InteropSocket),
576                std::ptr::from_mut(&mut error_info),
577            );
578            if result.is_ok() {
579                Ok(lx_process_result)
580            } else {
581                Err((result, error_info))
582            }
583        }
584    }
585
586    pub unsafe fn Shutdown(&self, force: i32) -> Result<()> {
587        unsafe {
588            let vtable = self.0.vtable() as *const _ as *const ILxssUserSession_Vtbl;
589            let result = ((*vtable).Shutdown)(self.0.as_raw(), force);
590            if result.is_ok() {
591                Ok(())
592            } else {
593                Err(result.into())
594            }
595        }
596    }
597}
598
599pub struct RegisterDistributionResult {
600    pub Guid: GUID,
601    pub InstalledName: PWSTR,
602}
603
604#[derive(Debug)]
605pub struct CreateLxProcessResult {
606    pub DistributionId: GUID,
607    pub InstanceId: GUID,
608    pub ProcessHandle: HANDLE,
609    pub ServerHandle: HANDLE,
610    pub StandardIn: HANDLE,
611    pub StandardOut: HANDLE,
612    pub StandardErr: HANDLE,
613    pub CommunicationChannel: HANDLE,
614    pub InteropSocket: HANDLE,
615}