如何实现excel电子表格审计追踪功能

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何实现excel电子表格审计追踪功能相关的知识,希望对你有一定的参考价值。

参考技术A 假设工作簿A中的公式引用工作簿B中的单元格
打开工作簿A,点击“公式”选项卡
下面有两种方法可以快速定位
点击“显示公式”
然后使用查找Ctrl+F,
查找公式中有工作簿B名称的单元格即可。
点击“监视窗口”
点击“添加监视”,
选取有数据的单元格,
然后观察“公式”那一列,是否包含工作簿B名称
双击其中的一行,即可快速定位引用工作簿B的单元格。
参考技术B 实现电子表格的合规使用,最靠谱的方法还是要借助第三方合规插件。einfotree合规插件是目前电子表格合规的最佳解决方案,他能够弥补标准excel在合规使用上的先天不足,这个先天不足就包括权限控制,审计追踪和电子签名。就审计追踪要素而言,einfotree能够做到记录对电子表格的任何修改:用户的登录,登出,对电子表格的任何操作,追踪到单元格级别,并且审计追踪信息无法被删除,篡改;同时能够实现限制性访问:采用唯一的用户名和密码,实现密码生命周期管理,多级别访问控制;还能够设置电子签名工作流,实现记录无纸化,为制药企业电子表格的合规使用保驾护航。

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 GCreateMyOperationMsg( 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 
");


    //======================================
    // 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  
", 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
");
        bOK = MSRTStartMsgService( &dwServiceID, NULL, NULL, 0, NULL, &scError ); // activate service
    }
    else    
    {
        printf("MultiClient with Prefix : %s
",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! 
");
       sprintf(szTmp, " Error1 = 0x%0x, Errortext = %s 
", 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
", 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
", scError.szErrorText, scError.dwError2);
                      iRet = -201;
              }
       }
  
    }


    if(bOK == FALSE)
    {
      printf ("#E301: GCreateMyOperationMsg()  - Error at MSRTCreateMsgInstanceWithComment()  szErrorText="%s"
", scError.szErrorText);
      iRet = -301;
    }
    //======================================


    //======================================
    // STOP_MESSAGE_SERVICE
    //======================================
    bOK= MSRTStopMsgService(    dwServiceID, &scError); 
    printf("End GCreateMyOperationMsg 
");
    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 EsigDialog(inputMsg, device) 

参数:

  • inputMsg,该参数记录操作消息,写入文本块@7%s@中。
  • device,该参数为设备名,写入到文本块@10%s@中。

返回值:


函数代码:

Function EsigDialog(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
");
    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
");
    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

  

以上是关于如何实现excel电子表格审计追踪功能的主要内容,如果未能解决你的问题,请参考以下文章

如何利用Excel电子表格实现纯表格循环扩展报表

电子表格文字怎么换行?

怎样做电子表格

如何在实现在html编辑器中插入Excel电子表格

excel 表格怎样设置追踪?

利用c语言怎么实现excel电子表格里面数据的提取