WinCC的电子签名与审计追踪
如何写入审计追踪记录
用脚本向Audit中添加记录有两种方法,一种方法是用InserAuditEntryNew函数写入,另一种方法是生成属于“操作员输入消息”类型的报警消息,该报警消息会记录到Audit中。
Audit记录中的TargetName列是操作内容,Reason列是操作员的注释。使用InserAuditEntryNew函数生成的记录在TargetName列的内容默认为VBScripting Runtime或CScripting Runtime,而操作内容只能记录在Reason列处,这与其他记录的形式不一致,效果不好,因此在此不使用InserAuditEntryNew函数。
第二种方法,在报警记录的“系统,无确认”下的“操作员输入消息”中建立报警消息,当这条消息触发后,消息不仅会记录到报警数据库,也会记录到Audit数据库。触发报警消息可以用变量触发,也可以用脚本触发。使用脚本触发可以给消息传递参数,即向@1%s@等文本块中写入值。
WinCC中默认有一条编号为12508141的操作员输入消息,定义如下图,该消息用于记录新值旧值的更改。
12508141消息的内容不完全符合Audit消息记录的需要,新建一条编号为12508142消息。项目中可能会用WinCC控制多台设备,定义文本块@10%s@的作用是记录该操作针对哪台设备,文本块@7%s@记录执行了什么操作。有些项目中不使用Audit trail组件,而是用报警记录的数据库记录审计追踪,因此可在错误点列填上@10%s@,使用报警控件查看时便于筛选。该消息定义如下:
使用C脚本生成操作员消息
WinCC支持C和VB两种脚本,C脚本中提供了GCreateMyOperationMsg()函数生成操作员消息。
函数原型:
int GCreateMyOperationMsg( DWORD dwFlags, DWORD dwMsgNum, char* lpszPictureName, char* lpszObjectName, DWORD dwMyTextID, double doValueOld, double doValueNew, char* pszComment)
参数:
- dwFlags,该参数指定如何生成注释,可用以下常量值,值可以用或运算“|”输入:
常数 |
值 |
描述 |
FLAG_COMMENT_PARAMETER |
0x00000001 |
文本在运行时直接作为消息输入,没有自己的注释对话框。对注释的指针不能等于“NULL”。 |
FLAG_COMMENT_DIALOG |
0x00000003 |
出现一个注释对话框,输入的注释被转移到消息中 |
FLAG_TEXTID_PARAMETER |
0x00000100 |
提供文本库中的一个文本ID,将文本插入到消息的过程值中。 |
- dwMsgNum,该参数为消息编号,触发该编号的消息。消息必须属于“操作员输入消息”类型,不可使用其他类型的消息。
- lpszPictureName,该参数未在函数中使用,不起任何作用。
- lpszObjectName,该参数会写入到文本块@1%s@中和szInstance属性中,szInstance属性等同于文本块@10%s@。
- dwMyTextID,该参数是文本库ID,值会被写入到值@8%g@中。如果dwFlags参数为FLAG_TEXTID_PARAMETER,会将文本库中该ID的文本写入到文本块@8%s@中。
- doValueOld,该参数是变量旧值,值会被写入到值@2%g@中,在audit中会写入到OldValue列中。
- doValueNew,该参数是变量新值,值会被写入到值@3%g@中,在audit中会写入到NewValue列中。
- pszComment,该参数是注释字符串,如果dwFlags参数为FLAG_COMMENT_PARAMETER,该字符串会写入到报警的注释中,写入到Audit的Reason列。
返回值:
值 |
描述 |
0 |
函数执行完成没有任何错误 |
-101 |
消息不能被编辑 |
-201 |
当调用“MSRTGetComment()”特征引发一个错误 |
-301 |
当调用“MSRTCreateMsgInstanceWithComment()”特征引发一个错误 |
GCreateMyOperationMsg()函数生成的消息文本中未使用文本块@7%s@,我们参考GCreateMyOperationMsg函数进行修改,重新创建一个操作员消息函数GCreateMyOperationMsg2(),该函数值用lpszMsg参数代替lpszPictureName参数,lpszMsg参数的值被写入文本块@7%s@中。GCreateMyOperationMsg2()函数配合12508142消息使用。
函数原型:
int GCreateMyOperationMsg2( DWORD dwFlags, DWORD dwMsgNum, char* lpszMsg, char* lpszObjectName, DWORD dwMyTextID, double doValueOld, double doValueNew, char* pszComment)
参数:
- lpszMsg,该参数记录操作消息,会写入文本块@7%s@中。
- 其他参数与GCreateMyOperationMsg()函数一致。
函数代码:
#pragma code ("kernel32.dll") void GetLocalTime( LPSYSTEMTIME lpSystemTime); BOOL GetComputerNameA(LPSTR Computername, LPDWORD size); VOID Sleep(DWORD dwMilliseconds); //RQ:1072746 #pragma code() #define FLAG_COMMENT_PARAMETER 0x00000001 #define FLAG_COMMENT_DIALOG 0x00000003 #define FLAG_TEXTID_PARAMETER 0x00000100 int GCreateMyOperationMsg2( DWORD dwFlags, DWORD dwMsgNum, char* lpszMsg, char* lpszObjectName, DWORD dwMyTextID, double doValueOld, double doValueNew, char* pszComment ) { MSG_RTDATA_INSTANCECOMMENT_STRUCT MsgCreateEx; MSG_RTDATA_STRUCT MsgRTData; // for comment dialog CMN_ERROR scError; int iRet= FALSE; DWORD dwServiceID = 0; BOOL bOK; SYSTEMTIME time; DWORD dwBufSize = 256; char szComputerName[256] = ""; char szSource[256] = ""; char szMyText[MSG_MAX_TB_CONTENT + 1] = ""; char szTmp[256] = ""; // for diagnosis output char* pszPrefix; // to define the type of WinCC project char* lpszCurrentUser; char* lpszParent; char * pszServerPrefix; //RQ:1072746 begin DWORD dwMaxCheckTime = 5000; // max time to wait 5000ms=5sec. DWORD dwStepCheckTime = 500; // start stepping waittime with 500ms DWORD dwReduceStepTime = 100; // reduce the steptime here by 100ms DWORD dwActWaitedTime = 0; DWORD dwInx = 0; //RQ:1072746 end printf("Start GCreateMyOperationMsg \r\n"); //====================================== // INIT_MESSAGE_STRUCT //====================================== memset(&MsgCreateEx,0,sizeof(MsgCreateEx)); memset(&MsgRTData,0,sizeof(MsgRTData)); memset(&scError,0,sizeof(scError)); GetLocalTime(&time); MsgCreateEx.stMsgTime = time; MsgRTData.stMsgTime = time; MsgCreateEx.dwMsgNr = dwMsgNum; MsgRTData.dwMsgNr = dwMsgNum; MsgCreateEx.wPValueUsed = (WORD)(0x0000 ); // no real process value used MsgRTData.wPValueUsed = (WORD)(0x0000 ); MsgCreateEx.wTextValueUsed = 0x001F; // text values 1 .. 5 used for textblocks 1 .. 5 MsgRTData.wTextValueUsed = 0x001F; // text values 1 .. 5 used for textblocks 1 .. 5 MsgCreateEx.dwFlags = MSG_FLAG_TEXTVALUES; MsgRTData.dwFlags = MSG_FLAG_COMMENT | MSG_FLAG_TEXTVALUES; MsgCreateEx.dwMsgState = MSG_STATE_COME; MsgRTData.dwMsgState = MSG_STATE_COME; GetComputerNameA(szComputerName, &dwBufSize); sprintf(szTmp, "Computername = %s \r\n", szComputerName); printf(szTmp); strncpy(MsgCreateEx.szComputerName, szComputerName, sizeof(MsgCreateEx.szComputerName) -1); lpszCurrentUser = GetTagChar("@NOP::@CurrentUser"); if (NULL != lpszCurrentUser ) { strncpy( MsgCreateEx.szUser, lpszCurrentUser, sizeof (MsgCreateEx.szUser) - 1); } if ( dwFlags & FLAG_TEXTID_PARAMETER) { MsgCreateEx.dPValue[7] = dwMyTextID; // TextID MsgCreateEx.wPValueUsed = 0x0080; // for process value 8 } MsgCreateEx.wPValueUsed = (WORD)(MsgCreateEx.wPValueUsed | 0x0006); MsgCreateEx.dPValue[1] = doValueOld; // old value MsgCreateEx.dPValue[2] = doValueNew; // new value //====================================== // START_MESSAGE_SERVICE //====================================== memset(&scError,0,sizeof(scError)); // GetServerPrefix to determine MC or Server GetServerTagPrefix(&pszServerPrefix, NULL, NULL); //Return-Type: void if (NULL == pszServerPrefix) { printf("Serverapplication or Single Client\r\n"); bOK = MSRTStartMsgService( &dwServiceID, NULL, NULL, 0, NULL, &scError ); // activate service } else { printf("MultiClient with Prefix : %s\r\n",pszServerPrefix); //Return - Type :char* bOK = MSRTStartMsgServiceMC( &dwServiceID, NULL, NULL, 0, NULL,pszServerPrefix, &scError ); // activate service } if (bOK == FALSE) { printf("GCreateMyOperationMsg() - Unable to start message service! \r\n"); sprintf(szTmp, " Error1 = 0x%0x, Errortext = %s \r\n", scError.dwError1, scError.szErrorText); printf(szTmp); return (-101); } //====================================== //====================================== // PARSE PARAMETERS //====================================== if ( ( dwFlags & FLAG_COMMENT_PARAMETER ) && ( NULL != pszComment ) ) { strncpy(MsgCreateEx.szComment, pszComment, sizeof (MsgCreateEx.szComment) - 1); MsgCreateEx.dwFlags |= MSG_FLAG_COMMENT; } if ( dwFlags & FLAG_COMMENT_DIALOG ) MsgCreateEx.dwFlags |= MSG_FLAG_COMMENT; if (lpszObjectName!= NULL) // = tagname { strncpy (szSource, lpszObjectName, sizeof (lpszObjectName) - 1); strncpy ( MsgCreateEx.szInstance, lpszObjectName, sizeof (MsgCreateEx.szInstance) - 1); strncpy ( MsgCreateEx.mtTextValue[0].szText, lpszObjectName, sizeof (MsgCreateEx.mtTextValue[1].szText) - 1); } if ( szMyText != NULL) { strncpy ( MsgCreateEx.mtTextValue[1].szText, szMyText , sizeof (MsgCreateEx.mtTextValue[1].szText) - 1); } if ( lpszMsg != NULL) { strncpy ( MsgCreateEx.mtTextValue[6].szText, lpszMsg , sizeof (MsgCreateEx.mtTextValue[6].szText) - 1); } //====================================== //====================================== // CREATE MESSAGE //====================================== bOK = MSRTCreateMsgInstanceWithComment(dwServiceID, &MsgCreateEx, &scError) ; if ( TRUE == bOK) { if (FLAG_COMMENT_DIALOG == (dwFlags & FLAG_COMMENT_DIALOG) ) { BOOL bOkay; HWND hWnd = FindWindow("PDLRTisAliveAndWaitsForYou", NULL); //RQ:1072746 begin MSG_COMMENT_STRUCT mComment; sprintf( mComment.szUser, MsgCreateEx.szUser, sizeof(mComment.szUser) - 1 ); for (dwInx = 1; dwActWaitedTime < dwMaxCheckTime; dwInx++) { memset(&scError,0,sizeof(scError)); mComment.dwMsgNr = dwMsgNum; mComment.stTime = time; bOkay = MSRTGetComment (dwServiceID, &mComment, &scError); if (TRUE == bOkay) { break; } else { Sleep(dwStepCheckTime); printf("#W401: GCreateMyOperationMsg(): pre MSRTGetComment() performance warning: waited=%ld / step=%ld/ round=%ld\r\n", dwActWaitedTime+dwStepCheckTime, dwStepCheckTime, dwInx); } dwActWaitedTime += dwStepCheckTime; if (100 < dwStepCheckTime) { dwStepCheckTime -=dwReduceStepTime; } } //RQ:1072746 memset(&scError,0,sizeof(scError)); bOkay= MSRTDialogComment (hWnd, &MsgRTData, &scError); if (TRUE == bOkay) { //MSG_COMMENT_STRUCT mComment; //RQ:1072746 mComment.dwMsgNr = dwMsgNum; mComment.stTime = time; sprintf( mComment.szUser, MsgCreateEx.szUser, sizeof(mComment.szUser) - 1 ); memset(&scError,0,sizeof(scError)); bOkay = MSRTGetComment (dwServiceID, &mComment, &scError); if (TRUE == bOkay) { strncpy(MsgCreateEx.szComment, mComment.szText, sizeof (MsgCreateEx.szComment) - 1); } } else { printf("#E201: GCreateMyOperationMsg() - Error at MSRTGetComment() szErrorText=\"%s\" error2=%d\r\n", scError.szErrorText, scError.dwError2); iRet = -201; } } } if(bOK == FALSE) { printf ("#E301: GCreateMyOperationMsg() - Error at MSRTCreateMsgInstanceWithComment() szErrorText=\"%s\"\r\n", scError.szErrorText); iRet = -301; } //====================================== //====================================== // STOP_MESSAGE_SERVICE //====================================== bOK= MSRTStopMsgService( dwServiceID, &scError); printf("End GCreateMyOperationMsg \r\n"); return (iRet); }
C脚本操作员消息函数二次封装
在不要电子签名只需记录操作消息的地方,为了更方便使用,对函数进行二次封装为函数CreateMyOpMsg()。
函数原型:
int CreateMyOpMsg(char* lpszMsg, char* lpszdevice)
参数:
- lpszMsg,该参数记录操作消息,写入文本块@7%s@中。
- lpszdevice,该参数为设备名,写入到文本块@10%s@中。
返回值:
值 |
描述 |
0 |
函数执行完成没有任何错误 |
-101 |
消息不能被编辑 |
-201 |
当调用”MSRTGetComment()”特征引发一个错误 |
-301 |
当调用”MSRTCreateMsgInstanceWithComment()”特征引发一个错误 |
函数代码:
#include "apdefap.h" int CreateMyOpMsg(char* lpszMsg, char* lpszdevice) { char userComm[1024]=""; return GCreateMyOperationMsg2(0x00000001 ,12508142 , lpszMsg, lpszdevice, 0, 0 , 0 , userComm); //Return-Type: long int }
用VB脚本生成操作员消息
使用VB脚本创建操作员消息的脚本如下:
\'创建操作员消息 dim myAlarm Set myAlarm = HMIRuntime.Alarms(12508142) MyAlarm.State = 5 \'-------------------------- \'State Alarm Log Status \'1 Came In \'2 Went Out \'5 Came in and comment \'6 Gone and comment \'-------------------------- myAlarm.Comment = userComment myAlarm.UserName = userName myAlarm.ProcessValues(10) = Device myAlarm.ProcessValues(7) = Msg MyAlarm.Create
注意:使用VBS生成的操作员消息,在报警记录中没有计算机名。
VB脚本操作员消息函数二次封装
在不要电子签名只需记录操作消息的地方,为了更方便使用,对函数进行二次封装为函数CreateMyOpMsg()。
函数原型:
Function CreateMyOpMsg(inputMsg, device)
参数:
- inputMsg,该参数记录操作消息,写入文本块@7%s@中。
- device,该参数为设备名,写入到文本块@10%s@中。
返回值:
无
函数代码:
Function CreateMyOpMsg(inputMsg, device) \'创建操作员消息 dim myAlarm Set myAlarm = HMIRuntime.Alarms(12508142) MyAlarm.State = 5 \'-------------------------- \'State Alarm Log Status \'1 Came In \'2 Went Out \'5 Came in and comment \'6 Gone and comment \'-------------------------- myAlarm.Comment = userComment myAlarm.UserName = userName myAlarm.ProcessValues(10) = Device myAlarm.ProcessValues(7) = inputMsg MyAlarm.Create End Function
如何生成电子签名
C脚本执行电子签名
函数原型:
int EsigDialog(const char * inputMsg, const char * device)
参数:
- inputMsg,该参数为执行该操作的描述字符串,写入到文本块@7%s@中。
- device,该参数为设备名,写入到文本块@10%s@中。
返回值:
值 | 描述 |
-1 | 函数执行遇到错误 |
1 | 电子签名通过 |
2 | 用户取消了电子签名 |
3 | 三次电子签名未通过 |
注意:
该函数依赖函数GCreateMyOperationMsg2()
函数代码:
#include "apdefap.h" int EsigDialog(const char * inputMsg, const char * device) { //获取用户名和计算机名 char *userName = GetTagChar("@NOP::@CurrentUser"); //Return-Type: char* char *displayedUser = GetTagChar("@NOP::@CurrentUserName"); //Return-Type: char* char *computerName = GetTagChar("@NOP::@LocalMachineName"); //Return-Type: char* char *Domain = ""; //如果加入了域,在此填写域名 int nRet = 0; char szBuf[1024]=""; char myComm[1024]=""; char userComm[1024]=""; VARIANT vtComment; __object* EsigDlg; /*---判断用户是否登录---*/ if (strlen(userName) == 0) { MessageBox(NULL,"用户未登录,请登录后再执行!","Error",MB_SYSTEMMODAL|MB_OK); return -1; } /*---电子签名---*/ EsigDlg = __object_create("CCESigDlg.ESIG"); if (!EsigDlg) { printf("Failed to create Picture Object\n"); return -1; } EsigDlg->forcecomment = FALSE; //非强制注释 nRet = EsigDlg->ShowDialog(userName,displayedUser,Domain,GetLanguage(),&vtComment); __object_delete(EsigDlg); /*---提取注释---*/ sprintf(userComm,"%ls",vtComment.u.bstrVal); VariantClear(&vtComment); /*---合并注释---*/ /* if (strlen(inputMsg)>0 && strlen(userComm)>0) { sprintf(myComm,"%s:%s", inputMsg, userComm); } else if (strlen(inputMsg)>0) { sprintf(myComm,"%s", inputMsg); } else if (strlen(userComm)>0) { sprintf(myComm,"%s", userComm); } */ switch(nRet) { case 1: //InsertAuditEntryNew("","",myComm,0,szBuf); GCreateMyOperationMsg2(0x00000001, 12508142, inputMsg, device, 0, 0, 0, userComm); //Return-Type: long int break; case 2: break; case 3: break; } return nRet; }
C脚本执行电子签名并写入变量新值:
函数原型:
int TagNewValueES(const char *winccTagName,double newValue, const char *description, const char *device)
参数:
- winccTagName,该参数为WinCC变量名,与参数description拼接后写入到文本块@7%s@中。
- newValue,该参数为要写入的新值,只能为数值,不支持字符串,写入到数值块@3%g@中。
- description,该参数为变量描述,说明该变量的含义,与参数winccTagName拼接后写入到文本块@7%s@中。
- device,该参数为设备名,写入到文本块@10%s@中。
返回值:
值 | 描述 |
-1 | 函数执行遇到错误 |
1 | 电子签名通过 |
2 | 用户取消了电子签名 |
3 | 三次电子签名未通过 |
注意:
该函数依赖函数GCreateMyOperationMsg2()
函数代码:
#include "apdefap.h" int TagNewValueES(const char *winccTagName,double newValue, const char *description, const char *device) { //获取用户名和计算机名 char *userName = GetTagChar("@NOP::@CurrentUser"); //Return-Type: char* char *displayedUser = GetTagChar("@NOP::@CurrentUserName"); //Return-Type: char* char *computerName = GetTagChar("@NOP::@LocalMachineName"); //Return-Type: char* char *Domain = "" //如果加入了域,在此填写域名 int nRet = 0; char szBuf[1024]=""; char myComm[1024]=""; char userComm[1024]=""; char tagDescription[1024]=""; double oldValue; VARIANT vtComment; __object* EsigDlg; /*---判断用户是否登录---*/ if (strlen(userName) == 0) { MessageBox(NULL,"用户未登陆,请登陆后再执行!","Error",MB_SYSTEMMODAL|MB_OK); return -1; } /*---电子签名---*/ EsigDlg = __object_create("CCESigDlg.ESIG"); if (!EsigDlg) { printf("Failed to create Picture Object\n"); return -1; } EsigDlg->forcecomment = FALSE; //非强制注释 nRet = EsigDlg->ShowDialog(userName,displayedUser,Domain,GetLanguage(),&vtComment); __object_delete(EsigDlg); /*---提取注释---*/ sprintf(userComm,"%ls",vtComment.u.bstrVal); VariantClear(&vtComment); /*---合并注释---*/ /* if (strlen(description)>0 && strlen(userComm)>0) { sprintf(myComm,"%s:%s", description, userComm); } else if (strlen(description)>0) { sprintf(myComm,"%s", description); } else if (strlen(userComm)>0) { sprintf(myComm,"%s", userComm); } */ switch(nRet) { case 1: //获取旧值 //strcpy(oldValue,GetTagChar(winccTagName)); oldValue = GetTagDouble(winccTagName); //Return-Type: double //写入新值 //SetTagChar(winccTagName,newValue); SetTagDouble(winccTagName,newValue); //Return-Type: BOOL //创建操作员消息 //InsertAuditEntryNew(oldValue,newValue,myComm,0,szBuf); sprintf(tagDescription,"%s %s", winccTagName, description); GCreateMyOperationMsg2(0x00000001 ,12508141 , tagDescription, device, 0, oldValue , newValue , userComm); //Return-Type: long int break; case 2: break; case 3: break; } return nRet; }
VB脚本执行电子签名
函数原型:
Function EsigDialog(inputMsg, device)
参数:
- inputMsg,该参数为执行该操作的描述字符串,写入到文本块@7%s@中。
- device,该参数为设备名,写入到文本块@10%s@中。
返回值:
值 | 描述 |
-1 | 函数执行遇到错误 |
1 | 电子签名通过 |
2 | 用户取消了电子签名 |
3 | 三次电子签名未通过 |
函数代码:
Function EsigDialog(inputMsg, device) \'获取用户名和计算机名 Dim userName, displayedUser, Domain, computerName userName = HMIRuntime.Tags("@NOP::@CurrentUser").Read displayedUser = HMIRuntime.Tags("@NOP::@CurrentUserName").Read computerName = HMIRuntime.Tags("@NOP::@LocalMachineName").Read Domain = "" If userName = "" Then Msgbox "用户未登陆,请登陆后再执行" EsigDialog = -1 Exit Function End If \'电子签名 Dim myEsig Dim myComment Dim userComment Dim ret Set myEsig = CreateObject("CCEsigDlg.ESIG") \'不强制注释 myEsig.forcecomment = False ret = myEsig.showDialog(userName,displayedUser,Domain, HMIRuntime.Language, userComment) \' If Trim(inputMsg)<>"" And Trim(userComment)<>"" Then \' myComment = inputMsg&":"&userComment \' Elseif Trim(inputMsg)<>"" Then \' myComment = inputMsg \' Elseif Trim(userComment)<>"" Then \' myComment = userComment \' Else \' myComment = "" \' End If Select Case ret Case 1 \'用户成功获得验证 \'Call InsertAuditEntryNew("", "", myComment, 0) \'创建操作员消息 dim myAlarm Set myAlarm = HMIRuntime.Alarms(12508142) MyAlarm.State = 5 \'-------------------------- \'State Alarm Log Status \'1 Came In \'2 Went Out \'5 Came in and comment \'6 Gone and comment \'-------------------------- myAlarm.Comment = userComment myAlarm.UserName = userName myAlarm.ProcessValues(10) = device myAlarm.ProcessValues(7) = inputMsg MyAlarm.Create Case 2 \'用户使用“取消”按钮关闭了对话框。 Case 3 \'用户 3 次验证均失败。 End Select EsigDialog = ret End Function
VB脚本执行电子签名并写入新值
函数原型:
Function TagNewValueES(WinccTagName,NewValue,description,device)
参数:
- winccTagName,该参数为WinCC变量名,与参数description拼接后写入到文本块@7%s@中。
- newValue,该参数为要写入的新值,只能为数值,不支持字符串,写入到数值块@3%g@中。
- description,该参数为变量描述,说明该变量的含义,与参数winccTagName拼接后写入到文本块@7%s@中。
- device,该参数为设备名,写入到文本块@10%s@中。
返回值:
值 | 描述 |
-1 | 函数执行遇到错误 |
1 | 电子签名通过 |
2 | 用户取消了电子签名 |
3 | 三次电子签名未通过 |
函数代码:
Function TagNewValueES(WinccTagName,NewValue,description,device) \'获取用户名和计算机名 Dim userName, displayedUser, Domain, computerName userName = HMIRuntime.Tags("@NOP::@CurrentUser").Read displayedUser = HMIRuntime.Tags("@NOP::@CurrentUserName").Read computerName = HMIRuntime.Tags("@NOP::@LocalMachineName").Read Domain = "" If userName = "" Then Msgbox "用户未登陆,请登陆后再执行" TagNewValueES = -1 Exit function End If \'电子签名 Dim myEsig Dim myComment Dim userComment Dim ret Set myEsig = CreateObject("CCEsigDlg.ESIG") \'不强制注释 myEsig.forcecomment = False ret = myEsig.showDialog(userName,displayedUser, Domain, HMIRuntime.Language, userComment) \' If Trim(description)<>"" And Trim(userComment)<>"" Then \' myComment = description&":"&userComment \' Elseif Trim(description)<>"" Then \' myComment = description \' Elseif Trim(userComment)<>"" Then \' myComment = userComment \' Else \' myComment = "" \' End If Select Case ret Case 1 \'用户成功获得验证 \'获取变量旧值 dim OldValue, WinCCTag Set WinCCTag = HMIRuntime.Tags(WinccTagName) OldValue = WinCCTag.read \'给变量写入新值 WinCCTag.Write NewValue If WinCCTag.LastError <> 0 Then Msgbox WinCCTag.ErrorDescription TagNewValueES = -1 Exit Function End If \'创建操作员消息 dim myAlarm set myAlarm = HMIRuntime.Alarms(12508141) \'@10%s@: @7%s@ @102%s@ 新=@3%g@ @8%s@ 旧=@2%g@ @8%s@\' MyAlarm.State = 5 \'-------------------------- \'State Alarm Log Status \'1 Came In \'2 Went Out \'5 Came in and comment \'6 Gone and comment \'-------------------------- myAlarm.Comment = userComment myAlarm.UserName = userName myAlarm.ProcessValues(2) = OldValue\'旧值 myAlarm.ProcessValues(3) = NewValue\'新值 myAlarm.ProcessValues(7) = WinccTagName &" "&description \'变量名+变量描述 myAlarm.ProcessValues(10) = device\'设备名 MyAlarm.Create \'InsertAuditEntryNew(OldValue, NewValue, myComment, 0) Case 2 \'用户使用“取消”按钮关闭了对话框。 Case 3 \'用户 3 次验证均失败。 End Select TagNewValueES = ret End Function