Closed hbl917070 closed 1 year ago
Can only return primitive types,int,string,number。
Can only return primitive types,int,string,number。
In net4.7 or net4.8, there are no such restrictions
Can only return primitive types,int,string,number。
In net4.7 or net4.8, there are no such restrictions
I'm sorry I didn't read it carefully. I tried to return object before, but all failed. You have given me confidence here. Use your code for normal use in my environment, here is my environment
OS: Windows 10 18363
WebView2 version: 1.0.1370.28
User agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36 Edg/109.0.1518.78
Can I call Stream.Write(byte[] buffer, int offset, int count) in javaScript?
Can I call Stream.Write(byte[] buffer, int offset, int count) in javaScript?
I don't think it will work to use stream.Write
directly in js.
Although js can obtain a C# Object and use its properties and methods, js does not have the same Method Overloading feature as C#.
If there is such a need, it is still recommended to create a function inside C# to handle this matter and then expose it to js.
Also, byte[] seems to have the problem of transformation failure, you can try to use string instead.
js
var fileStream = await WV_Stream.NewFileStream("D:\\output.jpg");
var bytes = await WV_Stream.GetBytes("D:\\input.jpg");
await WV_Stream.WriteStream(fileStream, JSON.stringify(bytes), 0, bytes.length);
fileStream.Dispose();
Form1.cs
using Microsoft.Web.WebView2.WinForms;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WV_netToJs {
public partial class Form1 : Form {
public WebView2 wv2;
public Form1() {
InitializeComponent();
Init();
}
public async void Init() {
wv2 = new WebView2();
this.Controls.Add(wv2);
wv2.Dock = DockStyle.Fill;
await wv2.EnsureCoreWebView2Async();
wv2.CoreWebView2.AddHostObjectToScript("WV_Stream", new WV_Stream());
string path = @"C:\Users\u1\Desktop\netToJs.html";
wv2.Source = new Uri(path);
}
}
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class WV_Stream {
public FileStream NewFileStream(string path) {
FileStream fs = File.Create(path) ;
return fs;
}
public byte[] GetBytes(string path) {
byte[] result;
using (FileStream fs = File.OpenRead(path)) {
result = new byte[fs.Length];
fs.Read(result, 0, (int)fs.Length);
}
return result;
}
public void WriteStream(Stream fs, string buffer, int offset, int count) {
fs.Write(StringToBytes(buffer), offset, count);
}
private byte[] StringToBytes(string data) {
string[] splitData = data.Trim(new char[] { '[', ']' }).Split(',');
byte[] result = new byte[splitData.Length];
for (int i = 0; i < splitData.Length; i++) {
result[i] = byte.Parse(splitData[i]);
}
return result;
}
}
}
Of course, the prerequisite for doing this is to be able to pass C# objects to js. In net7, we can only pass primitive types to js. This gives me a headache.
Hi @hbl917070, thanks for your feedback! I'm looking into this issue, this really a issue and I'm tring to find the root cause. If there is any discovery, I will update the progress here.
Hi @hbl917070 , I have identified the problem:
In .NET Framework, the [ComVisible(true)] attribute is used to indicate that a type or assembly is visible to COM interop, allowing COM clients to access and use the types. This attribute is commonly used when developing components that need to be consumed by COM applications.
However, in .NET 5 and later versions (including .NET 6), the default behavior for ComVisible is changed. By default, types and members are no longer visible to COM interop unless explicitly marked with [ComVisible(true)]. This change was introduced to align with the more secure and modern development practices and to reduce the surface area for potential security vulnerabilities.
As a workaround, you can wrap the System.IO.Stream:
#pragma warning disable CS0618
[ClassInterface(ClassInterfaceType.AutoDual)]
#pragma warning restore CS0618
[ComVisible(true)]
public class MyStream
{
public Stream stream { get; set; }
public MyStream(Stream stream)
{
this.stream = stream;
}
}
And use the MyStream
instead of Stream:
public MyStream GetStream(string path)
{
System.IO.Stream fs = File.OpenRead(path);
return new MyStream(fs);
}
public void SaveStream(MyStream fs, string path)
{
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write))
{
fs.stream.CopyTo(fileStream);
}
}
[ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class WV_Stream { public Stream GetStream(string path) { Stream fs = File.OpenRead(path); return fs; } public void SaveStream(Stream fs,string path) { using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) { fs.CopyTo(fileStream); } } }
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class testType
{
public void test(Object param)
{
Console.WriteLine(param);
}
}
How do you get an object that javascript passes in? Call C# in the console, passing object
C# will get a System.__ComObject type parameter, this type I looked up a lot of information, can not get the key and value inside. Can you help me
Can I call Stream.Write(byte[] buffer, int offset, int count) in javaScript?
I don't think it will work to use
stream.Write
directly in js. Although js can obtain a C# Object and use its properties and methods, js does not have the same Method Overloading feature as C#. If there is such a need, it is still recommended to create a function inside C# to handle this matter and then expose it to js.Also, byte[] seems to have the problem of transformation failure, you can try to use string instead.
js
var fileStream = await WV_Stream.NewFileStream("D:\\output.jpg"); var bytes = await WV_Stream.GetBytes("D:\\input.jpg"); await WV_Stream.WriteStream(fileStream, JSON.stringify(bytes), 0, bytes.length); fileStream.Dispose();
Form1.cs
using Microsoft.Web.WebView2.WinForms; using System; using System.IO; using System.Runtime.InteropServices; using System.Windows.Forms; namespace WV_netToJs { public partial class Form1 : Form { public WebView2 wv2; public Form1() { InitializeComponent(); Init(); } public async void Init() { wv2 = new WebView2(); this.Controls.Add(wv2); wv2.Dock = DockStyle.Fill; await wv2.EnsureCoreWebView2Async(); wv2.CoreWebView2.AddHostObjectToScript("WV_Stream", new WV_Stream()); string path = @"C:\Users\u1\Desktop\netToJs.html"; wv2.Source = new Uri(path); } } [ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class WV_Stream { public FileStream NewFileStream(string path) { FileStream fs = File.Create(path) ; return fs; } public byte[] GetBytes(string path) { byte[] result; using (FileStream fs = File.OpenRead(path)) { result = new byte[fs.Length]; fs.Read(result, 0, (int)fs.Length); } return result; } public void WriteStream(Stream fs, string buffer, int offset, int count) { fs.Write(StringToBytes(buffer), offset, count); } private byte[] StringToBytes(string data) { string[] splitData = data.Trim(new char[] { '[', ']' }).Split(','); byte[] result = new byte[splitData.Length]; for (int i = 0; i < splitData.Length; i++) { result[i] = byte.Parse(splitData[i]); } return result; } } }
Of course, the prerequisite for doing this is to be able to pass C# objects to js. In net7, we can only pass primitive types to js. This gives me a headache.
In my tests, I passed Uint8Array and ArrayBuffer data. __ComObject in C#, which is the closest type to byte data
The only good news is that you can pass Array[int]. In C# it can keep an array state
Hi @yunate ,
The solution of enclosing the stream in another class can work smoothly.
Since this solution works, is it possible to use the Inheritance solution?
The following code works in net4.8, but gets an error in net7
Error (0x13D) while retrieving error. (0x80131509) at <anonymous>:1:28550
#pragma warning disable CS0618
[ClassInterface(ClassInterfaceType.AutoDual)]
#pragma warning restore CS0618
[ComVisible(true)]
public class WV_Form {
public MyForm NewForm() {
var w = new MyForm();
w.Show();
return w;
}
}
MyForm
#pragma warning disable CS0618
[ClassInterface(ClassInterfaceType.AutoDual)]
#pragma warning restore CS0618
[ComVisible(true)]
public class MyForm : Form {
}
@hbl917070, If the parent class is not visible, and the child class is marked as ComVisible(true), the child class will not be visible. In COM interop, the visibility of a class is determined by the visibility of its members and their accessibility from COM. If the parent class is not visible, its members will not be accessible from COM, including any child classes derived from it. So, this code may work well in net4.8, but will not in net7.
[ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class WV_Stream { public Stream GetStream(string path) { Stream fs = File.OpenRead(path); return fs; } public void SaveStream(Stream fs,string path) { using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) { fs.CopyTo(fileStream); } } }
[ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class testType { public void test(Object param) { Console.WriteLine(param); } }
How do you get an object that javascript passes in? Call C# in the console, passing object
C# will get a System.__ComObject type parameter, this type I looked up a lot of information, can not get the key and value inside. Can you help me
Hi @DebugCodeBody, {a:1}
will be seem as an object, on the C # side, we did not perform any special processing on this, as it is treated as a Dispatch object (which can be understood as an interface) , and if you want to handle this, you can convert it into a string and then pass it to C#.
[ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class WV_Stream { public Stream GetStream(string path) { Stream fs = File.OpenRead(path); return fs; } public void SaveStream(Stream fs,string path) { using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) { fs.CopyTo(fileStream); } } }
[ClassInterface(ClassInterfaceType.AutoDual)] [ComVisible(true)] public class testType { public void test(Object param) { Console.WriteLine(param); } }
How do you get an object that javascript passes in? Call C# in the console, passing object C# will get a System.__ComObject type parameter, this type I looked up a lot of information, can not get the key and value inside. Can you help me
Hi @DebugCodeBody,
{a:1}
will be seem as an object, on the C # side, we did not perform any special processing on this, as it is treated as a Dispatch object (which can be understood as an interface) , and if you want to handle this, you can convert it into a string and then pass it to C#.
I already know what to do, thank you for your reply
Here is a simple test Get a Stream Object from C# using js, save it as a js variable Then send this js variable (C# Object) back to C#
netToJs.html
Form1.cs
This code works fine in net4.7 or net4.8,
But in net6 or net7,
await WV_Stream.GetStream("D:\\a.jpg")
returnsnull
I'm not sure if this is a limitation due to security concerns or simply a bug.
OS:
Windows 10 19045.2965
WebView2 version:1.0.1774.30
User agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50