2013-05-02 5 views
7

Tentativo di aggiungere testo a un controllo di modifica all'interno di una finestra di dialogo. Non riesco a ottenere _tcscat_s da aggiungere correttamente. Si blocca e dice qualcosa sul buffer troppo piccolo o qualcosa su una stringa terminata da null.Win32 - Aggiunta di testo a un controllo di modifica

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 
{ 
    return DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DlgProc); 
} 

BOOL CALLBACK DlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) 
{ 
    switch(Message) 
    { 
     case WM_INITDIALOG: 
      OpenAndReadFile(hwnd); 
      return TRUE; 
     case WM_COMMAND: 
      switch(LOWORD(wParam)) 
      { 
       case IDSTART: 
        EndDialog(hwnd, IDSTART); 
        break; 
       case IDQUIT: 
        EndDialog(hwnd, IDQUIT); 
        break; 
      } 
      break; 
     case WM_CLOSE: 
      EndDialog(hwnd, 0); 
      break; 
     default: 
      return FALSE; 
    } 
    return TRUE; 
} 

BOOL OpenAndReadFile(const HWND &hwnd) 
{ 
    // Open the file 

    HANDLE hFile; 
    hFile = CreateFile(TEXT("sites.txt"), // file to open 
         GENERIC_READ,   // open for reading 
         FILE_SHARE_READ,  // share for reading 
         NULL,     // default security 
         OPEN_EXISTING,   // existing file only 
         FILE_ATTRIBUTE_NORMAL, // normal file 
         NULL);     // no attr. template 

    if (hFile == INVALID_HANDLE_VALUE) 
    { 
     SetDlgItemText(hwnd, IDC_OUTPUT, TEXT("Error: File could not be opened\r\n")); 
     return FALSE; 
    } 
    else 
     SetDlgItemText(hwnd, IDC_OUTPUT, TEXT("sites.txt opened\r\n")); 

    AppendText(hwnd, TEXT("TEXT")); 

    // Read data from file 

    const DWORD BUFFERSIZE = GetFileSize(hFile, NULL); 
    char *ReadBuffer = new char [BUFFERSIZE](); 
    DWORD dwBytesRead = 0; 

    // read one character less than the buffer size to save room for the 
    // terminate NULL character. 
    //if (FALSE == ReadFile(hFile, ReadBuffer, BUFFERSIZE - 1, &dwBytesRead, NULL)) 
    { 

    } 

    return TRUE; 
} 

void AppendText(const HWND &hwnd, TCHAR *newText) 
{ 
    // get size to determine buffer size 
    int outLength = GetWindowTextLength(GetDlgItem(hwnd, IDC_OUTPUT)); 

    // create buffer to hold current text in edit control 
    TCHAR * buf = (TCHAR *) GlobalAlloc(GPTR, outLength + 1); 

    // get existing text from edit control and put into buffer 
    GetDlgItemText(hwnd, IDC_OUTPUT, buf, outLength + 1); 

    // append the newText to the buffer 
    _tcscat_s(buf, outLength + 1, newText); 

    // Set the text in the dialog 
    SetDlgItemText(hwnd, IDC_OUTPUT, buf); 
} 
+0

ho presupposto che _tcscat_s sarebbe aggiungere lo spazio necessario quando si fa una concatenazione. È sbagliato? In tal caso, dovrei creare uno spazio aggiuntivo per il nuovo testo durante GlobalAlloc? – ShrimpCrackers

+0

Perché usarlo a tutti? Per usare lo stesso algoritmo, basta caricare il testo in un 'std :: string', aggiungerlo e salvarlo di nuovo. Almeno in C++ 11, i dati sono garantiti come contigui. Se non si utilizza C++ 11, 'std :: vector' funziona quasi altrettanto bene per caricare il testo. In alternativa (e probabilmente meglio), usa [questa tecnica] (http://stackoverflow.com/a/12538062/962089). Nessuna memoria gestionale per questo. Non preoccuparti di aggiungere. Semplice da usare. – chris

+0

@ShrimpCrackers: '_tcscat_s()' NON aumenta il buffer se il testo aggiunto supera i limiti del buffer. '_tcscat_s()' ha un parametro per specificare la dimensione del buffer in modo da troncare il testo concatenato se diventa troppo lungo. Di conseguenza, è necessario allocare il buffer alla dimensione finale richiesta prima di iniziare a inserire il testo. –

risposta

14

GetWindowTextLength() restituisce il numero di elementi TCHAR nel testo, ma si aspetta GlobalAlloc() conta invece un byte. Se si sta compilando per Unicode, TCHAR è 2 byte, non 1 byte, ma non lo si tiene conto. Inoltre, non si sta allocando il buffer abbastanza grande da contenere sia il testo esistente che il nuovo testo aggiunto. Stai perdendo anche la memoria che hai allocato.

Prova questo:

void AppendText(const HWND &hwnd, TCHAR *newText) 
{ 
    // get edit control from dialog 
    HWND hwndOutput = GetDlgItem(hwnd, IDC_OUTPUT); 

    // get new length to determine buffer size 
    int outLength = GetWindowTextLength(hwndOutput) + lstrlen(newText) + 1; 

    // create buffer to hold current and new text 
    TCHAR * buf = (TCHAR *) GlobalAlloc(GPTR, outLength * sizeof(TCHAR)); 
    if (!buf) return; 

    // get existing text from edit control and put into buffer 
    GetWindowText(hwndOutput, buf, outLength); 

    // append the newText to the buffer 
    _tcscat_s(buf, outLength, newText); 

    // Set the text in the edit control 
    SetWindowText(hwndOutput, buf); 

    // free the buffer 
    GlobalFree(buf); 
} 

alternativa:

#include <vector> 

void AppendText(const HWND &hwnd, TCHAR *newText) 
{ 
    // get edit control from dialog 
    HWND hwndOutput = GetDlgItem(hwnd, IDC_OUTPUT); 

    // get new length to determine buffer size 
    int outLength = GetWindowTextLength(hwndOutput) + lstrlen(newText) + 1; 

    // create buffer to hold current and new text 
    std::vector<TCHAR> buf(outLength); 
    TCHAR *pbuf = &buf[0]; 

    // get existing text from edit control and put into buffer 
    GetWindowText(hwndOutput, pbuf, outLength); 

    // append the newText to the buffer 
    _tcscat_s(pbuf, outLength, newText); 

    // Set the text in the edit control 
    SetWindowText(hwndOutput, pbuf); 
} 

Detto questo, ottenendo testo attuale della finestra nella memoria, aggiungendo ad esso, e poi sostituendo il testo della finestra è un modo molto inefficiente per aggiungere testo a un controllo di modifica. Utilizzare il messaggio EM_REPLACESEL invece:

void AppendText(const HWND &hwnd, TCHAR *newText) 
{ 
    // get edit control from dialog 
    HWND hwndOutput = GetDlgItem(hwnd, IDC_OUTPUT); 

    // get the current selection 
    DWORD StartPos, EndPos; 
    SendMessage(hwndOutput, EM_GETSEL, reinterpret_cast<WPARAM>(&StartPos), reinterpret_cast<WPARAM>(&EndPos)); 

    // move the caret to the end of the text 
    int outLength = GetWindowTextLength(hwndOutput); 
    SendMessage(hwndOutput, EM_SETSEL, outLength, outLength); 

    // insert the text at the new caret position 
    SendMessage(hwndOutput, EM_REPLACESEL, TRUE, reinterpret_cast<LPARAM>(newText)); 

    // restore the previous selection 
    SendMessage(hwndOutput, EM_SETSEL, StartPos, EndPos); 
} 
+0

Inefficiente e aggiunge solo più probabilità di avvitamento qualcosa, che è abbastanza evidente in tutti quei problemi che hai sottolineato. – chris

+0

Grazie, Remy. Funziona alla grande. Essendo nuovo per win32 API, ho tre domande sull'ultimo esempio. (1) È necessario l'incarico hwndOutput? Ho pensato che stavo passando in gestione al controllo di dialogo come parametro di funzione. È il hwnd dalla funzione DlgProc() un handle per qualcos'altro? (2) Perché reinterpret_cast su WPARAM e LPARAM? (3) Perché outLength deve essere inviato a entrambi i parametri WPARAM e LPARAM? – ShrimpCrackers

+1

1) i messaggi 'EM _...' vanno direttamente al controllo di modifica, quindi ha senso afferrare il suo HWND una volta e utilizzarlo più volte. Il DlgProc() HWND è la finestra di dialogo genitore. 2) 'reinterpret_cast' è il modo sicuro da digitare in C++, compatibile con il compilerime, per digitare cast un puntatore a un intero e viceversa (tra le altre cose). 3) [leggi la documentazione] (http://msdn.microsoft.com/en-us/library/windows/desktop/bb761661.aspx). 4) hai una perdita di memoria in 'OpenAndReadFile()'. –

8

http://support.microsoft.com/kb/109550

è la vostra risposta:

string buffer = "append this!" 
    HWND hEdit = GetDlgItem (hDlg, ID_EDIT); 
    int index = GetWindowTextLength (hEdit); 
    SetFocus (hEdit); // set focus 
    SendMessageA(hEdit, EM_SETSEL, (WPARAM)index, (LPARAM)index); // set selection - end of text 
    SendMessageA(hEdit, EM_REPLACESEL, 0, (LPARAM)buffer.c_str()); // append!