成果物
環境
Windows 11 24H2
rustc 1.89.0
解説
EnumDisplayMonitors関数でデスクトップ領域のサイズを取得して、そのサイズのウィンドウを生成。SetWindowCompositionAttribute関数で生成したウィンドウにアクリル素材を適用しています。
EnumDisplayMonitors
EnumDisplayMonitors 関数ですべてのモニターのデスクトップ領域を取得します。
unsafe extern "system" fn enum_proc(_: HMONITOR, _: HDC, rect: *mut RECT, _: LPARAM) -> BOOL {
// RECT 構造体へのポインタを Rust の参照に変換
let rect = unsafe { &*rect };
// 各モニターのデスクトップ領域ごとにウィンドウを生成(後述)
create_window(rect).ok();
// 処理を継続する場合は TRUE を返す
true.into()
}
fn main() {
_ = unsafe { EnumDisplayMonitors(None, None, Some(enum_proc), LPARAM::default()) };
}
CreateWindowExW
CreateWindowExW 関数でウィンドウを生成します。第 1 引数に WS_EX_NOREDIRECTIONBITMAP を渡して目に見えるコンテンツのないウィンドウスタイル(よく分からない
)を設定します。第 5 ~ 8 引数でデスクトップ領域の大きさと同じウィンドウサイズを設定します。さらに SetWindowLongW 関数ですべてのウィンドウスタイルを無効にしてタイトルバーやボーダーのないウィンドウスタイルを設定しています。
fn create_window(rect: &RECT) -> Result<()> {
let hwnd = unsafe {
CreateWindowExW(
WS_EX_NOREDIRECTIONBITMAP, // 目に見えるコンテンツのないウィンドウ?
CLASS_NAME,
w!("Acrylic"),
WS_BORDER,
rect.left, // ウィンドウ左上の X 座標
rect.top, // ウィンドウ左上の Y 座標
rect.right - rect.left, // ウィンドウの幅
rect.bottom - rect.top, // ウィンドウの高さ
None,
None,
None,
None,
)?
};
// ウィンドウにアクリル素材を適用する(後述)
enable_blur_window(hwnd)?;
// タイトルバーのないウィンドウ
unsafe { SetWindowLongW(hwnd, GWL_STYLE, 0) };
// ウィンドウの表示
unsafe { ShowWindow(hwnd, SW_SHOW).ok()? };
unsafe { UpdateWindow(hwnd).ok()? };
Ok(())
}
SetWindowCompositionAttribute
SetWindowCompositionAttribute 関数でアクリル素材をウィンドウに適用します。SetWindowCompositionAttribute 関数はヘッダーファイルで宣言されていないので windows crate から呼び出せません。なので LoadLibraryW 関数と GetProcAddres 関数で SetWindowCompositionAttribute 関数へのポインタを取得する必要があります。
// SetWindowCompositionAttribute 関数の型を定義
type SetWindowCompositionAttribute = unsafe fn(HWND, *const WinCompAttrData) -> BOOL;
// DLL から動的にモジュールを読み込むための構造体
struct Lib {
module: HMODULE,
}
impl Lib {
fn new() -> Result<Self> {
// user32.dll を読み込む
let module = unsafe { LoadLibraryW(w!("user32"))? };
Ok(Self { module })
}
fn set_window_composition_attribute(&self, hwnd: HWND, data: &WinCompAttrData) -> Result<()> {
// user32.dll から SetWindowCompositionAttribute の関数ポインタを取得
let p = unsafe {
GetProcAddress(self.module, s!("SetWindowCompositionAttribute"))
.context("no proc address")?
};
// SetWindowCompositionAttribute 型へ変換
let set_window_composition_attribute =
unsafe { std::mem::transmute::<_, SetWindowCompositionAttribute>(p) };
// SetWindowCompositionAttribute を実行
unsafe { set_window_composition_attribute(hwnd, data).ok()? };
Ok(())
}
}
// スコープを抜けるときに自動的にリソースを解放する
impl Drop for Lib {
fn drop(&mut self) {
_ = unsafe { FreeLibrary(self.module) };
}
}
fn enable_blur_window(hwnd: HWND) -> Result<()> {
let lib = Lib::new()?;
let data = WinCompAttrData::new(AccentPolicy::default());
lib.set_window_composition_attribute(hwnd, &data)?;
Ok(())
}
大事なことは大体説明したのでコードを全部載せます。
コピペ用サンプルコード
[package]
name = "acrylic"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0"
[dependencies.windows]
version = "0.62"
features = [
"Win32_Graphics_Gdi",
"Win32_UI_WindowsAndMessaging",
"Win32_System_LibraryLoader",
]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use anyhow::{Context, Result};
use windows::{
Win32::{
Foundation::{FreeLibrary, HMODULE, HWND, LPARAM, LRESULT, RECT, WPARAM},
Graphics::Gdi::{EnumDisplayMonitors, HDC, HMONITOR, UpdateWindow},
System::LibraryLoader::{GetProcAddress, LoadLibraryW},
UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DispatchMessageW, GWL_STYLE, GetMessageW, MSG,
PostQuitMessage, RegisterClassW, SW_SHOW, SetWindowLongW, ShowWindow, TranslateMessage,
WM_DESTROY, WNDCLASSW, WS_BORDER, WS_EX_NOREDIRECTIONBITMAP,
},
},
core::{BOOL, PCWSTR, s, w},
};
const CLASS_NAME: PCWSTR = w!("acylic_window_class");
#[repr(i32)]
#[derive(Debug, Clone, Copy)]
enum WindowCompositionAttrib {
AccentPolicy = 0x13,
}
impl Default for WindowCompositionAttrib {
fn default() -> Self {
Self::AccentPolicy
}
}
#[allow(dead_code)]
#[repr(i32)]
#[derive(Debug, Clone, Copy)]
enum AccentState {
Disabled = 0,
BlurBehind = 3,
}
impl Default for AccentState {
fn default() -> Self {
Self::BlurBehind
}
}
#[repr(C)]
#[derive(Debug, Default, Clone, Copy)]
struct AccentPolicy {
accent_state: AccentState,
flgas: i32,
color: i32,
animation_id: i32,
}
#[repr(C)]
#[derive(Debug, Default)]
struct WinCompAttrData {
attribute: WindowCompositionAttrib,
data: *const u8,
data_size: usize,
}
impl WinCompAttrData {
fn new(policy: AccentPolicy) -> Self {
Self {
data: &policy as *const AccentPolicy as *const _,
data_size: size_of::<AccentPolicy>(),
..Default::default()
}
}
}
type SetWindowCompositionAttribute = unsafe fn(HWND, *const WinCompAttrData) -> BOOL;
struct Lib {
module: HMODULE,
}
impl Lib {
fn new() -> Result<Self> {
let module = unsafe { LoadLibraryW(w!("user32"))? };
Ok(Self { module })
}
fn set_window_composition_attribute(&self, hwnd: HWND, data: &WinCompAttrData) -> Result<()> {
let p = unsafe {
GetProcAddress(self.module, s!("SetWindowCompositionAttribute"))
.context("no proc address")?
};
let set_window_composition_attribute =
unsafe { std::mem::transmute::<_, SetWindowCompositionAttribute>(p) };
unsafe { set_window_composition_attribute(hwnd, data).ok()? };
Ok(())
}
}
impl Drop for Lib {
fn drop(&mut self) {
_ = unsafe { FreeLibrary(self.module) };
}
}
fn enable_blur_window(hwnd: HWND) -> Result<()> {
let lib = Lib::new()?;
let data = WinCompAttrData::new(AccentPolicy::default());
lib.set_window_composition_attribute(hwnd, &data)?;
Ok(())
}
unsafe extern "system" fn wnd_proc(
hwnd: HWND,
msg: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
match msg {
WM_DESTROY => unsafe {
PostQuitMessage(0);
},
_ => return unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
}
LRESULT::default()
}
fn create_window(rect: &RECT) -> Result<()> {
let hwnd = unsafe {
CreateWindowExW(
WS_EX_NOREDIRECTIONBITMAP,
CLASS_NAME,
w!("Acrylic"),
WS_BORDER,
rect.left,
rect.top,
rect.right - rect.left,
rect.bottom - rect.top,
None,
None,
None,
None,
)?
};
enable_blur_window(hwnd)?;
unsafe { SetWindowLongW(hwnd, GWL_STYLE, 0) };
unsafe { ShowWindow(hwnd, SW_SHOW).ok()? };
unsafe { UpdateWindow(hwnd).ok()? };
Ok(())
}
unsafe extern "system" fn enum_proc(_: HMONITOR, _: HDC, rect: *mut RECT, _: LPARAM) -> BOOL {
let rect = unsafe { &*rect };
create_window(rect).ok();
true.into()
}
fn main() -> Result<()> {
let wc = WNDCLASSW {
lpfnWndProc: Some(wnd_proc),
lpszClassName: CLASS_NAME,
..Default::default()
};
unsafe { RegisterClassW(&wc) };
_ = unsafe { EnumDisplayMonitors(None, None, Some(enum_proc), LPARAM::default()) };
let mut msg = MSG::default();
loop {
if !unsafe { GetMessageW(&mut msg, None, 0, 0) }.as_bool() {
break;
}
unsafe {
_ = TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
Ok(())
}
まとめ
終了するときはAlt + F4でウィンドウを閉じてください ![]()
参考
https://github.com/selastingeorge/Win32-Acrylic-Effect
https://stackoverflow.com/questions/7442939/opening-a-window-that-has-no-title-bar-with-win32
