VC Resource 관련

리소스 ID충돌을 피하기 위하여 다음과 같은 루틴을 사용하게 된다.

#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        263
#define _APS_NEXT_COMMAND_VALUE         33249
#define _APS_NEXT_CONTROL_VALUE         1152
#define _APS_NEXT_SYMED_VALUE           104
#endif
#endif

각각의 심볼의 의미는 다음과 같다.

_APS_NEXT_RESOURCE_VALUE is the next symbol value that will be used for a dialog resource, menu resource, and so on. The valid range for resource symbol values is 1 to 0x6FFF.


_APS_NEXT_COMMAND_VALUE is the next symbol value that will be used for a command identification. The valid range for command symbol values is 0x8000 to 0xDFFF.


_APS_NEXT_CONTROL_VALUE is the next symbol value that will be used for a dialog control. The valid range for dialog control symbol values is 8 to 0xDFFF.


_APS_NEXT_SYMED_VALUE is the next symbol value that will be issued when you manually assign a symbol value using the New command in the Symbol Browser.

여러개의 rc파일을 사용시 새로운 rc파일에서 리소스 추가 전에 충돌하지 않는 새로운 시작 ID를 할당한다. 너무 큰 숫자를 할당하게 되면 더이상 사용할 수 있는 리소스가 없어지므로 주의한다.

할당예)
#define _APS_NEXT_RESOURCE_VALUE  2000
#define _APS_NEXT_COMMAND_VALUE   42000
#define _APS_NEXT_CONTROL_VALUE   2000
#define _APS_NEXT_SYMED_VALUE     2000

Hooking과 VirtualAlloc을 통한 다른 프로그램의 ListView내용얻어오기

일반적으로 서로 다른 프로그램에서 Message전송만을 통한 Cotrol의 값의 복사가 가능하다. 하지만, 기본컨트롤이 아닌 다른 Cotrol에서 내용을 얻어오고자 할때는 Message와 VirtualAlloc을 이용해야 한다.

기본적인 순서는 다음과 같다.

1. 윈도우 핸들을 얻는다.
2. 차일드 윈도우목록에서 ListView의 핸들을 얻는다.
3. 메세지를 통해서 Item의 숫자를 얻는다.
4. ListView의 프로세스 id를 얻는다.
5. ListView의 프로세스에서 메모리를 할당한다.
6. 할당된 메모리와 Message를 이용하여 Item의 내용을 읽어온다.
7. 할당된 메모리를 해제

 다음의 예제는 어느 회사의 사용자를 검색하여 List에 출력하여 주는 간단한 델파일 프로그램을 후킹하여 사용자정보를 CSV파일로 저장하는 루틴이다.
리스트에 표현되는 정보는 순번,이름,직책,부서명,회사명,보직/담당업무,전화번호,핸드폰,이메일 이며 EnumChildWindows는 CallBack함수를 필요로한다.

HWND hFlatEdit;
HWND hListView;
CString filename;
int lineCount = 1;

BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lPara){


 HWND hEdit = ::FindWindowEx(hwnd,NULL, “TFlatEdit”,””);
 HWND hList = ::FindWindowEx(hwnd,NULL, “TListView”, “”);
 if(hList > 0){
  hListView = hList;
  //TRACE1(“List Handle : %d\n”, hList);
 }


 if(hEdit > 0){
  //TRACE1(“Edit Handle : %d\n”,hEdit);
  hFlatEdit = hEdit;  
 }
 return TRUE;
}

void Save()
{
 HWND hwnd = ::FindWindow(NULL,”사용자검색”);
 CWnd* cwnd = CWnd::FromHandle(hwnd);   
 HINSTANCE hInstance = (HINSTANCE)(::GetWindowLong(hwnd, GWL_HINSTANCE));
 

 if(cwnd == NULL || hInstance==0){
    MessageBox(“사용자 검색창을 열어주세요”,”사용자검색창”,MB_OK);
 } else {
 
  cwnd->SetForegroundWindow();
  ::EnumChildWindows(cwnd->GetSafeHwnd(), EnumChildProc,LPARAM(cwnd));
 
  int count=(int)::SendMessage(hListView, LVM_GETITEMCOUNT, 0, 0);
  int i;
  LVITEM lvi, *_lvi;
  char item[512], subitem1[512],subitem2[512],subitem3[512],subitem4[512],subitem5[512],subitem6[512],subitem7[512],subitem8[512];
  char *_item, *_subitem1,*_subitem2,*_subitem3,*_subitem4,*_subitem5,*_subitem6,*_subitem7,*_subitem8;
  unsigned long pid;
  HANDLE process;


  GetWindowThreadProcessId(hListView, &pid);
  process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
   PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);


  _lvi=(LVITEM*)VirtualAllocEx(process, NULL, sizeof(LVITEM),
   MEM_COMMIT, PAGE_READWRITE);
  _item=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
 
  _subitem1=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
  _subitem2=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
  _subitem3=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
  _subitem4=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
  _subitem5=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
  _subitem6=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
  _subitem7=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);
  _subitem8=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
   PAGE_READWRITE);


  lvi.cchTextMax=512;


   
  CFileDialog fileOpen(FALSE, “CSV저장”,NULL,OFN_HIDEREADONLY,”CSV파일 (*.csv)|*.csv|”,cwnd);
  if(filename.GetLength() > 0 || fileOpen.DoModal() == IDOK){


   CString mode = “”;
   bool header = false;
   if(filename.GetLength() == 0){
    mode =”wt”;
    filename = fileOpen.GetPathName();
    header = true;
   } else {
    mode =”a”;
   }


   FILE * out = fopen(filename,mode);
   if(header == true){
    fprintf(out, “\”순번\”,\”이름\”,\”직책\”,\”부서명\”,\”회사명\”,\”보직/담당업무\”,\”전화번호\”,\”핸드폰\”,\”이메일\”\n”);
   }
     
   for(i=0; i<count; i++) {
    lvi.iSubItem=0;
    lvi.pszText=_item;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    lvi.iSubItem=1;
    lvi.pszText=_subitem1;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    lvi.iSubItem=2;
    lvi.pszText=_subitem2;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    lvi.iSubItem=3;
    lvi.pszText=_subitem3;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    lvi.iSubItem=4;
    lvi.pszText=_subitem4;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    lvi.iSubItem=5;
    lvi.pszText=_subitem5;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    lvi.iSubItem=6;
    lvi.pszText=_subitem6;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    lvi.iSubItem=7;
    lvi.pszText=_subitem7;
    ::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    ::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    //lvi.iSubItem=8;
    //lvi.pszText=_subitem8;
    //::WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
    //::SendMessage(hListView, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);


    ::ReadProcessMemory(process, _item, item, 512, NULL);
    ::ReadProcessMemory(process, _subitem1, subitem1, 512, NULL);
    ::ReadProcessMemory(process, _subitem2, subitem2, 512, NULL);
    ::ReadProcessMemory(process, _subitem3, subitem3, 512, NULL);
    ::ReadProcessMemory(process, _subitem4, subitem4, 512, NULL);
    ::ReadProcessMemory(process, _subitem5, subitem5, 512, NULL);
    ::ReadProcessMemory(process, _subitem6, subitem6, 512, NULL);
    ::ReadProcessMemory(process, _subitem7, subitem7, 512, NULL);
    //::ReadProcessMemory(process, _subitem8, subitem8, 512, NULL);



    char output[2048];
    sprintf(output, “\”%d\”,\”%s\”,\”%s\”,\”%s\”,\”%s\”,\”%s\”,\”%s\”,\”%s\”,\”%s\””,lineCount,item,subitem1,subitem2,subitem3,subitem4,subitem5,subitem6,subitem7);
    fprintf(out,”%s\n”,output);
    lineCount++;
   }
   fclose(out);
  }


  ::VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _item, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _subitem1, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _subitem2, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _subitem3, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _subitem4, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _subitem5, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _subitem6, 0, MEM_RELEASE);
  ::VirtualFreeEx(process, _subitem7, 0, MEM_RELEASE);

 }
}

VC.NET2003 MFC DDE 에러 수정

 

This allows the application to register a document type and extension in the Registry database, allowing you to open the document by double-clicking the filename with a registered extension in Windows Explorer. it also allows for other shell commands, such as printing the document from Explorer.


Unfortunately, a code modification between versions 7.0 and 7.1 of Visual Studio introduced a bug that makes this feature inoperable. If you compile a program using VS 7.1 (.NET 2003) and try opening the file using a DDE mechanism, you will encounter no error message at all or the message:


Error: failed to execute DDE command” with no further explanation.


Sometimes, you can see that the process is not terminated and still running after that error and Task menager has to be used to terminate the application.


The code change was supposed to prevent a buffer overrun. See code below.


Microsoft coders somehow forgot abut one line of code that would copy a DDE command from a LPCTSTR string retrieved by UnpackDDElParam, to a TCHAR string used as a parameter in a call to a CWinApp virtual member, OnDDECommand. Therefore, the DDE command is always empty and the DDE fails to open or print the file.


We have two possibilities: Override OnDDECommand or handle message WM_DDE_EXECUTE. I have chosen the second one so the existing code will not be executed. Because the string is passed to the WM_DDE_EXECUTE handler and should be passed to OnDDECommand, overriding the WM_DDE_EXECUTE handler seems to be more appropriate.


To do so:


Add #include <dde.h> to your StdAfx.h file for a WM_DDE_EXECUTE definition.


In the CMainFrame class, insert a mapping macro:

BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)    

ON_MESSAGE(WM_DDE_EXECUTE, OnDDEExecute) END_MESSAGE_MAP()

And the definition of OnDDEExecute:

LRESULT CMainFrame::OnDDEExecute(WPARAM wParam, LPARAM lParam) {
    // unpack the DDE message
    UINT_PTR unused;    HGLOBAL hData;
    //IA64: Assume DDE LPARAMs are still 32-bit
    VERIFY(UnpackDDElParam(WM_DDE_EXECUTE, lParam, &unused,  (UINT_PTR*)&hData));
     // get the command string 
    TCHAR szCommand[_MAX_PATH * 2] = {0}; 
    LPCTSTR lpsz = (LPCTSTR)GlobalLock(hData);
    int commandLength = lstrlen(lpsz);
    // This line is added to original MS code.
    int arrayLen = sizeof(szCommand)/sizeof(TCHAR); 
    // This line is changed to avoid _countof (another include file). 
   if (commandLength >= arrayLen)    {
// The command would be truncated. this could be a security
   // problem.       
       TRACE0(“Warning: Command was ignored because it was too long.\n”);
       return 0;
    } 
    // This line is needed to rectify a problem. 
    lstrcpyn(szCommand, lpsz, arrayLen);
    GlobalUnlock(hData);
     // acknowledge now – before attempting to execute
    ::PostMessage((HWND)wParam, WM_DDE_ACK, (WPARAM)m_hWnd, 
//IA64: Assume DDE PARAMs are still 32-bit
ReuseDDElParam(lParam, WM_DDE_EXECUTE, WM_DDE_ACK, (UINT)0x8000, (UINT_PTR)hData));
    // don’t execute the command when the window is disabled
    if (!IsWindowEnabled())
    {
       TRACE(traceAppMsg, 0, _T(“Warning: DDE command ‘%s’ ignored 
because window is disabled.\n”), szCommand);
       return 0;
    }
     // execute the command
    if (!AfxGetApp()->OnDDECommand(szCommand))
       TRACE(traceAppMsg, 0, _T(“Error: failed to execute DDE
command ‘%s’.\n”), szCommand);
       return 0L;
 } 

In the header file:

afx_msg LRESULT OnDDEExecute(WPARAM wParam, LPARAM lParam);
The preceding change properly copies the string that is passed to OnDDECommand; 

now, DDE works like a charm.


출처 : Codeguru http://www.codeguru.com/cpp/w-d/doc_view/misc/print.php/c8549/

ToolBar 이어붙이기

ToolBar를 이어 붙이기

CMainFrame에 다음과 같은 함수를 추가한다.

void CMainFrame::DockControlBarLeftOf(CToolBar* Bar, CToolBar* LeftOf)
{
 CRect rect;
 DWORD dw;
 UINT n;

 // get MFC to adjust the dimensions of all docked ToolBars
 // so that GetWindowRect will be accurate
 RecalcLayout(TRUE);

 LeftOf->GetWindowRect(&rect);
 rect.OffsetRect(1,0);
 dw=LeftOf->GetBarStyle();
 n = 0;
 n = (dw&CBRS_ALIGN_TOP) ? AFX_IDW_DOCKBAR_TOP : n;
 n = (dw&CBRS_ALIGN_BOTTOM && n==0) ? AFX_IDW_DOCKBAR_BOTTOM : n;
 n = (dw&CBRS_ALIGN_LEFT && n==0) ? AFX_IDW_DOCKBAR_LEFT : n;
 n = (dw&CBRS_ALIGN_RIGHT && n==0) ? AFX_IDW_DOCKBAR_RIGHT : n;

 // When we take the default parameters on rect, DockControlBar will dock
 // each Toolbar on a seperate line. By calculating a rectangle, we
 // are simulating a Toolbar being dragged to that location and docked.
 DockControlBar(Bar,n,&rect);
}

사용예)

m_wndToolBar1.EnableDocking(CBRS_ALIGN_ANY);
 m_wndToolBar2.EnableDocking(CBRS_ALIGN_ANY);
 EnableDocking(CBRS_ALIGN_ANY);
 DockControlBar(&m_wndToolBar1);
 DockControlBarLeftOf(&m_wndToolBar2,&m_wndToolBar1);

출처 : CodeProject
http://www.codeproject.com/docking/toolbar_docking.asp?df=100&forumid=54&exp=0&select=1013370

Dialog EditBox에서 엔터키 처리

Dialog에서는 Edit에서 Enter키를 Want Return을 지정하더라도 Enter키 입력시 다이어로그가 종료된다.

1. PreTranslateMessage(MSG* pMsg)를 다음과 같이 오버라이드 한다.
2. 엔터키 입력시 원하는 처리를 각각 추가 한다.

BOOL CHMC_CleanerDlg::PreTranslateMessage(MSG* pMsg)
{
 if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_RETURN)
 {      
  if (GetDlgItem(IDC_EDIT1) == GetFocus())
  {
   return TRUE;
  }
  if (GetDlgItem(IDC_EDIT2) == GetFocus())
  {
   // 여기에 추가
   return TRUE;
  }
  if (GetDlgItem(IDC_EDIT4) == GetFocus())
  {
   // 여기에 추가
   return TRUE;
  }
 }
 return CDialog::PreTranslateMessage(pMsg);
}

 

Listview컨트롤 Text가져오기


I was recently trying to steal strings from another program’s listview control. You need to pass a pointer so it knows where to put the string. Normally this wouldn’t be a problem, but because Windows uses virtual memory pointers are not valid across programs.


Virtual memory is how Windows deals out memory to all it’s programs. It makes programs think they have 2 Gb of memory to use. It also protects programs from using each other’s memory so if one program crashes it doesn’t take down the whole system with it.


So after coding a fair bit, I realized my pointers were all invalid and it wouldn’t work. A few hours of digging through MSDN brought me to the functions VirtualAllocEx(), VirtualFreeEx(), WriteProcessMemory() and ReadProcessMemory(). Armed with this new information, I set out to modify my code. Here is what I had so far:


Collapse
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <windows.h>
#include <commctrl.h>
int main(void) {
/* Run through the windows until we find our listview. */
HWND hwnd=FindWindow(NULL, “Stealing Program’s Memory: ListView”);
HWND listview=FindWindowEx(hwnd, NULL, “SysListView32”, NULL);
int count=(int)SendMessage(listview, LVM_GETITEMCOUNT, 0, 0);
int i; char item[512], subitem[512];
/* Shove all items of listview into item and subitem
and print out one by one. */
LVITEM lvi;
lvi.cchTextMax=512;
for(i=0; i<count; i++) {
lvi.iSubItem=0;
lvi.pszText=item;
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)&lvi);
lvi.iSubItem=1;
lvi.pszText=subitem;
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)&lvi);
printf(“%s – %s\n”, item, subitem);
}

return 0;
}


As I said before, this won’t work. The pointers to lvi, item, and subitem all get screwed when they go across process. The solution? Use WriteProcessMemory() and ReadProcessMemory() to use the other programs memory, perform LVM_GETITEMTEXT on it, and read it back. Hackish yes, but then again reading items from another program’s listview control is one giant hack.


First, we get the process of the listview like this:

 unsigned long pid;
HANDLE process;
GetWindowThreadProcessId(listview, &pid);
process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);

Next We create three pointers, LVITEM *_lvi, char *_item, and char *_subitem and allocate them in the other program’s virtual memory space with VirtualAllocEx():

LVITEM *_lvi=(LVITEM*)VirtualAllocEx(process, NULL, sizeof(LVITEM),
MEM_COMMIT, PAGE_READWRITE);
char *_item=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);
char *_subitem=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);

Now we point lvi.pszText to _item, and copy it’s memory to _lvi using WriteMemoryProcess():

lvi.pszText=_item;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);

Now that we have an LVITEM pointer that is valid in the other programs virtual memory, we can shoot off LVM_GETITEMTEXT to listview and copy _item‘s text into item so we can read it in our program:

SendMessage(hwnd, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
ReadProcessMemory(process, _item, item, max, NULL);

Repeat that for subitem, then free the memory we used in the other program’s memory:

VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
VirtualFreeEx(process, _item, 0, MEM_RELEASE);
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE);

Yay, all done. In case that didn’t make too much sense to you, here is our new code, all fixed up:


Collapse
#define WIN32_LEAN_AND_MEAN #include <stdio.h> 
#include <windows.h>
#include <commctrl.h>
int main(void) {
HWND hwnd=FindWindow(NULL, “Stealing Program’s Memory: ListView”);
HWND listview=FindWindowEx(hwnd, NULL, “SysListView32”, NULL);
int count=(int)SendMessage(listview, LVM_GETITEMCOUNT, 0, 0);
int i;
LVITEM lvi, *_lvi;
char item[512], subitem[512];
char *_item, *_subitem;
unsigned long pid;
HANDLE process;
GetWindowThreadProcessId(listview, &pid);
process=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_READ|
PROCESS_VM_WRITE|PROCESS_QUERY_INFORMATION, FALSE, pid);
_lvi=(LVITEM*)VirtualAllocEx(process, NULL, sizeof(LVITEM),
MEM_COMMIT, PAGE_READWRITE);
_item=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);
_subitem=(char*)VirtualAllocEx(process, NULL, 512, MEM_COMMIT,
PAGE_READWRITE);
lvi.cchTextMax=512;
for(i=0; i<count; i++) {
lvi.iSubItem=0;
lvi.pszText=_item;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
lvi.iSubItem=1;
lvi.pszText=_subitem;
WriteProcessMemory(process, _lvi, &lvi, sizeof(LVITEM), NULL);
SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)i, (LPARAM)_lvi);
ReadProcessMemory(process, _item, item, 512, NULL);
ReadProcessMemory(process, _subitem, subitem, 512, NULL);
printf(“%s – %s\n”, item, subitem);
}
VirtualFreeEx(process, _lvi, 0, MEM_RELEASE);
VirtualFreeEx(process, _item, 0, MEM_RELEASE);
VirtualFreeEx(process, _subitem, 0, MEM_RELEASE);
return 0;
}

If you’re looking to use a program’s memory for another reason, or have had a similar problem to mine, adapting this should be fairly easy.

출처- codeproject-