Реализация Base64 шифрования/расшифрования.
Зачем это? Когда нужно чтобы работало под всеми Windows, а не только в WinXP (т.е. нельзя пользоваться CryptAPI аналогичными ф-циями CryptStringToBinary и CryptBinaryToString). Или когда пишется код в 6ой студии (т.е. нет библиотеки ATL из 7ой студии — ATL::Base64Encode / ATL::Base64Decode).
Достоинства:
1. Быстро. Нет операций умножения/деления. Они заменены на битовый сдвиг.
(не относится к подсчёту нужного обьёма выходного буфера)
2. Работа с большими обьёмами данных.
3. Обработка ошибок:
— защита 'от дурака' (проверка вх. параметров)
— обьяснение ошибки с помощью SetLastError
— валидация символов вх.буфера на принадлежность Base64 алфавиту при декриптовании.
4. Ф-ция криптования быстрее аналогичной CryptoAPI ф-ции CryptBinaryToString.
5. Поддержка UNICODE
6. Поддержка 'чистого' API. Т.е. будет работать как в Win32 API, так и в классовых пакетах (WTL/ATL, MFC).
7. Для удобства кодирования/раскодирования строк предусмотрены функции-обёртки возвращающие класс строк.
Недостатки:
1. Ф-ция декриптования чуть медленнее аналогичной CryptoAPI ф-ции CryptStringToBinary
Пример.
Вместе с ф-циями Base64 (де)криптования, в WinMain части приведён ещё и код тестирующий их и сравнивающий работу с аналогичными ф-циями из CryptAPI — CryptStringToBinary/CryptBinaryToString.
В качестве класса срок — пользуюсь CString из библиотеки WTL 7.0.
#define _ATL_USE_CSTRING_FLOAT
#include
#include
#pragma comment(lib, "Crypt32.lib")
#pragma comment(lib, "ShlwApi.lib")
// include для CString'а
//#include // ATL из 7ой студии
// библиотека WTL 7.0
#include
#include
//extern
CComModule _Module;
#include //
////////////////////////////////////////////////////////////////////////////////
// BASE 64
////////////////////////////////////////////////////////////////////////////////
BOOL Base64_Code(
IN BYTE const *pData, // на вход - данные, которые надо зашифровать
IN DWORD dwSize,
OUT LPTSTR szCodeData, // на выход - зашифрованная строка
IN OUT DWORD &dwLenInChar // length in TCHAR (if szCodeData == NULL then return length include EOL)
) {
if (!dwSize) {
::SetLastError(ERROR_INVALID_DATA); // ERROR_INVALID_PARAMETER
return FALSE;
}
DWORD dwNeedLen = ((((dwSize-1)/3)+1)<<2) + 1;
if (szCodeData == NULL) {
dwLenInChar = dwNeedLen;
::SetLastError(DISP_E_BUFFERTOOSMALL); // ERROR_INSUFFICIENT_BUFFER
return TRUE;
}
if (!pData) {
::SetLastError(ERROR_INVALID_DATA); // ERROR_INVALID_PARAMETER
return FALSE;
}
if (!szCodeData || (dwLenInChar < dwNeedLen)) {
dwLenInChar = dwNeedLen;
::SetLastError(DISP_E_BUFFERTOOSMALL); // ERROR_INSUFFICIENT_BUFFER
return FALSE;
}
szCodeData[dwNeedLen-1] = 0; // EOL
const static TCHAR base64ABC[] = _T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
DWORD j = 0;
DWORD i = 0;
/** /
// Нижеприведённый цикл разбиваю на 2 части.
// Таким образом оптимизирую по скорости работы (для больших массивов данных) -
// удаляю в каждой части лишние проверки.
for (; i>18) & 0x3F];
szCodeData[j++] = base64ABC[(l>>12) & 0x3F];
if (i+1 < dwSize) szCodeData[j++] = base64ABC[(l>> 6) & 0x3F];
if (i+2 < dwSize) szCodeData[j++] = base64ABC[(l ) & 0x3F];
}
/**/
for (; dwSize>2 && i>18) & 0x3F];
szCodeData[j++] = base64ABC[(l>>12) & 0x3F];
szCodeData[j++] = base64ABC[(l>> 6) & 0x3F];
szCodeData[j++] = base64ABC[(l ) & 0x3F];
}
if (i>18) & 0x3F];
szCodeData[j++] = base64ABC[(l>>12) & 0x3F];
if (i+1 < dwSize) szCodeData[j++] = base64ABC[(l>> 6) & 0x3F];
}
/**/
switch (dwSize%3) {
case 1: szCodeData[j++] = _T('=');
case 2: szCodeData[j++] = _T('=');
}
dwLenInChar = j;
::SetLastError(NO_ERROR);
return TRUE;
}
__forceinline int IndexB64(TCHAR c) {
if ((c >= _T('A')) && (c <= _T('Z'))) return c-_T('A');
if ((c >= _T('a')) && (c <= _T('z'))) return c-_T('a')+26;
if ((c >= _T('0')) && (c <= _T('9'))) return c-_T('0')+52;
if (c == _T('+')) return 62;
if (c == _T('/')) return 63;
return -1;
}
BOOL Base64_Decode(
IN LPCTSTR szIn, // На вход - зашифрованная строка
IN int iLenIn, // Длина входной строки. Если == -1, то считаю длину сам...
OUT BYTE *pData, // На выход - расшифрованные данные
IN OUT DWORD &dwSize // IN - размер буфера; OUT - сколько данных записано
) { // Если dwSize==0 - возвращаю нужную длину буфера
if (!szIn || !szIn[0]) {
::SetLastError(ERROR_INVALID_DATA); // ERROR_INVALID_PARAMETER
return FALSE;
}
if (iLenIn == -1) {
iLenIn = lstrlen(szIn);
}
DWORD dwNeedSize = (iLenIn>>2)*3 -
(szIn[iLenIn-1]==_T('=') ? 1 : 0) -
(szIn[iLenIn-2]==_T('=') ? 1 : 0);
if (pData == NULL) {
dwSize = dwNeedSize;
::SetLastError(DISP_E_BUFFERTOOSMALL); // ERROR_INSUFFICIENT_BUFFER
return TRUE;
}
if (dwSize < dwNeedSize) {
dwSize = dwNeedSize;
::SetLastError(DISP_E_BUFFERTOOSMALL); // ERROR_INSUFFICIENT_BUFFER
return FALSE;
}
if (iLenIn < 4) {
// минимальный размер строки - 4 символа
::SetLastError(ERROR_INVALID_DATA); // ERROR_INVALID_PARAMETER
return FALSE;
}
const static TCHAR base64ABC[] = _T("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
dwSize = 0;
int i=0;
/** /
// Нижеприведённый цикл разбиваю на 2 части.
// Таким образом оптимизирую по скорости работы (для больших массивов данных) -
// удаляю в каждой части лишние проверки.
for (; i>16) & 0xFF);
if (in1 && bE1 && bE2) pData[dwSize++] = ((l>>8 ) & 0xFF);
if (in2 && bE2 && bE3) pData[dwSize++] = ((l>>0 ) & 0xFF);
}
/**/
for (; i>16) & 0xFF);
pData[dwSize++] = ((l>>8 ) & 0xFF);
pData[dwSize++] = ((l>>0 ) & 0xFF);
}
{
bool bE0=false, bE1=false, bE2=false, bE3=false; // не символ ли "="
int iF0=0 , iF1=0 , iF2=0 , iF3=0 ; // индекс в строке алфавита (признак, входит ли символ в Base64 алфавит)
LONG l = ((bE0 = (szIn[i+0] != _T('='))) ? LONG(iF0 = IndexB64(szIn[i ])) << 18 : 0) |
((bE1 = (szIn[i+1] != _T('='))) ? LONG(iF1 = IndexB64(szIn[i+1])) << 12 : 0) |
((bE2 = (szIn[i+2] != _T('='))) ? LONG(iF2 = IndexB64(szIn[i+2])) << 6 : 0) |
((bE3 = (szIn[i+3] != _T('='))) ? LONG(iF3 = IndexB64(szIn[i+3])) << 0 : 0);
if ((iF0 == -1) || (iF1 == -1) || (iF2 == -1) || (iF3 == -1)) { // во входную строку "затесался" символ не из Base64 алфавита...
::SetLastError(ERROR_INVALID_DATA); // ERROR_INVALID_PARAMETER
return TRUE; // По аналогии с CryptStringToBinary..
}
if (bE0 && bE1) pData[dwSize++] = ((l>>16) & 0xFF);
if (bE1 && bE2) pData[dwSize++] = ((l>>8 ) & 0xFF);
if (bE2 && bE3) pData[dwSize++] = ((l>>0 ) & 0xFF);
}
/**/
::SetLastError(NO_ERROR);
return TRUE;
}
CString Base64_Code(
IN LPCTSTR szIn // на вход - сторка, которую надо зашифровать
) {
CString strOut; // на выход - зашифрованная строка
if (!szIn || !szIn[0]) {
::SetLastError(ERROR_INVALID_DATA); // ERROR_INVALID_PARAMETER
return strOut;
}
DWORD dwSizeInBytes = lstrlen(szIn) << (sizeof(TCHAR)-1);
DWORD dwNeed = 0;
BOOL bRes = Base64_Code(NULL, dwSizeInBytes, NULL, dwNeed); // Узнаю сколько нужно символов (c EOL)
if (bRes) {
TCHAR *szCodeData = new TCHAR[dwNeed];
if (szCodeData == NULL) {
::SetLastError(ERROR_OUTOFMEMORY);
} else {
bRes = Base64_Code((BYTE*)szIn, dwSizeInBytes, szCodeData, dwNeed);
if (bRes) {
strOut = szCodeData;
}
delete [] szCodeData; szCodeData = NULL;
}
}
return strOut;
}
CString Base64_Decode(
IN LPCTSTR szIn // на вход - зашифрованная строка
) {
CString strOut; // на выход - расшифрованная строка
if (!szIn || !szIn[0]) {
::SetLastError(ERROR_INVALID_DATA); // ERROR_INVALID_PARAMETER
return strOut;
}
DWORD dwSizeInChar = lstrlen(szIn);
DWORD dwNeed = 0;
BOOL bRes = Base64_Decode(szIn, dwSizeInChar, NULL, dwNeed);
if (bRes) {
BYTE *pDecodeBuff = new BYTE[dwNeed];
if (pDecodeBuff == NULL) {
::SetLastError(ERROR_OUTOFMEMORY);
} else {
((TCHAR*)pDecodeBuff)[dwNeed-1] = 0;
bRes = Base64_Decode(szIn, dwSizeInChar, pDecodeBuff, dwNeed);
if (bRes) {
strOut = CString((TCHAR*)pDecodeBuff, dwNeed>>(sizeof(TCHAR)-1));
}
delete [] pDecodeBuff; pDecodeBuff = NULL;
}
}
return strOut;
}
////////////////////////////////////////////////////////////////////////////////
CString SelectFile(BOOL bOpenDialog, HWND hWndOwner, LPCTSTR szDefSelectedFile, LPCTSTR szDefExt, LPCTSTR szFilter, LPCTSTR szTitle, LPCTSTR szInitialDir, DWORD dwFlags) {
CString strFileName;
DWORD nMaxFile = max(MAX_PATH, (szDefSelectedFile ? lstrlen(szDefSelectedFile)+1 : 0));
anew1:
TCHAR *szFile = new TCHAR [nMaxFile];
anew2:
if (szFile == NULL) {
::SetLastError(ERROR_OUTOFMEMORY);
} else {
szFile[0] = 0;
if (szDefSelectedFile) {
lstrcpyn(szFile, szDefSelectedFile, min(nMaxFile, (DWORD)lstrlen(szDefSelectedFile)+1));
}
OPENFILENAME OpenFileName = {
sizeof(OPENFILENAME), // DWORD lStructSize;
hWndOwner , // HWND hwndOwner;
NULL , // HINSTANCE hInstance;
szFilter , // LPCTSTR lpstrFilter;
NULL , // LPTSTR lpstrCustomFilter;
0 , // DWORD nMaxCustFilter;
0 , // DWORD nFilterIndex;
szFile , // LPTSTR lpstrFile;
nMaxFile , // DWORD nMaxFile;
NULL , // LPTSTR lpstrFileTitle;
0 , // DWORD nMaxFileTitle;
szInitialDir , // LPCTSTR lpstrInitialDir;
szTitle , // LPCTSTR lpstrTitle;
dwFlags , // DWORD Flags;
0 , // WORD nFileOffset;
0 , // WORD nFileExtension;
szDefExt , // LPCTSTR lpstrDefExt;
0 , // LPARAM lCustData;
NULL , // LPOFNHOOKPROC lpfnHook;
NULL // LPCTSTR lpTemplateName;
};
if (bOpenDialog ? ::GetOpenFileName(&OpenFileName) : ::GetSaveFileName(&OpenFileName)) {
strFileName = szFile;
} else {
DWORD dwError = ::CommDlgExtendedError();
switch (dwError) {
case FNERR_BUFFERTOOSMALL:
{
delete [] szFile; szFile = NULL;
nMaxFile = nMaxFile << 1;
goto anew1;
} break;
case FNERR_INVALIDFILENAME:
{
szDefSelectedFile = NULL;
goto anew2;
} break;
}
}
delete [] szFile; szFile = NULL;
}
return strFileName;
}
////////////////////////////////////////////////////////////////////////////////
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
TCHAR szDir[MAX_PATH] = {0};
::GetCurrentDirectory(MAX_PATH, szDir);
TCHAR szFilter[] = _T("Any file (*.*)\0\0");
CString strFileName = ::SelectFile(TRUE, ::GetDesktopWindow(), NULL, NULL,
szFilter,
_T("Please select file to encrypted"), szDir, OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST);
if (strFileName.IsEmpty()) return -1;
CString strReportCode_API(_T("> Crypt data (API) - ERROR!\r\n"));
CString strReportCode_My (_T("> Crypt data (My) - ERROR!\r\n"));
CString strReportDecode_API(_T("> Decode data (API) - ERROR!\r\n"));
CString strReportDecode_My (_T("> Decode data (My) - ERROR!\r\n"));
/**/
// CODE part
{
HANDLE hFileIn = ::CreateFile(strFileName , GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hFileOutAPI = ::CreateFile(strFileName+_T(".API.b64"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hFileOutMy = ::CreateFile(strFileName+_T( ".My.b64"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileSize = (hFileIn == INVALID_HANDLE_VALUE) ? 0 : ::GetFileSize(hFileIn, NULL);
if ((hFileIn != INVALID_HANDLE_VALUE) &&
(hFileOutAPI != INVALID_HANDLE_VALUE) &&
(hFileOutMy != INVALID_HANDLE_VALUE) && (dwFileSize != INVALID_FILE_SIZE))
{
BYTE *pFileData = new BYTE[dwFileSize];
if (pFileData) {
DWORD dwNBR = 0;
BOOL bRes = ::ReadFile(hFileIn, pFileData, dwFileSize, &dwNBR, NULL) && (dwNBR==dwFileSize);
if (bRes) {
{ // Кодирую с помощью CryptoAPI
DWORD dwLenInChar = 0;
bRes = ::CryptBinaryToString(pFileData, dwFileSize, CRYPT_STRING_BASE64, NULL, &dwLenInChar); // Узнаю сколько нужно символов (c EOL)
if (bRes) {
TCHAR *szCodeData = new TCHAR[dwLenInChar];
if (szCodeData) {
FILETIME ftFirst = {0,0}, ftSecond = {0,0};
::GetSystemTimeAsFileTime(&ftFirst); // засекаю время работы
bRes = ::CryptBinaryToString(pFileData, dwFileSize, CRYPT_STRING_BASE64, szCodeData, &dwLenInChar); // криптую
::GetSystemTimeAsFileTime(&ftSecond);
if (bRes) {
DWORD dwNBW = 0;
//if (false) {
// DWORD dwLenInBytes = dwLenInChar<<(sizeof(TCHAR)-1);
// bRes = ::WriteFile(hFileOutAPI, szCodeData, dwLenInBytes, &dwNBW, NULL) && (dwNBW==dwLenInBytes);
//} else
{
// CryptoAPI через каждые 50 символов делает перенос строки... Удаляю эти левые символы.
CString strCodeData(szCodeData); strCodeData.Remove(_T('\r')); strCodeData.Remove(_T('\n'));
DWORD dwLenInBytes = strCodeData.GetLength()<<(sizeof(TCHAR)-1);
bRes = ::WriteFile(hFileOutAPI, (LPCTSTR)strCodeData, dwLenInBytes, &dwNBW, NULL) && (dwNBW==dwLenInBytes);
}
ULARGE_INTEGER iFirst ; iFirst .LowPart = ftFirst .dwLowDateTime; iFirst .HighPart = ftFirst .dwHighDateTime;
ULARGE_INTEGER iSecond; iSecond.LowPart = ftSecond.dwLowDateTime; iSecond.HighPart = ftSecond.dwHighDateTime;
ULONGLONG lDiff = iSecond.QuadPart - iFirst.QuadPart; // в десятках наносекунд
float fDiff = float(int(lDiff))/10000.f;
strReportCode_API.Format(_T("> Writed code data (API) -> %s; time - %.3f millisecond\r\n"), bRes ? _T("Ok") : _T("Err"), fDiff);
}
delete [] szCodeData; szCodeData = NULL;
}
}
}
{ // Криптую своей ф-цией
DWORD dwLenInChar = 0;
bRes = Base64_Code(NULL, dwFileSize, NULL, dwLenInChar); // Узнаю сколько нужно символов (c EOL)
if (bRes) {
TCHAR *szCodeData = new TCHAR[dwLenInChar];
if (szCodeData) {
FILETIME ftFirst = {0,0}, ftSecond = {0,0};
::GetSystemTimeAsFileTime(&ftFirst); // засекаю время работы
bRes = Base64_Code(pFileData, dwFileSize, szCodeData, dwLenInChar);
::GetSystemTimeAsFileTime(&ftSecond);
if (bRes) {
DWORD dwNBW = 0;
DWORD dwLenInBytes = dwLenInChar<<(sizeof(TCHAR)-1);
bRes = ::WriteFile(hFileOutMy, szCodeData, dwLenInBytes, &dwNBW, NULL) && (dwNBW==dwLenInBytes);
ULARGE_INTEGER iFirst ; iFirst .LowPart = ftFirst .dwLowDateTime; iFirst .HighPart = ftFirst .dwHighDateTime;
ULARGE_INTEGER iSecond; iSecond.LowPart = ftSecond.dwLowDateTime; iSecond.HighPart = ftSecond.dwHighDateTime;
ULONGLONG lDiff = iSecond.QuadPart - iFirst.QuadPart; // в десятках наносекунд
float fDiff = float(int(lDiff))/10000.f;
strReportCode_My.Format(_T("> Writed code data (My) -> %s; time - %.3f millisecond\r\n"), bRes ? _T("Ok") : _T("Err"), fDiff);
}
delete [] szCodeData; szCodeData = NULL;
}
}
}
}
delete [] pFileData; pFileData = NULL;
}
}
::CloseHandle(hFileOutAPI); hFileOutAPI = NULL;
::CloseHandle(hFileOutMy ); hFileOutMy = NULL;
::CloseHandle(hFileIn ); hFileIn = NULL;
}/**/
::OutputDebugString(strReportCode_API);
::OutputDebugString(strReportCode_My);
/**/
// DECODE part
{
HANDLE hFileIn = ::CreateFile(strFileName+_T(".My.b64" ), GENERIC_READ , FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hFileOutAPI = ::CreateFile(strFileName+_T(".API.decode"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
HANDLE hFileOutMy = ::CreateFile(strFileName+_T( ".My.decode"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD dwFileSize = (hFileIn == INVALID_HANDLE_VALUE) ? 0 : ::GetFileSize(hFileIn, NULL);
if ((hFileIn != INVALID_HANDLE_VALUE) &&
(hFileOutAPI != INVALID_HANDLE_VALUE) &&
(hFileOutMy != INVALID_HANDLE_VALUE) && (dwFileSize != INVALID_FILE_SIZE))
{
DWORD dwFileSizeInChar = dwFileSize>>(sizeof(TCHAR)-1);
TCHAR *szFileData = new TCHAR[dwFileSizeInChar+1];
if (szFileData) {
szFileData[dwFileSizeInChar] = 0; // EOL
DWORD dwNBR = 0;
BOOL bRes = ::ReadFile(hFileIn, szFileData, dwFileSize, &dwNBR, NULL) && (dwNBR==dwFileSize);
if (bRes) {
{ // Расшифровываю с помощью CryptoAPI
DWORD dwNeed = 0;
DWORD dwSkip = 0;
DWORD dwFlags = 0;
bRes = ::CryptStringToBinary(szFileData, dwFileSizeInChar, CRYPT_STRING_BASE64, NULL, &dwNeed, &dwSkip, &dwFlags);
if (bRes) {
BYTE *pDecodeBuff = new BYTE[dwNeed];
if (pDecodeBuff) {
FILETIME ftFirst = {0,0}, ftSecond = {0,0};
::GetSystemTimeAsFileTime(&ftFirst); // засекаю время работы
bRes = ::CryptStringToBinary(szFileData, dwFileSizeInChar, CRYPT_STRING_BASE64, pDecodeBuff, &dwNeed, &dwSkip, &dwFlags);
::GetSystemTimeAsFileTime(&ftSecond);
if (bRes) {
DWORD dwNBW = 0;
bRes = ::WriteFile(hFileOutAPI, pDecodeBuff, dwNeed, &dwNBW, NULL) && (dwNBW==dwNeed);
ULARGE_INTEGER iFirst ; iFirst .LowPart = ftFirst .dwLowDateTime; iFirst .HighPart = ftFirst .dwHighDateTime;
ULARGE_INTEGER iSecond; iSecond.LowPart = ftSecond.dwLowDateTime; iSecond.HighPart = ftSecond.dwHighDateTime;
ULONGLONG lDiff = iSecond.QuadPart - iFirst.QuadPart; // в десятках наносекунд
float fDiff = float(int(lDiff))/10000.f;
strReportDecode_API.Format(_T("> Writed decode data (API) -> %s; time - %.3f millisecond\r\n"), bRes ? _T("Ok") : _T("Err"), fDiff);
}
delete [] pDecodeBuff; pDecodeBuff = NULL;
}
}
}
{ // Расшифровываю своей ф-цией
DWORD dwNeed = 0;
bRes = Base64_Decode(szFileData, dwFileSizeInChar, NULL, dwNeed);
if (bRes) {
BYTE *pDecodeBuff = new BYTE[dwNeed];
if (pDecodeBuff) {
FILETIME ftFirst = {0,0}, ftSecond = {0,0};
::GetSystemTimeAsFileTime(&ftFirst); // засекаю время работы
bRes = Base64_Decode(szFileData, dwFileSizeInChar, pDecodeBuff, dwNeed);
::GetSystemTimeAsFileTime(&ftSecond);
if (bRes) {
DWORD dwNBW = 0;
bRes = ::WriteFile(hFileOutMy, pDecodeBuff, dwNeed, &dwNBW, NULL) && (dwNBW==dwNeed);
ULARGE_INTEGER iFirst ; iFirst .LowPart = ftFirst .dwLowDateTime; iFirst .HighPart = ftFirst .dwHighDateTime;
ULARGE_INTEGER iSecond; iSecond.LowPart = ftSecond.dwLowDateTime; iSecond.HighPart = ftSecond.dwHighDateTime;
ULONGLONG lDiff = iSecond.QuadPart - iFirst.QuadPart; // в десятках наносекунд
float fDiff = float(int(lDiff))/10000.f;
strReportDecode_My.Format(_T("> Writed decode data (My) -> %s; time - %.3f millisecond\r\n"), bRes ? _T("Ok") : _T("Err"), fDiff);
}
delete [] pDecodeBuff; pDecodeBuff = NULL;
}
}
}
}
delete [] szFileData; szFileData = NULL;
}
}
::CloseHandle(hFileOutAPI); hFileOutAPI = NULL;
::CloseHandle(hFileOutMy ); hFileOutMy = NULL;
::CloseHandle(hFileIn ); hFileIn = NULL;
}
/**/
::OutputDebugString(strReportDecode_API);
::OutputDebugString(strReportDecode_My);
::MessageBox(::GetDesktopWindow(), strReportCode_API+strReportCode_My+strReportDecode_API+strReportDecode_My, _T("Report"), MB_OK+MB_ICONINFORMATION);
return 0;
}
![](http://www.narod.ru/counter.xhtml)
![Hosted by uCoz Hosted by uCoz](http://s202.ucoz.net/img/cp/7.gif)