문제

뭐?

리소스 DLL에서 DLGTEMPLATE가로드되어 있습니다. 프로그래밍 방식으로 런타임시 컨트롤에 할당 된 문자열을 어떻게 변경할 수 있습니까?

대화 상자가 생성되기 전에이 작업을 수행 할 수 있기를 원합니다. 따라서 디스플레이의 문자열이 리소스 DLL에서 나온 것이 아니라 대화 상자가 초기화 될 때 호출에서 SetWindowText 로의 호출이 아니라는 것을 알 수 있습니다.

Google은 코드에서 dlgtemplate을 만들거나 간단한 스타일 비트를 두드리는 예를 찾았지만 메모리의 문자열 편집에는 아무것도 발견되지 않았습니다.

어떻게?

대화/속성 시트 제작 API를 연결 하여이 작업을 수행하고 있습니다. 실제 대화 상자가 생성되기 전에 그리고 HWND가 있기 전에 dlgtemplate에 액세스 할 수 있습니다.

왜요?

런타임 현지화 및 현지화 테스트를 수행하고 싶습니다. 이미이 구현 문자열 (MFC 7.0 래퍼 포함), 메뉴 및 가속기 테이블을 위해 구현했지만 대화 상자/속성 시트 작성을 처리하기 위해 고군분투하고 있습니다.

코드 예제는 완벽한 답이 될 것입니다. 이상적으로는 Dlgtemplate 주위를 감싸는 클래스입니다. 내 자신의 솔루션을 작성하면 게시하겠습니다.

도움이 되었습니까?

해결책

메모리에서 문자열을 편집 할 수 없습니다. dlgtemplate 구조는 자원 DLL의 관련 바이트의 직접 파일 매핑입니다. 독서 만 읽습니다.

전체 dlgtemplate 구조를 처리하고 길이가 변경된 길이 문자열로 새로운 구조를 작성해야합니다.

솔직히 WM_INITDIALOG를 연결하고 dlgtemplate 작가를 구축하는 것보다 컨트롤과 상호 작용하여 문자열을 변경하는 것이 더 쉬울 것입니다. 주변에는 많은 사람들이 없기 때문입니다. 실제로 변경된 대화 자원을 디스크에 디스크로 저장 해야하는 추가 요구 사항이 없다면 (또는 .dll inplace를 수정하려고 시도) 실제로이 접근법을 피하는 것이 좋습니다.

이미 Accellerator 테이블 및 메뉴 문자열에 대해이 작업을 수행하고 있다고 말합니다. 문자열에 패치가 더 짧아 지도록 보장 할 수 있다면 Dlgtemplate 구조물의 이진 사본을 만들고 찾는 데 필요한 비 사소한 스캔 코드를 작성하십시오. 각 문자열이 있으므로 사본을 제자리에 패치 할 수 있습니다.

다른 팁

어딘가에 파일이 있습니다 (Microsoft에서 시작했지만 완전히 확실하지 않음) Resfmt.zip이라는 일부 코드 예제와 함께 설명합니다. Raymond Chen은 또한 그의 블로그에서 이것에 대한 훌륭한 설명을합니다. 대화 상자 및 대화 상자 컨트롤의 형식은 다릅니다.

다른 답변에서 언급했듯이 처음부터 구조를 다시 만들어야합니다. 기본 정보가 이미 있기 때문에 이것은 모두 나쁘지 않습니다. 컨트롤을 추가하는 것은 어려운 곳입니다.

기본적으로, 큰 기억의 블록을 단어 *lpin에 할당하십시오. 그런 다음 그 위에 구조를 추가하십시오. 대화 상자에 대한 기본 정보를 추가하고 (dlgtemplate 참조) MSDN에 정보가 있으므로 컨트롤이 분명합니다.

당신이 직면하게 될 두 가지 가장 큰 문제는 다음과 같습니다. 다양한 부분이 정렬 경계에서 시작하고 대화 제어의 값을 해석하는 것입니다. 각 컨트롤은 짝수 경계에서 시작해야합니다.

첫 번째 (어딘가에서 빌린 resfmt.zip이라고 생각합니다) :

WORD *AlignDwordPtr (WORD *lpIn)
    {
    ULONG ul;

    ul = (ULONG) lpIn;
    ul +=3;
    ul >>=2;
    ul 

내가 한 일은 다음과 같은 일련의 기능을 구축하여 메모리에서 대화 상자를 조립할 수있게 해주었습니다. (내 필요는 매우 기본적인 메시지를 위해 관련 RC 파일이 필요하지 않은 일반적인 코드를 가질 수있었습니다).

여기 예입니다 ...

WORD *AddStringOrOrdinalToWordMem( WORD *lpw, char    *sz_Or_Ord )
    {
    LPWSTR  lpwsz;
    int     BufferSize;

    if (sz_Or_Ord == NULL)
        {
        *lpw++ = 0;
        }
    else
        {
        if (HIWORD(sz_Or_Ord) == 0) //MAKEINTRESOURCE macro 
            {
            *lpw++ = 0xFFFF;
            *lpw++ = LOWORD(sz_Or_Ord);
            }
        else
            {
            if (strlen(sz_Or_Ord))
                {
                lpwsz = ( LPWSTR ) lpw;
                BufferSize = MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, 0 );
                MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, BufferSize );
                lpw = lpw +  BufferSize;
                }
            else
                {
                *lpw++ = 0;
                }
            }
        }
    return( lpw );
    }

전체 모듈에 대한 헤더 파일에는 다음 기능이 포함되었습니다.

WORD *AddControlToDialogTemplateEx(MTDialogTemplateType *dlgtmp, char *Title, WORD Id, char *WinClass, DWORD Style, short x, short y, short cx, short cy, DWORD ExStyle, int HelpID); int DestroyDlgTemplateEx(MTDialogTemplateType *dlgtmp); MTDialogTemplateType *CreateDlgTemplateEx( char *Name, // We use name just for reference, so it can be NULL short x, short y, short cx, short cy, DWORD ExtendedStyle, DWORD Style, char *Menu, char *WinClass, char *Caption, char *FontTypeFace, int FontSize, int FontWeigth, int FontItalic, int Charset, int HelpID, int NumberOfControls);

코드에서 전체 대화를 쉽게 조립할 수있었습니다.

API 기능을 참조하십시오 ::열거적 인 위에(Hwnd, Wndenumproc, lparam)

compview :: create 또는 cdialog :: oninitdialog에서 이것을 호출하여 제어 캡션을 교체 할 수있는 기회를 제공합니다. 걱정하지 마십시오. 오래된 문자열은 교체하기 전에 깜박 거지 않습니다.

대화 자원에서 컨트롤 캡션을 어떤 종류의 사전에서 키로 설정하십시오. 컴파일 /CLR 인 경우 관리 된 문자열 테이블 리소스를 사용할 수 있습니다. 콜백에서 사전에서 번역 된 문자열을 찾아 컨트롤 캡션을 번역으로 설정하십시오. /clr 및 관리되는 문자열 테이블의 또 다른 이점은 이미 시스템을 설정 한 Windows (또는 귀하)별로 올바른 언어를 자동으로 조회 할 수 있다는 것입니다.

이 같은

CMyDialog::OnInitDialog()
{
    ::EnumChildWindows(
        this->GetSafeHwnd(),
        CMyDialog::UpdateControlText,
        (LPARAM)this )
}

BOOL CALLBACK CMyDialog::UpdateControlText( HWND hWnd, LPARAM lParam )
{
    CMyDialog* pDialog = (CMyDialog*)lParam;
    CWnd* pChildWnd = CWnd::FromHandle( hWnd );

    int ctrlId = pChildWnd->GetDlgCtrlID();
    if (ctrlId)
    {
        CString curWindowText;
        pChildWnd->GetWindowText( curWindowText );
        if (!curWindowText.IsEmpty())
        {
            CString newWindowText = // some look up
            pChildWnd->SetWindowText( newWindowText );
        }
    }
}

템플릿을 나타내는 MEM 버퍼에서 수정하려는 문자열을 찾아야합니다. 그렇게하는 유일한 방법은 전체 템플릿을 가로 지르는 것입니다. 쉽지 않습니다. 이 작업을 마치면 새 문자열이 원래 문자열보다 길면 버퍼에 바이트를 삽입하십시오. 또는 새 문자열이 짧은 경우 버퍼를 수축시킵니다.

Chris가 쓴 것처럼 WM_INITDIALOG에서 텍스트를 수정하고 SetWindOwText ()를 호출 할 수 없다는 요구 사항을 다시 제출하려고 시도하는 것이 훨씬 쉽습니다.

감사합니다. 실제로 문제에 대해 24 시간 동안 휴식을 취한 다음 WM_INITDIALOG를 필터링하는 글로벌 Windows 후크를 사용하여 훨씬 간단한 방법이었고, API 후크가 필요하지 않으며, 2 페이지의 코드를 몇 줄로 줄였습니다.

모든 답변에 감사드립니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top