【转】DICOM医学图像处理:DIMSE消息发送与接收“大同小异”之DCMTK fo-dicom mDCM

转自:https://my.oschina.net/zssure/blog/354816

背景:

        从DICOM网络传输一文开始,相继介绍了C-ECHO、C-FIND、C-STORE、C-MOVE等DIMSE-C服务的简单实现,博文中的代码给出的实例都是基于fo-dicom库来实现的,原因只有一个:基于C#的fo-dicom库具有高封装性。对于初学者来说实现大多数的DIMSE-C、DIMSE-N服务几乎都是“傻瓜式”操作——构造C-XXX-RQ、N-XXX-RQ然后绑定相应的OnResponseReceived处理函数即可。本博文希望在前几篇预热的基础上,对比DCMTK、fo-dicom、mDCM三种库构建DIMSE消息的具体操作,来分析一下三者对于DIMSE消息的发送和接收的实现,为后续搭建简易版的Dicom Server服务器做准备。

DIMSE:

        DIMSE,是DICOM Message Service Element的简称。DICOM3.0第7部分指出:DIMSE为对等DICOM应用实体进行医学影像及相关信息交换提供了一种应用服务元素定义(Application Service Element),包括服务和协议(DIMSE Service 和DIMSE Protocol)。

DIMSE Protocol:

        DIMSE基于DIMSE协议来提供服务,DIMSE协议规定了构造消息必需的编码规则。一条DICOM MESSAGE由固定的指令集合(Command Set),外加可选择的数据集合(Data Set)构成,如下截图所示:

 

 

        可以简单的理解为Command Set就是本博文即将要介绍的各种服务的请求和应答消息;而Data Set可以认为类似于DCM后缀的文件,是我们希望在对等DICOM实体间进行传输的信息。但是从本质上来说Command Set和Data Set两者都遵循DICOM3.0协议中IOD的定义都是“以(Group Number,Element Number)对”来进行标记的Data Element元素的集合,更形象一点的说明可参考早期的博文(http://blog.csdn.net/zssureqh/article/details/9275271)。

        DIMSE Protocol指出Message可能会分片(fragmented,这与传统的TCP/IP中的概念类似),消息的具体传输是基于ASSOCIATE(DICOM3.0第8部分)中的P-DATA Service(博文http://blog.csdn.net/zssureqh/article/details/41016091中有介绍)。

DIMSE Services:

        DIMSE服务因操作SOP类型的不同分为DIMSE-C ServicesDIMSE-N Services,DIMSE-C服务支持在对等DICOM实体间进行Composite SOP Instance操作,主要包括C-ECHO、C-FIND、C-STORE、C-MOVE、C-GET等;而DIMSE-N服务支持Normalized SOP Instance操作,主要包括N-EVENT-REPORT、N-GET、N-SET、N-CREATE、N-ACTION、N-DELETE。

 

 

        从上图可以看出DIMSE-C服务只提供操作服务,即对等DICOM实体一方请求另一方对Composite SOP Instance进行操作(operation);而DIMSE-N服务除了提供操作以外,还提供通知(notification)服务。DIMSE中的所有操作和通知都是确认服务(confirmed services),即一方发出的请求都需要得到对方的应答(原文:All DIMSE operations and notifications are confirmed services. The performing DIMSE-service-user shall report the response of each operation or notificationover the same Association on which the operation or notification was invoked.)。每种服务具体方式不同,例如某些操作可能会触发后续的子操作、某些操作可能需要多个响应等等,如下图:

 

 

DCMTK、fo-dicom、mDCM构建DIMSE-C消息:

        DIMSE-C服务在医学领域应用最广泛,常见的PACS、HIS、RIS、LIS等系统都会用到,而DIMSE-N服务主要应用在MPPS和DICOM打印中,日常学习中可能没有实际应用和测试的机会,因此这里就暂时不介绍,主要以DIMSE-C消息的构造为主,来分别介绍三种库的具体操作。

DCMTK:

        博文http://blog.csdn.net/zssureqh/article/details/41016091之前简单介绍了一下DCMTK对于网络传输方面的封装,更多的是偏重于协议的各层(Layer),例如对最底层的基于TCP/IP的Dicom Upper Layer的封装以DUL_为前缀;对实体连接层的封装以ASC_为前缀;最顶层的是DIMSE层,以DIMSE_为前缀。本博文会从DIMSE Services中的DIMSE-C各种消息入手,介绍DCMTK对于消息的封装和操作:

DCMTK之C-ECHO:

        DCMTK开源库相较于其他两者来说最大的优势是有完整的说明文档、稳定的维护团队,同时也有成功的商业产品。在源码中也给出了各种服务工具包,前面的好多博文都已经介绍过DCMTK的工具包,例如针对于C-ECHO的echoscu.exe(博文后续的工程实例是用dcmqrscp.exe作为mini DICOM服务端进行测试的)

DCMTK对DIMSE-C中的各种消息的定义在dimse.h头文件中,其中C-ECHO-RQ消息定义如下:

 

/* C-ECHO */
struct T_DIMSE_C_EchoRQ {
DIC_US MessageID; /* M */
DIC_UI AffectedSOPClassUID; /* M */
T_DIMSE_DataSetType DataSetType; /* M */
} ;
struct T_DIMSE_C_EchoRSP {
DIC_US MessageIDBeingRespondedTo; /* M */
DIC_UI AffectedSOPClassUID; /* U(=) */
T_DIMSE_DataSetType DataSetType; /* M */
DIC_US DimseStatus; /* M */
unsigned int opts; /* which optional items are set */
#define O_ECHO_AFFECTEDSOPCLASSUID 0x0001
} ;

 

 

        dimse.h中对于每一种DIMSE-C服务的请求消息(request)和响应消息(response)都给出了定义,并以union方式来统一了DICOM Message结构,如下所示:

 

/*
* Composite DIMSE Message
*/
struct T_DIMSE_Message {
T_DIMSE_Command CommandField; /* M */
union {
/* requests */
T_DIMSE_C_StoreRQ CStoreRQ;
T_DIMSE_C_EchoRQ CEchoRQ;
T_DIMSE_C_FindRQ CFindRQ;
T_DIMSE_C_GetRQ CGetRQ;
T_DIMSE_C_MoveRQ CMoveRQ;
T_DIMSE_C_CancelRQ CCancelRQ;
T_DIMSE_N_EventReportRQ NEventReportRQ;
T_DIMSE_N_GetRQ NGetRQ;
T_DIMSE_N_SetRQ NSetRQ;
T_DIMSE_N_ActionRQ NActionRQ;
T_DIMSE_N_CreateRQ NCreateRQ;
T_DIMSE_N_DeleteRQ NDeleteRQ;
/* responses */
T_DIMSE_C_StoreRSP CStoreRSP;
T_DIMSE_C_EchoRSP CEchoRSP;
T_DIMSE_C_FindRSP CFindRSP;
T_DIMSE_C_GetRSP CGetRSP;
T_DIMSE_C_MoveRSP CMoveRSP;
T_DIMSE_N_EventReportRSP NEventReportRSP;
T_DIMSE_N_GetRSP NGetRSP;
T_DIMSE_N_SetRSP NSetRSP;
T_DIMSE_N_ActionRSP NActionRSP;
T_DIMSE_N_CreateRSP NCreateRSP;
T_DIMSE_N_DeleteRSP NDeleteRSP;
} msg;
};

 

 

        DICOM3.0第7部分中有关于C-ECHO消息的参数说明以及具体指令编码,正如前文所述Command同样也是以(Group Number,Element Number)标记的Data Element元素的集合,因此按照DICOM3.0标准中的要求只要向C-ECHO-RQ或者C-ECHO-RSP指令中插入规定的Data Element元素即可。

 

 

        如上图所示,构造T_DIMSE_CEchoRQ需要填充MessageID/Affected SOP Class UID等,具体构造代码如下:(代码封装在DIMSE_echoUser函数中)

 

T_DIMSE_Message req, rsp;
T_ASC_PresentationContextID presID;
const char *sopClass = UID_VerificationSOPClass;
bzero((char*)&req, sizeof(req));
bzero((char*)&rsp, sizeof(rsp));
req.CommandField = DIMSE_C_ECHO_RQ;
req.msg.CEchoRQ.MessageID = msgId;
strcpy(req.msg.CEchoRQ.AffectedSOPClassUID,
sopClass);
req.msg.CEchoRQ.DataSetType = DIMSE_DATASET_NULL;

 

 

        上面代码中的rsp与我们自己构建的req类似,唯一不同的是req是在C-ECHO SCU端构造,而rsp是在C-ECHO SCP端构造并通过网络传送过来的。

      (具体的测试代码可参见博文后文给出的连接)

DCMTK之C-FIND:

        下面我们看一下比较复杂的消息C-FIND,相较于C-ECHO消息,C-FIND中需要给出我们希望查询的目标属性列表(记住:同样也是一个DcmDataset类型,即Dicom Element集合)。

 

 

        C-FIND-RQ消息的构造代码如下:

 

//定义临时变量
T_ASC_PresentationContextID presId;
T_DIMSE_C_FindRQ req;
T_DIMSE_C_FindRSP rsp;
DcmFileFormat dcmff;
OFString temp_str;
presId=ASC_findAcceptedPresentationContextID(assoc,abstractSyntax);
//构造C-FIND-RQ消息
bzero(OFreinterpret_cast(char*, &req), sizeof(req));
strcpy(req.AffectedSOPClassUID,abstractSyntax);
req.DataSetType=DIMSE_DATASET_PRESENT;
req.Priority=DIMSE_PRIORITY_LOW;
req.MessageID=assoc->nextMsgID++;
//构造数据体,即我们具体希望在C-FIND SCP端获得的信息
DcmDataset* dcmdataset=new DcmDataset();
dcmdataset->putAndInsertString(DCM_StudyInstanceUID,"");
dcmdataset->putAndInsertString(DCM_StudyDate,"");
dcmdataset->putAndInsertString(DCM_QueryRetrieveLevel,"STUDY");
DcmDataset *statusDetail = NULL;
//在DIMSE_findUser内部会将dcmdataset数据合并到req中,统一构成T_DIMSE_Message
OFCondition cond=DIMSE_findUser(assoc,presId,&req,dcmdataset,NULL,NULL,blockMode,dimse_timeout,&rsp,&statusDetail);

 

 

        上述代码比较复杂的是需要构造参数列表中的Identifier元素,该元素包含了我们希望从C-FIND SCP服务端提供查询获得的属性,上面选择了STUDY级别的查询,因此需要添加DCM_QueryRetrieveLevel元素、StudyInstanceUID等(DCM_QueryRetrieveLevel元素必须添加,有时候会误认为添加了AffectedSOPClassUID后就不需要了,这是错误的。否则服务端会返回如下错误,如下图)。

 

 

        注:关于Patient、Study、Series等不同级别的查询的详细介绍可参考DICOM3.0标准第4部分的附录C。

DCMTK之C-STORE:

        C-STORE与C-FIND类似,同样需要添加额外的数据,不同于C-FIND添加查询属性列表的是,C-STORE添加的是准备发送的DCM文件的数据体,即下图中的Data Set。

 

OFCondition cond = EC_Normal;
T_DIMSE_Message req, rsp;
DcmDataset 
bzero((char*)&req, sizeof(req));
bzero((char*)&rsp, sizeof(rsp));
/* set corresponding values in the request message variable */
req.CommandField = DIMSE_C_STORE_RQ;
request->DataSetType = DIMSE_DATASET_PRESENT;
request->req.msg.CStoreRQ = *request;

 

 

        暂时我们就只介绍C-ECHO、C-FIND和C-STORE三种服务的请求消息构造方法,其他的类似。

fo-dicom:

        fo-dicom是基于C#开发的,封装性更强,封装思路更倾向于按DICOM消息流来进行,即fo-dicom库开发者在实现了整个DIMSE消息流框架的基础上,通过给用户预留各阶段的接口来方便用户定制自己的实现。对于DIMSE消息流框架的封装在DicomService.cs文件中(同时也有类似于DCMTK中的ASC_方面的封装,主要指的是A-ASSOCIATE服务及协议,在DICOM3.0第8部分有详细介绍),对于网络底层的封装放在DicomServer.cs文件中(等同于DCMTK中的DUL_层)。

        DICOM Message消息的基类在DicomMessage.cs文件中,然后根据请求和应答派生了两个基类DicomRequest和DicomResponse。从fo-dicom库的封装以及fo-dicom对于Dataset的设计可以看出Command和Dataset都是数据集合,不同的是两者存储的元素类型不同。

        在fo-dicom库中构造各类消息很方便,可谓是“傻瓜式”操作,详情如下:

fo-dicom之C-ECHO:

 

DicomCEchoRequest cechoRQ=new DicomCEchoRequest();

 

 

        一行代码就顺利的构建了一个C-ECHO-RQ请求指令。分析源码可知DicomCEchoRequest继承自DicomRequest,DicomReqeust继承自DicomMessage。逐级查看各类的构造函数可以发现。虽然我们调用的是DicomCEchoRequest的默认构造函数,但是在相继调用了基类DicomRequest(DicomCommandField.CEchoRequest, DicomUID.Verification, priority)和DicomMessage()后,顺利的完成了对C-ECHO-RQ指令中各个参数构造,其中DicomMessage中构造了空的Command Set和DataSet,DicomRequest中对MessageID、Priority、SOPClassUID以及CommandFieldType进行了赋值,这简直是太容易啦,不过也正因为此,刚入手的时候可能不知道如何来定制化自己的请求,以为fo-dicom库留给我们的可操作性太少,其实不然,继续往下看。

fo-dicom之C-FIND:

 

DicomCFindRequest cfind=DicomCFindRequest.CreateStudyQuery(patientId:”12345”);
cfind.OnResponseReceived=(rq,rsp)=>
{
//接收到C-FIND-RSP响应消息后,本机C-FIND SCU进行的操作
//例如可以输出到屏幕或其他窗口
Console.WriteLine("PatientAge:{0} PatientName:{1}", rsp.Dataset.Get<string>(DicomTag.PatientAge), rsp.Dataset.Get<string>(DicomTag.PatientName));
}

 

 

        通过对比fo-dicom与DCMTK中C-FIND的构造,是不是觉得很容易。但是越容易学习和上手的东西,倘若不掌握其本质越容易忘。查看DicomCFindRequest.cs源码,可以发现CreateStudyQuery函数已经帮助我们添加了Study查询级别所需的所有字段,也就是上文中提到的Identifier参数部分。代码如下:

 

        那么如果我们想像DCMTK那样自由添加字段怎么办?例如在已知服务端是自己定制实现的基础上来查询我们的私有字段。很简单直接覆盖一下CreateStudyQuery函数即可。另外fo-dicom还有一个比价便利的地方是将每种消息的回调函数直接绑定到消息中,程序写起来比较方便,逻辑上更清晰。

fo-dicom之C-STORE:

 

DicomCStoreRequest cstore=new DicomCStoreRequest(@”c:\\test4.dcm”);

 

 

        在DicomCStoreRequest一级只需要数据要发送的dcm文件名(全路径名),同样通过逐级来完成CommandSet和Dataset的赋值。基本流程如下:

 

/// <summary>/// Initializes DICOM C-Store request to be sent to SCP./// </summary>/// <param name="file">DICOM file to be sent</param>/// <param name="priority">Priority of request</param>public DicomCStoreRequest(DicomFile file, DicomPriority priority = DicomPriority.Medium) : base(DicomCommandField.CStoreRequest, file.Dataset.Get<DicomUID>(DicomTag.SOPClassUID), priority) {File = file;Dataset = file.Dataset;SOPInstanceUID = File.Dataset.Get<DicomUID>(DicomTag.SOPInstanceUID);}
<span style="white-space:pre">		</span>//DicomRequest.cs文件protected DicomRequest(DicomCommandField type, DicomUID affectedClassUid, DicomPriority priority) : base() {Type = type;SOPClassUID = affectedClassUid;MessageID = GetNextMessageID();Priority = priority;Dataset = null;}
<span style="white-space:pre">		</span>//DicomMessage.cs文件public DicomMessage() {Command = new DicomDataset();Dataset = null;}

 

mDCM:

        mDCM库与fo-dicom库其实是相同的,只不过fo-dicom利用了最新的C#技术来重构mDCM。如博文http://blog.csdn.net/zssureqh/article/details/39621533中给出的mDCM库的继承图所示,在顶层基类DcmNetworkBase中实现了DIMSE消息流的基本框架,然后按照Client和Server进行了两路派生。mDCM的封装有点处于DCMTK和fo-dicom之间的状况,既未做到像DCMTK那样完全提供各个层面底层操作函数,也没有像fo-dicom那样更抽象的封装

        下面来看一下mDCM对各种消息的构造:

mDCM之C-ECHO:

 

//DcmAssociation assoction;//已经顺利建立的DICOM对等实体间的连接
byte pcid=associate.FindAbstractSyntax(DicomUID.VerificationSOPClass;
SenCEchoRequest(pcid,NextMessageID(),Priority);

 

        mDCM比较特殊,对于DIMSE-C服务请求的参数赋值流程与fo-dicom类似,大多参数赋值都在基类中完成,例如DcmClientBase中完成了MaxPDU、Priority,DicomClient完成CallingAE和CalledAE等;而对于整体请求消息的拼接却又类似DCMTK,在SendCEchoRequest函数内部调用CreateRequest来完成。

mDCM之C-FIND:

 

byte pcid = Associate.FindAbstractSyntax(FindSopClassUID);
if (Associate.GetPresentationContextResult(pcid) == DcmPresContextResult.Accept) {
DcmDataset dataset = query.ToDataset(Associate.GetAcceptedTransferSyntax(pcid));
SendCFindRequest(pcid, NextMessageID(), Priority, dataset);

 

        在query.ToDataset函数内部完成了查询级别QueryRetrieveLevel的赋值,另外需要注意的是此时在ToDataset函数内部调用了一个虚函数AdditonalMembers用于方便派生添加自已要查询的Identifier元素。最终还是在SendCFindRequest函数内部利用CreateRequest创建C-FIND-RQ消息(在mDCM中的类型是DcmCommand)。

mDCM之C-STORE:

 

internal void SendCStoreRequest(byte pcid, DicomUID instUid, Stream stream) {SendCStoreRequest(pcid, NextMessageID(), instUid, Priority, stream);}internal void SendCStoreRequest(byte pcid, DicomUID instUid, DcmDataset dataset) {SendCStoreRequest(pcid, NextMessageID(), instUid, Priority, dataset);}

 

        在CStoreClient类内部通过Load来载入dcm文件,提取DcmDataset数据体,然后调用SendCStoreRequest来发送C-STORE-RQ请求(DicomCStoreClient中有两种类型的SendCStoreRequest,一种是发送DcmDataset类型数据,一种是发送Stream类型数据)。

总结:

        通过对比分析三种开源库对DIMSE-C服务消息的构造方式,可以更清晰的了解DCMTK、fo-dicom、mDCM三者各自的优势。如果想了解DICOM协议的细节及底部代码的具体实现,自然DCMTK是首选,其按照Dicom Upper Layer、A-ASSOCIATE、DIMSE三层来划分的结构更方便我们研究DICOM网络传输的机制。并且DCMTK最新的3.6.1版本也逐渐开始按服务来对DUL_、ASC_、DIMSE_三类函数进行封装,已经实现了C-ECHO、C-STORE服务,即DcmSCU/DcmSCP和DcmStorageSCU/DcmStorageSCP。如果想快速入手,实现DICOM的相关服务,fo-dicom自然是首选,想必这对于C#程序员来说轻而易举(mDCM可以看做是DCMTK与fo-dicom的中间地带)。

DCMTK工程实例:

百度网盘:http://pan.baidu.com/s/1jGvaSr8

后续博文介绍:

fo-dicom搭建简单的DICOM Server

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/436166.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php记录登录时间,php记录 用户当前页面停留时间

onunload JS定时器 ajax 实现每秒写一次数据库肯定是不行的资源释放都来不及&#xff0c;使用js计时器&#xff0c;在离开页面时&#xff0c;调用ajax异步插入数据库&#xff0c;即不延迟跳转&#xff0c;也不会占用资源&#xff0c;插入完就释放了HTML onunload 事件属性定义…

WinCE驱动开发问题精华集锦

如何让系统加载自己写的驱动程序&#xff1f;两种办法&#xff1a; 1、在[HKEY_LOCAL_MACHINE/Drivers/BuiltIn]下添加注册键。 2、在应用程序中调用ActivateDeviceEx。 在一些文件中用分号来表示注释&#xff0c;例如下面的内容 ; CESYSGEN IF SERVERS_MODULES_HTTPD …

公司间交易学习笔记---概述

本系列笔记是我在学习公司间交易的过程中的随笔&#xff0c;有些是我自己的想法&#xff0c;内容可能跟教程有所出入&#xff0c;由于对AX的应用部分理解得很浅&#xff0c;所以如果错误还请多多指教。 为了处理集团公司中各个分公司之间的交易&#xff0c;AX采用了InterCompan…

【转】走进windows编程的世界-----对话框、文本框、按钮

一、对话框 1 对话框的分类 2 对话框的基本使用方式 3 对话框资源 4 有模式对话框的使用 int DialogBox( HINSTANCE hInstance, LPCTSTR lpTemplate, HWND hWndParent, DLGPROC lpDialogFunc ); 5 无模式对话框的使用 5.1 添加对话框资源 5.2 定义窗口处理函数 …

php接口datatype= text,关于php:dataType:“json”不起作用

我正在尝试使用数组中的json将多个变量从php文件发送回ajax。 php文件中的代码完美运行&#xff0c;并且应该像我的数据库一样完成所有操作。 但是当我在ajax中添加dataType&#xff1a;"json"时&#xff0c;php文件中就没有任何事情发生了。 我google了一下&#xf…

做饭记

小时候,听奶奶讲:最没出息的男人才要做饭,有出息的男人是应该赚钱养家的,而女人则是要做饭的. 于是一直对做饭没什么兴趣, 现在的生活每天都在代码,突然想学做饭,真的学了,才发现做饭可不是一件容易的事儿. 做饭的时候,发现自己可以全身心的投入到做饭的"艺术&quo…

用VS2005开发WinCE程序调试图文教程

一、WinCE 模拟器通过ActiveSync 6.1(即Windows Mobile设备中心)连接PC ActiveSync 6.1&#xff1a;http://www.cr173.com/soft/26994.html 1.启动WinCE模拟器 命令行&#xff1a; start .\DeviceEmulator.exe WINCE镜像\Wince5.bin /memsize 256 /video 480x272x16 /sharedf…

【转】win32 的DLL中创建windows窗口

转自&#xff1a;https://blog.51cto.com/wangyw/1071967 因工作需求&#xff0c;需要写一个DLL&#xff0c;并在其中创建windows窗口&#xff0c;网上有很多关于DLL创建窗口的文章&#xff0c;不过都是基于MFC的&#xff0c; 却鲜见直接用win32的DLL创建的。经过研究调试&…

php 版权信息自动变化,php自动更新版权信息显示的方法

本文实例讲述了php自动更新版权信息显示的方法。。具体分析如下&#xff1a;我们一般会在页面下方输出版权信息&#xff0c;包含年份信息&#xff0c;每年都要修改&#xff0c;这段简单的代码帮你解决这个问题&#xff0c;自动更新年份function autoUpdatingCopyright($startYe…

【转】 ADO.NET最佳实践

本文转自&#xff1a;http://blog.csdn.net/spidertan/archive/2003/12/13/17110.aspx 概述&#xff1a; 本文在微软站点资源的基础上加工整理而成&#xff0c;意在介绍在你的ADO.NET应用程序中执行和完成性能优化、稳定性和功能性方面提供最佳的解决方案&#xff1b;同…

【转】Win32 Slider Control的用法

2.1 获得控件的窗口句柄。 slider control也是一个窗口&#xff0c;是对话框的子窗口&#xff0c;因此&#xff0c;要用GetDlgItem来实现&#xff1a; backlight_wnd GetDlgItem(hDlg, IDC_SLIDER_BACKLIGHT); //注意下面示例中蓝色标出的区别 2.2 设置slider control的区…

wince编译

在WinCE下面&#xff0c;创建工程&#xff0c;然后编译是需要花很长时间的。当我们第一编译完工程以后&#xff0c;如果需要改我们的驱动或者BSP&#xff0c;我们是不需要重新编译整个工程的&#xff1a;1. 如果更改了driver&#xff0c;以WinCE6.0为例&#xff0c;我们可以在菜…

php 变量文件间传递,同一文件的两个JS函数之间如何传变量?

满意答案ed_ch2013.11.06采纳率&#xff1a;44% 等级&#xff1a;12已帮助&#xff1a;8801人定义成全局变量就可以了var style_key;function change_mystyle(my){style_keymy.options[my.selectedIndex].value;if(style_key 114la){document.getElementById(shoostyleicon…

获取 Web 设计的免费资源

Web 开发人员可以找到很多免费资源&#xff0c;尽管其中一些资源会比另外一些资源更加自由。如果您正在设计网站或 Web 应用程序&#xff0c;那么不管是静态的还是所有可以想到的动态 Ajax 内容&#xff0c;都能找到减轻您的劳动强度并为网站增色的资源。本文从免费图标到 Web …

设计模式C++实现(1)——工厂模式

软件领域中的设计模式为开发人员提供了一种使用专家设计经验的有效途径。设计模式中运用了面向对象编程语言的重要特性&#xff1a;封装、继承、多态&#xff0c;真正领悟设计模式的精髓是可能一个漫长的过程&#xff0c;需要大量实践经验的积累。最近看设计模式的书&#xff0…

【转】关于CreateWindow的HINSTANCE参数?

学习Windows程序设计时&#xff0c;看见CreateWindows中有参数HINSTANCE&#xff0c;但不知道其用途&#xff0c;于是上网搜得两资料&#xff0c;并且&#xff0c;得出结果是&#xff0c;用于区分不同的应用实例。 What is the HINSTANCE passed to CreateWindow and RegisterC…

java对象间的转型,详细讲述Java中的对象转型

向上转型&#xff1a;子类对象转为父类&#xff0c;父类可以是接口。公式&#xff1a;Father f new Son();Father是父类或接口&#xff0c;son是子类。向下转型&#xff1a;父类对象转为子类。公式&#xff1a;Son s (Son)f;我们将形参设为父类Animal类型&#xff0c;当执行t…

给C盘减减肥,让你电脑飞一般速度!

当你买了台新电脑时&#xff0c;觉得性能&#xff0c;速度&#xff0c;你都比较满意&#xff0c;但是随着时间推移&#xff0c;你觉得你C盘空间越来越小&#xff0c;速度也降下了&#xff0c;那我们该怎么办呢&#xff1f;对于菜鸟来说&#xff0c;有一点你必须注意&#xff1a…

C++设计模式之一 工厂模式(简单工厂、工厂和抽象工厂)

今天开始这个系列之前&#xff0c;心里有些恐慌&#xff0c;毕竟园子里的高手关于设计模式的经典文章很多很多&#xff0c;特别是大侠李会军、吕震宇 老师的文章更是堪称经典。他们的文笔如行云流水&#xff0c;例子活泼生动&#xff0c;讲解深入浅出。好在他们都是用C#描述&am…

【转】Windows消息传递机制详解

林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka Windows是一个消息&#xff08;Message&#xff09;驱动系统。Windows的消息提供了应用程序之间、应用程序与Windows系统之间进行通信的手段。应用程序想要实现的功能由消息来触发&#xff0c;并且靠对消…