在这个CSDN的博客,也曾想多写些技术文章,把自己的开发感悟和经验记录下来,供那些同在这条路上奔波劳累码农们多少减轻点负担。但是,一番忙碌后,项目结束了,人也累了,就没有了写的激情。这个博客对于那些辛勤写作的程序猿们,也没有什么奖励,偶尔想下载的小工具、小程序,也得充值成为VIP才行,于是就更没有了动手的激情~~~~~。
开这个博客,就当个记事薄,平时偶尔解决个小问题,随手记录,日后再遇到,也有个参考,如果恰巧有帮到某个“猿”,就算是赚了~~~~~~~~~~~~。
(1)要用tomcat发布个小网页,在自己机器上挺好的,那些运行所需要环境,也不知什么时候就具备了,要布置到别人的服务器上,事先准备好,总是好的,可以避免现场的混乱,浪费时间。最好把需要的环境都放到tomcat的同一个目录,最紧要的是Java的JDK或JRE,这是tomcat必须的运行环境,注意tomcat对Java版本也是要求的。
tomcat是会自己检查主机的Java环境并使用的,也会自己检查本尊的安装目录,如果主机环境变量没有Java的环境或者其版本不满足你的tomcat的要求,总会有点不方便。为避免现场的忙乱,最好是直接把需要的版本拷贝到tomcat目录下并设置,就可以在客户那里复制粘贴及了事,可不潇洒........。
啰嗦半天了,言归正传:将需要的Java JDK或JRE目录拷贝到tomcat目录下,在tomcat\bin子目录下,找到批处理文件:startup.bat 。
在call "%EXECUTABLE%" start %CMD_LINE_ARGS%之前,加上一句:
set "JAVA_HOME=%CATALINA_HOME%\jdk1.8.0_25" //这里应设置为你的版本
或者:set "JRE_HOME=%CATALINA_HOME%\jre1.8.0_25"
注意:tomcat一般使用JRE,如果它检测到JRE存在,会直接使用你设定目录下的JRE目录。
CATALINA_HOME是tomcat的安装目录,它会自己检测到,不要任何设置,倒很方便。
在同一目录下,还有一个setclasspath.bat ,这里会设置JAVA的位置,如果在startup.bat
做了设置,那么它就会跳过JAVA目录的设置,相当于截了胡。其实在这个目录中设置也是
可以的。
(2)在各种JAVA开发应用中,经常要适应logger,对各种的操作或处理信息做日志文件。通常使用apache的log4j,如果没有正确的配置,经常会出现如下的错误提示:
log4j:WARN No appenders could be found for logger (SmartLic.SmartLicClient).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
在不同的开发环境下可能解决方法不同,但总体来讲,要确定
(1)需要一个log4j的设置正确配置文件log4j.properties.
(2)文件的位置,应在正确目录下。
下面是一个正确的配置文件范本,可以将日志信息输出到文件中:
log4j.rootLogger=INFO,logfile
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.ImmediateFlush=true
log4j.appender.logfile.Append=true
log4j.appender.logfile.Threshold=DEBUG
log4j.appender.logfile.File=log\\logs.log //这个目录根据实际情况修改。
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
第二个问题,可以通过一个函数指定:
PropertyConfigurator.configure("log4j.properties");
(3)今天又遇到一个常见的问题:UDF-8编码的中文在GBK的JAVA开发环境中出现乱码,需要从UTF-8编码的字符串,转换为UNICODE的字符串,本以为这个是常见的问题,所有就在网上百度解决方法,结果很是意外,虽然有很多博客文章谈及这个问题,但是花了几个小时,也没有找到能用程序,很多朋友的程序和解决方法,拿来却不可用,只有自己动手了。理解了下UTF-8编码的知识:一个中文字符是3个字节编码,首字符的高四位,从高位到低位有多少个比特为1,则表示有几个字节的编码,首字节的低四位对应该汉字编码的UNICODE编码的高四位,第二字节的高2位为10,低6位为对应汉字UNICODE编码的中间6位,第三字节的高2位也是10,低6位对UNICODE编码的低6位,这样就可以得到该汉字16位的UNICODE编码。之后就可以转换为UNICODE的字符串了。程序如下,供参考。调用前,确保byteArr是UTF-8编码的字节数组。通过JAVA函数getBytes可以很容易得到UTF-8编码的字节数组。
public static String UDF8TouUDF16BE(byte[] byteArr)
{
StringBuffer sb=new StringBuffer();
String ch="";
byte[] bs=new byte[]{0,0};
int len=byteArr.length;
int b1=0,b2=0,b3=0,b=0;
if (len%3!=0) return "";
for (int i=0;i<len;i=i+3)
{
b1=byteArr[i]&(byte)0x0f;
b1=b1<<12;
b2=byteArr[i+1]&(byte)0x3f;
b2=b2<<6;
b3=byteArr[i+2]&(byte)0x3f;
b=b1+b2+b3;
bs[0]=(byte)(b>>>8);
bs[1]=(byte)(b&0x00ff);
try {
ch=new String(bs,"UTF-16BE");
sb.append(ch);
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return sb.toString();
}
(3)CString 和 char* 的转换
将char*转换为CString 比较简单 直接使用char*使用CString的构造函数生成CString
char str[10]="abc";CString cstr(str);
CString 转为 char* :
CString是MFC封装的字符串类型,如果项目字符集属性设置为UNICODE:使用wcstombs_s可以转换为char*,如果项目字符属性为多字节或为未设置:使用getBuffer(0), 即可获取到char*的字符串。
(4)BMP格式图像文件的结构(2022.01.25)
BMP格式的图像文件一般由四个部分组成:
1.文件信息头。所有该类型文件必须有的数据结构,14字节,其结构定义为:
typedef struct tagBITMAPFILEHEADER {
WORD bfType; //文件类型标志,必须是0x4D42(19778)
DWORD bfSize; //文件大小
WORD bfReserved1; //保留,为0
WORD bfReserved2; //保留,为0
DWORD bfOffBits; //颜色数据或颜色索引数据到文件首部的偏移。
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;
2.图像数据信息结构,40字节,其定义为:
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //本结构大小
LONG biWidth; //图像的宽度,单位是像素
LONG biHeight; //图像的高度, 像素
WORD biPlanes; //图像位面,一般都是1
WORD biBitCount; //每像素比特数
DWORD biCompression; //压缩方式:RLE4或RLE8,一般不压缩,为0
DWORD biSizeImage; //颜色数据或颜色索引数据的大小。多种方式可以计算,可以为0
LONG biXPelsPerMeter; //每米X方向像素数
LONG biYPelsPerMeter; //每米Y方向像素数
DWORD biClrUsed; //使用的颜色数。可以为0,指所有颜色
DWORD biClrImportant; //重要的颜色数。 为0,指所有颜色都重要
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;
3.调色板。24位的真彩图像,没有调色板。
颜色深度为1:2个色板,深度4:16个色板,深度8:256个色板
每种颜色板的结构:
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
4.这一部分对24位真彩图像,为RGB的颜色值,其结构为:
typedef struct tagRGBTRIPLE {
BYTE rgbtBlue;
BYTE rgbtGreen;
BYTE rgbtRed;
} RGBTRIPLE, *PRGBTRIPLE, NEAR *NPRGBTRIPLE, FAR *LPRGBTRIPLE;
其它深度的图像,这部分是图像调色板的索引值。
需要注意的是:图像的每行数据的字节数必须是4的整数倍,不足的需要补0。PHOTOSHOP生成的BMP文件格式结尾可能会加两个值为0字节。(待证实)
编程注意事项:
1. 应灵活应用上述结构定义,对从文件中读取的数据进行强制类型转换,可以方便获取有关数据。
2.在使用StretchDIBits时,有个结构:BITMAPINFO
int WINAPI StretchDIBits(_In_ HDC hdc, _In_ int xDest, _In_ int yDest, _In_ int DestWidth, _In_ int DestHeight, _In_ int xSrc, _In_ int ySrc, _In_ int SrcWidth, _In_ int SrcHeight,
_In_opt_ CONST VOID * lpBits, _In_ CONST BITMAPINFO * lpbmi, _In_ UINT iUsage, _In_ DWORD rop);
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
} BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO;
指向这个结构的指针要注意:这个结构中的第二个成员,其实是一个调色板。要正确的显示,这个指针指向的数据必须包含有需显示图像的图像信息和调色板【必须指向BITMAPINFOHEADER的指针】。不需要提供其它的调色板,这个成员本身包含调色板的指针。VOID * lpBits参数是图像数据的指针。
3.在图像处理时,应该注意每行额外补充的数据0。
4.VC MFC实现BMP文件的处理,可以创建SDI应用程序。SDI应用程序,包含三个重要的类:主窗口:CMainFrm,文档类DOC和视类View。可以定义一个CDib类,专用于处理BMP图像,打开图像时,获取所有的图像数据和相关结构的指针。在DOC类中,可以在打开文件时,完成CDib对象的初始化,在View类中的OnDraw事件中,完成显示。在CMainFrm中添加菜单和对话框的方法是:在CMainFrm中的OnCreate方法中:
CMenu *pFrameMenu = GetMenu();
if (pFrameMenu!=NULL)
{
CMenu* pNewPopMenu = new CMenu;
pNewPopMenu->CreatePopupMenu();
pNewPopMenu->AppendMenuW(MF_STRING, ID_MENU_NORMAL,_T("常规"));
pNewPopMenu->AppendMenu(MF_STRING, ID_MENU_TODOWN, _T("向下扫"));
pNewPopMenu->AppendMenuW(MF_STRING, ID_MENU_TOUP,_T("向上扫"));
pNewPopMenu->AppendMenu(MF_STRING, ID_MENU_TORIGHT, _T("向右扫"));
pNewPopMenu->AppendMenuW(MF_STRING, ID_MENU_TOLEFT,_T("向左扫"));
pNewPopMenu->AppendMenuW(MF_STRING, ID_MENU_GRADUAL,_T("淡入"));
pFrameMenu->InsertMenuW(3, MF_BYPOSITION | MF_POPUP, (UINT)pNewPopMenu->m_hMenu,_T("显示(D)"));
CMenu* pNewPopMenu2 = new CMenu;
pNewPopMenu2->CreatePopupMenu();
pNewPopMenu2->AppendMenuW(MF_STRING, ID_MENU_MOVE,_T("平移"));
pNewPopMenu2->AppendMenuW(MF_STRING, ID_MENU_STRETCH,_T("放大"));
pNewPopMenu2->AppendMenuW(MF_STRING, ID_MENU_XMIRROR,_T("横向镜像"));
pNewPopMenu2->AppendMenuW(MF_STRING, ID_MENU_YMIRROR,_T("纵向镜像"));
pFrameMenu->InsertMenuW(4, MF_BYPOSITION | MF_POPUP, (UINT)pNewPopMenu2->m_hMenu,_T("变换(T)"));
}
上述代码,可以在主菜单中添加菜单项并附加上弹出式菜单。其中的菜单ID需要在Resource.h中定义。
然后,需要给菜单添加事件处理代码:包括消息映射和事件处理代码。
如果事件处理中,需要弹出对话框完成参数的输入,按如下方式操作:
(a) 在解决方案右击鼠标,选择【添加】【资源】-【对话框】。之后完成对话框中控件的设计。
(b)在解决方案右击鼠标, 选择【添加】【类】【MFC】,这时注意选择基类为CDiaglog(EX), 选
择刚设计对话框资源。然后,就可以给对话框添加事件处理、变量等。
(5)VS MFC SDI方式下,有时需要非模式对话框,与上面提到的模式对话框的产生有些不同。总结如下:首先还是在资源中添加一个对话框资源,然后以CDialog或CDialogEx为基类添加一个MFC类,选择刚创建的对话框资源ID,就产生了一个对话框类。和模式对话框不同的是,不能用Domodal方法显示对话框:例如,
if (m_pThresholdDlg==NULL)
{
m_pThresholdDlg=new CThresholdDlg(pDib,this);
m_pThresholdDlg->Create(IDD_DIALOG4,this);
}
m_pThresholdDlg->ShowWindow(SW_SHOW);
这里m_pThresholdDlg是在对话框的父窗口中定义的一个所设计对话框的指针变量。
上面的代码就会显示一个非模式对话框。注意,退出对话框的时候,应该向父窗口发送信息,以便释放对话框资源。主窗口退出,也应该向对话框发送退出消息,释放相关资源。
(6)获取本机IP地址
获取本机IP地址或主机名是常用的操作,下面是关键代码:
必须先启动网络驱动。WSASetUP.
if((hostinfo = gethostbyname(hn))!= NULL) //获得本地ipv4地址
{ //hn是主机名字符串,
for (int i=0;;i++) //循环获取所有IP
{
p = hostinfo->h_addr_list[i];
if (p!=NULL)
szIP = ::inet_ntoa(*(in_addr*)p); //这个串中就是IP
else break;
}