From 81d5b0cde3a3ada3a582bf9ff1eaa8982f82e26a Mon Sep 17 00:00:00 2001 From: pdjdev Date: Mon, 10 Aug 2026 20:31:05 +0900 Subject: [PATCH 1/5] feat: implement minimize/restore functionality with hidden border --- utable/Forms/TableForm.vb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index 95b7cae..09f77a0 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -72,6 +72,16 @@ Public Class TableForm #Region "Aero 그림자 효과 (Vista이상)" + '테두리는 숨긴 채 WS_MINIMIZEBOX만 다시 추가하여 작업 표시줄 버튼으로 최소화/복원 구현 + Protected Overrides ReadOnly Property CreateParams As CreateParams + Get + Const WS_MINIMIZEBOX As Integer = &H20000 + Dim nativeParams As CreateParams = MyBase.CreateParams + nativeParams.Style = nativeParams.Style Or WS_MINIMIZEBOX + Return nativeParams + End Get + End Property + Protected Overrides Sub OnHandleCreated(e As EventArgs) CreateDropShadow(Me) MyBase.OnHandleCreated(e) From 0e4dacb9fcb8efd0381a87a309d61a75051d195c Mon Sep 17 00:00:00 2001 From: pdjdev Date: Mon, 10 Aug 2026 23:30:50 +0900 Subject: [PATCH 2/5] feat: update WebView2 DLL download logic and renew table layout --- utable/Forms/DLLDownloader.vb | 77 +++++++---- utable/Forms/TableForm.vb | 6 +- utable/UserControls/CellControl.Designer.vb | 48 ++++--- utable/UserControls/LoadingSplash.Designer.vb | 113 ---------------- utable/UserControls/LoadingSplash.resx | 123 ------------------ utable/UserControls/LoadingSplash.vb | 34 ----- utable/uTable.vbproj | 15 +-- 7 files changed, 85 insertions(+), 331 deletions(-) delete mode 100644 utable/UserControls/LoadingSplash.Designer.vb delete mode 100644 utable/UserControls/LoadingSplash.resx delete mode 100644 utable/UserControls/LoadingSplash.vb diff --git a/utable/Forms/DLLDownloader.vb b/utable/Forms/DLLDownloader.vb index f074f0e..a49e7ae 100644 --- a/utable/Forms/DLLDownloader.vb +++ b/utable/Forms/DLLDownloader.vb @@ -1,22 +1,16 @@ Imports System.Net +Imports System.IO +Imports System.IO.Compression + Public Class DLLDownloader Dim colorMode As String = Nothing - Dim WithEvents wc As New Net.WebClient - Dim downComplete As Boolean = False - Dim exeFullpath As String = Application.ExecutablePath Dim finalDir As String = exeFullpath.Substring(0, exeFullpath.LastIndexOf("\")) - Const repoLink As String = "https://github.com/pdjdev/uTable/raw/master/res/webview2dlls/" - - Dim downList As New List(Of String) From - {"Microsoft.Web.WebView2.Core.dll", - "Microsoft.Web.WebView2.WinForms.dll", - "WebView2Loader.dll"} - - Dim downCount As Integer = 1 + ' WebView2 SDK의 공식 NuGet 패키지 + Const webView2PackageUrl As String = "https://api.nuget.org/v3-flatcontainer/microsoft.web.webview2/1.0.1774.30/microsoft.web.webview2.1.0.1774.30.nupkg" #Region "Aero 그림자 효과 (Vista이상)" @@ -47,20 +41,45 @@ Public Class DLLDownloader MainLabel.ForeColor = theme.Text End Sub - Sub DownloadFiles() - For Each fileName In downList - MainLabel.Text = $"다운로드 중... ({downCount}/{downList.Count}):" + vbCr + fileName + Private Function DownloadFiles() As Boolean + Dim packagePath = Path.Combine(Path.GetTempPath(), "uTable-WebView2-" & Guid.NewGuid().ToString("N") & ".nupkg") + Dim files As New Dictionary(Of String, String) From { + {"lib/net45/Microsoft.Web.WebView2.Core.dll", "Microsoft.Web.WebView2.Core.dll"}, + {"lib/net45/Microsoft.Web.WebView2.WinForms.dll", "Microsoft.Web.WebView2.WinForms.dll"}, + {"runtimes/win-x86/native/WebView2Loader.dll", "runtimes/win-x86/native/WebView2Loader.dll"}, + {"runtimes/win-x64/native/WebView2Loader.dll", "runtimes/win-x64/native/WebView2Loader.dll"}, + {"runtimes/win-arm64/native/WebView2Loader.dll", "runtimes/win-arm64/native/WebView2Loader.dll"} + } + + Try + MainLabel.Text = "필요 요소 다운로드 중..." + vbCr + "Microsoft.Web.WebView2" Refresh() - Threading.Thread.Sleep(100) - Try - Dim client As New WebClient() - client.DownloadFile(repoLink & fileName, finalDir & "\" & fileName) - Catch ex As Exception - MainLabel.Text = $"작업 실패... ({downCount}/{downList.Count}):" + vbCr + fileName - End Try - downCount += 1 - Next - End Sub + Using client As New WebClient() + client.DownloadFile(webView2PackageUrl, packagePath) + End Using + + Using package = ZipFile.OpenRead(packagePath) + Dim count = 1 + For Each file In files + MainLabel.Text = $"필요 요소 설치 중... ({count}/{files.Count}):" + vbCr + Path.GetFileName(file.Value) + Refresh() + Dim entry = package.GetEntry(file.Key) + If entry Is Nothing Then Throw New InvalidDataException("WebView2 패키지에 필요한 파일이 없습니다: " & file.Key) + + Dim destination = Path.Combine(finalDir, file.Value.Replace("/", "\")) + Directory.CreateDirectory(Path.GetDirectoryName(destination)) + entry.ExtractToFile(destination, True) + count += 1 + Next + End Using + Return True + Catch ex As Exception + MainLabel.Text = "작업 실패: " + ex.Message + Return False + Finally + If File.Exists(packagePath) Then File.Delete(packagePath) + End Try + End Function Private Sub DLLDownloader_Load(sender As Object, e As EventArgs) Handles MyBase.Load Opacity = 0 @@ -68,11 +87,17 @@ Public Class DLLDownloader End Sub Private Sub DLLDownloader_Shown(sender As Object, e As EventArgs) Handles Me.Shown - DownloadFiles() + If Not DownloadFiles() Then + MsgBox("다운로드 실패! 인터넷 연결과 실행 폴더의 쓰기 권한을 확인해 주세요.", vbExclamation) + Close() + Return + End If If Not (My.Computer.FileSystem.FileExists(finalDir + "\Microsoft.Web.WebView2.Core.dll") And My.Computer.FileSystem.FileExists(finalDir + "\Microsoft.Web.WebView2.WinForms.dll") And - My.Computer.FileSystem.FileExists(finalDir + "\WebView2Loader.dll")) Then + My.Computer.FileSystem.FileExists(finalDir + "\runtimes\win-x86\native\WebView2Loader.dll") And + My.Computer.FileSystem.FileExists(finalDir + "\runtimes\win-x64\native\WebView2Loader.dll") And + My.Computer.FileSystem.FileExists(finalDir + "\runtimes\win-arm64\native\WebView2Loader.dll")) Then MsgBox("다운로드 실패!", vbExclamation) Close() Else diff --git a/utable/Forms/TableForm.vb b/utable/Forms/TableForm.vb index 09f77a0..f7f1c62 100644 --- a/utable/Forms/TableForm.vb +++ b/utable/Forms/TableForm.vb @@ -834,8 +834,10 @@ Public Class TableForm If Not (My.Computer.FileSystem.FileExists(finalDir + "\Microsoft.Web.WebView2.Core.dll") And My.Computer.FileSystem.FileExists(finalDir + "\Microsoft.Web.WebView2.WinForms.dll") And - My.Computer.FileSystem.FileExists(finalDir + "\WebView2Loader.dll")) Then - If MsgBox("에브리타임 시간표를 불러오기 위해서는 추가적인 다운로드(0.6MB)가 필요합니다. 파일은 uTable이 실행된 위치에 저장됩니다." _ + My.Computer.FileSystem.FileExists(finalDir + "\runtimes\win-x86\native\WebView2Loader.dll") And + My.Computer.FileSystem.FileExists(finalDir + "\runtimes\win-x64\native\WebView2Loader.dll") And + My.Computer.FileSystem.FileExists(finalDir + "\runtimes\win-arm64\native\WebView2Loader.dll")) Then + If MsgBox("에브리타임 시간표를 불러오기 위해서는 추가적인 다운로드(약 3MB)가 필요합니다. 파일은 uTable이 실행된 위치에 저장됩니다." _ + vbCr + vbCr + "'확인'을 누르면 다운로드 및 불러오기 작업이 진행되며, '취소'를 누르면 작업이 중단됩니다.", vbOKCancel + vbInformation, "다운로드 필요") = vbOK Then DLLDownloader.ShowDialog(Me) End If diff --git a/utable/UserControls/CellControl.Designer.vb b/utable/UserControls/CellControl.Designer.vb index be734cb..e1f60e6 100644 --- a/utable/UserControls/CellControl.Designer.vb +++ b/utable/UserControls/CellControl.Designer.vb @@ -43,11 +43,10 @@ Partial Class CellControl ' Me.TopTimeLabel.Dock = System.Windows.Forms.DockStyle.Fill Me.TopTimeLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.75!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.TopTimeLabel.Location = New System.Drawing.Point(19, 0) + Me.TopTimeLabel.Location = New System.Drawing.Point(24, 0) Me.TopTimeLabel.Margin = New System.Windows.Forms.Padding(0) Me.TopTimeLabel.Name = "TopTimeLabel" - Me.TopTimeLabel.Padding = New System.Windows.Forms.Padding(0, 2, 0, 0) - Me.TopTimeLabel.Size = New System.Drawing.Size(125, 19) + Me.TopTimeLabel.Size = New System.Drawing.Size(156, 24) Me.TopTimeLabel.TabIndex = 0 Me.TopTimeLabel.Text = "TopTimeLabel" Me.TopTimeLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft @@ -57,9 +56,10 @@ Partial Class CellControl Me.BottomTimeLabel.AutoSize = True Me.BottomTimeLabel.Dock = System.Windows.Forms.DockStyle.Bottom Me.BottomTimeLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.75!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.BottomTimeLabel.Location = New System.Drawing.Point(0, 243) + Me.BottomTimeLabel.Location = New System.Drawing.Point(0, 300) + Me.BottomTimeLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) Me.BottomTimeLabel.Name = "BottomTimeLabel" - Me.BottomTimeLabel.Size = New System.Drawing.Size(118, 17) + Me.BottomTimeLabel.Size = New System.Drawing.Size(164, 25) Me.BottomTimeLabel.TabIndex = 1 Me.BottomTimeLabel.Text = "BottomTimeLabel" Me.BottomTimeLabel.TextAlign = System.Drawing.ContentAlignment.BottomRight @@ -70,9 +70,10 @@ Partial Class CellControl Me.Panel1.Controls.Add(Me.ProfLabel) Me.Panel1.Controls.Add(Me.TitleLabel) Me.Panel1.Dock = System.Windows.Forms.DockStyle.Fill - Me.Panel1.Location = New System.Drawing.Point(0, 21) + Me.Panel1.Location = New System.Drawing.Point(0, 26) + Me.Panel1.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) Me.Panel1.Name = "Panel1" - Me.Panel1.Size = New System.Drawing.Size(144, 222) + Me.Panel1.Size = New System.Drawing.Size(180, 274) Me.Panel1.TabIndex = 2 ' 'MemoLabel @@ -80,10 +81,11 @@ Partial Class CellControl Me.MemoLabel.AutoSize = True Me.MemoLabel.Dock = System.Windows.Forms.DockStyle.Top Me.MemoLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.MemoLabel.Location = New System.Drawing.Point(0, 37) - Me.MemoLabel.MaximumSize = New System.Drawing.Size(144, 0) + Me.MemoLabel.Location = New System.Drawing.Point(0, 52) + Me.MemoLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.MemoLabel.MaximumSize = New System.Drawing.Size(180, 0) Me.MemoLabel.Name = "MemoLabel" - Me.MemoLabel.Size = New System.Drawing.Size(42, 15) + Me.MemoLabel.Size = New System.Drawing.Size(53, 21) Me.MemoLabel.TabIndex = 2 Me.MemoLabel.Text = "Memo" Me.MemoLabel.UseMnemonic = False @@ -93,10 +95,11 @@ Partial Class CellControl Me.ProfLabel.AutoSize = True Me.ProfLabel.Dock = System.Windows.Forms.DockStyle.Top Me.ProfLabel.Font = New System.Drawing.Font("Noto Sans KR", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.ProfLabel.Location = New System.Drawing.Point(0, 20) - Me.ProfLabel.MaximumSize = New System.Drawing.Size(144, 0) + Me.ProfLabel.Location = New System.Drawing.Point(0, 27) + Me.ProfLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.ProfLabel.MaximumSize = New System.Drawing.Size(180, 0) Me.ProfLabel.Name = "ProfLabel" - Me.ProfLabel.Size = New System.Drawing.Size(67, 17) + Me.ProfLabel.Size = New System.Drawing.Size(93, 25) Me.ProfLabel.TabIndex = 1 Me.ProfLabel.Text = "ProfName" Me.ProfLabel.UseMnemonic = False @@ -107,9 +110,10 @@ Partial Class CellControl Me.TitleLabel.Dock = System.Windows.Forms.DockStyle.Top Me.TitleLabel.Font = New System.Drawing.Font("Noto Sans KR", 11.25!, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.TitleLabel.Location = New System.Drawing.Point(0, 0) - Me.TitleLabel.MaximumSize = New System.Drawing.Size(144, 0) + Me.TitleLabel.Margin = New System.Windows.Forms.Padding(4, 0, 4, 0) + Me.TitleLabel.MaximumSize = New System.Drawing.Size(180, 0) Me.TitleLabel.Name = "TitleLabel" - Me.TitleLabel.Size = New System.Drawing.Size(40, 20) + Me.TitleLabel.Size = New System.Drawing.Size(55, 27) Me.TitleLabel.TabIndex = 0 Me.TitleLabel.Text = "Title" Me.TitleLabel.UseMnemonic = False @@ -118,8 +122,9 @@ Partial Class CellControl ' Me.TopNotchPanel.Dock = System.Windows.Forms.DockStyle.Top Me.TopNotchPanel.Location = New System.Drawing.Point(0, 0) + Me.TopNotchPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) Me.TopNotchPanel.Name = "TopNotchPanel" - Me.TopNotchPanel.Size = New System.Drawing.Size(144, 2) + Me.TopNotchPanel.Size = New System.Drawing.Size(180, 2) Me.TopNotchPanel.TabIndex = 3 ' 'Timer1 @@ -133,8 +138,9 @@ Partial Class CellControl Me.TopPanel.Controls.Add(Me.ChkBox1) Me.TopPanel.Dock = System.Windows.Forms.DockStyle.Top Me.TopPanel.Location = New System.Drawing.Point(0, 2) + Me.TopPanel.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) Me.TopPanel.Name = "TopPanel" - Me.TopPanel.Size = New System.Drawing.Size(144, 19) + Me.TopPanel.Size = New System.Drawing.Size(180, 24) Me.TopPanel.TabIndex = 3 ' 'ChkBox1 @@ -142,8 +148,9 @@ Partial Class CellControl Me.ChkBox1.Dock = System.Windows.Forms.DockStyle.Left Me.ChkBox1.Image = Global.uTable.My.Resources.Resources.check1_w_96 Me.ChkBox1.Location = New System.Drawing.Point(0, 0) + Me.ChkBox1.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) Me.ChkBox1.Name = "ChkBox1" - Me.ChkBox1.Size = New System.Drawing.Size(19, 19) + Me.ChkBox1.Size = New System.Drawing.Size(24, 24) Me.ChkBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom Me.ChkBox1.TabIndex = 3 Me.ChkBox1.TabStop = False @@ -154,7 +161,7 @@ Partial Class CellControl ' 'CellControl ' - Me.AutoScaleDimensions = New System.Drawing.SizeF(96.0!, 96.0!) + Me.AutoScaleDimensions = New System.Drawing.SizeF(120.0!, 120.0!) Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi Me.BackColor = System.Drawing.Color.Transparent Me.Controls.Add(Me.Panel1) @@ -164,8 +171,9 @@ Partial Class CellControl Me.DoubleBuffered = True Me.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) Me.ForeColor = System.Drawing.Color.White + Me.Margin = New System.Windows.Forms.Padding(4, 4, 4, 4) Me.Name = "CellControl" - Me.Size = New System.Drawing.Size(144, 260) + Me.Size = New System.Drawing.Size(180, 325) Me.Panel1.ResumeLayout(False) Me.Panel1.PerformLayout() Me.TopPanel.ResumeLayout(False) diff --git a/utable/UserControls/LoadingSplash.Designer.vb b/utable/UserControls/LoadingSplash.Designer.vb deleted file mode 100644 index 6f9a450..0000000 --- a/utable/UserControls/LoadingSplash.Designer.vb +++ /dev/null @@ -1,113 +0,0 @@ - _ -Partial Class LoadingSplash - Inherits System.Windows.Forms.UserControl - - 'UserControl은 Dispose를 재정의하여 구성 요소 목록을 정리합니다. - _ - Protected Overrides Sub Dispose(ByVal disposing As Boolean) - Try - If disposing AndAlso components IsNot Nothing Then - components.Dispose() - End If - Finally - MyBase.Dispose(disposing) - End Try - End Sub - - 'Windows Form 디자이너에 필요합니다. - Private components As System.ComponentModel.IContainer - - '참고: 다음 프로시저는 Windows Form 디자이너에 필요합니다. - '수정하려면 Windows Form 디자이너를 사용하십시오. - '코드 편집기에서는 수정하지 마세요. - _ - Private Sub InitializeComponent() - Me.components = New System.ComponentModel.Container() - Me.TableLayoutPanel1 = New System.Windows.Forms.TableLayoutPanel() - Me.P1 = New System.Windows.Forms.Panel() - Me.P2 = New System.Windows.Forms.Panel() - Me.P3 = New System.Windows.Forms.Panel() - Me.P4 = New System.Windows.Forms.Panel() - Me.Timer1 = New System.Windows.Forms.Timer(Me.components) - Me.TableLayoutPanel1.SuspendLayout() - Me.SuspendLayout() - ' - 'TableLayoutPanel1 - ' - Me.TableLayoutPanel1.BackColor = System.Drawing.Color.Transparent - Me.TableLayoutPanel1.ColumnCount = 3 - Me.TableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 45.0!)) - Me.TableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 10.0!)) - Me.TableLayoutPanel1.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 45.0!)) - Me.TableLayoutPanel1.Controls.Add(Me.P1, 0, 0) - Me.TableLayoutPanel1.Controls.Add(Me.P2, 2, 0) - Me.TableLayoutPanel1.Controls.Add(Me.P3, 0, 2) - Me.TableLayoutPanel1.Controls.Add(Me.P4, 2, 2) - Me.TableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill - Me.TableLayoutPanel1.Location = New System.Drawing.Point(0, 0) - Me.TableLayoutPanel1.Name = "TableLayoutPanel1" - Me.TableLayoutPanel1.RowCount = 3 - Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 45.0!)) - Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10.0!)) - Me.TableLayoutPanel1.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 45.0!)) - Me.TableLayoutPanel1.Size = New System.Drawing.Size(120, 120) - Me.TableLayoutPanel1.TabIndex = 0 - ' - 'P1 - ' - Me.P1.Dock = System.Windows.Forms.DockStyle.Fill - Me.P1.Location = New System.Drawing.Point(3, 3) - Me.P1.Name = "P1" - Me.P1.Size = New System.Drawing.Size(48, 48) - Me.P1.TabIndex = 0 - ' - 'P2 - ' - Me.P2.Dock = System.Windows.Forms.DockStyle.Fill - Me.P2.Location = New System.Drawing.Point(69, 3) - Me.P2.Name = "P2" - Me.P2.Size = New System.Drawing.Size(48, 48) - Me.P2.TabIndex = 1 - ' - 'P3 - ' - Me.P3.Dock = System.Windows.Forms.DockStyle.Fill - Me.P3.Location = New System.Drawing.Point(3, 69) - Me.P3.Name = "P3" - Me.P3.Size = New System.Drawing.Size(48, 48) - Me.P3.TabIndex = 2 - ' - 'P4 - ' - Me.P4.Dock = System.Windows.Forms.DockStyle.Fill - Me.P4.Location = New System.Drawing.Point(69, 69) - Me.P4.Name = "P4" - Me.P4.Size = New System.Drawing.Size(48, 48) - Me.P4.TabIndex = 3 - ' - 'Timer1 - ' - Me.Timer1.Enabled = True - Me.Timer1.Interval = 500 - ' - 'LoadingSplash - ' - Me.AutoScaleDimensions = New System.Drawing.SizeF(96.0!, 96.0!) - Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi - Me.BackColor = System.Drawing.Color.Transparent - Me.Controls.Add(Me.TableLayoutPanel1) - Me.Font = New System.Drawing.Font("Noto Sans KR", 9.0!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(129, Byte)) - Me.Name = "LoadingSplash" - Me.Size = New System.Drawing.Size(120, 120) - Me.TableLayoutPanel1.ResumeLayout(False) - Me.ResumeLayout(False) - - End Sub - - Friend WithEvents TableLayoutPanel1 As TableLayoutPanel - Friend WithEvents P1 As Panel - Friend WithEvents P2 As Panel - Friend WithEvents P3 As Panel - Friend WithEvents P4 As Panel - Friend WithEvents Timer1 As Timer -End Class diff --git a/utable/UserControls/LoadingSplash.resx b/utable/UserControls/LoadingSplash.resx deleted file mode 100644 index d0d99f4..0000000 --- a/utable/UserControls/LoadingSplash.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - \ No newline at end of file diff --git a/utable/UserControls/LoadingSplash.vb b/utable/UserControls/LoadingSplash.vb deleted file mode 100644 index 9306afa..0000000 --- a/utable/UserControls/LoadingSplash.vb +++ /dev/null @@ -1,34 +0,0 @@ -Public Class LoadingSplash - Public highColor As New Color - Public lowColor As New Color - Dim activePos As Integer = 1 - - Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick - Select Case activePos - Case 1 - P1.BackColor = highColor - P2.BackColor = lowColor - P3.BackColor = lowColor - P4.BackColor = lowColor - activePos = 2 - Case 2 - P1.BackColor = lowColor - P2.BackColor = highColor - P3.BackColor = lowColor - P4.BackColor = lowColor - activePos = 3 - Case 3 - P1.BackColor = lowColor - P2.BackColor = lowColor - P3.BackColor = lowColor - P4.BackColor = highColor - activePos = 4 - Case 4 - P1.BackColor = lowColor - P2.BackColor = lowColor - P3.BackColor = highColor - P4.BackColor = lowColor - activePos = 1 - End Select - End Sub -End Class diff --git a/utable/uTable.vbproj b/utable/uTable.vbproj index f3598c2..5b03f8d 100644 --- a/utable/uTable.vbproj +++ b/utable/uTable.vbproj @@ -298,12 +298,6 @@ - - LoadingSplash.vb - - - UserControl - True True @@ -330,9 +324,7 @@ CellControl.vb - - UserControl - + SettingMenu.vb @@ -367,9 +359,6 @@ StartupAsk.vb - - LoadingSplash.vb - VbMyResourcesResXFileCodeGenerator My.Resources @@ -457,4 +446,4 @@ Static - + \ No newline at end of file From f0b21854eff0e70bec890f5219906ddd9c0daddf Mon Sep 17 00:00:00 2001 From: pdjdev Date: Tue, 11 Aug 2026 09:58:12 +0900 Subject: [PATCH 3/5] feat: add application manifest and enhance startup shortcut handling --- .gitignore | 4 +- uTablePackage/Package.appxmanifest | 43 ++++++++++++++++++ utable/ApplicationEvents.vb | 4 ++ utable/Modules/WinModule.vb | 73 +++++++++++++++++++++++------- 4 files changed, 107 insertions(+), 17 deletions(-) create mode 100644 uTablePackage/Package.appxmanifest diff --git a/.gitignore b/.gitignore index 95b2366..593f75d 100644 --- a/.gitignore +++ b/.gitignore @@ -339,4 +339,6 @@ ASALocalRun/ # BeatPulse healthcheck temp database healthchecksdb -uTablePackage/ \ No newline at end of file +uTablePackage/* +!uTablePackage/ +!uTablePackage/Package.appxmanifest diff --git a/uTablePackage/Package.appxmanifest b/uTablePackage/Package.appxmanifest new file mode 100644 index 0000000..35611f5 --- /dev/null +++ b/uTablePackage/Package.appxmanifest @@ -0,0 +1,43 @@ + + + + + uTable + PBJSoftware + Images\StoreLogo.png + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/utable/ApplicationEvents.vb b/utable/ApplicationEvents.vb index 7c3b7f9..a4cd1b4 100644 --- a/utable/ApplicationEvents.vb +++ b/utable/ApplicationEvents.vb @@ -9,6 +9,10 @@ Namespace My ' NetworkAvailabilityChanged: 네트워크가 연결되거나 연결이 끊어질 때 발생합니다. Partial Friend Class MyApplication + Private Sub MyApplication_Startup(sender As Object, e As StartupEventArgs) Handles Me.Startup + MigrateStoreStartupShortcut() + End Sub + Private Sub MyApplication_UnhandledException(sender As Object, e As UnhandledExceptionEventArgs) Handles Me.UnhandledException e.ExitApplication = False ErrorForm.RichTextBox1.Text = e.Exception.Message + vbCr + "=====" + vbCr + DateTime.Now.ToString + vbCr + vbCr + e.Exception.ToString diff --git a/utable/Modules/WinModule.vb b/utable/Modules/WinModule.vb index 9dcf331..6aeccf9 100644 --- a/utable/Modules/WinModule.vb +++ b/utable/Modules/WinModule.vb @@ -5,6 +5,7 @@ Module WinModule Private Const ShortcutName As String = "\uTable.lnk" Private Const AppLaunchCmd As String = "C:\Windows\explorer.exe" Private Const AppCode As String = "shell:appsFolder\49490PBJSoftware.uTable_fv4zvza0919de!App" + Private Const StoreAliasName As String = "uTable.exe" Private Const ErrorInsufficientBuffer As Integer = 122 @@ -33,22 +34,36 @@ Module WinModule End Get End Property + Private ReadOnly Property StoreAliasPath As String + Get + Return IO.Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), + "Microsoft", + "WindowsApps", + StoreAliasName) + End Get + End Property + Public Function checkStartUp() As Boolean Dim destlnk As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName If Not IO.File.Exists(destlnk) Then Return False - Dim wsh As Object = CreateObject("WScript.Shell") - Dim shortcut As Object = wsh.CreateShortcut(destlnk) - Dim targetPath As String = CStr(shortcut.TargetPath) - Dim arguments As String = CStr(shortcut.Arguments).Trim() + Try + Dim wsh As Object = CreateObject("WScript.Shell") + Dim shortcut As Object = wsh.CreateShortcut(destlnk) + Dim targetPath As String = CStr(shortcut.TargetPath) + Dim arguments As String = CStr(shortcut.Arguments).Trim() - ' MSIX 바로가기는 Explorer가 AppsFolder 경로를 실행한다. - If IsStoreApp AndAlso PathsEqual(targetPath, AppLaunchCmd) AndAlso - String.Equals(arguments, AppCode, StringComparison.OrdinalIgnoreCase) Then Return True + If IsStoreApp Then + Return PathsEqual(targetPath, StoreAliasPath) AndAlso String.IsNullOrEmpty(arguments) + End If - ' 일반 exe 바로가기 및 이전 버전이 남긴 "exe + shell:appsFolder" 형식도 인정한다. - If Not PathsEqual(targetPath, Application.ExecutablePath) Then Return False - Return String.IsNullOrEmpty(arguments) OrElse arguments.StartsWith("shell:appsFolder\", StringComparison.OrdinalIgnoreCase) + ' 일반 exe 바로가기 및 이전 버전이 남긴 "exe + shell:appsFolder" 형식도 인정한다. + If Not PathsEqual(targetPath, Application.ExecutablePath) Then Return False + Return String.IsNullOrEmpty(arguments) OrElse arguments.StartsWith("shell:appsFolder\", StringComparison.OrdinalIgnoreCase) + Catch + Return False + End Try End Function Private Function PathsEqual(firstPath As String, secondPath As String) As Boolean @@ -59,21 +74,47 @@ Module WinModule Sub SetStartup() Dim path As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName - Dim wsh As Object = CreateObject("WScript.Shell") - Dim myShortcut As Object = wsh.CreateShortcut(path) + Dim targetPath As String If IsStoreApp Then - myShortcut.TargetPath = wsh.ExpandEnvironmentStrings(AppLaunchCmd) - myShortcut.Arguments = AppCode + targetPath = StoreAliasPath + If Not IO.File.Exists(targetPath) Then + Throw New InvalidOperationException("uTable 앱 실행 별칭을 찾을 수 없습니다: " & targetPath) + End If Else - myShortcut.TargetPath = wsh.ExpandEnvironmentStrings(Application.ExecutablePath) - myShortcut.Arguments = "" + targetPath = Application.ExecutablePath End If + Dim wsh As Object = CreateObject("WScript.Shell") + Dim myShortcut As Object = wsh.CreateShortcut(path) + myShortcut.TargetPath = targetPath + myShortcut.Arguments = "" + myShortcut.WorkingDirectory = IO.Path.GetDirectoryName(targetPath) myShortcut.WindowStyle = 4 myShortcut.Save() End Sub + Public Sub MigrateStoreStartupShortcut() + If Not IsStoreApp Then Return + + Dim path As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName + If Not IO.File.Exists(path) Then Return + + Try + Dim wsh As Object = CreateObject("WScript.Shell") + Dim shortcut As Object = wsh.CreateShortcut(path) + Dim targetPath As String = CStr(shortcut.TargetPath) + Dim arguments As String = CStr(shortcut.Arguments).Trim() + + If PathsEqual(targetPath, AppLaunchCmd) AndAlso + String.Equals(arguments, AppCode, StringComparison.OrdinalIgnoreCase) Then + SetStartup() + End If + Catch + ' 기존 바로가기를 읽거나 교체하지 못해도 앱 실행은 계속한다. + End Try + End Sub + Sub RemoveStartup() Dim path As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName If IO.File.Exists(path) Then My.Computer.FileSystem.DeleteFile(path) From 0e12aa4520c97cad696f712f11215bc259bde5de Mon Sep 17 00:00:00 2001 From: pdjdev Date: Tue, 11 Aug 2026 09:59:53 +0900 Subject: [PATCH 4/5] fix: remove obsolete startup task extension from app manifest --- uTablePackage/Package.appxmanifest | 3 --- 1 file changed, 3 deletions(-) diff --git a/uTablePackage/Package.appxmanifest b/uTablePackage/Package.appxmanifest index 35611f5..6d42556 100644 --- a/uTablePackage/Package.appxmanifest +++ b/uTablePackage/Package.appxmanifest @@ -25,9 +25,6 @@ - - - From 113bd38b82e34f5bf43257900906060390008d3c Mon Sep 17 00:00:00 2001 From: pdjdev Date: Tue, 11 Aug 2026 10:03:33 +0900 Subject: [PATCH 5/5] fix: refactor startup shortcut handling for consistency and clarity --- utable/Modules/WinModule.vb | 82 +++++++++++++++++++++---------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/utable/Modules/WinModule.vb b/utable/Modules/WinModule.vb index 6aeccf9..3fd4498 100644 --- a/utable/Modules/WinModule.vb +++ b/utable/Modules/WinModule.vb @@ -2,7 +2,7 @@ Module WinModule #Region "시작프로그램설정" - Private Const ShortcutName As String = "\uTable.lnk" + Private Const ShortcutFileName As String = "uTable.lnk" Private Const AppLaunchCmd As String = "C:\Windows\explorer.exe" Private Const AppCode As String = "shell:appsFolder\49490PBJSoftware.uTable_fv4zvza0919de!App" Private Const StoreAliasName As String = "uTable.exe" @@ -44,36 +44,52 @@ Module WinModule End Get End Property + Private ReadOnly Property StartupShortcutPath As String + Get + Return IO.Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.Startup), + ShortcutFileName) + End Get + End Property + Public Function checkStartUp() As Boolean - Dim destlnk As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName - If Not IO.File.Exists(destlnk) Then Return False + If Not IO.File.Exists(StartupShortcutPath) Then Return False - Try - Dim wsh As Object = CreateObject("WScript.Shell") - Dim shortcut As Object = wsh.CreateShortcut(destlnk) - Dim targetPath As String = CStr(shortcut.TargetPath) - Dim arguments As String = CStr(shortcut.Arguments).Trim() + Dim targetPath As String = Nothing + Dim arguments As String = Nothing + If Not TryReadStartupShortcut(targetPath, arguments) Then Return False - If IsStoreApp Then - Return PathsEqual(targetPath, StoreAliasPath) AndAlso String.IsNullOrEmpty(arguments) - End If + If IsStoreApp Then + Return PathsEqual(targetPath, StoreAliasPath) AndAlso String.IsNullOrEmpty(arguments) + End If - ' 일반 exe 바로가기 및 이전 버전이 남긴 "exe + shell:appsFolder" 형식도 인정한다. - If Not PathsEqual(targetPath, Application.ExecutablePath) Then Return False - Return String.IsNullOrEmpty(arguments) OrElse arguments.StartsWith("shell:appsFolder\", StringComparison.OrdinalIgnoreCase) + If Not PathsEqual(targetPath, Application.ExecutablePath) Then Return False + Return String.IsNullOrEmpty(arguments) + End Function + + Private Function TryReadStartupShortcut(ByRef targetPath As String, ByRef arguments As String) As Boolean + Try + Dim wsh As Object = CreateObject("WScript.Shell") + Dim shortcut As Object = wsh.CreateShortcut(StartupShortcutPath) + targetPath = CStr(shortcut.TargetPath) + arguments = CStr(shortcut.Arguments).Trim() + Return True Catch Return False End Try End Function Private Function PathsEqual(firstPath As String, secondPath As String) As Boolean - Return String.Equals(IO.Path.GetFullPath(firstPath).TrimEnd("\"c), - IO.Path.GetFullPath(secondPath).TrimEnd("\"c), - StringComparison.OrdinalIgnoreCase) + Try + Return String.Equals(IO.Path.GetFullPath(firstPath).TrimEnd("\"c), + IO.Path.GetFullPath(secondPath).TrimEnd("\"c), + StringComparison.OrdinalIgnoreCase) + Catch + Return False + End Try End Function Sub SetStartup() - Dim path As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName Dim targetPath As String If IsStoreApp Then @@ -86,7 +102,7 @@ Module WinModule End If Dim wsh As Object = CreateObject("WScript.Shell") - Dim myShortcut As Object = wsh.CreateShortcut(path) + Dim myShortcut As Object = wsh.CreateShortcut(StartupShortcutPath) myShortcut.TargetPath = targetPath myShortcut.Arguments = "" myShortcut.WorkingDirectory = IO.Path.GetDirectoryName(targetPath) @@ -96,28 +112,24 @@ Module WinModule Public Sub MigrateStoreStartupShortcut() If Not IsStoreApp Then Return + If Not IO.File.Exists(StartupShortcutPath) Then Return - Dim path As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName - If Not IO.File.Exists(path) Then Return - - Try - Dim wsh As Object = CreateObject("WScript.Shell") - Dim shortcut As Object = wsh.CreateShortcut(path) - Dim targetPath As String = CStr(shortcut.TargetPath) - Dim arguments As String = CStr(shortcut.Arguments).Trim() + Dim targetPath As String = Nothing + Dim arguments As String = Nothing + If Not TryReadStartupShortcut(targetPath, arguments) Then Return - If PathsEqual(targetPath, AppLaunchCmd) AndAlso - String.Equals(arguments, AppCode, StringComparison.OrdinalIgnoreCase) Then + If PathsEqual(targetPath, AppLaunchCmd) AndAlso + String.Equals(arguments, AppCode, StringComparison.OrdinalIgnoreCase) Then + Try SetStartup() - End If - Catch - ' 기존 바로가기를 읽거나 교체하지 못해도 앱 실행은 계속한다. - End Try + Catch + ' 기존 바로가기를 교체하지 못해도 앱 실행은 계속한다. + End Try + End If End Sub Sub RemoveStartup() - Dim path As String = Environment.GetFolderPath(Environment.SpecialFolder.Startup) & ShortcutName - If IO.File.Exists(path) Then My.Computer.FileSystem.DeleteFile(path) + If IO.File.Exists(StartupShortcutPath) Then My.Computer.FileSystem.DeleteFile(StartupShortcutPath) End Sub #End Region