c++使用CreateProcess启动程序

有时候需要在程序中启动第三方程序,执行某个命令行之类的事情,也可以做个简单的启动程序启动浏览器之类的传入一些参数,下面来看个简单的例子:

创建一个命令行程序,启动程序的时候可能会遇到一些权限问题,在项目的属性里面可以设置启动的权限, 使用管理员权限

image

下面是个启动浏览器的简单示例

#include "stdafx.h"
#include <windows.h>
#include <string>
#include<stdio.h>

#pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
int main(int argc, _TCHAR* argv[])
{
	TCHAR cmdline[] = L"C:\\Program Files\\Internet Explorer\\iexplore.exe http://www.fullstacks.cn";
	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));  
	si.cb = sizeof(STARTUPINFO);  
	si.dwFlags = STARTF_USESHOWWINDOW;  
	si.wShowWindow = SW_SHOW;  
	PROCESS_INFORMATION pi;
	CreateProcess(NULL,cmdline,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi);

	return 0;
}

其中#pragma comment …这一行的意思是启动的时候不显示命令行窗口,双击打开目标程序的时候直接就打开浏览器了;

对于启动其他程序或者启动java程序等可能需要设置一些环境变量或者获取一些环境变量

可以通过方法GetEnvironmentVariableA 获取环境变量,方法SetEnvironmentVariableA设置环境变量

char userdir[256];
memset(userdir,0,buffSize);
//获取环境变量APPDATA的值
GetEnvironmentVariableA("APPDATA", userdir, 256);
//设置环境变量
SetEnvironmentVariableA("GOOGLE_API_KEY"," ");
SetEnvironmentVariableA("GOOGLE_DEFAULT_CLIENT_ID"," ");
SetEnvironmentVariableA("GOOGLE_DEFAULT_CLIENT_SECRET"," ");

将char转换为tchar

//转换为tchar
TCHAR lastcmd[256];
memset(lastcmd,0,256);
MultiByteToWideChar(CP_ACP, 0, userdir, -1, lastcmd, strlen(userdir));

总结

做个启动快捷方式的时候如果使用系统的快捷方式,有时候会被一些软件修改掉,比如说启动浏览器并打开某个指定网站的启动快捷方式,很多应用系统需要这样的快捷方式放在桌面上,以便用户进入系统,但是往往会被一些流氓软件轻轻松松改掉打开一些广告网站,这样就影响很不好了,那么如果使用上面这种方式写一个exe启动你的浏览器然后以你的系统作为默认打开的状态,那么这个体验就非常不错而且不用担心被流氓软件修改你的启动程序。

c++创建atl activex的简单步骤

创建activex可以基于mcf框架也可以基于atl创建,如果是创建没有界面的activex控件,atl更加小巧,当然atl也可以支持界面的操作,但是比较简陋可以安装wtl进行扩展。下面来看看一个简单的atl创建控件的简单示例

1.创建项目,勾选下面的选项,其他默认

image

2.打开类视图,添加一个类

image

3.添加一个简单对象,给个类名就可以了

image

4.勾选下面的选项,其他默认

image

5.在类图里面找到接口,在接口上添加方法

image

6.添加输入参数输出参数,注意输出参数必须是指针类型,要不然选不了out类型

image

7.找源文件Add.cpp,看到新添加的方法实现,写上方法的实现逻辑

STDMETHODIMP CAdd::add(LONG a, LONG b, LONG* c)
{

	*c = a + b;

	return S_OK;
}

这样整个过程就完成了,编译项目这个空间就会自动注册了,也可以通过命令行注册

regsvr32 demo.dll

8.注册之后的classid可以从扩展名为idl的文件中找到,如下

image

9.在页面上可以如下使用

<object classid="clsid:8FA96F06-568C-40E3-8F51-69E77D984D9E" id="myAddObj" name="myAddObj"/>
<script>
	var result = myAddObj.add(1,2);
	alert(result);
</script>

总结

atl创建的控件最后打包的是dll文件,而mfc貌似是扩展名为ocx的文件,其实本质都是dll,通过注册可以在页面上根据classid来引用,那么编译完成之后的dll可以打包成cab文件在页面上使用,前面文章有介绍如何打包并签名cab文件。

c++对图片进行base64编码

有时候我们需要把图片编码成base64编码的文本,然后保存起来,这时候就需要把图片的二进制数据读到内存,然后按照base64的算法进行编码得到一大串文本,这样就方便存放或者在网络上传输,当然也可以在网页上显示。

下面是一个完整的c++实现的base64编码解码程序代码

base64.h

#include "stdafx.h"
#include <string>

std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);

base64.cpp

#include "base64.h"
#include <iostream>
using namespace std;

static const std::string base64_chars = 
             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
             "abcdefghijklmnopqrstuvwxyz"
             "0123456789+/";

static inline bool is_base64(unsigned char c) {
  return (isalnum(c) || (c == '+') || (c == '/'));
}

/**
* 对二进制进行编码
*/
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {

  std::string ret;
  int i = 0;
  int j = 0;
  unsigned char char_array_3[3];
  unsigned char char_array_4[4];

  while (in_len--) {
    char_array_3[i++] = *(bytes_to_encode++);
    if (i == 3) {
      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
      char_array_4[3] = char_array_3[2] & 0x3f;

      for(i = 0; (i <4) ; i++)
        ret += base64_chars[char_array_4[i]];
      i = 0;
    }
  }

  if (i)
  {
    for(j = i; j < 3; j++)
      char_array_3[j] = '\0';

    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
    char_array_4[3] = char_array_3[2] & 0x3f;

    for (j = 0; (j < i + 1); j++)
      ret += base64_chars[char_array_4[j]];

    while((i++ < 3))
      ret += '=';

  }

  return ret;

}

/**
* 对base64串进行解码
*/
std::string base64_decode(std::string const& encoded_string) {
  int in_len = encoded_string.size();
  int i = 0;
  int j = 0;
  int in_ = 0;
  unsigned char char_array_4[4], char_array_3[3];
  std::string ret;

  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
    char_array_4[i++] = encoded_string[in_]; in_++;
    if (i ==4) {
      for (i = 0; i <4; i++)
        char_array_4[i] = base64_chars.find(char_array_4[i]);

      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

      for (i = 0; (i < 3); i++)
        ret += char_array_3[i];
      i = 0;
    }
  }

  if (i) {
    for (j = i; j <4; j++)
      char_array_4[j] = 0;

    for (j = 0; j <4; j++)
      char_array_4[j] = base64_chars.find(char_array_4[j]);

    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];

    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
  }

  return ret;
}

base64编码后会得到类似这样的一串文本:“/9j/4AAQSkZJRgABAQAAAQABA…”

在网页上可以直接显示base64编码的图片,需要在前面加上前缀:“data:image/jpg;base64,”

下面是显示图片的例子

<img id="jpg" src="data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABA..."/>

总结

图片不一定非要保存成文件存放在服务器上,也不一定非得保持二进制格式存放,也可以变成base64编码的字符串存放在数据库里面,比如说读取的身份证头像数据完全可以转换成base64编码的字符串存在数据库里,显示的时候直接能够显示不管是在网页上或者在文档里面都是可以的。

windows下cab打包与签名

cab的打包有各式各样的工具,windows下一直有个默认的工具iexpress可以做到这一点,下面简单看一下一个简单的示例如何打包一个cab并在网页上引用

1、制作inf文件

default.INF
最开始一般是[Version]区:

[Version]
signature=” $CHICAGO$ ”
AdvancedINF=2.0
接下来就是最重要的[Add.Code]区:

[Add.Code]
getcertocx.ocx=getcertocx.ocx
前面是要下载的文件名,后面是对应这个文件的区域名,可以是任何名字,不过一般都是和文件的名字相同,这样方便维护。
再接下来是各个文件的区域了
[getcertocx.ocx]
file-win32-x86=thiscab
RegisterServer=yes
clsid={0A0488CF-F9AB-4AC4-AD8F-CD893553DD22}
DestDir=11
FileVersion=1,0,0,1

[getcertocx.ocx]区域中的第一个file值告诉ie到哪里去得到这个ocx,file一共包括三个部分,第一部分是file,这个永远都是这样的;第二部分告诉声明支持的OS,win32表示windows,mac就是苹果MAC OX了;第三部分是CPU类型,比如说x86、mips等

file的值可以取三个:一个URL、ignore和thiscab,如果是URL则说明到URL所在的位置去下;如果是ignore说明对于这种OS和CPU,不需要下载这个文件;如果是thiscab就在当前的cab文件中了。

第二部分是RegisterServer,可以取两个值yes和no,如果为yes则说明ie要注册该ocx,如果是no就不必了;

接下来是clsid,需要填写该ocx的class guid。

再下来是DestDir,它的值是ocx将要存到本地硬盘的位置,如果它的值是10,则将ocx放到\Windows或者\WinNT下;如果是11,则放到\Windows\System或者

\WinNT\System32下;如果是空(就是没有值)则会放到\Windows或者\WinNT下的Downloaded Program Files目录下;

最后是FileVersion,说明了ocx的版本号。

完整的示例default.INF文件如下

[Version] 
signature="$CHICAGO$" 
AdvancedINF=2.0 

[Add.Code] 
getcertocx.ocx=getcertocx.ocx 

[getcertocx.ocx] 
file-win32-x86=thiscab 
RegisterServer=yes 
clsid={0A0488CF-F9AB-4AC4-AD8F-CD893553DD22} 
DestDir=11 
FileVersion=1,0,0,1 

2、制作cab文件

利用iexpress.exe(windows提供的一个向导式cab制作工具) 在路径C:\Windows\System32\iexpress.exe

1

选“创建新的自解压缩指定文件”,点“下一步”。

2

选“仅创建压缩文件(ActiveX安装)”,点“下一步”。

3

点“添加”将上边制作好的default.inf和getcertocx.ocx文件添加进来,点“下一步”。

4

点“浏览”输入要生成的CAB文件名称,并选中“在软件包中使用长文件名保存文件”,之后点“下一步”。

5

选“不保存”,点“下一步”。

6

点“下一步”。

8

点“完成”退出向导,cab文件生成。

9

3.web上引用

<object width="0" HEIGHT="0"
 classid="CLSID:0A0488CF-F9AB-4AC4-AD8F-CD893553DD22"
 codebase="getcert.cab#Version=1,0,0,1">
</object>

注意classid和版本号要与inf文件中的一样,而且版本号是dll或者ocx文件本身的真实版本,这样的话后续版本修改之后网页可自动更新。

4.对cab文件进行签名

一般来说如果做一个自签名的证书其实意义不是很大,只有正版的证书才有意义

这里是制作一个自签名的证书并对cab文件进行签名,使用的是Visual Studio2010的命令行工具

image

执行下面四行命令可对文件进行签名

makecert -r -sv fullstacks_key.pvk -n "CN=fullstacks" fullstacks.cer
cert2spc fullstacks.cer fullstacks.spc
pvk2pfx -pvk fullstacks_key.pvk -pi fullstacks -spc fullstacks.spc -pfx fullstacks.pfx -po fullstacks
signtool  sign  /f fullstacks.pfx /p fullstacks  *.cab

5.在cab安装的时候执行exe

下面是一个示例每次都执行的

[Setup Hooks]
hook1=hook1

[hook1]
run=%EXTRACT_DIR%\example_setup.exe /q

[Version]
; This section is required for compatibility on both Windows 95 and Windows NT.
Signature="$CHICAGO$"
AdvancedInf=2.0

下面是一个有条件执行,当机器里没有注册指定的dll的时候运行,当然也可以加上文件版本,如果版本更新也可以执行

[Add.Code]
example.ocx=example.ocx

[example.ocx]
Clsid={...}
hook=hook1
FileVersion=1,0,0,1

[hook1]
run=%EXTRACT_DIR%\example_setup.exe /q

[Version]
; This section is required for compatibility on both Windows 95 and Windows NT.
Signature="$CHICAGO$"
AdvancedInf=2.0

总结

cab在网页使用的场景比较多,最大的好处就是能够自动下载安装,如果浏览器里面当前网页在新人区里的话可以自动下载安装没有任何提示,这个在用户体验上也比较好,可以默默无闻的进行更新,这种情况比较适合计算机水平比较低的最终用户,不需要操作就可以使用最新的功能,但是对一些高级用户就不一合适。不过这些只是比较合适IE的用户,其他浏览器或多或少的总是有一些不一样的地方。

windows下编译使用libjpeg

libjpeg是一个图片处理c语言实现,可以比较方便的将几种图片格式互相转换,是个开放源代码软件,需要下载源代码编译之后才能使用,下载地址http://www.infai.org/jpeg/,windows下下载jpegsr9a.zip文件,使用Visual Studio 2010编译工具进行编译。

1.使用Visual Studio2010编译

首先将文件解压到某个目录,将文件jconfig.vc改为jconfig.h

然后打开Visual Studio2010的命令行工具

image

在解压目录下输入编译命令

nmake /f makefile.vc nodebug=1

这样就编译完成了,目录下会生成libjpeg.lib文件,还有其他一些工具如cjpeg.exe

可以使用cjpeg.exe通过命令行直接转换文件如

cjpeg.exe a.bmp b.jpg

可以直接将a.bmp文件转换为b.jpg文件。

2.使用libjpeg.lib

将这四个文件libjpeg.lib,jconfig.h,jmorecfg.h,jpeglib.h复制得到自己的项目中

在项目属性里面引入libjpeg.ib

image

在项目中需要用的的地方引入头文件

extern "C" {
	#include "jpeglib.h"
	#include "jmorecfg.h"
	#include "jconfig.h"
}

这样就可以调用库函数了

3.c与c++混合使用

Visual Studio2010里面混合使用c与c++需要配置不要使用预编译头文件

image

4.bmp转换为jpg示例

libjpeg.lib里面并没与读取bmp的辅助方法,但是cjpeg.exe 工具有,使用到以下几个文件

rdbmp.c,cdjpeg.c,cdjpeg.h,jinclude.h,jerror.h

若果是自己实现读取bmp文件的话就不需要添加上面的5个文件,这些文件可以独立放在一个目录里面再在项目中引用即可;

下面是一个完整的示例,使用Visual Studio2010创建一个c++命令行程序,这里是c和c++混合使用,所以需要配置不使用预编译头才能编译通过,使用下面的代码即可编译运行

#include "stdafx.h"
#include <stdio.h>

extern "C" {  
	#include "jpeglib.h"  
	#include "jmorecfg.h"  
	#include "jconfig.h"  
	#include "cdjpeg.h"
}

/**
* bmp 文件转换为jpg文件
*/
int bmp2jpg(char * input_file_name,char * output_file_name){
	//输入输出文件
	FILE * input_file;
	FILE * output_file;

	//图片转换相关数据结构
	cjpeg_source_ptr src_mgr;
	struct jpeg_compress_struct cinfo;
	struct jpeg_error_mgr jerr;
	JDIMENSION num_scanlines;

	//初始化
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);

	cinfo.in_color_space = JCS_RGB; /* arbitrary guess */
	jpeg_set_defaults(&cinfo);

	//读取源文件
	input_file = fopen(input_file_name,"rb");  
	if(input_file==0) return 0; 
	src_mgr =jinit_read_bmp(&cinfo);

	src_mgr->input_file = input_file;
	(*src_mgr->start_input) (&cinfo, src_mgr);
	jpeg_default_colorspace(&cinfo);
	
	//创面目标文件
	output_file = fopen(output_file_name,"wb");
	if(output_file==0) return 0;
	jpeg_stdio_dest(&cinfo, output_file);

	/* Start compressor */
	jpeg_start_compress(&cinfo, TRUE);

	/* Process data */
	while (cinfo.next_scanline < cinfo.image_height) {
		num_scanlines = (*src_mgr->get_pixel_rows) (&cinfo, src_mgr);
		(void) jpeg_write_scanlines(&cinfo, src_mgr->buffer, num_scanlines);
	}

	/* Finish compression and release memory */
	(*src_mgr->finish_input) (&cinfo, src_mgr);
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	fclose(input_file);
	fclose(output_file);

	return 1;
}


int _tmain(int argc, _TCHAR* argv[])
{
	char input_file_name[128]  = "c:\\a.bmp";
	char output_file_name[128] = "c:\\b.jpg";

	bmp2jpg(input_file_name,output_file_name);
}

这里可以手工截屏使用画图工具保存为a.bmp文件,然后执行上面的程序就看到转换之后的文件了。

总结

c语言实现的转换效率还是非常之高的,可以写成公共库让其他平台的语言调用,只需要传入文件名即可,非常的方便,在linux下的话需要重新编译。