프로그래밍 방식으로 dlgtemplate을 조작하는 방법은 무엇입니까?
-
03-07-2019 - |
문제
뭐?
리소스 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 페이지의 코드를 몇 줄로 줄였습니다.
모든 답변에 감사드립니다.