多核编程学习笔记之同步(二)——采用Win32 线程API

多核编程学习笔记之同步(二)——采用Win32 线程API

分布式 2010-04-10 00:25:15 阅读44 评论0   字号: 订阅

I. 总结积累

1.1 C/C++调用外部程序

描述:运行指定的应用程序,这个函数是专用来兼容16位windows的。应用程序应该使用CreateProces函数。

原型:

UINT WINAPI WinExec( __in LPCSTR lpCmdLine, __in UINT uCmdShow );

参数:

lpCmdLine:将要运行的应用程序的命令行(文件名称加上可选参数)。如果在lpCmdLine参数中的可执行文件的名字没有包含目录的路径。系统将按顺序搜索可执行的文件:

  • 应用程序载入的目录;
  • 当前目录;
  • Windows系统文件目录。GetSystemDirectory函数可以返回这个目录的路径;
  • Windows目录,GetWindowsDirectory函数可以返回这个目录的路径;
  • 在PATH环境变量中列出的目录。

uCmdShow:显示选项。想要查看可选参数的列表,请参阅ShowWindows函数中nCmdShow参数的描述。

 

SW_FORCEMINIMIZE

Windows 2000/XP: Minimizes a window, even if the thread that owns the window is not responding. This flag should only be used when minimizing windows from a different thread.

 

SW_HIDE

Hides the window and activates another window.

 

SW_MAXIMIZE

Maximizes the specified window.

 

SW_MINIMIZE

Minimizes the specified window and activates the next top-level window in the Z order.

 

SW_RESTORE

Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when restoring a minimized window.

 

SW_SHOW

Activates the window and displays it in its current size and position.

 

SW_SHOWDEFAULT

Sets the show state based on the SW_ value specified in the STARTUPINFO structure passed to the CreateProcess function by the program that started the application.

 

SW_SHOWMAXIMIZED

Activates the window and displays it as a maximized window.

 

SW_SHOWMINIMIZED

Activates the window and displays it as a minimized window.

 

SW_SHOWMINNOACTIVE

Displays the window as a minimized window. This value is similar to SW_SHOWMINIMIZED, except the window is not activated.

 

SW_SHOWNA

Displays the window in its current size and position. This value is similar to SW_SHOW, except the window is not activated.

 

SW_SHOWNOACTIVATE

Displays a window in its most recent size and position. This value is similar to SW_SHOWNORMAL, except the window is not actived.

 

SW_SHOWNORMAL

Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time.

备注:

可执行程序的名字中有空格(比如C://Program Files//xxx.exe),有可能会引发风险:一个不同的应用程序被执行,因为该函数解析空格。

 

1.2 Win API 判断某一个文件/目录是否存在

法一:使用文件打开操作判断;

法二:

(部分摘自:http://blog.csdn.net/roger_77/archive/2007/03/23/1538447.aspx

(1)检查文件是否存在:

#define _WIN32_WINNT 0x0400

#include "windows.h"

int
main(int argc, char *argv[])
{
  WIN32_FIND_DATA FindFileData;
  HANDLE hFind;

  printf ("Target file is %s. ", argv[1]);

  hFind = FindFirstFile(argv[1], &FindFileData);

  if (hFind == INVALID_HANDLE_VALUE) {
    printf ("Invalid File Handle. Get Last Error reports %d ", GetLastError ());
  } else {
    printf ("The first file found is %s ", FindFileData.cFileName);
    FindClose(hFind);
  }

  return (0);
}

(2)检查某一目录是否存在:

///目录是否存在的检查:
bool  CheckFolderExist(const string &strPath)
{
    WIN32_FIND_DATA  wfd;
    bool rValue = false;
    HANDLE hFind = FindFirstFile(strPath.c_str(), &wfd);
    if ((hFind != INVALID_HANDLE_VALUE) && (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
    {
        rValue = true;  
    }
    FindClose(hFind);
    return rValue;
}

注意在检查目录是否存在的时候,要使用绝对路径,并且路径字符串不能以”/”结尾。

 

1.3 【C++语法回顾】STL

    在实际的C++面向对象程序设计中,STL库将起着举足轻重的作用。STL库是从实际的变成时间中发展而来的,因此它是一个非常庞大、复杂的类库。尽管STL库十分庞大,它的语法形式有时令人感到迷惑,目前已经有不少专注介绍STL。但实际上运用STL库来设计应用程序还是比较简单的。

    C++中常用的类库有vector,list, map,queue,string。

    下面主要介绍map的使用方法。

    map类模板定义了一个关联数组的容器。关联数组时一种成对数组(pair),其中一个值是实际的数据值,另外一个是用来查找数据的关键字,关键字与数据值一一对应,即映射。在一个map类容器中关键字必须是唯一的。通俗地说,map类对象中包含了一系列关键字/值的匹配对(key/value pair)。map的功能在于只要知道了一个值的关键字,就可以找到这个值,

CODE:

int main(void) {
    map<string, string> mymap;

    mymap.insert(pair<string, string>("yes", "no"));
    mymap.insert(pair<string, string>("up", "down"));
    map<string, string>::iterator iter;
    iter = mymap.find("up");
    if (iter != mymap.end()) {
        cout << "first: " << iter->first << "  second: " << iter->second;
    }
    return 0;
}

iter->first输出的是key部分的值,iter->second是value部分。

 

1.4 【C++语法回顾】 迭代器

迭代器的声明:

vector<type>/map<type,type>/string::iterator iter = object.begin(); i != object.end();i++

CODE:

int main(void) {
    string str = "Hello C++";

    for(string::iterator iter = str.begin(); iter != str.end(); iter++) {
        cout << *iter;
    }
    cout << endl;
    return 0;
}

 

1.5 【C++语法回顾】string的使用

1.5.1 find函数

寻找指定字符的下标,该下标从0开始。

CODE:

int main(void) {
    string str = "Hello C++";

    int index = str.find(' ');
    cout << index << endl;
    return 0;
}

结果:5

注意:该函数如果查找失败,则返回string::npos,比如下面代码:

CODE:

bool CheckForbiddenWord(string str) {
        string forbidden_path = this->GetForbiddenWordPath();
        ifstream inFile;
        string tmpStr = "";
        int pos = 0;
        inFile.open(forbidden_path.c_str());
        if (!inFile.is_open()) {
            return false;
        }
        while (!inFile.eof()) {
            inFile >> tmpStr;
            if ((tmpStr.c_str()[0] != '#') && (tmpStr != "")) {
                if (str.find(tmpStr, 0) != string::npos) {
                    return true;
                }
            }
        }
        inFile.close();
        return false;
    }

 

1.5.2 subStr

str.subStr(int pos = npos, int npos);

该函数是可变参函数,第一个参数默认是npos开始的!不是从0开始的。

CODE:

while (!inFile.eof()) {
        inFile >> tmpStr;
        if (tmpStr.c_str()[0] != '#') {
            key = tmpStr.substr(0, tmpStr.find('='));
            value = tmpStr.substr(tmpStr.find('='), tmpStr.size() - tmpStr.find('='));
            configTable.insert(pair<string, string> (key, value));
        }
        iCount++;
    }

 

1.6 CString与string的转换

CODE:

#include <afxwin.h>
#include <string>
#include <iostream>

using namespace std;

int main(int argc, char* argv[]) {
    string ss;
    CString cs;

    cout << "string to CString: " << endl;
    ss = "this is string!";
    cs = ss.c_str();
    cout << "string = " << ss << "  CString = " << cs.GetString() << endl;
    cout << "CString to string: " << endl;
    cs = "this is cstring!";
    ss = cs;
    cout << "CString = " << cs.GetString() << "  string = " << ss << endl;

    return 0;
}

 

1.7 遍历目录下所有文件

采用API + MFC的方式,其中使用了MFC的CString类和CFileFinder。

CODE:

#include <afxwin.h>
#include <iostream>

using namespace std;

void Recurse(LPCTSTR pstr)
{
   CFileFind finder;

  // build a string with wildcards

   CString strWildcard(pstr);
   strWildcard += _T("//*.*");

  // start working for files

  BOOL bWorking = finder.FindFile(strWildcard);

  while (bWorking)
  {
       bWorking = finder.FindNextFile();

      // skip . and .. files; otherwise, we'd

      // recur infinitely!

      if (finder.IsDots())
        continue;

           CString sFileName = finder.GetFileName();
          cout << (LPCTSTR)sFileName << endl;//输出查找文件夹下的所有文件名

  }

   finder.Close();
}

int main()
{
  if (!AfxWinInit(GetModuleHandle(NULL), NULL, GetCommandLine(), 0))//初始化MFC

      cout << "panic!" << endl;
  else
       Recurse(_T("."));
        return 0;
}

 

1.8 CString与int、string、char之间互转大全


string 转 CString 
CString.format("%s", string.c_str()); 

 

char 转 CString 
CString.format("%s", char*); 

char 转 string 
string s(char *); 

string 转 char * 
char *p = string.c_str(); 

//  CString转std::string
CString str = dlg.GetPathName();
setlocale(LC_ALL, "chs");
char *p = new char[256];
wcstombs( p, str, 256 );
m_fileName = p;

1,string -> CString 
CString.format("%s", string.c_str()); 
用c_str()确实比data()要好. 
2,char -> string 
string s(char *); 
你的只能初始化,在不是初始化的地方最好还是用assign(). 
3,CString -> string 
string s(CString.GetBuffer()); 
GetBuffer()后一定要ReleaseBuffer(),否则就没有释放缓冲区所占的空间. 

《C++标准函数库》中说的 
有三个函数可以将字符串的内容转换为字符数组和C—string 
1.data(),返回没有”/0“的字符串数组 
2,c_str(),返回有”/0“的字符串数组 
3,copy() 

CString互转int 

将字符转换为整数,可以使用atoi、_atoi64或atol。 
而将数字转换为CString变量,可以使用CString的Format函数。如 
CString s; 
int i = 64; 
s.Format("%d", i) 
Format函数的功能很强,值得你研究一下。 

void CStrDlg::OnButton1() 

// TODO: Add your control notification handler code here 
CString 
ss="1212.12"; 
int temp=atoi(ss); 
CString aa; 
aa.Format("%d",temp); 
AfxMessageBox("var is " + aa); 

sart.Format("%s",buf); 

CString互转char* 

///char * TO cstring 
CString strtest; 
char * charpoint; 
charpoint="give string a value"; 
strtest=charpoint; 

///cstring TO char * 
charpoint=strtest.GetBuffer(strtest.GetLength()); 

标准C里没有string,char *==char []==string 

可以用CString.Format("%s",char *)这个方法来将char *转成CString。要把CString转成char *,用操作符(LPCSTR)CString就可以了。 

CString转换 char[100] 

char a[100]; 
CString str("aaaaaa"); 
strncpy(a,(LPCTSTR)str,sizeof(a)); 
2 CString类型的转换成int 
CString类型的转换成int 
将字符转换为整数,可以使用atoi、_atoi64或atol。 

//CString aaa = "16" ;
//int int_chage = atoi((lpcstr)aaa) ; 

而将数字转换为CString变量,可以使用CString的Format函数。如 
CString s; 
int i = 64; 
s.Format("%d", i) 
Format函数的功能很强,值得你研究一下。 
如果是使用char数组,也可以使用sprintf函数。

//CString ss="1212.12"; 
//int temp=atoi(ss); 
//CString aa; 
//aa.Format("%d",temp); 

数字->字符串除了用CString::Format,还有FormatV、sprintf和不需要借助于Afx的itoa 
3 char* 在装int
#include <stdlib.h>
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
long long atoq(const char *nptr); 
4 CString,int,string,char*之间的转换
string aa("aaa");
char *c=aa.c_str();

cannot convert from 'const char *' to 'char *'
const char *c=aa.c_str(); 
5 CString,int,string,char*之间的转换 
string.c_str()只能转换成const char *,
要转成char *这样写:

string mngName;
char t[200]; memset(t,0,200); strcpy(t,mngName.c_str());

 

 

1.9 WinExec()的使用与DOS的内部外部命令

(摘自:http://blog.csdn.net/vagrxie/archive/2008/01/04/2025132.aspx

DOS命令在以前就分两种:内部命令和外部命令。其中比较常用的比如dir, del都属于内部命令,特定时直接加载进内存。而外部命令是可以在目录中找到具体的文件的。当时就会常常遇到PATH设定不对导致的外部命令调用错误,而需要找到目录去调用的情况。既然加载到内存中,到哪里去找?有个变通的方法,在win中,DOS的实际调用都是用cmd程序,那么我们可以用它来调用,具体方法是以/c为参数调用,比如我们要调用dir命令,具体方法是:

WinExec(“cmd /c dir”)

此API版本太老,没有Unicode版本,苦闷的Unicode版本程序因此无法较好地使用,在以CString为字符串调用的时候似乎只有两种方法,一种是以ANSI方式编译,一种就是通过Unicode到ANSI的转换,这样的转换很可能还会丢失中文信息。因此,推荐只在直接调用DOS命令的时候使用WinExec,而且也推荐直接调用的时候使用,因为WinExec只需要两个参数,很容易调用。但是要在Unicode程序中以CString为参数调用怎么办?

—————

在win下,cmd中的目录要表示成”/”。比如del命令:

WinExec(“cmd /c d://test.txt”);  这在命令行下将被解析成:cmd /c d:/test.txt

如果写成cmd /c d:/test.txt 则提示:参数格式不正确。

 

1.10 【C++语法回顾】利用string的find方法和replace来替换其中的字符

将一个字符串中的/替换为/

int main(void) {
    string str = "sbc/dsf/wer/sfdsfsdfds/sdfsdf";
    int pos = 0;
    while (1) {
        cout << str << endl;
        pos = str.find("/", pos);
        if (pos == -1) {
            break;
        }
        str = str.replace(pos, 1, "//");
    }
    cout << "After replace: " << str << endl;
    return 0;
}

注意find方法,如果查找失败则返回-1,必须将find返回值的判断放在find与replace之间,因为一旦find返回-1,在replace中使用-1将报错。

 

*****CreateProcess专区*****
1.11 利用CreateProcess来运行外部程序

(摘自:http://zhidao.baidu.com/question/25792387

我们可以利用CreateProcess API函数来创建相应的进程,该函数的原型如下:    CreateProcess(              LPCWSTR lpszImageName, //指向可执行的模块的指针             LPCWSTR lpszCmdLine, //指向可执行命令行字符串的指针            LPSECURITY_ATTRIBUTES lpsaProcess, //CE 不支持            LPSECURITY_ATTRIBUTES lpsaThread, //CE 不支持            BOOL fInheritHandles, //CE 不支持            DWORD fdwCreate, //创建标志            LPVOID lpvEnvironment, //CE 不支持            LPWSTR lpszCurDir, //CE 不支持            LPSTARTUPINFOW lpsiStartInfo, //CE 不支持            LPPROCESS_INFORMATION lppiProcInfo //指向进程信息结构体的指针         );        该函数最重要的两个参数是lpszImageName和lpszCmdLine,下面的几个应用也主要是通过设置这两个参数来达到目的。(以下的程序在Pocket 
PC 2003 SE上通过测试)1、打开指定的URL   TCHAR szAppName[_MAX_PATH] = TEXT("iexplore.exe");//IE浏览器程序  TCHAR szCmdLine[_MAX_PATH] = TEXT("http://www.cnblogs.com");  CreateProcess(szAppName,szCmdLine, NULL, NULL,FALSE, 0, NULL, NULL, NULL, NULL);2、打开指定的文件夹   TCHAR szAppName[_MAX_PATH] = TEXT("fexplore.exe");//资源管理器程序  TCHAR szCmdLine[_MAX_PATH] = TEXT(//Windows//);  CreateProcess(szAppName,szCmdLine, NULL, NULL,FALSE, 0, NULL, NULL, NULL, NULL);3、打开帮助文件    PROCESS_INFORMATION pi;   TCHAR szAppName[_MAX_PATH] = TEXT("peghelp.exe");//系统帮助程序,PC上是在c:/WINDOWS目下的winhlp32.exe  TCHAR szCmdLine[_MAX_PATH] = TEXT("myhelp.html");//自己制作的帮助文档,甚至是系//统原有的帮助文件,如wince.htm、bluetooth.htm等等。myhelp.html应该放在/Windows目录下  CreateProcess(szAppName,szCmdLine, NULL, NULL,FALSE, 0, NULL, NULL, NULL, &pi);通过以上的方式,可以在自己的应用程序里打开相应的帮助文件。    从以上的3种应用可以看出,只要知道系统自带的应用程序名,就可以通过设定szAppName和szCmdLine相应的值,来调用相应的程序,来达到自己想
要的目的。只要认真挖掘,一定可以发现更多的应用。 

也可以这么使用:CreateProcess(NULL,"d://test//te.exe",NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))

注意用到的参数有以下几种:

lpszImageName:指向可执行的模块指针;

lpszCmdLine:指向可执行命令行字符串指针;

fdwCreate:创建标志

lppiProcInfo:指向进程信息结构体的指针。

执行成功,返回非0值,否则返回0.返回类型为BOOL。

注释:CreateProcess函数除了创建一个进程,还创建一个线程对象。这个线程连同一个已初始化了的堆栈一起被创建,堆栈的大小由可执行文件的文件头中的描述决定。(这就是为什么最终要释放两个句柄!

新进程和新线程的句柄被以全局访问权限创建。对于这两个句柄中的任一个,如果没有安全描述符,那么这个句柄就可以再任何需要句柄类型作为参数的函数中被使用。当提供安全描述符的时候,在接下来的时候当句柄被使用时,总是会先进行访问权限的检查,如果访问权限检查拒绝访问,请求的进程将不能使用这个句柄访问这个进程。

在进程中的所有线程都终止且进程所有的句柄和他们的线程被通过调用CloseHandle函数终止前,进程会留在系统中。进程和主线程的句柄都必须通过调用CloseHandle函数关闭。如果不再需要这些句柄,最好在创建进程后立刻关闭它们。

当进程中最后一个线程终止时:

  • 所有由进程打开的对象都会关闭;
  • 进程的终止组昂太(GetExitCodeProcess函数返回)从它的初始值STILL_ACTIVE变为最后一个结束的线程结束状态;
  • 主线程的线程对象被设置为标志状态,供其他等待这个对象的线程使用;
  • 进程对象被设置为标志状态,供其他等待这个对象的线程使用;

     

    1.12 使用CreateProcess来调用外部程序,使用同步机制使主线程等待其结束。

    (部分摘自:http://www.goffconcepts.com/techarticles/development/cpp/createprocess.html

    code 1-1 使用CreateProcess调用外部程序

    #include <windows.h>
    #include <iostream>
    #include <string>

    1 CString,int,string,char*之间的转换

    using namespace std;

    int main(void) {
        STARTUPINFO si; // 一些必备参数设置
        memset(&si, 0, sizeof(STARTUPINFO));
        string lpCmdLine = "notepad";

        si.cb = sizeof(STARTUPINFO);
        si.dwFlags = STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_SHOW;
        PROCESS_INFORMATION pi; // 必备参数设置结束
        memset(&pi, 0, sizeof(PROCESS_INFORMATION));
        if (!CreateProcess(NULL, (LPSTR)lpCmdLine.c_str(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
            cout << "Create Process failed! " << endl;
            exit(0);
        } else {
            WaitForSingleObject(pi.hProcess, INFINITE);
        }

        cout << "The main Process is ended!" << endl;
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);

        return 0;
    }

     

    注意:第二个参数如果类型不匹配,可能导致进程创建失败!在“多字符集”环境下,使用字符串字面值可以实现。如果使用字符串变量,需要进行转化——LPSTR。使用WaitForSingleObject来等待,内部的参数是该进程的句柄。,此句柄是pi(PROCESS_INFORMATION)中获取的。这样主进程会等待子进程结束后才继续,此即同步机制。

    *****使用CreateProcess来实现重定向*****

    描述:在调用外部编译器的时候,需要重定向输出编译错误,一般情况下,只使用上面的方法是不能够重定向的,必须借助管道。

    结构体STARTUPINFO

    在创建时刻为一个进程指定窗口位置,桌面,标准句柄和主窗口的样式。

    PROCESS_INFORMATION

    这个结构体包含了一个新创加你的进程的信息以及它的主线程。该结构体一般与CreateProcess, CreateProcessAsUser, CreateProcessWithLogonW或者CreateProcessWithTokenW一起使用。

    SECURITY_ATTRIBUTES

    这个结构体包含了一个对象的安全描述符,并且通过指定这个结构体是否可被继承来制定这个句柄是否可以被检索。这个结构体提供了由多种函数创建的对象的安全设置。类似CreateFile, CreatePipe, CreatepProcess。

    CreatePipe

    创建一个无名管道,返回一个句柄,该句柄用来在管道的末尾读或者写。

    memset

    设置缓冲区指定的字符。

    CODE 1-2 GCC编译时的错误重定向

    #include <iostream>
    #include <string>
    #include <windows.h>
    using namespace std;

    int main()
    {
        string OutputFileName =  "D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_results//5.out";
        STARTUPINFO siStartInfo;
        PROCESS_INFORMATION piProcessInfo;

        memset(&siStartInfo, 0, sizeof(STARTUPINFO));
        memset(&piProcessInfo, 0, sizeof(PROCESS_INFORMATION));

        SECURITY_ATTRIBUTES psa = {sizeof(psa), NULL, TRUE};
        HANDLE hOutFile = CreateFile(OutputFileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ |
            FILE_SHARE_WRITE, &psa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

        siStartInfo.cb = sizeof(STARTUPINFO);
        siStartInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
        siStartInfo.wShowWindow = SW_HIDE;
        siStartInfo.hStdError = hOutFile;

        string cmd = "D://Program_Files//Multi-Core//bak//XOJ_ACS//GCC4.3.2_for_win//bin//g++ -o D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_executables//5 D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_user_code//5.c -ansi -fno-asm -Wall -lm --static -DONLINE_JUDGE ";
        if (!CreateProcess(NULL, (LPSTR)cmd.c_str(), NULL, NULL, TRUE, 0, NULL, NULL,
            &siStartInfo, &piProcessInfo)) {
                cout << "FATAL ERROR: Create process failed in CompileThread!" << endl;
                return 0; // if create failed, exit this thread!!!
        } else {
            WaitForSingleObject(piProcessInfo.hProcess, INFINITE);
            CloseHandle(piProcessInfo.hProcess);
            CloseHandle(piProcessInfo.hThread);

      CloseHandle(hOutFile);
        }

        return 0;
    }

     

    代码解析:

    1. 对比CODE 1-1不难发现二者的区别:

    siStartInfo的值增加了STARTF_USERSTDHANDLES。hStdInput, hStdOutput 和hStdError成员包含的附加信息。

    如果这个标志在创建一个进程时被指定,这个句柄必须被继承,并且CreateProcess的bInhreitHandles(该函数的第五个参数)必须被指定为TRUE。

    2. 对于错误重定向,正真起作用的是siStartInfo.hStdError = hOutFile这一句。如果编译有错,则将错误重定向输出到一个文件中,如果没有错,则输出的文件是0KB,注意即使没有错误,也会生成一个文件,只不过这个文件的大小是0。

    3. 注意GCC重定向的参数,原来的编译命令:

    g++ foo.cpp -o foo -ansi -fno-asm -Wall -lm --static -DONLINE_JUDGE 2>error.txt

    并不适用于CreateProcess。在CMD中,该命令可以正常使用,但若要使用CreateProcess,在必须用CreateFile来辅助,而且在CreateProcess的命令中不能含有 “2>” ,即:

    D://Program_Files//Multi-Core//bak//XOJ_ACS//GCC4.3.2_for_win//bin//g++ -o D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_executables//5 D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_user_code//5.c -ansi -fno-asm -Wall -lm --static -DONLINE_JUDGE

    在输出文件中写上输出文件的完整路径。

     

    CODE 1-3 测试编译后的可执行程序:重定向输入输出

    // 在CODE 1-2的基础上添加如下代码

    string OutputFileName =  "D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_results//5.out";    // 结果输出
    string InputFileName = "D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_testdata//1//1-1.in";
    // 输入文件
    string ErrorFileName =  "D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_results//5.error";   // 错误输出

    HANDLE hOutFile = CreateFile(OutputFileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ |  // 结果输出
        FILE_SHARE_WRITE, &psa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    HANDLE hInputFile = CreateFile(InputFileName.c_str(), GENERIC_READ, FILE_SHARE_READ |
    // 测试数据输入
        FILE_SHARE_WRITE, &psa, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    HANDLE hErrorFile = CreateFile(ErrorFileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ | 
    // 错误输出
        FILE_SHARE_WRITE, &psa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

    siStartInfo.hStdError = hErrorFile;  // 错误输出
    siStartInfo.hStdInput = hInputFile;  // 测试数据输入
    siStartInfo.hStdOutput = hOutFile;   // 结果输出

    // 原来的cmd改成

    string cmd = "D://Program_Files//Multi-Core//bak//XOJ_ACS//$_xoj_executables//5.exe";

    注意这里虽然也有错误重定向输出,但是这里不同于GCC的编译错误输出,而是运行错误输出。新建立一个句柄的同时还要在STARTUPINFO结构体中注册。这两点不能忘。

    注意CreateFile中的黄色字体参数部分,读入和输出文件的参数不同。

    另外CreateProcess中cmd也要改变。

    注意:在1-2中,错误重定向输出如果在STARTUPINFO注册的是hStdOutput,则错误将不能输出,结果只是在给定路径下创建一个0字节的空文件,不会将错误输出,只有用hStdError才能输出错误。

     

    1.13 获取系统环境变量

    不要使用GetEnvironmentStrings函数来获取或设备系统环境变量。应该使用GetEnvironmentVariable和SetEnvironmentVariable函数来访问当前进程的系统环境变量。当不再需要当前进程的系统环境变量块时,应该调用FreeEnviromentStrings函数来释放。

    CODE:

    int main()
    {
        string value = "JAVA_HOME";
        char lpBuffer[40];
        GetEnvironmentVariable(value.c_str(), lpBuffer, 40);
        cout << lpBuffer << endl;

        return 0;
    }

     

    1.14 Windows的UAC安全机制

    (User Agent Client)用户代理客户端

    (部分摘自百度百科:http://baike.baidu.com/view/750250.htm

    当UAC横空出世后,pc中几乎所有的进程与运行的程序都可以被拦截,尤其对那些试图使用管理员权限自动安装或自动运行的程序有显著的效果。

      Windows Vista还带来了很多其它的安全特性。Windows防火墙的升级版可以对出站和入站连接进行管理。而以往版本的Windows防火墙只能对入站连接进行管理,这意味着有可能在用户毫不知情的情况下成为攻击其它计算机的DDoS攻击者中的一员。另外,Windows Defender可以免费提供对常见恶意软件的防护。

      同时自动更新也应当被包含在安全组合中,虽然它不象前面提到的那些安全程序,但对于系统安全而言仍是必不可少的组成部分。微软每个月都会通过Windows Update对自己或其它研究机构发现的漏洞进行定期修补。

      想在一台没有安装防病毒软件的机器上和病毒交锋,那么几乎所有的重担都将落在UAC的头上。既没有防病毒软件也没有UAC的机器可以被病毒无声无息的轻易攻破。

      病毒可以通过电子邮件(如果用户运行包含病毒的附件)或其它程序感染电脑。一个非常有效的途径就是不法分子将商业软件破解(如“warez”)后植入病毒,然后通过网站、FTP、BT网络、即时通讯软件,甚至IRC进行大范围的传播。如果不安装防病毒软件对此类来源的软件进行扫描,那么就算被感染了用户也发现不了。

      更糟糕的是,病毒通常都会极快的产生各种类型的“变种”,大多数的防病毒软件是通过特征码的形式进行病毒识别的,因此如果病毒变种后的代码与防病毒软件的定义不符,那么它同样可以感染“受保护”的电脑。

    UAC需要授权的动作包括:

      * 配置Windows Update

      * 增加或删除用户帐户

      * 改变用户的帐户类型

      * 改变UAC 设置

      * 安装ActiveX

      * 安装或卸载程序

      * 安装设备驱动程序

      * 设置家长控制

      * 将文件移动或复制到Program Files或Windows目录

      * 查看其他用户文件夹

     

    1.15 【C++语法回顾】创建链表

    在一个链表类中创建量表,在插入的时候,如果是用结构体直接定义,则在函数结束后该结构体变量会被直接释放掉,只有用动态开辟的才能真正构建一个链表。所以将结构体改为类,采用Class *c = new Class()的方式来创建结点才能实现目标。

    CODE:

    /*
    * File Name:    solution.h
    * Author:        Johnson Wei
    * Desc:        Define the solution class
    */
    #pragma once

    #include "stdafx.h"

    class Solution {
    public:
        Solution() {}

        Solution(string run_id, string error_message) {
            this->run_id = run_id;
            this->error_message = error_message;
        }

        void SetRunId(string run_id) {
            this->run_id = run_id;
        }

        string GetRunId() {
            return this->run_id;
        }

        void SetErrorMsg(string error_message) {
            this->error_message = error_message;
        }

        string GetErrorMsg() {
            return this->error_message;
        }
    private:
        string run_id;
        string error_message;
    };

    class LinkNode {
    public:
        Solution solution;
        LinkNode* next;

        LinkNode () {}

        LinkNode (Solution solution) {
            this->solution = solution;
            this->next = NULL;
        }
    };

    class SolutionQueue {
    public:
        SolutionQueue() {
            this->front = NULL;
            this->rear = NULL;
            this->length = 0;
        }

        bool IsEmpty() {
            if (this->length == 0) {
                return true;
            }
            return false;
        }

        bool EnterQueue(Solution solution) {
            LinkNode *new_node = new LinkNode(solution);
            if (this->rear == NULL) {
                this->rear = new_node;
                this->front = new_node;
            } else {
                this->rear->next = new_node;
                this->rear = this->rear->next;           
            }
            this->length++;
            return true;
        }

        bool DeleteQueue(Solution* solution) {
            LinkNode *del;
            if (this->IsEmpty()) {
                return false;
            }
            solution->SetRunId(this->front->solution.GetRunId());
            solution->SetErrorMsg(this->front->solution.GetErrorMsg());
            del = this->front;
            this->front = this->front->next;
            delete(del);
            this->length--;
            return true;
        }
        ~SolutionQueue() {}
    private:
        LinkNode* front;
        LinkNode* rear;
        int length;
    };

    // 测试部分

    Solution solution1("1", "1");
    Solution solution2("2", "2");
    Solution solution3("3", "3");
    SolutionQueue queue;
    Solution result;
    queue.EnterQueue(solution1);
    queue.EnterQueue(solution2);
    queue.EnterQueue(solution3);
    queue.DeleteQueue(&result);
    cout << result.GetRunId() << endl;
    queue.DeleteQueue(&result);
    cout << result.GetRunId() << endl;
    queue.DeleteQueue(&result);
    cout << result.GetRunId() << endl;

    输出结果:

    1
    2
    3

    II. 转载

    2.1 GCC与G++的区别

    (摘自:http://www.yuanma.org/data/2007/0406/article_2498.htm

    gcc和g++都是GNU(组织)的一个编译器。

    误区一:gcc只能编译c代码,g++只能编译c++代码
    两者都可以,但是请注意:
    1.后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是c++程序,注意,虽然c++是c的超集,但是两者对语法的要求是有区别的。C++的语法规则更加严谨一些。
    2.编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。
    误区二:gcc不会定义__cplusplus宏,而g++会
    实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释,如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。
    误区三:编译只能用gcc,链接只能用g++
    严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++或者gcc -lstdc++。因为gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价。

    gcc和g++的区别

    我们在编译c/c++代码的时候,有人用gcc,有人用g++,于是各种说法都来了,譬如c代码用gcc,而c++代码用g++,或者说编译用gcc,链接用g++,一时也不知哪个说法正确,如果再遇上个extern "C",分歧就更多了,这里我想作个了结,毕竟知识的目的是令人更清醒,而不是更糊涂。

    误区一:gcc只能编译c代码,g++只能编译c++代码

    两者都可以,但是请注意:

    1.后缀为.c的,gcc把它当作是C程序,而g++当作是c++程序;后缀为.cpp的,两者都会认为是c++程序,注意,虽然c++是c的超集,但是两者对语法的要求是有区别的,例如:

    #include <stdio.h>

    int main(int argc, char* argv[]) {
       if(argv == 0) return;

       printString(argv);

    return;
    }
    int printString(char* string) {
      sprintf(string, "This is a test./n");
    }

    如果按照C的语法规则,OK,没问题,但是,一旦把后缀改为cpp,立刻报三个错:“printString未定义”;

    “cannot convert `char**' to `char*”;

    ”return-statement with no value“;

    分别对应前面红色标注的部分。可见C++的语法规则更加严谨一些。

    2.编译阶段,g++会调用gcc,对于c++代码,两者是等价的,但是因为gcc命令不能自动和C++程序使用的库联接,所以通常用g++来完成链接,为了统一起见,干脆编译/链接统统用g++了,这就给人一种错觉,好像cpp程序只能用g++似的。

    误区二:gcc不会定义__cplusplus宏,而g++会

    实际上,这个宏只是标志着编译器将会把代码按C还是C++语法来解释,如上所述,如果后缀为.c,并且采用gcc编译器,则该宏就是未定义的,否则,就是已定义。

    误区三:编译只能用gcc,链接只能用g++


    严格来说,这句话不算错误,但是它混淆了概念,应该这样说:编译可以用gcc/g++,而链接可以用g++或者gcc -lstdc++。因为gcc命令不能自动和C++程序使用的库联接,所以通常使用g++来完成联接。但在编译阶段,g++会自动调用gcc,二者等价。

    误区四:extern "C"与gcc/g++有关系

    实际上并无关系,无论是gcc还是g++,用extern "c"时,都是以C的命名方式来为symbol命名,否则,都以c++方式命名。试验如下:
    me.h
    extern "C" void CppPrintf(void);

    me.cpp:
    #include <iostream>
    #include "me.h"
    using namespace std;
    void CppPrintf(void)
    {
         cout << "Hello/n";
    }

    test.cpp:
    #include <stdlib.h>
    #include <stdio.h>
    #include "me.h"       
    int main(void)
    {
        CppPrintf();
        return 0;
    }

    1. 先给me.h加上extern "C",看用gcc和g++命名有什么不同

    [root@root G++]# g++ -S me.cpp
    [root@root G++]# less me.s
    .globl _Z9CppPrintfv        //注意此函数的命名
            .type   CppPrintf, @function

    [root@root GCC]# gcc -S me.cpp
    [root@root GCC]# less me.s
    .globl _Z9CppPrintfv        //注意此函数的命名
            .type   CppPrintf, @function
    完全相同!
    2. 去掉me.h中extern "C",看用gcc和g++命名有什么不同


    [root@root GCC]# gcc -S me.cpp
    [root@root GCC]# less me.s
    .globl _Z9CppPrintfv        //注意此函数的命名
            .type   _Z9CppPrintfv, @function

    [root@root G++]# g++ -S me.cpp
    [root@root G++]# less me.s
    .globl _Z9CppPrintfv        //注意此函数的命名
            .type   _Z9CppPrintfv, @function
    完全相同!

    【结论】完全相同,可见extern "C"与采用gcc/g++并无关系,以上的试验还间接的印证了前面的说法:在编译阶段,g++是调用gcc的。

     

    2.2

     

    X. 异常

    x.1 error C2664: “FindFirstFileA”: 不能将参数 2 从“WIN32_FIND_DATA *”转换为“LPWIN32_FIND_DATAA”

    在程序中使用FindFirstFileA函数,而第二个参数的类型是:WIN32_FIND_DATA,则会报次错。WIN32_FIND_DATA应改为WIN32_FIND_DATAA。

     

    x.2

    1

    运行以下代码时报上面的错误

    int main(){
        string s;
        s="visualc++";
        for(string::iterator i=s.end()-1;i>=s.begin();i--)
        cout<<*i;
        return 0;
        return 0;
    }

    修改成以下代码:

    int main(void) {
        map<string, string> mymap;
        int i;

        mymap.insert(pair<string, string>("yes", "no"));
        mymap.insert(pair<string, string>("up", "down"));
        string test = "abcdefg";
        for(string::iterator iter = test.begin(); iter != test.end(); iter++) {
            cout << *iter;
        }
        cout << endl;

        return 0;
    }

    二者的区别就在for循环中间的条件语句,前者是“<=”,而后者是”!=”。

    int main(){
        string s = "Hello C++";
        for(basic_string<char>::iterator it=s.begin();it!=s.end();++it)
            cout<<*it<<endl;
        return 0;
    }

    上面的代码也可以通过。

     

    x.3 Building MFC application with /MD[d] (CRT dll version) requires MFC shared dll version. Please #define _AFXDLL or do not use

    项目属性——配置属性——常规,在“项目属性值”的“MFC使用”中选择“在共享DLL中使用MFC”。

     

    x.4 fatal error C1189: #error :  WINDOWS.H already included.  MFC apps must not #include <windows.h>

    将使用MFC的头文件afxwin.h放在windows.h之前即可!

     

    x.5 warning C4800: “BOOL”: 将值强制为布尔值“true”或“false”(性能警告)

    bool是定义在C++中的关键字,而BOOL定义在MFC中的宏:

    typedef int BOOL;

    #define FALSE 0

    #define TRUE 1

    二者的区别如下:

    1)BOOL实际上是一个整型,在32位机器上除了TRUE和FALSE,其他的位都可以使用,可以返回2^32个状态,这样可以捕捉多个状态的信息,而bool关键字定义的布尔型,只有两个值可以使用,如果函数接口有多个返回状态,会混乱。

    2)MFC4.2以前定义的bool为int,但从5.0之后改为定义bool为1个字节:

    char  sizeof(bool) == 1; 

    int   sizeof(BOOL) == 4

    因此,当结构体中含有bool时,会发生内存冲突,所以应尽量使用BOOL。

     

    x.6 error C2679: 二进制“=”: 没有找到接受“CString”类型的右操作数的运算符(或没有可接受的转换)

    该问题,因属于项目编码的问题。解决方法也有两种:

    1)项目的属性中,将字符集换成“使用多字节字符集”即可。

    2)使用如下代码:

    int main(void) {
        CStringA cs = "This is CString!";
        string ss = "This is string!";
        ss = cs;
        cout << ss << endl;

        return 0;
    }

    更多请参见:http://msdn.microsoft.com/zh-cn/library/cc468203(VS.71).aspx

    以下摘自:http://topic.csdn.net/u/20080724/09/75f46508-5990-4b92-a5ea-f9d598db05a5.html

    支持多字节字符集 (MBCS)
    多字节字符集 (MBCS) 是一种替代 Unicode 以支持无法用单字节表示的字符集(如日文和中文)的方法。为国际市场编程时应考虑使用 Unicode 或 MBCS,或使程序能够通过更改开关来生成支持两种字符集之一的程序。
    最常见的 MBCS 实现是双字节字符集 (DBCS)。一般来说,Visual C++(尤其是 MFC)完全支持 DBCS。
    有关示例,请参见 MFC 源代码文件。
    对于语言使用大字符集的市场所使用的平台,代替 Unicode 的最佳方法是 MBCS。MFC 通过使用可国际化的数据类型和 C 运行时函数来支持 MBCS。您也应在自己的代码中这样操作。
    在 MBCS 下,字符被编码为单字节或双字节。在双字节字符中,第一个字节(即前导字节)表示它和下一个字节将被解释为一个字符。第一个字节来自留作前导字节的代码范围。哪个范围的字节可以用作前导字节取决于所使用的代码页。例如,日文代码页 932 使用 0x81 到 0x9F 范围内的字节作为前导字节,而朝鲜语代码页 949 则使用其他范围的字节。
    在 MBCS 编程中需考虑下列所有因素。
    环境中的 MBCS 字符
    MBCS 字符可以出现在文件名和目录名等字符串中。
    编辑操作
    MBCS 应用程序上的编辑操作应在字符上操作,而非在字节上操作。插入符号不应拆分字符,向右键应向右移动一个字符等。Delete 应删除一个字符;Undo 则应将字符重新插入。
    字符串处理
    在使用 MBCS 的应用程序中,字符串处理引起特殊问题。两种宽度的字符混合在一个字符串中;因此必须记住检查前导字节。
    运行时库支持
    C 运行时库和 MFC 支持单字节、MBCS 和 Unicode 编程。单字节字符串用 str 运行时函数族处理,MBCS 字符串用相应的 _mbs 函数处理,而 Unicode 字符串用相应的 wcs 函数处理。MFC 类成员函数的实现使用可移植运行时函数,这些可移植运行时函数在正常情况下映射到标准 str 函数族、MBCS 函数或 Unicode 函数,如“MBCS/Unicode 可移植性”中所述。
    MBCS/Unicode 可移植性
    使用 Tchar.h 头文件可以用同一个源生成单字节的 MBCS 应用程序和 Unicode 应用程序。Tchar.h 定义以 _tcs 为前缀的宏,这些宏根据相应的情况映射到 str、_mbs 或 wcs 函数。若要生成 MBCS,请定义 _MBCS 符号。若要生成 Unicode,请定义 _UNICODE 符号。默认情况下,为 MFC 应用程序定义的是 _MBCS。有关更多信息,请参见 Tchar.h 中的一般文本映射。
    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!注意!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
    如果同时定义了 _UNICODE 和 _MBCS,则行为不确定。
    Mbctype.h 和 Mbstring.h 头文件定义了 MBCS 特定的函数和宏,在某些情况下可能需要这些函数和宏。例如,_ismbblead 能告诉您某个字符串中的特定字节是否为前导字节。
    为获得国际可移植性,使用 Unicode 或多字节字符集 (MBCS) 编码程序。
    您希望做什么?
    在程序中启用 MBCS
    在程序中启用 Unicode 和 MBCS
    使用 MBCS 创建国际化程序
    了解 MBCS 编程的大概情况
    了解字节宽度可移植性的一般文本映射
    请参见
    概念
    国际编程
    Visual C++ 中的 MBCS 支持

     

     

     

     

     

     

     

     

     

     

     

     

     

    • 0
      点赞
    • 2
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值