#c #winapi #unicode #utf-8 #c-strings
#c #winapi #юникод #utf-8 #c-строки
Вопрос:
Я создал небольшое приложение на C, которое шифрует и расшифровывает строку с использованием шифра Цезаря. После расшифровки строки (unicode) она преобразуется в UTF8 с помощью WideCharToMultiByte . Это работает примерно в 90% случаев, но иногда WideCharToMultiByte, похоже, завершается с ошибкой, и функция GetLastError выводит 122, что означает ERROR_INSUFFICIENT_BUFFER.
Моя функция для преобразования строк unicode в UTF8 выглядит следующим образом:
LPSTR convert(LPWSTR wideStringToDecryptAndConvertUTF) {
LPWSTR res = shift(wideStringToDecryptAndConvertUTF, -6); //Decrypting the string by shifting -6 letters.
MessageBoxW(NULL, res, L"Decrypted output", MB_OK); //Check if the string was successfully decrypted - judging by the output of the messagebox this is always the case, the decrypted string looks as expected.
LPSTR retVal = 0;
ULONG cb = 0, cch = (ULONG)strl(res);
cb = WideCharToMultiByte(CP_UTF8, 0, res, cch * sizeof(WCHAR), retVal, cb, 0, 0);
retVal = GlobalAlloc(GMEM_FIXED, cb);
WideCharToMultiByte(CP_UTF8, 0, res, cch * sizeof(WCHAR), retVal, cb, 0, 0);
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { //Check for debugging purposes. At random times (even with the same string) this specific error occurs.
MessageBoxW(NULL, L"Error", L"Debug", MB_OK);
}
GlobalFree(res);
return retVal;
}
Моя функция сдвига для расшифровки / шифрования строк выглядит следующим образом (в этом случае она расшифровывается):
LPWSTR shift(LPWSTR shift, int param) {
LPWSTR allc = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz[]<>()[]";
LPWSTR encrypted = (LPWSTR)GlobalAlloc(GMEM_FIXED, strl(shift) * sizeof(WCHAR) 1);
int r = 0;
if (encrypted != NULL) {
for (size_t i = 0; i < strl(shift); i ) {
r ;
if (strchr42a((LPWSTR)allc, shift[i]) != NULL) {
LPWSTR e = strchr42a((LPWSTR)allc, shift[i]);
int index = (int)(e - allc);
encrypted[i] = allc[index param];
}
else {
encrypted[i] = shift[i];
}
}
}
if (encrypted != NULL) {
encrypted[r] = 0;
}
return encrypted;
}
Хотя это довольно просто, вот функция strl, используемая в обеих функциях выше:
size_t strl(LPWSTR s) {
size_t i = 0;
while (s amp;amp; *s != '') {
s ;
i ;
}
return i;
}
Все строки, которые я пытаюсь зашифровать, были зашифрованы с помощью одной и той же программы и состоят только из букв.
Я знаю, что само шифрование имеет ограничения (например, при шифровании нельзя сдвигать более 8 букв), но это не моя проблема.
Я не понимаю, почему функция WideCharToMultiByte в моем случае работает нестабильно. Любые предложения приветствуются!
Комментарии:
1. Обратите внимание на аргументы. Четвертый аргумент WideCharToMultiByte никогда не вычисляет размер байта; это простая величина (т. Е. Количество преобразуемых символов wide), или
-1
если строка завершается с известным завершением, и вы просто хотите, чтобы WCTMBS выполнял вычисления за вас. Уsizeof(WCHAR)
вас нет ничего общего с этим числом, и его там не должно быть. Кроме того, следите за тем, чтобы при указании точной длины WCTMBS не включал пробел для разделителя, если он вам нужен, поэтому вам также необходимо добавить его к размеру вашего целевого буфера.2. Спасибо за комментарий. Однако, если я удалю sizeof(WCHAR), кажется, что завершение строки завершается ошибкой. Если я проверяю возвращаемое значение функции convert, вывод иногда представляет собой расшифрованную строку несколько символов мусора.
3. Ты читал продолжение в этом комментарии? И, кстати, поскольку эти строки (предположительно) в любом случае завершаются,
strl
их бессмысленно использовать в экземпляре, просто отправьте-1
в качестве четвертого аргумента и позвольте WCTMBS выполнить вычисление длины, включая терминатор для вас.4. Большое вам спасибо! В настоящее время я все еще тестирую, но, похоже, теперь это работает. В C. я всегда сталкиваюсь с этими мелкими деталями…
5. Документация, безусловно, помогает: WideCharToMultiByte .
Ответ №1:
Вы вызываете WideCharToMultiByte
неправильно. 4-й параметр — это количество символов, а не количество байтов. Вы также не retVal
завершаете буфер нулевым завершением и вообще не выполняете никакой обработки ошибок.
Кроме того, shift()
вычисляется неправильный размер байта для его encrypted
буфера. И что еще хуже, если входная shift
строка содержит какой-либо символ A..F
, найденный индекс, смещенный на -6
, выйдет за пределы allc
.
Попробуйте что-то более похожее на это:
LPCWSTR strchr42a(LPCWSTR str, int ch)
{
do {
if (*str == ch) return str;
} while (*str );
return NULL;
}
LPWSTR shift(LPCWSTR shift, int param) {
LPCWSTR allc = L"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz[]<>()[]";
ULONG shift_len = strl(shift);
LPWSTR encrypted = (LPWSTR) GlobalAlloc(GMEM_FIXED, (shift_len 1) * sizeof(WCHAR));
if (!encrypted) return NULL;
int r = 0;
for (size_t i = 0; i < shift_len; i) {
r;
LPCWSTR e = strchr42a(allc, shift[i]);
if (e != NULL) {
int index = (int)(e - allc) param;
if (index < 0) {
// OUT OF BOUNDS! Need to wrap the index, or fail the function...
}
encrypted[i] = allc[index];
}
else {
encrypted[i] = shift[i];
}
}
encrypted[r] = 0;
return encrypted;
}
LPSTR convert(LPWSTR wideStringToDecryptAndConvertUTF) {
LPWSTR res = shift(wideStringToDecryptAndConvertUTF, -6); //Decrypting the string by shifting -6 letters.
if (!res) {
MessageBoxW(NULL, L"Error", L"Debug", MB_OK);
return NULL;
}
MessageBoxW(NULL, res, L"Decrypted output", MB_OK); //Check if the string was successfully decrypted - judging by the output of the messagebox this is always the case, the decrypted string looks as expected.
ULONG cch = (ULONG) strl(res);
ULONG cb = WideCharToMultiByte(CP_UTF8, 0, res, cch, retVal, 0, NULL, NULL);
if (cb == 0) {
DWORD errCode = GetLastError();
MessageBoxW(NULL, L"Error", L"Debug", MB_OK);
return NULL;
}
LPSTR retVal = GlobalAlloc(GMEM_FIXED, cb 1);
if (!retVal) {
DWORD errCode = GetLastError();
MessageBoxW(NULL, L"Error", L"Debug", MB_OK);
return NULL;
}
WideCharToMultiByte(CP_UTF8, 0, res, cch, retVal, cb, NULL, NULL);
retVal[cb] = 0;
GlobalFree(res);
return retVal;
}