Qt 6.7버전 부터 발생한 사소한 QNetworkRequest 문제

사소하면서 어쩌면 사소하지 않은 문제가 6.7버전부터 발생하였다.

6.6버전까지 HTTP 헤더는 아래와같은 형식의 네이밍을 가진다.

6.7버전 부터는 아래와같은 네이밍을 가진다.

문자열이 아닌 QByteArray여서 그런지 라인피드가 이스케이프되어 표시되는데 보기 편하지 않지만 자세히 보면 6.6버전까지는 단어 첫 글자가 대문자로 시작하지만 6.7버전 부터는 모든 글자가 소문자로 변했다. 보통의 웹서버는 문제가 없지만 간혹 문제가 되는 서버가 있다.

예를 들면 SHIP같은 가벼운 웹서버 같은 경우다.

프로젝트 주소: https://github.com/xgfone/ship

대소문자 무시하여 헤더를 얻지않기때문에 소문자로 되어 있으면 Content-Type 헤더를 얻어오지 못한다. 덕분에 Bad Request를 응답하게 된다. 따라서 이걸 사용한 장비가 있다면 직접 HTTP 프로토콜을 구현하거나 Qt 소스를 고쳐야한다.

이것도 Qt의 버그라고 해야할지 모호하기도 하지만 일반적으로 Content-Type로 헤더를 보내지 content-type로 보내는 경우는 지금까지 본적이 없다. 버그리포트 하기가 좀… 귀찮기도하고 그냥 지금은 Qt 6.6.3을 받아서 써야겠다.

GDI의 SelectObject 함수

요즘은 GDI를 사용할 일이 없겠지만 간혹 사용할 일이 생긴다. 최근 uxtheme를 파고들다보니 다시 접하게 되었는데 GDI 정보가 생각보다 일관성이 없다.

일단 내가 오래전에 봤던 내용 중 기억에 남아있고 하던 코딩 방식은 아래와같았다.

static void TestDC()
{
    HDC hdc = GetDC(NULL);
    HDC memDC = CreateCompatibleDC(hdc);
    HBITMAP memBitmap = CreateCompatibleBitmap(hdc, 100, 100);
    HBITMAP oldBitmap = (HBITMAP)SelectObject(memDC, memBitmap);

    // ...

    SelectObject(memDC, oldBitmap);

    DeleteObject(memBitmap);
    DeleteDC(memDC);
    ReleaseDC(NULL, hdc);
}

DC 객체를 생성하고 처음 SelectObject하면 반환되는 기본 객체는 DC를 지우기 전 다시 SelectObject로 기본 객체를 선택하여 DC를 지워야한다는 것이였다.

GDI 오브젝트 개수를 확인하는 코드를 구글링하니 GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS) 이러한 코드가 나왔다(참조 링크). 이걸 참조하여 아래와같이 수정하여 실행해보았다.

Continue reading GDI의 SelectObject 함수

윈도우에서 라즈베리파이 커널 빌드

WSL이나 qemu등을 사용하지 않고 MSYS2에서 라즈베리파이 커널 빌드와 busybox를 initd로 사용하여 부팅가능한 SD카드 만드는 과정을 기록한다. MSYS2에서 만들면 발생하는 문제점으로는 라즈베리파이가 부팅 된 이후 특별히 할 수 있는것이 없다.

WSL을 사용하거나 qemu에서 HAXM 가속으로 리눅스를 부팅해서 작업하는것이 시간도 몇배나 절약되고 자잘한 문제가 덜 발생한다. 예로 윈도우에서 빌드한 툴체인에서 libc를 static 라이브러리로 링크하면 _Unwind_Resume등을 찾을 수 없어서 링크가 불가능하여 라즈베리파이에서 구동되는 컴파일러를 빌드할 수 없는 등 할 수 없는 작업들이 많다. (LDPlayer같은 프로그램을 사용한다면 하이퍼바이저를 사실상 사용할 수 없기때문에 WSL을 사용할 수 없다.)

만약 부팅가능한 SD 카드를 만들고 젠투 리눅스같은 배포본을 설치 할 예정이라면 MSYS2에서도 시도할만하다.

Continue reading 윈도우에서 라즈베리파이 커널 빌드

Qt 6.6.1 MSVC 2022로 빌드

MSVC 2022로 Qt 6.6.1버전 빌드 시 발생하는 오류 리스트

  1. 소스코드 파일 인코딩 오류 (비 유니코드 언어가 영어 (미국)이 아닌경우)
  2. qtwebengine/src/3rdparty/gn/src/gn/variables.cc 텍스트 오류 (1번 항목과 동일한 환경, 577번 라인의 0xA0(NBSP) 캐릭터에서 에러 발생하며 0x20(SPACE)으로 변경하면 해결)
  3. qtwebengine/src/3rdparty/chromium/third_party/webrtc/rtc_base/checks.h LogStreamer의 연산자 오류
  4. gperf 버전이 3.1이상일 경우 오류 발생 ((standard input):1915: warning: junk after %% is ignored 메시지 발생하며 파일이 정상처리 안됨)

크게 네가지 오류가 나타난다. 이 중 1, 2번은 이 글에서 “지역설정” 항목을 참조하여 변경하면 해결 된다. 3번 항목은 vcpkg 프로젝트에서 qtwebengine 패키지의 패치(clang-cl.patch, msvc-template.patch)를 그대로 적용하면 해결된다. 4번은 Qt 5.15.x 버전을 다운받은 후 gnuwin32\bin 경로를 PATH의 맨 앞으로 추가하여 clean & build 하면 해결된다.

만약 시스템 로케일 변경이 귀찮거나 재부팅을 하기에 곤란하다면 아래 소스코드를 빌드 후 실행하여 UTF-8 BOM을 추가하고 환경변수에 “PYTHONUTF8=1″을 추가(프롬프트에서 SET PYTHONUTF8=1 실행)하여 파이선의 파일 인코딩을 UTF-8로 인식하도록 설정한다.

아래 코드는 NBSP문자를 SPACE로 변환 및 확장 아스키 영역의 문자가 있을 경우 UTF-8 BOM을 추가한다.

/*
 * 파일명: CSourceSetUtf8Bom.cpp
 * 빌드명령: cl CSourceSetUtf8Bom.cpp /O2 /std:c++20 /EHsc
 * 실행: CSourceSetUtf8Bom <qt source path>
 * 실행 예: CSourceSetUtf8Bom D:\OpenSource\Qt6.6.1.src
 */

#include <algorithm>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

static void fetch(const std::filesystem::path& source_path, std::size_t& count)
{
    std::cout << count << "\r";
    std::filesystem::directory_iterator it(source_path);

    for (; it != std::filesystem::end(it); it++)
    {
        if (std::filesystem::is_directory(*it))
        {
            fetch(*it, count);
        }
        else
        {
            std::string path = it->path().string();

            if (!path.ends_with(".c") && !path.ends_with(".cc") && !path.ends_with(".cpp") && !path.ends_with(".h"))
            //if (!path.ends_with(".py"))
            {
                continue;
            }

            count++;

            std::basic_ifstream<unsigned char> istream(path);
            std::basic_string<unsigned char> content((std::istreambuf_iterator<unsigned char>(istream)), std::istreambuf_iterator<unsigned char>());

            istream.close();

            if (content.size() >= 3 && content[0] == 0xEF && content[1] == 0xBB && content[2] == 0xBF)
            {
                continue;
            }

            bool extended = std::find_if(content.begin(), content.end(), [](unsigned char c) { return c > 127; }) != content.end();
            if (!extended)
            {
                continue;
            }

            std::cout << count << ", " << it->path().string() << ", " << content.size() << " bytes\n";

            // replace NBSP -> SPACE
            std::replace(content.begin(), content.end(), 0xA0, 0x20);
            std::basic_ofstream<unsigned char> ofstream(path);

            ofstream << static_cast<unsigned char>(0xEF);
            ofstream << static_cast<unsigned char>(0xBB);
            ofstream << static_cast<unsigned char>(0xBF);
            ofstream << content;

            ofstream.close();
        }
    }
}

int main(int argc, char* argv[])
{
    if (argc < 2)
    {
        std::cout << argv[0] << " <FOLDER>\n";

        return EXIT_FAILURE;
    }

    std::size_t count = 0;
    std::filesystem::path source_root(argv[1]);

    if (!std::filesystem::is_directory(source_root))
    {
        return EXIT_FAILURE;
    }
    fetch(source_root, count);

    return EXIT_SUCCESS;
}

윈도우 11 22H2의 작업 관리자 성능 모니터링 문제

CPU의 스레드별 사용률을 프로그래밍으로 얻기위해 NtQuerySystemInformation 함수로 얻어진 카운터로 사용률 계산해보니 작업 관리자의 성능 모니터 값과 두배 이상 차이나는 현상이 발생하였다.

작업관리자의 프로세스와 성능 화면은 아래와같다.

ConsoleApp1의 사용률 약 42%
성능 모니터의 프로세서 사용률 약 43%
Continue reading 윈도우 11 22H2의 작업 관리자 성능 모니터링 문제