| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
共有 622 人关注过本帖
标题:吹班webview2 vcx封装,AI回答,附生成的PRG脚本
只看楼主 加入收藏
ykxby001
Rank: 4
等 级:业余侠客
帖 子:127
专家分:236
注 册:2023-7-6
结帖率:50%
收藏
已结贴  问题点数:20 回复次数:7 
吹班webview2 vcx封装,AI回答,附生成的PRG脚本
要将6个VFP PRG文件封装为VCX类(VFP可视化类库),需遵循**模块化拆分、事件绑定、API封装、实例化管理**四大核心原则,将分散的常量定义、事件处理、类方法、API声明、演示逻辑整合为可复用的VCX类库。以下是详细步骤和实现方案:

### 一、核心思路
VFP的VCX类库本质是“类的容器”,支持封装**属性、方法、事件**。需将6个PRG文件的功能按“基础依赖→核心类→扩展功能”分层迁移,最终形成:
1. **基础常量类**:存储`myWebView_h.prg`中的宏定义(消息ID、事件ID、设置ID等);
2. **核心WebView类**:封装`myWebView_class.prg`的核心方法(导航、JS执行、Cookie管理等)和`myWebView_event.prg`的事件处理;
3. **API声明类**:整合`myWebView_api.prg`的外部API声明(避免重复声明);
4. **演示工具类**:可选,封装`myWebView_proc.prg`的演示函数(如消息通信、DevTools调用);
5. **入口初始化类**:替代`myWebView.txt`的全局初始化逻辑(环境配置、表单加载)。

### 二、分步实现步骤

#### 步骤1:创建VCX类库容器
1. 打开VFP,新建“类库”(文件→新建→类→保存为`myWebView.vcx`);
2. 在VCX中创建5个类(基于不同父类),对应上述分层:
   | 类名                | 父类          | 作用                          | 依赖PRG文件               |
   |---------------------|---------------|-------------------------------|--------------------------|
   | `cls_WebView_Const` | `Custom`      | 存储全局常量(宏定义)        | `myWebView_h.prg`        |
   | `cls_WebView_Core`  | `Session`     | 核心WebView功能+事件处理      | `myWebView_class.prg`、`myWebView_event.prg` |
   | `cls_WebView_API`   | `Custom`      | 外部API声明(静态类)         | `myWebView_api.prg`      |
   | `cls_WebView_Demo`  | `Custom`      | 演示功能(可选)              | `myWebView_proc.prg`     |
   | `cls_WebView_Init`  | `Custom`      | 初始化环境(全局配置)        | `myWebView.txt`          |

#### 步骤2:迁移基础常量类(cls_WebView_Const)
`myWebView_h.prg`的宏定义(`#define`)需转为类的**属性**(常量属性),避免全局宏污染。
1. 打开`cls_WebView_Const`的“类设计器”;
2. 添加“属性”(菜单→类→新建属性),属性名与宏名一致,属性值为宏定义的值,示例:
   | 属性名(常量)                | 属性值 | 备注                          |
   |-------------------------------|--------|-------------------------------|
   | `MY_WEBVIEW_MESSAGE`          | `0x401`| 自定义消息ID                  |
   | `WAIT_TIMEOUT`                | `20`   | 同步等待时间(秒)            |
   | `EventId_OnNavigationStarting`| `1`    | 导航开始事件ID                |
   | `IS_SCRIPT_ENABLED`           | `1`    | JS脚本启用设置ID              |
   | `COREWEBVIEW2_HOST_RESOURCE_ACCESS_KIND_ALLOW` | `1` | 跨源资源允许访问              |
3. 设所有属性为**只读**(属性窗口→“只读”设为`.T.`),模拟常量特性。

#### 步骤3:封装核心WebView类(cls_WebView_Core)
此类是核心,需整合`myWebView_class.prg`的方法和`myWebView_event.prg`的事件处理,关键是**事件与方法绑定**和**内存管理**。

##### 3.1 迁移属性(来自myWebView_class.prg)
在`cls_WebView_Core`中添加属性:
| 属性名           | 类型    | 初始值       | 作用                          |
|------------------|---------|--------------|-------------------------------|
| `form`           | `Object`| `.NULL.`     | 关联的表单对象                |
| `Version`        | `String`| `""`         | WebView版本号                 |
| `isDynamicPage`  | `Logical`| `.F.`       | 是否监听动态页面生成          |
| `hWebView`       | `Numeric`| `0`          | WebView窗口句柄(内部维护)   |

##### 3.2 迁移核心方法(来自myWebView_class.prg)
将`myWebView_class.prg`的函数转为`cls_WebView_Core`的**方法**,需注意:
- 方法名与原函数名一致(如`Navigate`、`ExecuteScript`);
- 依赖`cls_WebView_API`的API声明(通过`THISFORM`或`NEWOBJECT`调用);
- 处理字符串编码(如`STRCONV`、`CTOBIN`)和内存释放(如`CoTaskMemFree`)。

**示例方法:Navigate(导航到URL)**
```foxpro
FUNCTION Navigate(cUrl, nWaitTimeout)
    * 依赖cls_WebView_API的WebView_Navigate声明
    LOCAL oAPI, cWideUrl
    oAPI = NEWOBJECT("cls_WebView_API", "myWebView.vcx")
    cWideUrl = STRCONV(cUrl + CHR(0), 5)  && 转为Unicode字符串(LPCWSTR)
    oAPI.WebView_Navigate(cWideUrl, nWaitTimeout)
    RELEASE oAPI
ENDFUNC
```

**示例方法:getMemData(内存数据读取,核心工具方法)**
```foxpro
FUNCTION getMemData(pData)
    * 读取指定内存地址的Unicode字符串(对应原getMemData函数)
    LOCAL nLen, cData
    oAPI = NEWOBJECT("cls_WebView_API", "myWebView.vcx")
    nLen = oAPI.apiWcslenPtr(pData)  && 调用msvcrt的wcslen获取字符串长度
    cData = SYS(2600, pData, nLen * 2)  && 读取Unicode字节流(每个字符2字节)
    cData = STRCONV(cData, 6)  && 转为ANSI字符串
    oAPI.apiCoTaskMemFree(pData)  && 释放内存(遵循WebView2 API约定)
    RELEASE oAPI
    RETURN cData
ENDFUNC
```

##### 3.3 绑定事件处理(来自myWebView_event.prg)
`myWebView_event.prg`的事件函数(如`onNavigationStarting`)需转为`cls_WebView_Core`的**事件方法**,并通过`onMessage`方法分发事件。
1. 在`cls_WebView_Core`中添加自定义事件(菜单→类→新建事件):
   - `OnNavigationStarting`、`OnContentLoading`、`OnWebMessageReceived`等(与事件ID对应);
2. 实现`onMessage`方法(事件分发核心),替代原PRG的`onMessage`函数:
```foxpro
FUNCTION onMessage(hWnd, uMsg, wParam, lParam)
    * wParam=事件ID,lParam=事件参数(如URL地址、状态码)
    LOCAL oConst
    oConst = NEWOBJECT("cls_WebView_Const", "myWebView.vcx")
   
    DO CASE
        CASE wParam == oConst.EventId_OnNavigationStarting
            THIS.OnNavigationStarting(lParam)  && 触发自定义事件
        CASE wParam == oConst.EventId_OnWebMessageReceived
            THIS.OnWebMessageReceived(lParam)
        * 其他事件(如OnNavigationCompleted、OnDownloadStarting)同理
    ENDCASE
    RELEASE oConst
ENDFUNC
```
3. 实现事件具体逻辑(如`OnNavigationStarting`):
```foxpro
FUNCTION OnNavigationStarting(lParam)
    * 原onNavigationStarting函数逻辑迁移
    LOCAL pUrl, cUrl
    pUrl = CTOBIN(SYS(2600, lParam, 4), "4rs")  && 获取URL地址的内存指针
    cUrl = THIS.getMemData(pUrl)  && 读取URL字符串
    IF NOT ISNULL(THIS.form) AND NOT ISNULL(THIS.form.urlText)
        THIS.form.urlText.Value = cUrl  && 同步到表单的URL输入框
    ENDIF
    SYS(2600, lParam, 4, BINTOC(0, "4rs"))  && 返回0允许导航
ENDFUNC
```

#### 步骤4:封装API声明类(cls_WebView_API)
`myWebView_api.prg`的`DECLARE`语句需整合为`cls_WebView_API`的**静态方法**(或在类初始化时批量声明),避免全局API冲突。
1. 在`cls_WebView_API`的`Init`事件中声明所有外部API:
```foxpro
PROCEDURE Init
    * 声明系统API(msvcrt、user32、kernel32等)
    DECLARE long wcslen IN msvcrt as apiWcslenPtr long
    DECLARE long GetParent IN user32 as apiGetParent long
    DECLARE long Sleep IN kernel32 as apiSleep long
    DECLARE long CoTaskMemFree IN ole32 as apiCoTaskMemFree long
   
    * 声明WebView2核心API(myWebViewDll.dll)
    DECLARE long WebView_Create IN myWebViewDll long,long,long,long,long,long,long
    DECLARE long WebView_Navigate IN myWebViewDll string,double
    DECLARE long WebView_ExecuteScript IN myWebViewDll string,long,long
    * ... 其他所有WebView API声明(按原PRG顺序完整迁移)
ENDPROC
```
2. 提供API调用的“代理方法”(可选,简化外部调用):
```foxpro
FUNCTION WebView_Navigate(cUrl, nTimeout)
    * 直接调用声明的API(确保API已在Init中声明)
    RETURN WebView_Navigate(cUrl, nTimeout)
ENDFUNC
```

#### 步骤5:封装演示工具类(cls_WebView_Demo,可选)
`myWebView_proc.prg`的演示函数(如`demo_VfpSendToWeb`、`demo_WebRunVfp`)可封装为`cls_WebView_Demo`的方法,便于快速测试核心功能。
示例:`demo_VfpSendToWeb`(VFP向Web发送JSON消息)
```foxpro
FUNCTION demo_VfpSendToWeb(oForm)
    * oForm:传入的宿主表单对象
    LOCAL oWebView, htmlCode
    oWebView = oForm.web  && 假设表单上的WebView实例名为web
   
    * 构建测试HTML(原PRG中的TEXT块迁移)
    TEXT TO htmlCode TEXTMERGE NOSHOW PRETEXT 7
        <!DOCTYPE html>
        <html>
        <head><meta charset="gb2312"><title>VFP→Web消息测试</title>
        <script>
        window.chrome.webview.addEventListener('message', arg => {
            if ("SetColor" in arg.data) {
                document.getElementById("colorable").style.color = arg.data.SetColor;
            }
        });
        </script></head>
        <body><p id="colorable">等待VFP消息...</p></body>
        </html>
    ENDTEXT
   
    * 调用核心类的方法导航HTML
    oWebView.NavigateHtml(htmlCode, oWebView.Const.WAIT_TIMEOUT)
    oWebView.waitPause(3)
    oWebView.PostMessageAsJson('{"SetColor":"red"}')  && 发送JSON消息
ENDFUNC
```

#### 步骤6:封装初始化类(cls_WebView_Init)
替代`myWebView.txt`的全局初始化逻辑,避免全局变量污染,支持按需初始化。
```foxpro
FUNCTION InitEnvironment()
    * 1. 环境配置(原PRG中的SET语句)
    CLEAR ALL
    CLOSE TABLES ALL
    SET TALK OFF
    SET SAFETY OFF
    SET DELETED ON
    SET DEFAULT TO ADDBS(JUSTPATH(SYS(16)))  && 设为VCX所在路径
   
    * 2. 声明API(通过API类初始化)
    NEWOBJECT("cls_WebView_API", "myWebView.vcx")  && 触发API声明
   
    * 3. 禁止电源管理(原apiSetThreadExecutionState调用)
    oAPI = NEWOBJECT("cls_WebView_API", "myWebView.vcx")
    oAPI.apiSetThreadExecutionState(0x80000003)
    RELEASE oAPI
   
    * 4. 返回初始化状态
    RETURN .T.
ENDFUNC

FUNCTION DestroyEnvironment()
    * 恢复环境配置
    oAPI = NEWOBJECT("cls_WebView_API", "myWebView.vcx")
    oAPI.apiSetThreadExecutionState(0x80000000)  && 恢复电源管理
    RELEASE oAPI
   
    SET DELETED OFF
    CLOSE TABLES ALL
    CLEAR ALL
    RETURN .T.
ENDFUNC
```

#### 步骤7:整合入口表单(替代myWebView.scx)
原`myWebView.txt`中加载的`myWebView.scx`需改为“依赖VCX类”的表单:
1. 新建表单,在表单的`Init`事件中初始化WebView核心类:
```foxpro
PROCEDURE Init
    * 1. 初始化环境
    oInit = NEWOBJECT("cls_WebView_Init", "myWebView.vcx")
    oInit.InitEnvironment()
    RELEASE oInit
   
    * 2. 创建核心WebView实例
    THIS.web = NEWOBJECT("cls_WebView_Core", "myWebView.vcx")
    THIS.web.form = THIS  && 关联表单对象
   
    * 3. 创建WebView窗口(假设表单上有容器控件container1)
    LOCAL hParent, hMsg, nMsg
    hParent = THIS.container1.HWnd  && 父窗口句柄(容器控件)
    hMsg = THIS.HWnd  && 接收消息的窗口句柄(表单)
    nMsg = NEWOBJECT("cls_WebView_Const", "myWebView.vcx").MY_WEBVIEW_MESSAGE
    THIS.web.hWebView = THIS.web.Create(hParent, hMsg, nMsg, 0, 0, THIS.container1.Width, THIS.container1.Height)
ENDPROC
```
2. 在表单的`Destroy`事件中释放资源:
```foxpro
PROCEDURE Destroy
    * 1. 销毁WebView实例
    THIS.web.Destroy()  && 需在cls_WebView_Core中实现WebView_Destroy调用
   
    * 2. 恢复环境
    oInit = NEWOBJECT("cls_WebView_Init", "myWebView.vcx")
    oInit.DestroyEnvironment()
    RELEASE oInit
ENDPROC
```

### 三、关键注意事项
1. **API声明的唯一性**:`cls_WebView_API`的`Init`事件中声明的API是“全局有效”的,需确保仅初始化一次(避免重复声明报错);
2. **内存释放**:WebView2 API中返回`LPWSTR`的函数(如`WebView_GetTopUrl`),必须通过`CoTaskMemFree`释放内存(已在`getMemData`中处理);
3. **事件参数传递**:`lParam`等指针参数需通过`SYS(2600)`操作内存,确保VFP与WebView2的内存地址交互正确;
4. **编码一致性**:所有传递给WebView2 API的字符串(如URL、JS代码)需转为Unicode(`STRCONV(cStr,5)`),接收时转回ANSI(`STRCONV(cStr,6)`);
5. **依赖文件**:确保`myWebViewDll.dll`(WebView2核心库)与VCX/表单在同一目录,或在系统PATH中。

### 四、最终使用方式
封装完成后,其他项目可直接引用`myWebView.vcx`,快速创建WebView实例:
```foxpro
* 1. 引用VCX类库
SET CLASSLIB TO myWebView.vcx ADDITIVE

* 2. 初始化环境
oInit = NEWOBJECT("cls_WebView_Init")
oInit.InitEnvironment()

* 3. 创建WebView核心实例
oWeb = NEWOBJECT("cls_WebView_Core")
oWeb.form = THISFORM  && 关联当前表单

* 4. 导航到URL
oWeb.Navigate("https://www.baidu.com", 20)

* 5. 执行JS代码
oWeb.ExecuteScript("alert('Hello from VFP!')", .F., 0)

* 6. 释放资源
oWeb.Destroy()
oInit.DestroyEnvironment()
```

### 五、优势与优化
1. **可复用性**:VCX类库可被多个VFP项目引用,无需重复复制PRG文件;
2. **可维护性**:模块化拆分后,常量、方法、事件分离,修改时无需改动全局代码;
3. **扩展性**:新增功能(如WebSocket支持、文件上传)可直接在`cls_WebView_Core`中添加方法;
4. **隐藏实现细节**:外部项目仅需调用`Navigate`、`ExecuteScript`等公开方法,无需关注API声明和内存管理。

通过以上步骤,6个分散的PRG文件将被整合为结构清晰、可复用的VCX类库,完全符合VFP的面向对象开发规范。
搜索更多相关主题的帖子: 事件 long 声明 API PRG 
2026-01-08 11:25
schtg
Rank: 13Rank: 13Rank: 13Rank: 13
来 自:USA
等 级:贵宾
威 望:67
帖 子:2452
专家分:4937
注 册:2012-2-29
收藏
得分:4 
2026-01-09 06:24
DANSON
Rank: 2
等 级:论坛游民
帖 子:111
专家分:83
注 册:2025-10-4
收藏
得分:4 
2026-01-09 08:24
wxzd123
Rank: 3Rank: 3
等 级:论坛游侠
帖 子:485
专家分:104
注 册:2012-9-6
收藏
得分:4 
期待
2026-01-10 11:12
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:11129
专家分:43605
注 册:2014-5-20
收藏
得分:4 
在C++代码就封装成 WebView2 类(包括属性、方法和事件)。
vfp 调用 WebView_Create() 创建一个 WebView2 类实例并得到这个实例的类指针。
vfp 声明调用 WebView2 api 时带上这个指针,就可以通过不同实例的类指针执行各自实例的属性和方法并触发各自的事件。

vfp写一个表单类,在表单类初始化过程中创建一个WebView2类。用这个表单类每创建一个表单就可得到一个 WebView2 类实例的表单页面,单进程多页面的 WebView2 应该就可以实现。



[此贴子已经被作者于2026-1-10 14:36编辑过]

2026-01-10 14:28
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:11129
专家分:43605
注 册:2014-5-20
收藏
得分:0 
VCX文件类与prg文件类没什么本质区别。只是创建类对象的语句不同,执行时都是 类对象.属性/方法/事件
2026-01-10 14:40
吹水佬
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:版主
威 望:451
帖 子:11129
专家分:43605
注 册:2014-5-20
收藏
得分:0 
myWebViewDll.dll 这个 WebView2 api 没定义为类,应该不支持多实例,多个vfp的类实例使用很可能会发生冲突。

2026-01-10 15:00
ykxby001
Rank: 4
等 级:业余侠客
帖 子:127
专家分:236
注 册:2023-7-6
收藏
得分:0 
不会玩呢,有些东西能会用就不错了,哈哈哈。。。吹班一看就是顶级专业人士,膜拜大佬。
2026-01-10 17:45
快速回复:吹班webview2 vcx封装,AI回答,附生成的PRG脚本
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.017392 second(s), 8 queries.
Copyright©2004-2026, BCCN.NET, All Rights Reserved