专注于射频工程师,天线工程师和电子硬件工程师的培养
首页 > 技术文库 > 硬件设计 > 测试测量 > Labwindows 虚拟仪器高级应用-文本、文件类程序设计技术之:十六进制文本文件转换

Labwindows 虚拟仪器高级应用-文本、文件类程序设计技术之:十六进制文本文件转换

时间:2020-12-14 点击:

1.2 十六进制文本文件转换

1.2.1 十六进制文本文件转换设计方法

在LabWindows/CVI 程序设计中,一些参数需要进行初始化,如字符串常量、动态链接库、静态链接库等。如字符串常量内容非常大,通常都会以文本文件的形式存储到磁盘上,动态链接库、静态链接库等也是如此。有没有办法,将这些文件都一同打包到可执行文件中,使其只包含一个可执行文件呢?将常量文本、各种库文件都打包到可执行文件中,便于文件的管理与操作。办法是有的,可以将这些文件导出为十六进制文本文件格式,并以C 语言的.h 头文件形式出现,在.c 源程序中通过#include 方式引用并在实现代码中调用,这些文件就可以通过隐式方式随可执行文件一起装载到内存中,调用简单而且方便。

在本例程中,由于涉及大量数据的转换,而这些转换又是在一个for 循环语句中完成的,因此,程序执行期间不能及时响应外部的操作,如拖拽标题栏、点击面板按钮等,为了提高系统响应的及时性以及算法执行效率,采用多线程技术,将转换算法放入一个新线程中执行,即实现界面逻辑与算法逻辑的分离,效果会更好一些。

一个采用了多线程技术的应用程序可以更好地利用系统资源。其主要优势在于充分利用了CPU 的空闲时间片,可以用尽可能少的时间来对用户的要求做出响应,使得进程的整体运行效率得到较大提高,同时增强了应用程序的灵活性。更为重要的是,由于同一进程的所有线程是共享同一内存的,所以不需要特殊的数据传送机制,不需要建立共享存储区或共享文件,从而使得不同任务之间的协调操作与运行、数据的交互、资源的分配等问题更加易于解决。

对于本例程来说,界面逻辑虽然在算法执行过程中可以实现移动、按钮点击等操作,但如果在转换没有完成时进行第二次转换操作(如点击转换按钮),可能会出现逻辑错误,因此,最好在程序设计时考虑按钮的有效性或按钮自锁功能。

1.2.2 十六进制文本文件转换程序设计

(1)面板设计

编写一个十六进制文本文件转换程序,可将各种文件如.txt、.dll、.lib、.obj 、.exe 等转换为十六进制文本格式,如“0x0A3, 0xB4, … ”等,并以.h 或.c 等格式存储,方便在程序运行时载入。点击转换按钮,弹出打开文件对话框,选择要转换的文件,之后会弹出保存对话框,提示保存为何种类型的文件,最后程序自动进行转换,在转换进度条中显示实际的转换进度,进度条到达满刻度时,转换完成。在编辑状态下双击Numeric Slide 控件,弹出Edit Numeric Slide 对话框,设置Data type 为int,Control mode 为Indicator ,点击Range Values… 按钮,弹出Edit Range Values 对话框,设置最小值Minimum 为0,如图1-5 所示,点击Show/Hide Parts… 按钮,弹出Show/Hide Parts 对话框,设置相关属性为No Markers 、No Ticks 等,如图1-6 所示。面板设计如图1-7 所示,面板中主


图1-5 Edit Range Values 对话框


图1-6 Show/Hide Parts 对话框


图1-7 十六进制文本文件转换面板

1-5 控件属性设置表

常量名

控件类型

控件的主要属性

PANEL

Panel

标题:十六进制文本文件转换回调函数:panelCB

PROGRESS

Numeric Slide

标题:转换进度

TRANSLATE

Command Button

标题:转换回调函数:Translate

(2)程序源代码

//头文件声明

#include <ansi_c.h>

#include <formatio.h>

#include <utility.h>

#include <cvirte.h>

#include <userint.h>

#include "十六进制文本文件转换.h"

//全局静态变量

static int BytesRead;

static CmtThreadFunctionID ThreadID;

static int panelHandle;

static char *buffer;

char SaveDir[MAX_PATHNAME_LEN];

//线程回调函数声明

int CVICALLBACK HextoText (void *functionData);

//主函数

int main (int argc, char *argv[])

{
if (InitCVIRTE (0, argv, 0) == 0)

return -1;

/* out of memory */

if ((panelHandle = LoadPanel (0, " 十六进制文本文件转换.uir", PANEL)) < 0)

return -1;

DisplayPanel (panelHandle);

RunUserInterface ();

//释放资源

free (buffer);

DiscardPanel (panelHandle);

return 0;

}

//面板回调函数

int CVICALLBACK panelCB (int panel, int event, void *callbackData,

int eventData1, int eventData2)

{

switch (event)

{

case EVENT_CLOSE:

QuitUserInterface (0);

break;

}

return 0;

}
//转换按钮

int CVICALLBACK Translate (int panel, int control, int event,

void *callbackData, int eventData1, int eventData2)

{

int SaveSel;

int Present;

ssize_t FileSize;

int sel;

char path[MAX_PATHNAME_LEN];

int FileHandle;

switch (event)

{

case EVENT_COMMIT:

// 打开文件,获得文件信息

sel = FileSelectPopup ("", "*.txt", "*.txt;*.dll;*.lib;*.obj;*.exe", " 打开", VAL_LOAD_BUTTON,

0, 0, 1, 1, path);

Present = GetFileInfo (path, &FileSize);

// 保存为十六进制文本文件

SaveSel = FileSelectPopup ("", "*.h", "*.h;*.c;*.txt", " 保存", VAL_SAVE_BUTTON, 0, 0, 1, 1,

SaveDir);

// 判断文件是否存在

if ((sel > 0) && (Present == 1) && (SaveSel > 0))

{

buffer = malloc (FileSize * sizeof(char) + 1);buffer[0] =

'\0';

// 打开文件,读取数据并关闭文件

FileHandle = OpenFile (path, VAL_READ_ONLY, VAL_OPEN_AS_IS, VAL_BINARY);

BytesRead = ReadFile (FileHandle, buffer, FileSize);

CloseFile (FileHandle);

// 创建线程

CmtScheduleThreadPoolFunction

(DEFAULT_THREAD_POOL_HANDLE,

HextoText,

NULL, &ThreadID);

// 等待线程执行完毕

CmtWaitForThreadPoolFunctionCompletion

(DEFAULT_THREAD_POOL_HANDLE,

ThreadID, OPT_TP_PROCESS_EVENTS_WHILE_WAITING);

// 释放线程资源

CmtReleaseThreadPoolFunctionID (DEFAULT_THREAD_POOL_HANDLE, ThreadID);

}

break;

}

return 0;

}
//线程回调函数—将十六进制数据转换为文本形式

int CVICALLBACK HextoText (void *functionData)

{

int i;

int file;

int Bytes;

unsigned char temp;

unsigned char str[10];

unsigned char *bin;

//设置Numeric Slide 控件最大值,用作进度条

SetCtrlAttribute (panelHandle, PANEL_PROGRESS, ATTR_MAX_VALUE, BytesRead);

//转换为十六进制

file = OpenFile (SaveDir, VAL_WRITE_ONLY, VAL_TRUNCATE, VAL_ASCII);

bin = malloc (BytesRead * sizeof(char) * 6 + 1);

bin[0] =

'\0';

for (i = 0; i < BytesRead; i ++)

{

str[0] = '0';

str[1] = 'x';

//高四位
        

temp = (buffer[i] >> 4) & 0x0F;

if (temp >= 10)

{

str[2] = temp - 10 + 'A';

}

else

{

str[2] = temp + '0';

}

//低四位

temp = (buffer[i]) & 0x0F;

if (temp >= 10)

{

str[3] = temp - 10 + 'A';

}

else

{

str[3] = temp + '0';

}

str[4] = ',';

str[5] = '\0';

strcat (bin, str);

//设置Numeric Slide 控件当前值

SetCtrlVal (panelHandle, PANEL_PROGRESS, i);

}

//将数据保存到文件

Bytes = WriteFile (file, bin, strlen(bin));

//释放资源

free (bin);

CloseFile (file);return 0;

}

(3)程序注释

① CmtScheduleThreadPoolFunction 函数通知线程池创建一个新线程。如果线程池存在空闲线程,则被分配给该线程。如果没有空闲线程并且线程池中的线程没有达到线程池上限,线程池将创建一个新线程。当线程池达到线程上限且没有空闲线程,线程池将等待直到有空闲线程或有可用线程再分配给该线程。如果需要用到定时器回调函数,可以采用异步定时器(在自身线程中执行),不需要用定时器控件再开辟一个新线程。函数原型为:

int CmtScheduleThreadPoolFunction (int poolHandle, ThreadFunctionPtr threadFunction, void *threadFunctionData, int *threadFunctionID);

poolHandle :线程池句柄。可以采用DEFAULT_THREAD_POOL_HANDLE 常量,表示一个默认的线程池句柄。如果采用RT 应用,不能使用默认线程池句柄,由于默认线程池句柄即使在RTMain 函数退出后仍在运行,使系统运行不稳定,需要创建一个新线程池并且在退出RTMain 函数之前释放线程池句柄。

threadFunction :线程函数。函数原型为:

int CVICALLBACK ThreadFunction (void *functionData);

*functionData :线程函数参数传递数据。在任何线程中,可以通过CmtGetThreadPoolFunction-Attribute 函数的ATTR_TP_FUNCTION_RETURN_VALUE 属性获得该线程所要传递的数据。

*threadFunctionData :线程函数回调数据。不能传递局部变量地址或其他可能在函数执行过程中无效的地址。

*threadFunctionID :标识线程函数的唯一ID 号。可以通过ID 号来获得线程函数的各种属性,包括线程函数的返回值,并可以将ID 号传递给CmtWaitForThreadPoolFunctionCompletion 函数,等待线程函数执行完毕进行其他操作。如果不需要此参数,输入NULL 。如果该参数值不为NULL, 必须用CmtReleaseThreadPoolFunctionID 函数释放ID 号。值得注意的是,如果该工程使用的版本低于LabWindows/CVI 2009 ,并在LabWindows/CVI 2009 以上的版本中重新编译时,必须在工程中包含头文件cvi2009compat.h 。

② CmtWaitForThreadPoolFunctionCompletion 函数等待指定线程函数执行完毕。可以使用CmtScheduleThreadPoolFunctionAdv 函数等待指定线程函数执行完毕。如果在线程中调用CmtWaitForThreadPoolFunctionCompletion 函数并且使用了ActiveX 函数,由于在线程初始化时使用了ActiveX 内部线程模式,可能造成两个线程之间的死锁,可以调用CA_InitActiveXThreadStyle-ForCurrentThread 函数为ActiveX 设置单独线程。函数原型为:

int CmtWaitForThreadPoolFunctionCompletion (int poolHandle, int threadFunctionID, unsigned int options);

options:选项参数。OPT_TP_PROCESS_EVENTS_WHILE_WAITING 指当等待线程执行期间,线程可以响应事件;当参数为0 时指等待线程执行期间,线程不能响应事件。任何线程中所创建的面板或对话框窗口在没有被释放以前,一定会响应一些事件。如果此时线程不能响应事件,可能导致系统“挂起”。为了避免这种情况的产生,应当使用OPT_TP_PROCESS_EVENTS_WHILE_ WAITING 参数来等待响应发送消息事件、显示面板事件及对话框事件。

③ CmtReleaseThreadPoolFunctionID 函数释放被CmtScheduleThreadPoolFunction 函数或CmtScheduleThreadPoolFunctionAdv 函数创建的线程ID 号,如果 CmtScheduleThreadPoolFunction 函数或CmtScheduleThreadPoolFunctionAdv 函数的threadFunctionID 参数值设置为NULL,系统将自动在线程调用执行完毕之后释放线程ID,可以不必调用此函数。函数不会中断一个将要执行或正在执行的线程函数。调用CmtDiscardThreadPool 函数将自动释放属于指定线程池的任何线程ID 号。函数原型为:

int CmtReleaseThreadPoolFunctionID (int poolHandle, int threadFunctionID);

④ 不使用多线程技术的程序处理方法如果不使用多线程技术,只需要将Translate 回调函数中的CmtScheduleThreadPoolFunction 、CmtWaitForThreadPoolFunctionCompletion 和CmtRelease- ThreadPoolFunctionID 函数删除,直接调用HextoText 函数,但此函数需要一个空指针类型数据,可以借用Translate 中的callbackData 参数。采用这种技术能显著降低CPU 的使用率,在系统资源有限的情况下是不错的解决方案,其实现代码如下:

int CVICALLBACK Translate (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)

{  int SaveSel;  int Present; ssize_t FileSize; int sel;

 char path[MAX_PATHNAME_LEN];

 int FileHandle;

 switch (event)

{

case EVENT_COMMIT:

 // 打开文件,获得文件信息

 sel = FileSelectPopup ("", "*.txt", "*.txt;*.dll;*.lib;*.obj;*.exe", " 打开", VAL_LOAD_BUTTON, 0, 0, 1, 1, path);

 Present = GetFileInfo (path, &FileSize);

 // 保存为十六进制文本文件

SaveSel = FileSelectPopup ("", "*.h", "*.h;*.c;*.txt", " 保存", VAL_SAVE_BUTTON, 0, 0, 1, 1, SaveDir);

 // 判断文件是否存在

if ((sel > 0) && (Present == 1) && (SaveSel > 0))

 {

buffer = malloc (FileSize * sizeof(char) + 1);

  buffer[0] = '\0';

  // 打开文件,读取数据并关闭文件

  FileHandle = OpenFile (path, VAL_READ_ONLY, VAL_OPEN_AS_IS, VAL_BINARY);

BytesRead = ReadFile (FileHandle, buffer, FileSize);

  CloseFile (FileHandle);

  // 不使用多线程技术

  HextoText (callbackData);

}

 break;

 }

 return 0;

}

在HextoText 函数中添加SetSleepPolicy 函数,设置系统的Sleep 策略,使系统不进入Sleep 状态,以加快算法执行速度,提高算法效率,添加ProcessSystemEvents 函数,使系统能及时响应鼠标事件。算法只需要进行少量代码修改,就能完成与多线程方式类似的操作。HextoText 函数实现代码修改如下:

int CVICALLBACK HextoText (void *functionData)

{

int i;      

 int file;

 int Bytes;

 unsigned char temp;

unsigned char str[10];

unsigned char *bin;

//设置Numeric Slide 控件最大值,用作进度条

SetCtrlAttribute (panelHandle, PANEL_PROGRESS, ATTR_MAX_VALUE, BytesRead);

//转换为十六进制

file = OpenFile (SaveDir, VAL_WRITE_ONLY, VAL_TRUNCATE, VAL_ASCII);

bin = malloc (BytesRead * sizeof(char) * 6 + 1);  bin[0] = '\0'; for (i = 0; i < BytesRead; i ++) {

str[0] = '0'; str[1] = 'x'; //高四位temp = (buffer[i] >> 4) & 0x0F; if (temp >= 10){ str[2] = temp - 10 + 'A';
}
else
{
 

 str[2] = temp + '0'; } //低四位temp = (buffer[i]) & 0x0F;if (temp >= 10){

str[3] = temp - 10 + 'A';
}
else
{
 

 str[3] = temp + '0'; } str[4] = ','; str[5] = '\0'; strcat (bin, str); //设置Numeric Slide 控件当前值SetCtrlVal (panelHandle, PANEL_PROGRESS, i);//处理面板事件SetSleepPolicy (VAL_SLEEP_NONE); ProcessSystemEvents ();

 } //将数据保存到文件Bytes = WriteFile (file, bin, strlen(bin)); //释放资源 free (bin);  CloseFile (file);  return 0;

}

(4)运行效果图

点击工具栏中的Debug Project 按钮,程序开始运行,其效果如图1-8 所示。


图1-8 运行效果图

CopyRight © 2009-2021,易迪拓培训 All Rights Reserved,沪ICP备05048810-2号 版权所有

网站地图

Top