Subclassing 笔记

本文详细介绍了窗口子类化(window subclassing)的概念及其在Win32应用程序中的实现方法。通过改变窗口过程函数(WndProc)的地址,可以实现对特定窗口消息的拦截和自定义处理,而不影响同一类别的其他窗口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    window class中最重要的是他的lpfnWndProc,他处理由该类别产生的窗口的所有信息。在RegisterClassEx返回之前,系统配置一块内存来记录这个类别的相关信息,包括lpfnWndProc的值.注意是系统完成的!!!

   每当我们的程序用这个类别产生新的窗口,系统又会配置有新的窗口的内存块,用来存放与窗口相关的信息.其中也包括窗口处理函数WndProc的地址,而他是由系统从window class中复制过来的.当任何信息被分配到WndProc时,系统根据  窗口的内存块中的WndProc地址来调用窗口函数,而不是类别中的lpfnWndProc.类别中的那个只是用来做复制用的.

   为了subclassing一个窗口,必须改变窗口的内存块中的窗口函数地址,使他指向一个新的函数地址.但是他不会影响到别的从同一个类别中产生的窗口,因为只是改变了这个类别的其中一个实例的窗口处理地址,而不是改变的是这个类别中的lpfnWndProc.其中用来代替原来的窗口函数的新的函数必须和原来的函数的形式是一样的,也就是标准的窗口函数的样子
LRESULT WINAPI SubClassWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);

    一旦你改变了原来的窗口函数的地址用了新的代替,你可能要做下面的动作:
1.原封不动的调用原来的那个窗口函数。
2.限制只有某些消息可以传入原来的窗口函数.例如,你希望edit控件停止接受数字,你必须处理WM_CHAR,检验(wParam)看看否有0-9之间的数字.如果是,什么也不做就立即返回,如果不是,就将消息传给愿函数。
3.修改某个消息,然后继续传给原来的窗口处理函数。如果你希望combobox只接受大写字母,你应该处理每一个WM_CHAR,将wParam转换为大写(使用CharUpper函数),然后再将它传给原来的窗口处理函数.

    其实要对某个窗口做subclassing,只要取得原来的窗口处理函数代码,并用自己的代替
SubclassWindow宏可以做到,并返回原来的窗口函数地址,这个原来的窗口函数地址,要在新的窗口处理函数中使用,而SubclassWindow在另一个函数中调用,那么两个函数之间的变量是不可见得,除非将这个放原来的窗口函数的变量,声明成文件作用域,不然就使用hwnd的属性,因为,在win32中hwnd基本上都是,在不同函数间传来传去的,那么具体做法:

在调用subclassing的函数中
   SetProp(hwndEdit,"原来的窗口处理函数地址",(HANDLE)(DWORD)pfnWndProcOrg);

在新的窗口处理函数中
  CallWindowProc(
            (WNDPROC)(DWORD)GetProp(hwnd,"原来的窗口处理函数地址"),
            hwnd,uMsg,wParam,lParam);

     注意subclassing 只能在同一进程中使用,如果想在两个进程间使用,那么,看Advance Windows Breaking Through Process Boundary Walls

    其实窗口中有很多方法可以将附加信息放入,如GWL_USERDATA,窗口的属性,和额外元,使用他们都可以在函数与函数间传递变量!!


subclass通常用于由系统注册的window class 产生的窗口,那么由于是系统注册了window class,使得我们无法控制这个类别中的 类别额外字段和窗口额外字段.并且subclassing 的先决条件是必须有窗口存在.所以将一些信息和一个subclassed 窗口关联在一起,象刚才使原来的pfnWndProc和hwnd关联,使用窗口的properties是最好的方法,不过也可以使用GWL_USERDATA来存放一些信息,但是不是好方法,可能那个已经被系统用过了.

下面是Subclassing 的例子

线路:
Subclassing---New window procedure---message handle---Call old window procedure

 

NoDigitsCls.cpp
//调用WndProc的函数
BOOL WINAPI NoDigitsClass_ConvertEdit(HWND hwndEdit,BOOL fSubclass)
{
 BOOL fOk 
= FALSE;

 
if(fSubclass)
 
{
  fOk 
= SetProp(hwndEdit,g_szNoDigitsClassWndProcOrg,
   (HANDLE)(DWORD)SubclassWindow(hwndEdit,NoDigitsClass_WndProc)); 
 }

 
else{
  WNDPROC wp 
= (WNDPROC)(DWORD)
   RemoveProp(hwndEdit,g_szNoDigitsClassWndProcOrg);
  SubclassWindow(hwndEdit,wp);
  fOk 
= (wp != NULL);
 }

 
return(fOk);
}


//新的WndProc
LRESULT WINAPI NoDigitsClass_WndProc(HWND hwndEdit,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
//处理一部分信息
 switch(uMsg)
 
{
  HANDLE_MSG(hwndEdit,WM_CHAR,NoDigitsClass_OnChar);
 }

//调用原来消息处理器
 return(NoDigitsClass_CallOrigWndProc(hwndEdit,uMsg,wParam,lParam));
}


//OnChar处理器
void NoDigitsClass_OnChar(HWND hwndEdit,TCHAR ch,int cRepeat)
{
 
if(adgINRANGE(__TEXT('0'),ch,__TEXT('9')))
 
{
  MessageBeep(
0);
 }

 
else
 
{       //是字符,调用原来消息处理器
  FORWARD_WM_CHAR(hwndEdit,ch,cRepeat,NoDigitsClass_CallOrigWndProc);
 }

}


//如果在FORWARD_* 使用 CallWindowProc,那么会错误,因为FORWARD_*的最后一个参数支持的是标准消息处理器,所以定义NoDigitsClass_CallOrigWndProc,在他中调用CallWindowProc

LRESULT NoDigitsClass_CallOrigWndProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
 
return(CallWindowProc((WNDPROC)(DWORD) GetProp(hwnd,g_szNoDigitsClassWndProcOrg),
  hwnd,uMsg,wParam,lParam));
}



 

<think>好的,我现在需要处理用户的问题,他想在C#中使用Keras的Model SubclassingAPI来训练DNN模型的示例代码或教程。但根据之前的对话,Keras本身是Python库,C#无法直接调用,所以之前的解决方案是通过Python训练模型,导出后用TensorFlow.NET在C#中进行推理。现在用户可能希望更进一步,是否有可能在C#中直接进行模型训练,而不仅仅是推理? 首先,我需要确认用户的需求。用户之前的提问是关于如何在C#中使用ModelSubclassing API,而回答中已经指出C#无法直接调用Keras的API,只能通过跨语言协作。现在用户再次询问,可能希望找到在C#中直接训练的方法,或者是否有新的库支持这一点。接下来,我需要查阅现有的资料。根据引用内容,Keras的Model SubclassingAPI是Python特有的功能,用于构建自定义模型。C#中并没有原生的Keras支持,但可能有第三方库如TensorFlow.NET,它允许在C#中使用TensorFlow的功能。但TensorFlow.NET是否支持类似Keras的高级API,特别是ModelSubclassing呢? 查看TensorFlow.NET的文档,发现它主要提供TensorFlow的低级API绑定,虽然支持构建和训练模型,但可能没有直接实现Keras的Model SubclassingAPI。因此,在C#中直接使用ModelSubclassing API可能不可行。用户可能需要使用TensorFlow.NET的低级API来构建类似的结构,或者继续使用Python进行模型定义和训练,然后在C#中加载进行推理。用户可能希望有一个端到端的C#解决方案,但技术限制使得这不太现实。因此,回答需要明确说明这一点,并提供替代方案,比如在C#中使用TensorFlow.NET的低级API构建模型,或者继续使用Python训练后导出。另外,用户提供的引用内容提到,Keras的Sequential模型和Functional API在Python中的使用方式,以及Model Subclassing的方法。但这些都是Python代码,无法直接转换为C#。因此,需要强调跨语言协作的必要性,并可能提供如何在C#中构建类似结构的示例,但使用TensorFlow.NET的方式。 可能还需要考虑用户是否混淆了训练和推理的步骤,或者是否有其他库如ML.NET可以用于C#中的深度学习训练,但ML.NET主要针对传统机器学习,而非复杂的深度学习模型。 总结,回答需要明确在C#中无法直接使用Keras的ModelSubclassing API,但可以通过TensorFlow.NET的低级API构建和训练模型,或者继续使用Python进行训练,C#进行推理。同时,提供相应的代码示例,说明如何在C#中构建类似的DNN模型,尽管方式不同。</think>### C#与Keras Model Subclassing API的协同方案 由于Keras的Model Subclassing API是Python生态的专有特性,C#无法直接调用。但可通过以下两种方式实现**跨语言协作**或**等效建模**: --- #### 一、跨语言协作方案(推荐) **核心流程**: 1. **在Python中完成模型定义与训练** 2. **导出为SavedModel格式** 3. **在C#中通过TensorFlow.NET加载模型进行推理** ##### 1. Python端自定义模型(Model Subclassing API) ```python import tensorflow as tf class CustomDNN(tf.keras.Model): def __init__(self, input_dim=32, num_classes=10): super().__init__() self.dense1 = tf.keras.layers.Dense(64, activation='relu', input_shape=(input_dim,)) self.dropout = tf.keras.layers.Dropout(0.5) self.dense2 = tf.keras.layers.Dense(num_classes, activation='softmax') def call(self, inputs, training=False): x = self.dense1(inputs) if training: x = self.dropout(x) return self.dense2(x) # 训练并导出模型 model = CustomDNN() model.compile(optimizer='adam', loss='sparse_categorical_crossentropy') model.fit(x_train, y_train, epochs=5) tf.saved_model.save(model, "custom_dnn_model") ``` ##### 2. C#端加载模型推理(TensorFlow.NET) ```csharp using Tensorflow; using static Tensorflow.Binding; var graph = new Graph(); var session = tf.Session(graph); var model = tf.saved_model.loader.load(session, ["serve"], "custom_dnn_model"); // 输入输出张量名称需与Python导出时一致 var input_tensor = graph.OperationByName("serving_default_dense_input"); var output_tensor = graph.OperationByName("StatefulPartitionedCall"); // 构造输入数据(示例) var input_data = new float[,] { { 1.0f, 2.0f, ..., 32.0f } }; var tensor = tf.constant(input_data); // 执行推理 var results = session.run(output_tensor.outputs[0], new FeedItem(input_tensor.outputs[0], tensor)); ``` --- #### 二、C#原生建模方案(通过TensorFlow.NET) 若需**完全在C#中实现模型训练**,需使用TensorFlow.NET的低级API构建等效模型: ```csharp using Tensorflow; using static Tensorflow.Binding; // 定义模型结构 var model = new Sequential(); model.add(tf.keras.layers.Dense(64, activation: "relu", input_shape: new Shape(32))); model.add(tf.keras.layers.Dropout(0.5f)); model.add(tf.keras.layers.Dense(10, activation: "softmax")); // 编译模型 model.compile(optimizer: tf.keras.optimizers.Adam(), loss: tf.keras.losses.SparseCategoricalCrossentropy(), metrics: new[] { "accuracy" }); // 训练模型(需准备数据) model.fit(x_train, y_train, epochs: 5, batch_size: 32); // 保存模型 model.save("csharp_dnn_model"); ``` **关键差异**: - 使用`Sequential`类而非Model Subclassing API - 层定义通过方法链式调用实现 - 动态图支持有限(需提前定义静态计算图)[^1] --- ### 对比分析 | 方案 | 优势 | 局限性 | |---------------------|-------------------------------|----------------------------| | 跨语言协作 | 支持复杂自定义模型 | 需维护Python/C#两套代码 | | C#原生建模 | 全流程C#实现 | 无法完全复现Subclassing特性 | --- ### 技术要点解析 1. **TensorFlow.NET的层定义** - 层参数需显式指定(如`input_shape`) - 激活函数通过字符串名称传递(如`activation: "relu"`) 2. **数据格式转换** - C#数组需转换为`Tensor`对象: ```csharp var tensor = tf.constant(new float[,] { { 1.0f, 2.0f }, { 3.0f, 4.0f } }); ``` 3. **性能优化** - 使用`tf.data.Dataset`加速数据管道: ```csharp var dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train)) .shuffle(1000) .batch(32); ``` --- ### 常见问题解答 #### Q1: TensorFlow.NET是否支持动态图模式? 目前仅支持**静态图模式**,需预先定义计算图结构。动态图功能(如Eager Execution)尚在开发中[^2]。 #### Q2: 如何实现自定义损失函数? 需继承`Tensorflow.Keras.Losses.Loss`类: ```csharp public class CustomLoss : Loss { public override Tensor Apply(Tensor y_true, Tensor y_pred, bool from_logits = false) { return tf.reduce_mean(tf.square(y_true - y_pred)); } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值