Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing # ============================== # KHOI TAO FORM CHINH # ============================== $form = New-Object System.Windows.Forms.Form $form.Text = "Cong Cu Xoa Nguoi Dung Microsoft 365" $form.Size = New-Object System.Drawing.Size(700, 600) $form.StartPosition = "CenterScreen" $form.BackColor = [System.Drawing.Color]::FromArgb(245, 245, 250) $form.Font = New-Object System.Drawing.Font("Segoe UI", 9) $form.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog $form.MaximizeBox = $false # ============================== # HEADER & LOGO # ============================== $headerPanel = New-Object System.Windows.Forms.Panel $headerPanel.Dock = [System.Windows.Forms.DockStyle]::Top $headerPanel.Height = 60 $headerPanel.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 212) $headerLabel = New-Object System.Windows.Forms.Label $headerLabel.Text = "Cong Cu Quan Ly Nguoi Dung Microsoft 365" $headerLabel.ForeColor = [System.Drawing.Color]::White $headerLabel.Font = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold) $headerLabel.Location = New-Object System.Drawing.Point(15, 15) $headerLabel.AutoSize = $true $headerPanel.Controls.Add($headerLabel) $form.Controls.Add($headerPanel) # ============================== # DANH SACH EMAIL # ============================== $groupBox1 = New-Object System.Windows.Forms.GroupBox $groupBox1.Text = "Danh sach email loai tru" $groupBox1.Location = New-Object System.Drawing.Point(15, 75) $groupBox1.Size = New-Object System.Drawing.Size(655, 150) $groupBox1.Font = New-Object System.Drawing.Font("Segoe UI", 9) $excludedEmailsTextBox = New-Object System.Windows.Forms.TextBox $excludedEmailsTextBox.Multiline = $true $excludedEmailsTextBox.ScrollBars = "Vertical" $excludedEmailsTextBox.Location = New-Object System.Drawing.Point(10, 25) $excludedEmailsTextBox.Size = New-Object System.Drawing.Size(635, 115) $excludedEmailsTextBox.Text = "admin@lananhphuquoc.com.vn reservations@lananhphuquoc.com.vn hybrid.portal@lananhphuquoc.com.vn admin@lananhphuoccomvn.onmicrosoft.com" $groupBox1.Controls.Add($excludedEmailsTextBox) $form.Controls.Add($groupBox1) # ============================== # CAU HINH # ============================== $groupBox2 = New-Object System.Windows.Forms.GroupBox $groupBox2.Text = "Cau hinh thuc thi" $groupBox2.Location = New-Object System.Drawing.Point(15, 235) $groupBox2.Size = New-Object System.Drawing.Size(655, 110) $groupBox2.Font = New-Object System.Drawing.Font("Segoe UI", 9) # Batch Size $batchSizeLabel = New-Object System.Windows.Forms.Label $batchSizeLabel.Text = "So luong moi batch:" $batchSizeLabel.Location = New-Object System.Drawing.Point(10, 30) $batchSizeLabel.Size = New-Object System.Drawing.Size(150, 20) $batchSizeBox = New-Object System.Windows.Forms.NumericUpDown $batchSizeBox.Location = New-Object System.Drawing.Point(170, 28) $batchSizeBox.Size = New-Object System.Drawing.Size(80, 25) $batchSizeBox.Minimum = 1 $batchSizeBox.Maximum = 100 $batchSizeBox.Value = 20 # Pause Threshold $pauseThresholdLabel = New-Object System.Windows.Forms.Label $pauseThresholdLabel.Text = "Nghi sau so luong:" $pauseThresholdLabel.Location = New-Object System.Drawing.Point(10, 65) $pauseThresholdLabel.Size = New-Object System.Drawing.Size(150, 20) $pauseThresholdBox = New-Object System.Windows.Forms.NumericUpDown $pauseThresholdBox.Location = New-Object System.Drawing.Point(170, 63) $pauseThresholdBox.Size = New-Object System.Drawing.Size(80, 25) $pauseThresholdBox.Minimum = 100 $pauseThresholdBox.Maximum = 10000 $pauseThresholdBox.Increment = 100 $pauseThresholdBox.Value = 1000 # Pause Seconds $pauseSecondsLabel = New-Object System.Windows.Forms.Label $pauseSecondsLabel.Text = "Thoi gian nghi (giay):" $pauseSecondsLabel.Location = New-Object System.Drawing.Point(340, 30) $pauseSecondsLabel.Size = New-Object System.Drawing.Size(150, 20) $pauseSecondsBox = New-Object System.Windows.Forms.NumericUpDown $pauseSecondsBox.Location = New-Object System.Drawing.Point(500, 28) $pauseSecondsBox.Size = New-Object System.Drawing.Size(80, 25) $pauseSecondsBox.Minimum = 1 $pauseSecondsBox.Maximum = 300 $pauseSecondsBox.Value = 10 # Delay between batches $delayBatchLabel = New-Object System.Windows.Forms.Label $delayBatchLabel.Text = "Nghi giua batch (giay):" $delayBatchLabel.Location = New-Object System.Drawing.Point(340, 65) $delayBatchLabel.Size = New-Object System.Drawing.Size(150, 20) $delayBatchBox = New-Object System.Windows.Forms.NumericUpDown $delayBatchBox.Location = New-Object System.Drawing.Point(500, 63) $delayBatchBox.Size = New-Object System.Drawing.Size(80, 25) $delayBatchBox.Minimum = 0 $delayBatchBox.Maximum = 60 $delayBatchBox.Value = 2 # Maximum error count $maxErrorLabel = New-Object System.Windows.Forms.Label $maxErrorLabel.Text = "Loi toi da truoc khi dung:" $maxErrorLabel.Location = New-Object System.Drawing.Point(10, 95) $maxErrorLabel.Size = New-Object System.Drawing.Size(160, 20) $maxErrorLabel.Visible = $true $maxErrorBox = New-Object System.Windows.Forms.NumericUpDown $maxErrorBox.Location = New-Object System.Drawing.Point(170, 93) $maxErrorBox.Size = New-Object System.Drawing.Size(80, 25) $maxErrorBox.Minimum = 1 $maxErrorBox.Maximum = 100 $maxErrorBox.Value = 10 $maxErrorBox.Visible = $true $groupBox2.Controls.Add($batchSizeLabel) $groupBox2.Controls.Add($batchSizeBox) $groupBox2.Controls.Add($pauseThresholdLabel) $groupBox2.Controls.Add($pauseThresholdBox) $groupBox2.Controls.Add($pauseSecondsLabel) $groupBox2.Controls.Add($pauseSecondsBox) $groupBox2.Controls.Add($delayBatchLabel) $groupBox2.Controls.Add($delayBatchBox) $groupBox2.Controls.Add($maxErrorLabel) $groupBox2.Controls.Add($maxErrorBox) $groupBox2.Size = New-Object System.Drawing.Size(655, 130) $form.Controls.Add($groupBox2) # ============================== # LOG OUTPUT # ============================== $groupBox3 = New-Object System.Windows.Forms.GroupBox $groupBox3.Text = "Nhat ky hoat dong" $groupBox3.Location = New-Object System.Drawing.Point(15, 375) $groupBox3.Size = New-Object System.Drawing.Size(655, 140) $groupBox3.Font = New-Object System.Drawing.Font("Segoe UI", 9) $logTextBox = New-Object System.Windows.Forms.RichTextBox $logTextBox.Location = New-Object System.Drawing.Point(10, 25) $logTextBox.Size = New-Object System.Drawing.Size(635, 105) $logTextBox.ReadOnly = $true $logTextBox.BackColor = [System.Drawing.Color]::White $logTextBox.Font = New-Object System.Drawing.Font("Consolas", 9) $groupBox3.Controls.Add($logTextBox) $form.Controls.Add($groupBox3) # ============================== # ACTION BUTTONS # ============================== $connectButton = New-Object System.Windows.Forms.Button $connectButton.Location = New-Object System.Drawing.Point(15, 525) $connectButton.Size = New-Object System.Drawing.Size(150, 30) $connectButton.Text = "1. Ket noi Graph API" $connectButton.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 212) $connectButton.ForeColor = [System.Drawing.Color]::White $connectButton.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat $form.Controls.Add($connectButton) $getUsersButton = New-Object System.Windows.Forms.Button $getUsersButton.Location = New-Object System.Drawing.Point(175, 525) $getUsersButton.Size = New-Object System.Drawing.Size(150, 30) $getUsersButton.Text = "2. Lay danh sach users" $getUsersButton.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 212) $getUsersButton.ForeColor = [System.Drawing.Color]::White $getUsersButton.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat $getUsersButton.Enabled = $false $form.Controls.Add($getUsersButton) $deleteButton = New-Object System.Windows.Forms.Button $deleteButton.Location = New-Object System.Drawing.Point(335, 525) $deleteButton.Size = New-Object System.Drawing.Size(150, 30) $deleteButton.Text = "3. Xoa nguoi dung" $deleteButton.BackColor = [System.Drawing.Color]::FromArgb(209, 17, 65) $deleteButton.ForeColor = [System.Drawing.Color]::White $deleteButton.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat $deleteButton.Enabled = $false $form.Controls.Add($deleteButton) $pauseResumeButton = New-Object System.Windows.Forms.Button $pauseResumeButton.Location = New-Object System.Drawing.Point(335, 525) $pauseResumeButton.Size = New-Object System.Drawing.Size(150, 30) $pauseResumeButton.Text = "Tam dung" $pauseResumeButton.BackColor = [System.Drawing.Color]::FromArgb(255, 140, 0) $pauseResumeButton.ForeColor = [System.Drawing.Color]::White $pauseResumeButton.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat $pauseResumeButton.Enabled = $false $pauseResumeButton.Visible = $false $form.Controls.Add($pauseResumeButton) $resetButton = New-Object System.Windows.Forms.Button $resetButton.Location = New-Object System.Drawing.Point(520, 525) $resetButton.Size = New-Object System.Drawing.Size(150, 30) $resetButton.Text = "Lam moi" $resetButton.BackColor = [System.Drawing.Color]::FromArgb(170, 170, 170) $resetButton.ForeColor = [System.Drawing.Color]::White $resetButton.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat $resetButton.Enabled = $false $form.Controls.Add($resetButton) # ============================== # PROGRESS BAR # ============================== $progressBar = New-Object System.Windows.Forms.ProgressBar $progressBar.Location = New-Object System.Drawing.Point(15, 565) $progressBar.Size = New-Object System.Drawing.Size(655, 20) $progressBar.Style = "Continuous" $form.Controls.Add($progressBar) # ============================== # GLOBAL VARIABLES # ============================== $global:UsersToDelete = @() $global:ConnectedToGraph = $false $global:isProcessing = $false $global:isPaused = $false $global:errorCount = 0 # ============================== # FUNCTIONS # ============================== function Log-Message { param ( [string]$Message, [string]$Color = "Black" ) $logTextBox.SelectionStart = $logTextBox.TextLength $logTextBox.SelectionLength = 0 $logTextBox.SelectionColor = $Color $logTextBox.AppendText("$(Get-Date -Format 'HH:mm:ss') - $Message`r`n") $logTextBox.ScrollToCaret() $form.Refresh() } function Install-RequiredModules { try { Log-Message "Dang kiem tra module Microsoft.Graph..." "Blue" # Kiem tra xem module da duoc cai dat chua $mgModule = Get-Module -Name Microsoft.Graph -ListAvailable -ErrorAction SilentlyContinue if (-not $mgModule) { Log-Message "Module Microsoft.Graph chua duoc cai dat. Dang cai dat..." "Orange" # Kiem tra quyen admin $isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { $result = [System.Windows.Forms.MessageBox]::Show( "Can quyen Administrator de cai dat module Microsoft.Graph. Ban co muon khoi dong lai ung dung voi quyen Administrator khong?", "Yeu cau quyen Administrator", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Warning ) if ($result -eq [System.Windows.Forms.DialogResult]::Yes) { # Luu duong dan hien tai cua script $scriptPath = $MyInvocation.MyCommand.Path # Tao qua trinh moi voi quyen admin Start-Process powershell.exe -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`"" -Verb RunAs # Dong form hien tai $form.Close() return $false } else { Log-Message "Khong the cai dat module Microsoft.Graph ma khong co quyen Administrator." "Red" return $false } } # Cai dat module Log-Message "Dang cai dat Microsoft.Graph. Vui long doi..." "Blue" Install-Module -Name Microsoft.Graph -Scope CurrentUser -Force -AllowClobber Log-Message "Da cai dat module Microsoft.Graph thanh cong" "Green" } # Import module Log-Message "Dang import module Microsoft.Graph..." "Blue" Import-Module Microsoft.Graph.Authentication -ErrorAction Stop Log-Message "Da import module Microsoft.Graph thanh cong" "Green" return $true } catch { Log-Message "Loi khi cai dat/import module: $_" "Red" return $false } } function Connect-ToMsGraph { try { $global:isProcessing = $true Log-Message "Dang ket noi den Microsoft Graph..." "Blue" try { # Kiem tra xem module Microsoft.Graph da duoc cai dat va import if (-not (Get-Command Connect-MgGraph -ErrorAction SilentlyContinue)) { Log-Message "Module Microsoft.Graph chua duoc import hoac cai dat" "Orange" # Cai dat va import module $moduleResult = Install-RequiredModules if (-not $moduleResult) { return } } # Kiem tra xem da ket noi hay chua $graphStatus = Get-MgContext -ErrorAction Stop if ($graphStatus) { Log-Message "Da ket noi voi tai khoan: $($graphStatus.Account)" "Green" $global:ConnectedToGraph = $true $getUsersButton.Enabled = $true $resetButton.Enabled = $true return } } catch {} # Ket noi moi Connect-MgGraph -Scopes "User.ReadWrite.All" -ErrorAction Stop $graphContext = Get-MgContext if ($graphContext) { Log-Message "Ket noi thanh cong voi tai khoan: $($graphContext.Account)" "Green" $global:ConnectedToGraph = $true $getUsersButton.Enabled = $true $resetButton.Enabled = $true } else { Log-Message "Ket noi khong thanh cong" "Red" } } catch { $errorMessage = $_.ToString() Log-Message "Loi ket noi: $errorMessage" "Red" # Xu ly loi module khong duoc nhan dien if ($errorMessage -match "The term 'Connect-MgGraph' is not recognized") { Log-Message "Dang thu cai dat va import lai module..." "Orange" $moduleResult = Install-RequiredModules if ($moduleResult) { Log-Message "Dang thu ket noi lai..." "Blue" # Thu ket noi lai sau khi cai dat module Connect-ToMsGraph } } } finally { $global:isProcessing = $false } } function Get-MsGraphUsers { if (-not $global:ConnectedToGraph) { Log-Message "Vui long ket noi Microsoft Graph truoc" "Red" return } try { $global:isProcessing = $true # Lay danh sach email loai tru $ExcludeUsers = $excludedEmailsTextBox.Text -split "`r`n" | Where-Object { $_.Trim() -ne "" } # Tao hash set cho viec tim kiem nhanh $ExcludeHash = @{} foreach ($user in $ExcludeUsers) { $ExcludeHash[$user.ToLower()] = $true } Log-Message "Dang lay danh sach nguoi dung tu he thong..." "Blue" $global:UsersToDelete = @() $TotalRetrieved = 0 $Uri = "https://graph.microsoft.com/v1.0/users?`$select=id,userPrincipalName,displayName&`$top=999" do { $Response = Invoke-MgGraphRequest -Method GET -Uri $Uri foreach ($User in $Response.value) { if (-not $ExcludeHash.ContainsKey($User.userPrincipalName.ToLower())) { $global:UsersToDelete += $User } } $TotalRetrieved += $Response.value.Count Log-Message "Da tai: $TotalRetrieved nguoi dung..." "DarkGray" $Uri = $Response.'@odata.nextLink' } while ($Uri) Log-Message "Tong so nguoi dung can xoa: $($global:UsersToDelete.Count)" "Green" if ($global:UsersToDelete.Count -gt 0) { $deleteButton.Enabled = $true } } catch { Log-Message "Loi khi lay danh sach nguoi dung: $_" "Red" } finally { $global:isProcessing = $false } } function Toggle-PauseResume { if ($global:isPaused) { $global:isPaused = $false $pauseResumeButton.Text = "Tam dung" $pauseResumeButton.BackColor = [System.Drawing.Color]::FromArgb(255, 140, 0) Log-Message "Tiep tuc xoa nguoi dung..." "Blue" } else { $global:isPaused = $true $pauseResumeButton.Text = "Tiep tuc" $pauseResumeButton.BackColor = [System.Drawing.Color]::FromArgb(0, 180, 0) Log-Message "Da tam dung xoa nguoi dung. Nhan 'Tiep tuc' de tiep tuc." "Orange" } } function Delete-MsGraphUsers { if ($global:UsersToDelete.Count -eq 0) { Log-Message "Khong co nguoi dung nao de xoa" "Red" return } # Hien thi dialog xac nhan $confirmResult = [System.Windows.Forms.MessageBox]::Show( "Ban co chac chan muon xoa $($global:UsersToDelete.Count) nguoi dung?", "Xac nhan xoa nguoi dung", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Warning ) if ($confirmResult -ne [System.Windows.Forms.DialogResult]::Yes) { Log-Message "Da huy thao tac xoa nguoi dung" "Blue" return } try { $global:isProcessing = $true $global:isPaused = $false $global:errorCount = 0 # Hien thi nut tam dung, an nut xoa $deleteButton.Visible = $false $pauseResumeButton.Visible = $true $pauseResumeButton.Enabled = $true $pauseResumeButton.Text = "Tam dung" $pauseResumeButton.BackColor = [System.Drawing.Color]::FromArgb(255, 140, 0) # Lay gia tri cau hinh $BatchSize = [int]$batchSizeBox.Value $PauseThreshold = [int]$pauseThresholdBox.Value $PauseSeconds = [int]$pauseSecondsBox.Value $DelayBetweenBatch = [int]$delayBatchBox.Value $MaxErrorCount = [int]$maxErrorBox.Value $TotalUsers = $global:UsersToDelete.Count $DeletedCount = 0 Log-Message "Bat dau xoa nguoi dung..." "Blue" $progressBar.Maximum = $TotalUsers $progressBar.Value = 0 for ($i = 0; $i -lt $TotalUsers; $i += $BatchSize) { # Kiem tra neu da tam dung while ($global:isPaused) { Start-Sleep -Milliseconds 500 [System.Windows.Forms.Application]::DoEvents() # Kiem tra neu huy hoan toan qua trinh if (-not $global:isProcessing) { break } } # Kiem tra neu da huy qua trinh if (-not $global:isProcessing) { break } $CurrentBatch = $global:UsersToDelete | Select-Object -Skip $i -First $BatchSize $BatchRequests = @() $BatchId = 1 foreach ($User in $CurrentBatch) { $BatchRequests += @{ id = $BatchId.ToString() method = "DELETE" url = "/users/$($User.id)" } $BatchId++ } $BatchBody = @{ requests = $BatchRequests } | ConvertTo-Json -Depth 10 try { $BatchResponse = Invoke-MgGraphRequest -Method POST -Uri "https://graph.microsoft.com/v1.0/`$batch" -Body $BatchBody $SuccessCount = ($BatchResponse.responses | Where-Object { $_.status -ge 200 -and $_.status -lt 300 }).Count $ErrorsInBatch = ($BatchResponse.responses | Where-Object { $_.status -ge 400 }).Count $DeletedCount += $SuccessCount $global:errorCount += $ErrorsInBatch foreach ($Response in $BatchResponse.responses) { if ($Response.status -ge 400) { $FailedUser = $CurrentBatch[$([int]$Response.id - 1)] Log-Message "Loi khi xoa $($FailedUser.userPrincipalName): $($Response.status) - $($Response.body.error.message)" "Red" } } # Cap nhat progress bar $progressBar.Value = [Math]::Min($DeletedCount, $TotalUsers) # Hien thi tien do % $ProgressPercent = [Math]::Round(($DeletedCount / $TotalUsers) * 100, 2) Log-Message "Da xoa $DeletedCount/$TotalUsers nguoi dung ($ProgressPercent%)" "Green" # Kiem tra so loi da vuot qua nguong if ($global:errorCount -ge $MaxErrorCount) { Log-Message "Da dung qua trinh xoa do so loi vuot qua nguong ($global:errorCount/$MaxErrorCount)" "Red" $global:isProcessing = $false break } } catch { $errorMessage = $_.ToString() Log-Message "Loi trong batch: $errorMessage" "Red" # Kiem tra loi throttling va tu dong cho if ($errorMessage -match "Your request is throttled temporarily. Please try after (\d+) seconds") { $waitTime = [int]$Matches[1] Log-Message "Phat hien loi throttling. Tu dong tam dung $waitTime giay..." "Orange" # Hien thi dem nguoc for ($waitCounter = $waitTime; $waitCounter -gt 0; $waitCounter--) { if (($waitCounter % 10 -eq 0) -or ($waitCounter -le 5)) { Log-Message "Dang cho... con $waitCounter giay" "Blue" } # Kiem tra xem co huy qua trinh khong if (-not $global:isProcessing) { Log-Message "Da huy qua trinh cho" "Red" break } Start-Sleep -Seconds 1 [System.Windows.Forms.Application]::DoEvents() } if ($global:isProcessing) { Log-Message "Tiep tuc xoa sau thoi gian cho throttling" "Green" # Khong tang error count vi day la loi tam thoi $i -= $BatchSize # Thu lai batch hien tai continue } } else { # Cac loi khac van tinh vao errorCount $global:errorCount++ if ($global:errorCount -ge $MaxErrorCount) { Log-Message "Da dung qua trinh xoa do so loi vuot qua nguong ($global:errorCount/$MaxErrorCount)" "Red" $global:isProcessing = $false break } } } # Nghi sau moi batch if ($DelayBetweenBatch -gt 0) { Start-Sleep -Seconds $DelayBetweenBatch } # Nghi dai sau moi PauseThreshold user if ($DeletedCount -gt 0 -and ($DeletedCount % $PauseThreshold) -eq 0) { Log-Message "Da xoa $DeletedCount nguoi dung. Nghi $PauseSeconds giay..." "Blue" Start-Sleep -Seconds $PauseSeconds } # Check if operation was cancelled if (-not $global:isProcessing) { Log-Message "Thao tac xoa nguoi dung da bi dung" "Red" break } } if ($global:isProcessing) { Log-Message "Da hoan tat xoa. Tong so nguoi dung xoa thanh cong: $DeletedCount" "Green" $progressBar.Value = $progressBar.Maximum } } catch { Log-Message "Loi khi xoa nguoi dung: $_" "Red" } finally { $global:isProcessing = $false $global:isPaused = $false $resetButton.Enabled = $true # Hien thi lai nut xoa, an nut tam dung $deleteButton.Visible = $true $pauseResumeButton.Visible = $false $pauseResumeButton.Enabled = $false } } function Reset-Form { $global:UsersToDelete = @() $global:errorCount = 0 $deleteButton.Enabled = $false $progressBar.Value = 0 # Dam bao trang thai nut $deleteButton.Visible = $true $pauseResumeButton.Visible = $false $pauseResumeButton.Enabled = $false Log-Message "Da lam moi trang thai ung dung" "Blue" } # ============================== # EVENTS # ============================== $connectButton.Add_Click({ Connect-ToMsGraph }) $getUsersButton.Add_Click({ Get-MsGraphUsers }) $deleteButton.Add_Click({ Delete-MsGraphUsers }) $pauseResumeButton.Add_Click({ Toggle-PauseResume }) $resetButton.Add_Click({ Reset-Form }) # Hien thi form [void]$form.ShowDialog()