@ishibashi0112/webview2-bridge-gen 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/cli.js +62 -6
- package/dist/cli.js.map +1 -1
- package/dist/generate.d.ts +1 -0
- package/dist/generate.d.ts.map +1 -1
- package/dist/generate.js +1 -0
- package/dist/generate.js.map +1 -1
- package/dist/init.d.ts +32 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +93 -0
- package/dist/init.js.map +1 -0
- package/package.json +6 -3
- package/template/README.md +10 -0
- package/template/myapp/README.md +91 -0
- package/template/myapp/_gitignore +6 -0
- package/template/myapp/contract/contract.schema.json +66 -0
- package/template/myapp/contract/contract.ts +8 -0
- package/template/myapp/dotnet/Directory.Build.props +10 -0
- package/template/myapp/dotnet/MyApp.Contract/Generated/Dispatcher.Generated.vb +30 -0
- package/template/myapp/dotnet/MyApp.Contract/Generated/Dto.vb +39 -0
- package/template/myapp/dotnet/MyApp.Contract/Generated/Events.vb +32 -0
- package/template/myapp/dotnet/MyApp.Contract/Generated/Interfaces.vb +20 -0
- package/template/myapp/dotnet/MyApp.Contract/MyApp.Contract.vbproj +9 -0
- package/template/myapp/dotnet/MyApp.Contract/Runtime/Dispatcher.vb +151 -0
- package/template/myapp/dotnet/MyApp.Contract/Runtime/IBridgeEmitter.vb +22 -0
- package/template/myapp/dotnet/MyApp.Contract/Runtime/JsonRpc.vb +130 -0
- package/template/myapp/dotnet/MyApp.Host/Bridge/WebViewBridge.vb +106 -0
- package/template/myapp/dotnet/MyApp.Host/MainForm.vb +90 -0
- package/template/myapp/dotnet/MyApp.Host/MyApp.Host.vbproj +28 -0
- package/template/myapp/dotnet/MyApp.Host/Program.vb +12 -0
- package/template/myapp/dotnet/MyApp.Impl/CustomersApi.vb +38 -0
- package/template/myapp/dotnet/MyApp.Impl/MyApp.Impl.vbproj +9 -0
- package/template/myapp/dotnet/MyApp.sln +34 -0
- package/template/myapp/package.json +16 -0
- package/template/myapp/pnpm-workspace.yaml +6 -0
- package/template/myapp/web/index.html +13 -0
- package/template/myapp/web/package.json +23 -0
- package/template/myapp/web/src/bridge.ts +29 -0
- package/template/myapp/web/src/generated/contract-types.ts +33 -0
- package/template/myapp/web/src/main.tsx +37 -0
- package/template/myapp/web/src/vite-env.d.ts +6 -0
- package/template/myapp/web/tsconfig.json +8 -0
- package/template/myapp/web/vite.config.ts +17 -0
- package/template/myapp/webview2-bridge.gen.json +11 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
' <auto-generated>
|
|
2
|
+
' This code was generated by @ishibashi0112/webview2-bridge-gen from contract.schema.json.
|
|
3
|
+
' DO NOT EDIT. Changes will be lost when `pnpm gen` runs.
|
|
4
|
+
' </auto-generated>
|
|
5
|
+
Option Strict On
|
|
6
|
+
Option Explicit On
|
|
7
|
+
Option Infer On
|
|
8
|
+
|
|
9
|
+
Imports System
|
|
10
|
+
Imports WebView2Bridge.Runtime
|
|
11
|
+
|
|
12
|
+
Namespace Global.MyApp.Contract
|
|
13
|
+
|
|
14
|
+
''' <summary>Host → Web の型付きイベント発行ヘルパ。内部で IBridgeEmitter.Emit("event.<name>", payload) を呼ぶ</summary>
|
|
15
|
+
Public Class BridgeEvents
|
|
16
|
+
Private ReadOnly _emitter As IBridgeEmitter
|
|
17
|
+
|
|
18
|
+
Public Sub New(emitter As IBridgeEmitter)
|
|
19
|
+
If emitter Is Nothing Then Throw New ArgumentNullException(NameOf(emitter))
|
|
20
|
+
_emitter = emitter
|
|
21
|
+
End Sub
|
|
22
|
+
|
|
23
|
+
''' <summary>契約に含まれる全イベント名</summary>
|
|
24
|
+
Public Shared ReadOnly EventNames As String() = {"event.progress"}
|
|
25
|
+
|
|
26
|
+
''' <summary>JSON-RPC notification "event.progress"</summary>
|
|
27
|
+
Public Sub Progress(payload As ProgressEvent)
|
|
28
|
+
_emitter.Emit("event.progress", payload)
|
|
29
|
+
End Sub
|
|
30
|
+
End Class
|
|
31
|
+
|
|
32
|
+
End Namespace
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
' <auto-generated>
|
|
2
|
+
' This code was generated by @ishibashi0112/webview2-bridge-gen from contract.schema.json.
|
|
3
|
+
' DO NOT EDIT. Changes will be lost when `pnpm gen` runs.
|
|
4
|
+
' </auto-generated>
|
|
5
|
+
Option Strict On
|
|
6
|
+
Option Explicit On
|
|
7
|
+
Option Infer On
|
|
8
|
+
|
|
9
|
+
Imports System
|
|
10
|
+
Imports System.Threading.Tasks
|
|
11
|
+
|
|
12
|
+
Namespace Global.MyApp.Contract
|
|
13
|
+
|
|
14
|
+
''' <summary>"customers.*" メソッドの実装。人間が Implements して WebView2Bridge.Impl に置く</summary>
|
|
15
|
+
Public Interface ICustomersApi
|
|
16
|
+
''' <summary>JSON-RPC method "customers.list"</summary>
|
|
17
|
+
Function List(req As CustomersListRequest) As Task(Of CustomersListResponse)
|
|
18
|
+
End Interface
|
|
19
|
+
|
|
20
|
+
End Namespace
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
+
<PropertyGroup>
|
|
3
|
+
<TargetFramework>netstandard2.0</TargetFramework>
|
|
4
|
+
<RootNamespace>MyApp.Contract</RootNamespace>
|
|
5
|
+
</PropertyGroup>
|
|
6
|
+
<ItemGroup>
|
|
7
|
+
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
|
8
|
+
</ItemGroup>
|
|
9
|
+
</Project>
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
' <auto-generated>
|
|
2
|
+
' webview2-bridge VB JSON-RPC runtime (namespace WebView2Bridge.Runtime; needs Newtonsoft.Json)
|
|
3
|
+
' copied by @ishibashi0112/webview2-bridge-gen 0.3.0 from its bundled vb-runtime/runtime/.
|
|
4
|
+
' DO NOT EDIT. Changes will be lost when `pnpm gen` runs.
|
|
5
|
+
' To update, upgrade the npm package and run the generator again.
|
|
6
|
+
' </auto-generated>
|
|
7
|
+
Option Strict On
|
|
8
|
+
|
|
9
|
+
Imports System
|
|
10
|
+
Imports System.Collections.Generic
|
|
11
|
+
Imports System.Threading.Tasks
|
|
12
|
+
Imports Newtonsoft.Json
|
|
13
|
+
Imports Newtonsoft.Json.Linq
|
|
14
|
+
|
|
15
|
+
Namespace Global.WebView2Bridge.Runtime
|
|
16
|
+
|
|
17
|
+
''' <summary>
|
|
18
|
+
''' JSON-RPC 要求文字列を受け取り、登録されたハンドラへ振り分けて応答文字列を返す。
|
|
19
|
+
''' 手書き側(このファイル)は契約に依存しない汎用部分。
|
|
20
|
+
''' 生成側(Dispatcher.Generated.vb)が Register(api As IXxxApi) を提供し、RegisterHandler を呼ぶ。
|
|
21
|
+
''' WebView2 には依存しない。
|
|
22
|
+
''' </summary>
|
|
23
|
+
Partial Public Class Dispatcher
|
|
24
|
+
|
|
25
|
+
''' <summary>型消去したハンドラ。params(JToken) → 結果(Object)</summary>
|
|
26
|
+
Private Interface IHandler
|
|
27
|
+
Function InvokeAsync(params As JToken) As Task(Of Object)
|
|
28
|
+
End Interface
|
|
29
|
+
|
|
30
|
+
Private NotInheritable Class TypedHandler(Of TReq, TRes)
|
|
31
|
+
Implements IHandler
|
|
32
|
+
|
|
33
|
+
Private ReadOnly _handler As Func(Of TReq, Task(Of TRes))
|
|
34
|
+
Private ReadOnly _serializer As JsonSerializer
|
|
35
|
+
|
|
36
|
+
Public Sub New(handler As Func(Of TReq, Task(Of TRes)), serializer As JsonSerializer)
|
|
37
|
+
_handler = handler
|
|
38
|
+
_serializer = serializer
|
|
39
|
+
End Sub
|
|
40
|
+
|
|
41
|
+
Public Async Function InvokeAsync(params As JToken) As Task(Of Object) Implements IHandler.InvokeAsync
|
|
42
|
+
Dim req As TReq
|
|
43
|
+
Try
|
|
44
|
+
' params 省略時は {} として扱う(全プロパティ optional な入力を許す)
|
|
45
|
+
Dim source As JToken = If(params Is Nothing OrElse params.Type = JTokenType.Null, New JObject(), params)
|
|
46
|
+
If source.Type <> JTokenType.Object Then
|
|
47
|
+
Throw New JsonRpcException(JsonRpcErrorCodes.InvalidParams, "Invalid params: expected an object")
|
|
48
|
+
End If
|
|
49
|
+
req = source.ToObject(Of TReq)(_serializer)
|
|
50
|
+
Catch ex As JsonException
|
|
51
|
+
Throw New JsonRpcException(JsonRpcErrorCodes.InvalidParams, "Invalid params: " & ex.Message, ex.GetType().FullName)
|
|
52
|
+
End Try
|
|
53
|
+
Dim result As TRes = Await _handler(req).ConfigureAwait(False)
|
|
54
|
+
Return result
|
|
55
|
+
End Function
|
|
56
|
+
End Class
|
|
57
|
+
|
|
58
|
+
Private ReadOnly _handlers As New Dictionary(Of String, IHandler)(StringComparer.Ordinal)
|
|
59
|
+
Private ReadOnly _serializer As JsonSerializer
|
|
60
|
+
|
|
61
|
+
Public Sub New()
|
|
62
|
+
Me.New(JsonRpc.CreateSerializerSettings())
|
|
63
|
+
End Sub
|
|
64
|
+
|
|
65
|
+
Public Sub New(settings As JsonSerializerSettings)
|
|
66
|
+
If settings Is Nothing Then Throw New ArgumentNullException(NameOf(settings))
|
|
67
|
+
_serializer = JsonSerializer.Create(settings)
|
|
68
|
+
End Sub
|
|
69
|
+
|
|
70
|
+
''' <summary>JSON の読み書きに使うシリアライザ。Host 側の通知にも同じものを使う</summary>
|
|
71
|
+
Public ReadOnly Property Serializer As JsonSerializer
|
|
72
|
+
Get
|
|
73
|
+
Return _serializer
|
|
74
|
+
End Get
|
|
75
|
+
End Property
|
|
76
|
+
|
|
77
|
+
''' <summary>登録済みメソッド名</summary>
|
|
78
|
+
Public ReadOnly Property RegisteredMethods As IEnumerable(Of String)
|
|
79
|
+
Get
|
|
80
|
+
Return _handlers.Keys
|
|
81
|
+
End Get
|
|
82
|
+
End Property
|
|
83
|
+
|
|
84
|
+
''' <summary>
|
|
85
|
+
''' 型付きハンドラを登録する。通常は生成側の Register(api) から呼ばれる。
|
|
86
|
+
''' 同名メソッドは後勝ち。
|
|
87
|
+
''' </summary>
|
|
88
|
+
Public Sub RegisterHandler(Of TReq, TRes)(method As String, handler As Func(Of TReq, Task(Of TRes)))
|
|
89
|
+
If String.IsNullOrEmpty(method) Then Throw New ArgumentException("method is required", NameOf(method))
|
|
90
|
+
If handler Is Nothing Then Throw New ArgumentNullException(NameOf(handler))
|
|
91
|
+
_handlers(method) = New TypedHandler(Of TReq, TRes)(handler, _serializer)
|
|
92
|
+
End Sub
|
|
93
|
+
|
|
94
|
+
''' <summary>
|
|
95
|
+
''' 要求 JSON を処理し、応答 JSON を返す。例外は投げず、必ず JSON-RPC エラー応答にする。
|
|
96
|
+
''' 要求に id が無い(通知)場合は処理だけ行い Nothing を返す。
|
|
97
|
+
''' </summary>
|
|
98
|
+
Public Async Function HandleAsync(requestJson As String) As Task(Of String)
|
|
99
|
+
Dim id As JToken = Nothing
|
|
100
|
+
Dim hasId As Boolean = False
|
|
101
|
+
Try
|
|
102
|
+
Dim token As JToken
|
|
103
|
+
Try
|
|
104
|
+
token = JsonRpc.ParseToken(If(requestJson, String.Empty))
|
|
105
|
+
Catch ex As JsonException
|
|
106
|
+
Return JsonRpc.BuildError(Nothing, JsonRpcErrorCodes.ParseError, "Parse error: " & ex.Message, Nothing, _serializer)
|
|
107
|
+
End Try
|
|
108
|
+
|
|
109
|
+
Dim request = TryCast(token, JObject)
|
|
110
|
+
If request Is Nothing Then
|
|
111
|
+
Return JsonRpc.BuildError(Nothing, JsonRpcErrorCodes.InvalidRequest, "Invalid Request: expected an object", Nothing, _serializer)
|
|
112
|
+
End If
|
|
113
|
+
|
|
114
|
+
Dim idToken As JToken = Nothing
|
|
115
|
+
If request.TryGetValue("id", idToken) AndAlso idToken.Type <> JTokenType.Null Then
|
|
116
|
+
id = idToken
|
|
117
|
+
hasId = True
|
|
118
|
+
End If
|
|
119
|
+
|
|
120
|
+
Dim methodToken As JToken = Nothing
|
|
121
|
+
If Not request.TryGetValue("method", methodToken) OrElse methodToken.Type <> JTokenType.String Then
|
|
122
|
+
Return If(hasId, JsonRpc.BuildError(id, JsonRpcErrorCodes.InvalidRequest, "Invalid Request: method is required", Nothing, _serializer), Nothing)
|
|
123
|
+
End If
|
|
124
|
+
Dim method As String = methodToken.Value(Of String)()
|
|
125
|
+
|
|
126
|
+
Dim handler As IHandler = Nothing
|
|
127
|
+
If Not _handlers.TryGetValue(method, handler) Then
|
|
128
|
+
Return If(hasId, JsonRpc.BuildError(id, JsonRpcErrorCodes.MethodNotFound, "Method not found: " & method, Nothing, _serializer), Nothing)
|
|
129
|
+
End If
|
|
130
|
+
|
|
131
|
+
Dim params As JToken = Nothing
|
|
132
|
+
request.TryGetValue("params", params)
|
|
133
|
+
|
|
134
|
+
Dim result As Object = Await handler.InvokeAsync(params).ConfigureAwait(False)
|
|
135
|
+
Return If(hasId, JsonRpc.BuildResult(id, result, _serializer), Nothing)
|
|
136
|
+
|
|
137
|
+
Catch ex As JsonRpcException
|
|
138
|
+
Return If(hasId, JsonRpc.BuildError(id, ex.Code, ex.Message, ex.ErrorData, _serializer), Nothing)
|
|
139
|
+
Catch ex As Exception
|
|
140
|
+
' 実装側の未処理例外: -32000、message に例外メッセージ、data に例外型名(HANDOFF.md §5)
|
|
141
|
+
Return If(hasId, JsonRpc.BuildError(id, JsonRpcErrorCodes.ServerError, ex.Message, ex.GetType().FullName, _serializer), Nothing)
|
|
142
|
+
End Try
|
|
143
|
+
End Function
|
|
144
|
+
|
|
145
|
+
''' <summary>Host → Web の通知 JSON を作る(送信は Host 側)</summary>
|
|
146
|
+
Public Function BuildNotification(method As String, params As Object) As String
|
|
147
|
+
Return JsonRpc.BuildNotification(method, params, _serializer)
|
|
148
|
+
End Function
|
|
149
|
+
End Class
|
|
150
|
+
|
|
151
|
+
End Namespace
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
' <auto-generated>
|
|
2
|
+
' webview2-bridge VB JSON-RPC runtime (namespace WebView2Bridge.Runtime; needs Newtonsoft.Json)
|
|
3
|
+
' copied by @ishibashi0112/webview2-bridge-gen 0.3.0 from its bundled vb-runtime/runtime/.
|
|
4
|
+
' DO NOT EDIT. Changes will be lost when `pnpm gen` runs.
|
|
5
|
+
' To update, upgrade the npm package and run the generator again.
|
|
6
|
+
' </auto-generated>
|
|
7
|
+
Option Strict On
|
|
8
|
+
|
|
9
|
+
Namespace Global.WebView2Bridge.Runtime
|
|
10
|
+
|
|
11
|
+
''' <summary>
|
|
12
|
+
''' Host → Web の通知(JSON-RPC notification)を送る口。
|
|
13
|
+
''' Contract は WebView2 に依存しないので、実体は Host 側(WebViewBridge)が実装する。
|
|
14
|
+
''' 生成される BridgeEvents はこれを通して "event.<name>" を発行する。
|
|
15
|
+
''' </summary>
|
|
16
|
+
Public Interface IBridgeEmitter
|
|
17
|
+
''' <param name="method">"event.progress" のような通知名</param>
|
|
18
|
+
''' <param name="params">ペイロード(生成 DTO)。Nothing 可</param>
|
|
19
|
+
Sub Emit(method As String, params As Object)
|
|
20
|
+
End Interface
|
|
21
|
+
|
|
22
|
+
End Namespace
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
' <auto-generated>
|
|
2
|
+
' webview2-bridge VB JSON-RPC runtime (namespace WebView2Bridge.Runtime; needs Newtonsoft.Json)
|
|
3
|
+
' copied by @ishibashi0112/webview2-bridge-gen 0.3.0 from its bundled vb-runtime/runtime/.
|
|
4
|
+
' DO NOT EDIT. Changes will be lost when `pnpm gen` runs.
|
|
5
|
+
' To update, upgrade the npm package and run the generator again.
|
|
6
|
+
' </auto-generated>
|
|
7
|
+
Option Strict On
|
|
8
|
+
|
|
9
|
+
Imports System
|
|
10
|
+
Imports System.IO
|
|
11
|
+
Imports Newtonsoft.Json
|
|
12
|
+
Imports Newtonsoft.Json.Linq
|
|
13
|
+
|
|
14
|
+
Namespace Global.WebView2Bridge.Runtime
|
|
15
|
+
|
|
16
|
+
''' <summary>JSON-RPC 2.0 のエラーコード(HANDOFF.md §5)</summary>
|
|
17
|
+
Public NotInheritable Class JsonRpcErrorCodes
|
|
18
|
+
Private Sub New()
|
|
19
|
+
End Sub
|
|
20
|
+
|
|
21
|
+
Public Const ParseError As Integer = -32700
|
|
22
|
+
Public Const InvalidRequest As Integer = -32600
|
|
23
|
+
Public Const MethodNotFound As Integer = -32601
|
|
24
|
+
Public Const InvalidParams As Integer = -32602
|
|
25
|
+
Public Const InternalError As Integer = -32603
|
|
26
|
+
''' <summary>アプリ定義エラーの既定値。実装側の未処理例外はこのコードになる</summary>
|
|
27
|
+
Public Const ServerError As Integer = -32000
|
|
28
|
+
End Class
|
|
29
|
+
|
|
30
|
+
''' <summary>
|
|
31
|
+
''' 実装(IXxxApi)から意図的に JSON-RPC エラーを返したいときに投げる。
|
|
32
|
+
''' Code は -32000 以下のアプリ定義コードを使う。
|
|
33
|
+
''' </summary>
|
|
34
|
+
Public Class JsonRpcException
|
|
35
|
+
Inherits Exception
|
|
36
|
+
|
|
37
|
+
Public ReadOnly Property Code As Integer
|
|
38
|
+
Public ReadOnly Property ErrorData As Object
|
|
39
|
+
|
|
40
|
+
Public Sub New(code As Integer, message As String)
|
|
41
|
+
Me.New(code, message, Nothing)
|
|
42
|
+
End Sub
|
|
43
|
+
|
|
44
|
+
Public Sub New(code As Integer, message As String, data As Object)
|
|
45
|
+
MyBase.New(message)
|
|
46
|
+
Me.Code = code
|
|
47
|
+
Me.ErrorData = data
|
|
48
|
+
End Sub
|
|
49
|
+
End Class
|
|
50
|
+
|
|
51
|
+
''' <summary>JSON-RPC 封筒の組み立て・解釈。Dispatcher と Host(WebViewBridge)が共用する</summary>
|
|
52
|
+
Public NotInheritable Class JsonRpc
|
|
53
|
+
Private Sub New()
|
|
54
|
+
End Sub
|
|
55
|
+
|
|
56
|
+
Public Const Version As String = "2.0"
|
|
57
|
+
|
|
58
|
+
''' <summary>
|
|
59
|
+
''' ブリッジ全体で使う Newtonsoft の設定。
|
|
60
|
+
''' DateParseHandling.None: "2024-01-01T00:00:00Z" のような文字列を Date に変換せず、文字列のまま往復させる。
|
|
61
|
+
''' </summary>
|
|
62
|
+
Public Shared Function CreateSerializerSettings() As JsonSerializerSettings
|
|
63
|
+
Return New JsonSerializerSettings() With {
|
|
64
|
+
.DateParseHandling = DateParseHandling.None,
|
|
65
|
+
.NullValueHandling = NullValueHandling.Include,
|
|
66
|
+
.Formatting = Formatting.None
|
|
67
|
+
}
|
|
68
|
+
End Function
|
|
69
|
+
|
|
70
|
+
''' <summary>JSON 文字列 → JToken。日付文字列を自動変換しない</summary>
|
|
71
|
+
Public Shared Function ParseToken(json As String) As JToken
|
|
72
|
+
Using sr As New StringReader(json)
|
|
73
|
+
Using reader As New JsonTextReader(sr)
|
|
74
|
+
reader.DateParseHandling = DateParseHandling.None
|
|
75
|
+
Dim token = JToken.ReadFrom(reader)
|
|
76
|
+
' 末尾にゴミが無いことを確認する("{}{}" のような入力を弾く)
|
|
77
|
+
If reader.Read() Then
|
|
78
|
+
Throw New JsonReaderException("Additional text found after JSON root")
|
|
79
|
+
End If
|
|
80
|
+
Return token
|
|
81
|
+
End Using
|
|
82
|
+
End Using
|
|
83
|
+
End Function
|
|
84
|
+
|
|
85
|
+
''' <summary>成功応答 {"jsonrpc":"2.0","id":...,"result":...}</summary>
|
|
86
|
+
Public Shared Function BuildResult(id As JToken, result As Object, serializer As JsonSerializer) As String
|
|
87
|
+
Dim resultToken As JToken = If(result Is Nothing, CType(JValue.CreateNull(), JToken), JToken.FromObject(result, serializer))
|
|
88
|
+
Dim o As New JObject() From {
|
|
89
|
+
{"jsonrpc", Version},
|
|
90
|
+
{"id", NormalizeId(id)},
|
|
91
|
+
{"result", resultToken}
|
|
92
|
+
}
|
|
93
|
+
Return o.ToString(Formatting.None)
|
|
94
|
+
End Function
|
|
95
|
+
|
|
96
|
+
''' <summary>エラー応答 {"jsonrpc":"2.0","id":...,"error":{"code","message","data"}}</summary>
|
|
97
|
+
Public Shared Function BuildError(id As JToken, code As Integer, message As String, data As Object, serializer As JsonSerializer) As String
|
|
98
|
+
Dim dataToken As JToken = If(data Is Nothing, CType(JValue.CreateNull(), JToken), JToken.FromObject(data, serializer))
|
|
99
|
+
Dim err As New JObject() From {
|
|
100
|
+
{"code", code},
|
|
101
|
+
{"message", If(message, String.Empty)},
|
|
102
|
+
{"data", dataToken}
|
|
103
|
+
}
|
|
104
|
+
Dim o As New JObject() From {
|
|
105
|
+
{"jsonrpc", Version},
|
|
106
|
+
{"id", NormalizeId(id)},
|
|
107
|
+
{"error", err}
|
|
108
|
+
}
|
|
109
|
+
Return o.ToString(Formatting.None)
|
|
110
|
+
End Function
|
|
111
|
+
|
|
112
|
+
''' <summary>通知 {"jsonrpc":"2.0","method":...,"params":...}(id なし)</summary>
|
|
113
|
+
Public Shared Function BuildNotification(method As String, params As Object, serializer As JsonSerializer) As String
|
|
114
|
+
If String.IsNullOrEmpty(method) Then Throw New ArgumentException("method is required", NameOf(method))
|
|
115
|
+
Dim paramsToken As JToken = If(params Is Nothing, CType(JValue.CreateNull(), JToken), JToken.FromObject(params, serializer))
|
|
116
|
+
Dim o As New JObject() From {
|
|
117
|
+
{"jsonrpc", Version},
|
|
118
|
+
{"method", method},
|
|
119
|
+
{"params", paramsToken}
|
|
120
|
+
}
|
|
121
|
+
Return o.ToString(Formatting.None)
|
|
122
|
+
End Function
|
|
123
|
+
|
|
124
|
+
Private Shared Function NormalizeId(id As JToken) As JToken
|
|
125
|
+
If id Is Nothing Then Return JValue.CreateNull()
|
|
126
|
+
Return id.DeepClone()
|
|
127
|
+
End Function
|
|
128
|
+
End Class
|
|
129
|
+
|
|
130
|
+
End Namespace
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
' <auto-generated>
|
|
2
|
+
' webview2-bridge VB WinForms bridge (namespace WebView2Bridge.WinForms; needs Microsoft.Web.WebView2 and the runtime)
|
|
3
|
+
' copied by @ishibashi0112/webview2-bridge-gen 0.3.0 from its bundled vb-runtime/winforms/.
|
|
4
|
+
' DO NOT EDIT. Changes will be lost when `pnpm gen` runs.
|
|
5
|
+
' To update, upgrade the npm package and run the generator again.
|
|
6
|
+
' </auto-generated>
|
|
7
|
+
Option Strict On
|
|
8
|
+
|
|
9
|
+
Imports System
|
|
10
|
+
Imports System.Diagnostics
|
|
11
|
+
Imports System.Windows.Forms
|
|
12
|
+
Imports Microsoft.Web.WebView2.Core
|
|
13
|
+
Imports Microsoft.Web.WebView2.WinForms
|
|
14
|
+
Imports WebView2Bridge.Runtime
|
|
15
|
+
|
|
16
|
+
Namespace Global.WebView2Bridge.WinForms
|
|
17
|
+
|
|
18
|
+
''' <summary>
|
|
19
|
+
''' WebView2 コントロールと Dispatcher をつなぐ。
|
|
20
|
+
''' Web → Host: WebMessageReceived → Dispatcher.HandleAsync → PostWebMessageAsJson
|
|
21
|
+
''' Host → Web: Emit(method, payload) → 通知 JSON → PostWebMessageAsJson(UI スレッドへマーシャリング)
|
|
22
|
+
''' AddHostObjectToScript は使わない(HANDOFF.md §5)。
|
|
23
|
+
''' </summary>
|
|
24
|
+
Public NotInheritable Class WebViewBridge
|
|
25
|
+
Implements IBridgeEmitter
|
|
26
|
+
Implements IDisposable
|
|
27
|
+
|
|
28
|
+
Private ReadOnly _webView As WebView2
|
|
29
|
+
Private ReadOnly _dispatcher As Dispatcher
|
|
30
|
+
Private _core As CoreWebView2
|
|
31
|
+
Private _attached As Boolean
|
|
32
|
+
|
|
33
|
+
Public Sub New(webView As WebView2, dispatcher As Dispatcher)
|
|
34
|
+
If webView Is Nothing Then Throw New ArgumentNullException(NameOf(webView))
|
|
35
|
+
If dispatcher Is Nothing Then Throw New ArgumentNullException(NameOf(dispatcher))
|
|
36
|
+
_webView = webView
|
|
37
|
+
_dispatcher = dispatcher
|
|
38
|
+
End Sub
|
|
39
|
+
|
|
40
|
+
Public ReadOnly Property Dispatcher As Dispatcher
|
|
41
|
+
Get
|
|
42
|
+
Return _dispatcher
|
|
43
|
+
End Get
|
|
44
|
+
End Property
|
|
45
|
+
|
|
46
|
+
''' <summary>EnsureCoreWebView2Async の後に呼ぶ。WebMessageReceived を購読する</summary>
|
|
47
|
+
Public Sub Attach()
|
|
48
|
+
If _attached Then Return
|
|
49
|
+
If _webView.CoreWebView2 Is Nothing Then
|
|
50
|
+
Throw New InvalidOperationException("Call EnsureCoreWebView2Async before Attach")
|
|
51
|
+
End If
|
|
52
|
+
_core = _webView.CoreWebView2
|
|
53
|
+
AddHandler _core.WebMessageReceived, AddressOf OnWebMessageReceived
|
|
54
|
+
_attached = True
|
|
55
|
+
End Sub
|
|
56
|
+
|
|
57
|
+
Private Async Sub OnWebMessageReceived(sender As Object, e As CoreWebView2WebMessageReceivedEventArgs)
|
|
58
|
+
' Web 側は postMessage(obj) で送るので WebMessageAsJson で受ける(TryGetWebMessageAsString は使わない)
|
|
59
|
+
Dim requestJson As String = e.WebMessageAsJson
|
|
60
|
+
Dim responseJson As String
|
|
61
|
+
Try
|
|
62
|
+
' HandleAsync は例外を投げず JSON-RPC エラーに変換する。ここは UI スレッドに戻ってくる
|
|
63
|
+
responseJson = Await _dispatcher.HandleAsync(requestJson)
|
|
64
|
+
Catch ex As Exception
|
|
65
|
+
Debug.WriteLine($"[WebViewBridge] unexpected: {ex}")
|
|
66
|
+
Return
|
|
67
|
+
End Try
|
|
68
|
+
If responseJson Is Nothing Then Return ' 通知(id なし)には応答しない
|
|
69
|
+
Post(responseJson)
|
|
70
|
+
End Sub
|
|
71
|
+
|
|
72
|
+
''' <summary>Host → Web の通知。どのスレッドから呼んでもよい</summary>
|
|
73
|
+
Public Sub Emit(method As String, params As Object) Implements IBridgeEmitter.Emit
|
|
74
|
+
Dim json As String = _dispatcher.BuildNotification(method, params)
|
|
75
|
+
Post(json)
|
|
76
|
+
End Sub
|
|
77
|
+
|
|
78
|
+
Private Sub Post(json As String)
|
|
79
|
+
If _core Is Nothing OrElse _webView.IsDisposed Then Return
|
|
80
|
+
If _webView.InvokeRequired Then
|
|
81
|
+
_webView.BeginInvoke(New Action(Sub() PostOnUiThread(json)))
|
|
82
|
+
Else
|
|
83
|
+
PostOnUiThread(json)
|
|
84
|
+
End If
|
|
85
|
+
End Sub
|
|
86
|
+
|
|
87
|
+
Private Sub PostOnUiThread(json As String)
|
|
88
|
+
If _core Is Nothing OrElse _webView.IsDisposed Then Return
|
|
89
|
+
Try
|
|
90
|
+
_core.PostWebMessageAsJson(json)
|
|
91
|
+
Catch ex As Exception
|
|
92
|
+
' ナビゲーション中・破棄中など。ブリッジ自体は落とさない
|
|
93
|
+
Debug.WriteLine($"[WebViewBridge] PostWebMessageAsJson failed: {ex.Message}")
|
|
94
|
+
End Try
|
|
95
|
+
End Sub
|
|
96
|
+
|
|
97
|
+
Public Sub Dispose() Implements IDisposable.Dispose
|
|
98
|
+
If _attached AndAlso _core IsNot Nothing Then
|
|
99
|
+
RemoveHandler _core.WebMessageReceived, AddressOf OnWebMessageReceived
|
|
100
|
+
End If
|
|
101
|
+
_attached = False
|
|
102
|
+
_core = Nothing
|
|
103
|
+
End Sub
|
|
104
|
+
End Class
|
|
105
|
+
|
|
106
|
+
End Namespace
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Option Strict On
|
|
2
|
+
|
|
3
|
+
Imports System
|
|
4
|
+
Imports System.IO
|
|
5
|
+
Imports System.Windows.Forms
|
|
6
|
+
Imports Microsoft.Web.WebView2.Core
|
|
7
|
+
Imports Microsoft.Web.WebView2.WinForms
|
|
8
|
+
Imports MyApp.Contract
|
|
9
|
+
Imports MyApp.Impl
|
|
10
|
+
Imports WebView2Bridge.Runtime
|
|
11
|
+
Imports WebView2Bridge.WinForms
|
|
12
|
+
|
|
13
|
+
''' <summary>
|
|
14
|
+
''' 窓 + WebView2 + Dispatcher。人間が書くホスト側はこのファイルだけ(WebViewBridge は gen が Bridge/ に書く)。
|
|
15
|
+
''' Debug かつ環境変数 WEBVIEW2_BRIDGE_DEV_URL があれば Vite dev server へ(通常 http://localhost:5173)
|
|
16
|
+
''' それ以外は exe 隣の wwwroot を https://app.local/ にマッピングして index.html を開く
|
|
17
|
+
''' VS のフォームデザイナは使っていない(コードで WebView2 を Dock=Fill している)。
|
|
18
|
+
''' デザイナで部品を足したくなったら MainForm.Designer.vb に分離する。
|
|
19
|
+
''' </summary>
|
|
20
|
+
Public Class MainForm
|
|
21
|
+
Inherits Form
|
|
22
|
+
|
|
23
|
+
Public Const VirtualHost As String = "app.local"
|
|
24
|
+
Public Const DevUrlEnvVar As String = "WEBVIEW2_BRIDGE_DEV_URL"
|
|
25
|
+
|
|
26
|
+
Private ReadOnly WebView As New WebView2() With {.Dock = DockStyle.Fill}
|
|
27
|
+
Private _bridge As WebViewBridge
|
|
28
|
+
|
|
29
|
+
Public Sub New()
|
|
30
|
+
Text = "MyApp"
|
|
31
|
+
Width = 1000
|
|
32
|
+
Height = 700
|
|
33
|
+
Controls.Add(WebView)
|
|
34
|
+
End Sub
|
|
35
|
+
|
|
36
|
+
Private Async Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
|
|
37
|
+
Try
|
|
38
|
+
' ユーザーデータは exe 隣ではなく LocalAppData に置く(Program Files 配下でも動くように)
|
|
39
|
+
Dim userDataFolder = Path.Combine(
|
|
40
|
+
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "MyApp")
|
|
41
|
+
Dim env = Await CoreWebView2Environment.CreateAsync(Nothing, userDataFolder)
|
|
42
|
+
Await WebView.EnsureCoreWebView2Async(env)
|
|
43
|
+
|
|
44
|
+
Dim core = WebView.CoreWebView2
|
|
45
|
+
core.Settings.AreDevToolsEnabled = True ' F12 で DevTools
|
|
46
|
+
core.Settings.IsStatusBarEnabled = False
|
|
47
|
+
|
|
48
|
+
' 契約の実装を Dispatcher に登録する(API を増やしたらここに Register を足す)
|
|
49
|
+
Dim dispatcher As New Dispatcher()
|
|
50
|
+
_bridge = New WebViewBridge(WebView, dispatcher)
|
|
51
|
+
Dim events As New BridgeEvents(_bridge)
|
|
52
|
+
dispatcher.Register(New CustomersApi(events))
|
|
53
|
+
_bridge.Attach()
|
|
54
|
+
|
|
55
|
+
Dim devUrl = DevServerUrl()
|
|
56
|
+
If devUrl IsNot Nothing Then
|
|
57
|
+
Text = $"MyApp - dev ({devUrl})"
|
|
58
|
+
core.Navigate(devUrl)
|
|
59
|
+
Else
|
|
60
|
+
Dim wwwroot = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot")
|
|
61
|
+
If Not File.Exists(Path.Combine(wwwroot, "index.html")) Then
|
|
62
|
+
' 無いフォルダを SetVirtualHostNameToFolderMapping に渡すと DirectoryNotFoundException になるので、ここで止める
|
|
63
|
+
MessageBox.Show(
|
|
64
|
+
$"wwwroot が見つかりません: {wwwroot}{Environment.NewLine}" &
|
|
65
|
+
"`pnpm build:web` の後に Host をビルドすると web/dist がコピーされます。" & Environment.NewLine &
|
|
66
|
+
$"開発中は環境変数 {DevUrlEnvVar}=http://localhost:5173 を設定して起動してください。",
|
|
67
|
+
Text, MessageBoxButtons.OK, MessageBoxIcon.Warning)
|
|
68
|
+
Return
|
|
69
|
+
End If
|
|
70
|
+
core.SetVirtualHostNameToFolderMapping(VirtualHost, wwwroot, CoreWebView2HostResourceAccessKind.Allow)
|
|
71
|
+
core.Navigate($"https://{VirtualHost}/index.html")
|
|
72
|
+
End If
|
|
73
|
+
Catch ex As Exception
|
|
74
|
+
MessageBox.Show(ex.ToString(), "WebView2 の初期化に失敗しました", MessageBoxButtons.OK, MessageBoxIcon.Error)
|
|
75
|
+
End Try
|
|
76
|
+
End Sub
|
|
77
|
+
|
|
78
|
+
''' <summary>Debug ビルドで WEBVIEW2_BRIDGE_DEV_URL が設定されていればその URL、それ以外は Nothing</summary>
|
|
79
|
+
Private Shared Function DevServerUrl() As String
|
|
80
|
+
#If DEBUG Then
|
|
81
|
+
Dim url = Environment.GetEnvironmentVariable(DevUrlEnvVar)
|
|
82
|
+
If Not String.IsNullOrWhiteSpace(url) Then Return url.Trim()
|
|
83
|
+
#End If
|
|
84
|
+
Return Nothing
|
|
85
|
+
End Function
|
|
86
|
+
|
|
87
|
+
Private Sub MainForm_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
|
|
88
|
+
_bridge?.Dispose()
|
|
89
|
+
End Sub
|
|
90
|
+
End Class
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
+
<PropertyGroup>
|
|
3
|
+
<OutputType>WinExe</OutputType>
|
|
4
|
+
<TargetFramework>net48</TargetFramework>
|
|
5
|
+
<UseWindowsForms>true</UseWindowsForms>
|
|
6
|
+
<RootNamespace>MyApp.Host</RootNamespace>
|
|
7
|
+
<MyType>WindowsForms</MyType>
|
|
8
|
+
<StartupObject>MyApp.Host.Program</StartupObject>
|
|
9
|
+
<!--
|
|
10
|
+
AnyCPU のままだと Windows の dotnet build で exe は 64 ビット、WebView2Loader.dll は x86 だけ出力されて
|
|
11
|
+
起動時に BadImageFormatException になる(SDK が .NET Framework exe に win7-x86 を仮定し、WebView2 の
|
|
12
|
+
ローダーコピーと食い違う)。x64 に固定して揃える。32 ビット専用の DB ドライバ等が要るなら x86 にする。
|
|
13
|
+
-->
|
|
14
|
+
<PlatformTarget>x64</PlatformTarget>
|
|
15
|
+
<WebDistDir>$(MSBuildProjectDirectory)\..\..\web\dist</WebDistDir>
|
|
16
|
+
</PropertyGroup>
|
|
17
|
+
<ItemGroup>
|
|
18
|
+
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.4191.47" />
|
|
19
|
+
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
|
20
|
+
<ProjectReference Include="..\MyApp.Contract\MyApp.Contract.vbproj" />
|
|
21
|
+
<ProjectReference Include="..\MyApp.Impl\MyApp.Impl.vbproj" />
|
|
22
|
+
</ItemGroup>
|
|
23
|
+
<Target Name="CopyWebDist" AfterTargets="Build" Condition="Exists('$(WebDistDir)\index.html')">
|
|
24
|
+
<ItemGroup><WebDistFiles Include="$(WebDistDir)\**\*" /></ItemGroup>
|
|
25
|
+
<RemoveDir Directories="$(OutDir)wwwroot" />
|
|
26
|
+
<Copy SourceFiles="@(WebDistFiles)" DestinationFiles="@(WebDistFiles->'$(OutDir)wwwroot\%(RecursiveDir)%(Filename)%(Extension)')" />
|
|
27
|
+
</Target>
|
|
28
|
+
</Project>
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Option Strict On
|
|
2
|
+
Imports System
|
|
3
|
+
Imports System.Windows.Forms
|
|
4
|
+
|
|
5
|
+
Friend Module Program
|
|
6
|
+
<STAThread>
|
|
7
|
+
Friend Sub Main()
|
|
8
|
+
Application.EnableVisualStyles()
|
|
9
|
+
Application.SetCompatibleTextRenderingDefault(False)
|
|
10
|
+
Application.Run(New MainForm())
|
|
11
|
+
End Sub
|
|
12
|
+
End Module
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Option Strict On
|
|
2
|
+
|
|
3
|
+
Imports System
|
|
4
|
+
Imports System.Collections.Generic
|
|
5
|
+
Imports System.Linq
|
|
6
|
+
Imports System.Threading.Tasks
|
|
7
|
+
Imports MyApp.Contract
|
|
8
|
+
|
|
9
|
+
''' <summary>
|
|
10
|
+
''' "customers.*" の実装。人間が書く VB はこのようなクラスと MainForm だけ。
|
|
11
|
+
''' ここで SqlClient / Oracle.ManagedDataAccess 等を使い DB と話す。
|
|
12
|
+
''' 振る舞いは web/src/bridge.ts のモックと揃えておく(ブラウザ単体と WebView2 内で同じ動きにする)。
|
|
13
|
+
''' </summary>
|
|
14
|
+
Public Class CustomersApi
|
|
15
|
+
Implements ICustomersApi
|
|
16
|
+
|
|
17
|
+
Private ReadOnly _events As BridgeEvents
|
|
18
|
+
|
|
19
|
+
Private Shared ReadOnly Data As Customer() = {
|
|
20
|
+
New Customer With {.Id = "1", .Name = "山田商事"},
|
|
21
|
+
New Customer With {.Id = "2", .Name = "佐藤工業"},
|
|
22
|
+
New Customer With {.Id = "3", .Name = "鈴木電機"}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
''' <param name="events">進捗などのイベント発行先。Nothing なら発行しない</param>
|
|
26
|
+
Public Sub New(events As BridgeEvents)
|
|
27
|
+
_events = events
|
|
28
|
+
End Sub
|
|
29
|
+
|
|
30
|
+
Public Async Function List(req As CustomersListRequest) As Task(Of CustomersListResponse) Implements ICustomersApi.List
|
|
31
|
+
_events?.Progress(New ProgressEvent With {.Percent = 0})
|
|
32
|
+
Await Task.Delay(100).ConfigureAwait(False)
|
|
33
|
+
Dim kw = If(req.Keyword, "")
|
|
34
|
+
Dim hit = Data.Where(Function(c) c.Name.Contains(kw)).ToList()
|
|
35
|
+
_events?.Progress(New ProgressEvent With {.Percent = 100})
|
|
36
|
+
Return New CustomersListResponse With {.Items = hit}
|
|
37
|
+
End Function
|
|
38
|
+
End Class
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
+
<PropertyGroup>
|
|
3
|
+
<TargetFramework>net48</TargetFramework>
|
|
4
|
+
<RootNamespace>MyApp.Impl</RootNamespace>
|
|
5
|
+
</PropertyGroup>
|
|
6
|
+
<ItemGroup>
|
|
7
|
+
<ProjectReference Include="..\MyApp.Contract\MyApp.Contract.vbproj" />
|
|
8
|
+
</ItemGroup>
|
|
9
|
+
</Project>
|