About Keyboard Input

7월 11, 2006 at 11:46 오후 (Visual C++)

source: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/aboutkeyboardinput.asp

Applications should accept user input from the keyboard as well as from the mouse. An application receives keyboard input in the form of messages posted to its windows.This section covers the following topics:

Keyboard Input Model

The system provides device-independent keyboard support for applications by installing a keyboard device driver appropriate for the current keyboard. The system provides language-independent keyboard support by using the language-specific keyboard layout currently selected by the user or the application. The keyboard device driver receives scan codes from the keyboard, which are sent to the keyboard layout where they are translated into messages and posted to the appropriate windows in your application. Assigned to each key on a keyboard is a unique value called a scan code, a device-dependent identifier for the key on the keyboard. A keyboard generates two scan codes when the user types a key—one when the user presses the key and another when the user releases the key. The keyboard device driver interprets a scan code and translates (maps) it to a virtual-key code, a device-independent value defined by the system that identifies the purpose of a key. After translating a scan code, the keyboard layout creates a message that includes the scan code, the virtual-key code, and other information about the keystroke, and then places the message in the system message queue. The system removes the message from the system message queue and posts it to the message queue of the appropriate thread. Eventually, the thread’s message loop removes the message and passes it to the appropriate window procedure for processing. The following figure illustrates the keyboard input model. Keyboard input processing model

Keyboard Focus and Activation

The system posts keyboard messages to the message queue of the foreground thread that created the window with the keyboard focus. The keyboard focus is a temporary property of a window. The system shares the keyboard among all windows on the display by shifting the keyboard focus, at the user’s direction, from one window to another. The window that has the keyboard focus receives (from the message queue of the thread that created it) all keyboard messages until the focus changes to a different window. A thread can call the GetFocus function to determine which of its windows (if any) currently has the keyboard focus. A thread can give the keyboard focus to one of its windows by calling the SetFocus function. When the keyboard focus changes from one window to another, the system sends a WM_KILLFOCUS message to the window that has lost the focus, and then sends a WM_SETFOCUS message to the window that has gained the focus. The concept of keyboard focus is related to that of the active window. The active window is the top-level window the user is currently working with. The window with the keyboard focus is either the active window, or a child window of the active window. To help the user identify the active window, the system places it at the top of the Z order and highlights its title bar (if it has one) and border. The user can activate a top-level window by clicking it, selecting it using the ALT+TAB or ALT+ESC key combination, or selecting it from the Task List. A thread can activate a top-level window by using the SetActiveWindow function. It can determine whether a top-level window it created is active by using the GetActiveWindow function. When one window is deactivated and another activated, the system sends the WM_ACTIVATE message. The low-order word of the wParam parameter is zero if the window is being deactivated and nonzero if it is being activated. When the default window procedure receives the WM_ACTIVATE message, it sets the keyboard focus to the active window. To block keyboard and mouse input events from reaching applications, use BlockInput. Note, the BlockInput function will not interfere with the asynchronous keyboard input-state table. This means that calling the SendInput function while input is blocked will change the asynchronous keyboard input-state table.

Keystroke Messages

Pressing a key causes a WM_KEYDOWN or WM_SYSKEYDOWN message to be placed in the thread message queue attached to the window that has the keyboard focus. Releasing a key causes a WM_KEYUP or WM_SYSKEYUP message to be placed in the queue. Key-up and key-down messages typically occur in pairs, but if the user holds down a key long enough to start the keyboard’s automatic repeat feature, the system generates a number of WM_KEYDOWN or WM_SYSKEYDOWN messages in a row. It then generates a single WM_KEYUP or WM_SYSKEYUP message when the user releases the key. This section covers the following topics:

System and Nonsystem Keystrokes

The system makes a distinction between system keystrokes and nonsystem keystrokes. System keystrokes produce system keystroke messages, WM_SYSKEYDOWN and WM_SYSKEYUP. Nonsystem keystrokes produce nonsystem keystroke messages, WM_KEYDOWN and WM_KEYUP. If your window procedure must process a system keystroke message, make sure that after processing the message the procedure passes it to the DefWindowProc function. Otherwise, all system operations involving the ALT key will be disabled whenever the window has the keyboard focus. That is, the user won’t be able to access the window’s menus or System menu, or use the ALT+ESC or ALT+TAB key combination to activate a different window. System keystroke messages are primarily for use by the system rather than by an application. The system uses them to provide its built-in keyboard interface to menus and to allow the user to control which window is active. System keystroke messages are generated when the user types a key in combination with the ALT key, or when the user types and no window has the keyboard focus (for example, when the active application is minimized). In this case, the messages are posted to the message queue attached to the active window. Nonsystem keystroke messages are for use by application windows; the DefWindowProc function does nothing with them. A window procedure can discard any nonsystem keystroke messages that it does not need.

Virtual-Key Codes Described

The wParam parameter of a keystroke message contains the virtual-key code of the key that was pressed or released. A window procedure processes or ignores a keystroke message, depending on the value of the virtual-key code. A typical window procedure processes only a small subset of the keystroke messages that it receives and ignores the rest. For example, a window procedure might process only WM_KEYDOWN keystroke messages, and only those that contain virtual-key codes for the cursor movement keys, shift keys (also called control keys), and function keys. A typical window procedure does not process keystroke messages from character keys. Instead, it uses the TranslateMessage function to convert the message into character messages. For more information about TranslateMessage and character messages, see Character Messages.

Keystroke Message Flags

The lParam parameter of a keystroke message contains additional information about the keystroke that generated the message. This information includes the repeat count, the scan code, the extended-key flag, the context code, the previous key-state flag, and the transition-state flag. The following illustration shows the locations of these flags and values in the lParam parameter. An application can use the following values to manipulate the keystroke flags.

KF_ALTDOWN Manipulates the ALT key flag, which indicated if the ALT key is pressed.
KF_DLGMODE Manipulates the dialog mode flag, which indicates whether a dialog box is active.
KF_EXTENDED Manipulates the extended key flag.
KF_MENUMODE Manipulates the menu mode flag, which indicates whether a menu is active.
KF_REPEAT Manipulates the repeat count.
KF_UP Manipulates the transition state flag.

Repeat Count

You can check the repeat count to determine whether a keystroke message represents more than one keystroke. The system increments the count when the keyboard generates WM_KEYDOWN or WM_SYSKEYDOWN messages faster than an application can process them. This often occurs when the user holds down a key long enough to start the keyboard’s automatic repeat feature. Instead of filling the system message queue with the resulting key-down messages, the system combines the messages into a single key down message and increments the repeat count. Releasing a key cannot start the automatic repeat feature, so the repeat count for WM_KEYUP and WM_SYSKEYUP messages is always set to 1.

Scan Code

The scan code is the value that the keyboard hardware generates when the user presses a key. It is a device-dependent value that identifies the key pressed, as opposed to the character represented by the key. An application typically ignores scan codes. Instead, it uses the device-independent virtual-key codes to interpret keystroke messages.

Extended-Key Flag

The extended-key flag indicates whether the keystroke message originated from one of the additional keys on the enhanced keyboard. The extended keys consist of the ALT and CTRL keys on the right-hand side of the keyboard; the INS, DEL, HOME, END, PAGE UP, PAGE DOWN, and arrow keys in the clusters to the left of the numeric keypad; the NUM LOCK key; the BREAK (CTRL+PAUSE) key; the PRINT SCRN key; and the divide (/) and ENTER keys in the numeric keypad. The extended-key flag is set if the key is an extended key.

Context Code

The context code indicates whether the ALT key was down when the keystroke message was generated. The code is 1 if the ALT key was down and 0 if it was up.

Previous Key-State Flag

The previous key-state flag indicates whether the key that generated the keystroke message was previously up or down. It is 1 if the key was previously down and 0 if the key was previously up. You can use this flag to identify keystroke messages generated by the keyboard’s automatic repeat feature. This flag is set to 1 for WM_KEYDOWN and WM_SYSKEYDOWN keystroke messages generated by the automatic repeat feature. It is always set to 0 for WM_KEYUP and WM_SYSKEYUP messages.

Transition-State Flag

The transition-state flag indicates whether pressing a key or releasing a key generated the keystroke message. This flag is always set to 0 for WM_KEYDOWN and WM_SYSKEYDOWN messages; it is always set to 1 for WM_KEYUP and WM_SYSKEYUP messages.

Character Messages

Keystroke messages provide a lot of information about keystrokes, but they don’t provide character codes for character keystrokes. To retrieve character codes, an application must include the TranslateMessage function in its thread message loop. TranslateMessage passes a WM_KEYDOWN or WM_SYSKEYDOWN message to the keyboard layout. The layout examines the message’s virtual-key code and, if it corresponds to a character key, provides the character code equivalent (taking into account the state of the SHIFT and CAPS LOCK keys). It then generates a character message that includes the character code and places the message at the top of the message queue. The next iteration of the message loop removes the character message from the queue and dispatches the message to the appropriate window procedure. This section covers the following topics:

Nonsystem Character Messages

A window procedure can receive the following character messages: WM_CHAR, WM_DEADCHAR, WM_SYSCHAR, WM_SYSDEADCHAR, and WM_UNICHAR. The TranslateMessage function generates a WM_CHAR or WM_DEADCHAR message when it processes a WM_KEYDOWN message. Similarly, it generates a WM_SYSCHAR or WM_SYSDEADCHAR message when it processes a WM_SYSKEYDOWN message. An application that processes keyboard input typically ignores all but the WM_CHAR and WM_UNICHAR messages, passing any other messages to the DefWindowProc function. Note that WM_CHAR uses 16-bit Unicode Transformation Format (UTF) while WM_UNICHAR uses UTF-32. The system uses the WM_SYSCHAR and WM_SYSDEADCHAR messages to implement menu mnemonics. The wParam parameter of all character messages contains the character code of the character key that was pressed. The value of the character code depends on the window class of the window receiving the message. If the Unicode version of the RegisterClass function was used to register the window class, the system provides Unicode characters to all windows of that class. Otherwise, the system provides ASCII character codes. For more information, see Unicode and Character Sets. The contents of the lParam parameter of a character message are identical to the contents of the lParam parameter of the key-down message that was translated to produce the character message. For information, see Keystroke Message Flags.

Dead-Character Messages

Some non-English keyboards contain character keys that are not expected to produce characters by themselves. Instead, they are used to add a diacritic to the character produced by the subsequent keystroke. These keys are called dead keys. The circumflex key on a German keyboard is an example of a dead key. To enter the character consisting of an “o” with a circumflex, a German user would type the circumflex key followed by the “o” key. The window with the keyboard focus would receive the following sequence of messages:

  1. WM_KEYDOWN
  2. WM_DEADCHAR
  3. WM_KEYUP
  4. WM_KEYDOWN
  5. WM_CHAR
  6. WM_KEYUP

TranslateMessage generates the WM_DEADCHAR message when it processes the WM_KEYDOWN message from a dead key. Although the wParam parameter of the WM_DEADCHAR message contains the character code of the diacritic for the dead key, an application typically ignores the message. Instead, it processes the WM_CHAR message generated by the subsequent keystroke. The WM_CHAR parameter of the WM_CHAR message contains the character code of the letter with the diacritic. If the subsequent keystroke generates a character that cannot be combined with a diacritic, the system generates two WM_CHAR messages. The wParam parameter of the first contains the character code of the diacritic; the wParam parameter of the second contains the character code of the subsequent character key. The TranslateMessage function generates the WM_SYSDEADCHAR message when it processes the WM_SYSKEYDOWN message from a system dead key (a dead key that is pressed in combination with the ALT key). An application typically ignores the WM_SYSDEADCHAR message.

Key Status

While processing a keyboard message, an application may need to determine the status of another key besides the one that generated the current message. For example, a word-processing application that allows the user to press SHIFT+END to select a block of text must check the status of the SHIFT key whenever it receives a keystroke message from the END key. The application can use the GetKeyState function to determine the status of a virtual key at the time the current message was generated; it can use the GetAsyncKeyState function to retrieve the current status of a virtual key. The keyboard layout maintains a list of names. The name of a key that produces a single character is the same as the character produced by the key. The name of a noncharacter key such as TAB and ENTER is stored as a character string. An application can retrieve the name of any key from the device driver by calling the GetKeyNameText function.

Keystroke and Character Translations

The system includes several special purpose functions that translate scan codes, character codes, and virtual-key codes provided by various keystroke messages. These functions include MapVirtualKey, ToAscii, ToUnicode, and VkKeyScan. In addition, Microsoft Rich Edit 3.0 supports the HexToUnicode IME, which allows a user to convert between hexadecimal and Unicode characters by using hot keys. This means that when Rich Edit 3.0 is incorporated into an application, the application will inherit the features of the HexToUnicode IME.

Hot-Key Support

A hot key is a key combination that generates a WM_HOTKEY message, a message the system places at the top of a thread’s message queue, bypassing any existing messages in the queue. Applications use hot keys to obtain high-priority keyboard input from the user. For example, by defining a hot key consisting of the CTRL+C key combination, an application can allow the user to cancel a lengthy operation. To define a hot key, an application calls the RegisterHotKey function, specifying the combination of keys that generates the WM_HOTKEY message, the handle to the window to receive the message, and the identifier of the hot key. When the user presses the hot key, a WM_HOTKEY message is placed in the message queue of the thread that created the window. The wParam parameter of the message contains the identifier of the hot key. The application can define multiple hot keys for a thread, but each hot key in the thread must have a unique identifier. Before the application terminates, it should use the UnregisterHotKey function to destroy the hot key. Applications can use a hot key control to make it easy for the user to choose a hot key. Hot key controls are typically used to define a hot key that activates a window; they do not use the RegisterHotKey and UnregisterHotKey functions. Instead, an application that uses a hot key control typically sends the WM_SETHOTKEY message to set the hot key. Whenever the user presses the hot key, the system sends a WM_SYSCOMMAND message specifying SC_HOTKEY. For more information about hot key controls, see “Using Hot Key Controls” in Hot Key Controls.

Keyboard Keys for Browsing and Other Functions

Microsoft Windows provides support for keyboards with special keys for browser functions, media functions, application launching, and power management. The WM_APPCOMMAND supports the extra keyboard keys. In addition, the ShellProc function is modified to support the extra keyboard keys. It is unlikely that a child window in a component application will be able to directly implement commands for these extra keyboard keys. So when one of these keys is pressed, DefWindowProc will send a WM_APPCOMMAND message to a window. DefWindowProc will also bubble the WM_APPCOMMAND message to its parent window. This is similar to the way context menus are invoked with the right mouse button, which is that DefWindowProc sends a WM_CONTEXTMENU message on a right button click, and bubbles it to its parent. Additionally, if DefWindowProc receives a WM_APPCOMMAND message for a top-level window, it will call a shell hook with code HSHELL_APPCOMMAND. Windows also supports the Microsoft IntelliMouse Explorer, which is a mouse with five buttons. The two extra buttons support forward and backward browser navigation. For more information, see XBUTTONs.

Simulating Input

To simulate an uninterrupted series of user input events, use the SendInput function. The function accepts three parameters. The first parameter, cInputs, indicates the number of input events that will be simulated. The second parameter, rgInputs, is an array of INPUT structures, each describing a type of input event and additional information about that event. The last parameter, cbSize, accepts the size of the INPUT structure, in bytes. The SendInput function works by injecting a series of simulated input events into a device’s input stream. The effect is similar to calling the keybd_event or mouse_event function repeatedly, except that the system ensures that no other input events intermingle with the simulated events. When the call completes, the return value indicates the number of input events successfully played. If this value is zero, then input was blocked. The SendInput function does not reset the keyboard’s current state. Therefore, if the user has any keys pressed when you call this function, they might interfere with the events that this function generates. If you are concerned about possible interference, check the keyboard’s state with the GetAsyncKeyState function and correct as necessary.

Languages, Locales, and Keyboard Layouts

A language is a natural language, such as English, French, and Japanese. A sublanguage is a variant of a natural language that is spoken in a specific geographical region, such as the English sublanguages spoken in England and the United States. Applications use values, called language identifiers, to uniquely identify languages and sublanguages. Applications typically use locales to set the language in which input and output is processed. Setting the locale for the keyboard, for example, affects the character values generated by the keyboard. Setting the locale for the display or printer affects the glyphs displayed or printed. Applications set the locale for a keyboard by loading and using keyboard layouts. They set the locale for a display or printer by selecting a font that supports the specified locale. A keyboard layout not only specifies the physical position of the keys on the keyboard but also determines the character values generated by pressing those keys. Each layout identifies the current input language and determines which character values are generated by which keys and key combinations. Every keyboard layout has a corresponding handle that identifies the layout and language. The low word of the handle is a language identifier. The high word is a device handle, specifying the physical layout, or is zero, indicating a default physical layout. The user can associate any input language with a physical layout. For example, an English-speaking user who very occasionally works in French can set the input language of the keyboard to French without changing the physical layout of the keyboard. This means the user can enter text in French using the familiar English layout. Applications are generally not expected to manipulate input languages directly. Instead, the user sets up language and layout combinations, then switches among them. When the user clicks into text marked with a different language, the application calls the ActivateKeyboardLayout function to activate the user’s default layout for that language. If the user edits text in a language which is not in the active list, the application can call the LoadKeyboardLayout function with the language to get a layout based on that language. The ActivateKeyboardLayout function sets the input language for the current task. The hkl parameter can be either the handle to the keyboard layout or a zero-extended language identifier. Keyboard layout handles can be obtained from the LoadKeyboardLayout or GetKeyboardLayoutList function. The HKL_NEXT and HKL_PREV values can also be used to select the next or previous keyboard. The GetKeyboardLayoutName function retrieves the name of the active keyboard layout for the calling thread. If an application creates the active layout using the LoadKeyboardLayout function, GetKeyboardLayoutName retrieves the same string used to create the layout. Otherwise, the string is the primary language identifier corresponding to the locale of the active layout. This means the function may not necessarily differentiate among different layouts with the same primary language, so cannot return specific information about the input language. The GetKeyboardLayout function, however, can be used to determine the input language. The LoadKeyboardLayout function loads a keyboard layout and makes the layout available to the user. Applications can make the layout immediately active for the current thread by using the KLF_ACTIVATE value. An application can use the KLF_REORDER value to reorder the layouts without also specifying the KLF_ACTIVATE value. Applications should always use the KLF_SUBSTITUTE_OK value when loading keyboard layouts to ensure that the user’s preference, if any, is selected. For multilingual support, the LoadKeyboardLayout function provides the KLF_REPLACELANG and KLF_NOTELLSHELL flags. The KLF_REPLACELANG flag directs the function to replace an existing keyboard layout without changing the language. Attempting to replace an existing layout using the same language identifier but without specifying KLF_REPLACELANG is an error. The KLF_NOTELLSHELL flag prevents the function from notifying the shell when a keyboard layout is added or replaced. This is useful for applications that add multiple layouts in a consecutive series of calls. This flag should be used in all but the last call. The UnloadKeyboardLayout function is restricted in that it cannot unload the system default input language. This ensures that the user always has one layout available for enter text using the same character set as used by the shell and file system.


Permalink 댓글 남기기

증감 연산자

7월 10, 2006 at 1:18 오전 (Visual C++)

http://www.winapi.co.kr/clec/cpp3/28-3-2.htm

++연산자는 피연산자를 1증가시키는 단항 연산자이다. 비슷한 종류의 — 감소 연산자도 있는데 두 연산자는 증감 방향만 다를 뿐 오버로딩하는 방법은 동일하므로 ++ 연산자에 대해서만 예제를 만들어 보도록 하자. 다음 예제는 Time 객체에 ++ 연산자를 중복 정의한다.

예 제 : TimePlusPlus

#include <Turboc.h>

 

class Time

{

private:

     int hour,min,sec;

 

public:

     Time() { }

     Time(int h, int m, int s) { hour=h; min=m; sec=s; }

     void OutTime() {

          printf(“%d:%d:%d\n”,hour,min,sec);

     }

     Time &operator ++() {

          sec++;

          min += sec/60;

          sec %= 60;

          hour += min/60;

          min %= 60;

          return *this;

     }

     const Time operator ++(int dummy) {

          Time R = *this;

          ++*this;

          return R;

     }

};

 

void main()

{

     Time A(1,1,1);

     Time B;

 

     B=++A;

     A.OutTime();

     B.OutTime();

     B=A++;

     A.OutTime();

     B.OutTime();

}

증가 연산자는 값을 1 증가시키는데 구체적인 의미는 객체에 따라 조금씩 다르게 정의될 것이다. 복소수 객체는 실수부만 1.0 증가시키는 것이 합리적이고 Position 객체는 (x,y) 좌표를 오른쪽 아래로 한칸 이동시킬 수도 있고 x만 증가시키는 것으로 정의할 수도 있다. Person 객체의 경우는 적절한 증가 대상이 없어 ++ 연산자와는 잘 어울리지 않는데 굳이 하자면 나이 정도를 증가시킬 수는 있다.

시간인 경우는 초를 증가시키는 것으로 정의하는 것이 가장 합리적이며 그래서 예제의 operator ++ 연산자는 sec 멤버만 1증가시키는 형식으로 시간 객체에 대해 ++ 연산을 정의했다. 필요하다면 분이나 시를 증가시킬 수도 있다. ++ 연산자는 피연산자를 하나만 취하는 단항 연산자이며 예제의 operator ++은 멤버 연산자 함수로 정의되었으므로 이 함수를 호출하는 객체 자신이 피연산자가 된다. 따라서 이 함수는 별도의 인수를 가질 필요가 없다. 만약 ++ 연산자를 전역 함수로 정의한다면 변경할 대상 객체 하나만을 인수로 전달받으면 된다. 물론 객체의 멤버를 자유롭게 읽기 위해서는 프렌드 지정을 해야 한다.

Time &operator ++(Time &T) {

     T.sec++;

     T.min += T.sec/60;

     T.sec %= 60;

     T.hour += T.min/60;

     T.min %= 60;

     return T;

}

++ 연산자는 호출한 객체(전역 함수인 경우 인수로 전달된 객체, 어쨌든 피연산자)를 직접 변경하기 때문에 상수성을 가지지 않는다. 그래서 operator ++() 함수 다음에 const라는 지정이 없으며 전역 함수로 정의할 경우도 인수의 타입은 Time &여야지 const Time &가 되어서는 안된다. 이항 연산자들은 보통 피연산자를 읽기만 하는데 비해 ++, — 단항 연산자는 피연산자를 직접 변경하는 점이 조금 다르다.

연산자 함수의 리턴 타입은 Time &로 되어 있는데 ++ 연산자가 단순히 객체를 1 증가시키기만 한다면 리턴값이 없는 void 형으로 정의할 수도 있다. 그러나 이렇게 되면 ++A로 A를 1 증가시킬 수는 있지만 B=++A 연산문으로 증가된 결과를 다른 객체에 대입할 수는 없다. C의 모든 연산문은 리턴값을 가지며 그래서 수식내에서 연산문을 사용할 수 있다. 이 요구 조건을 만족시키기 위해 ++ 연산자도 값을 증가시킨 후 증가된 객체 그 자체(*this)를 리턴해야 한다.

잘 알고 있겠지만 증감 연산자는 다른 연산자와는 달리 전위형(prefix)과 후위형(postfix) 두 가지 형식으로 쓸 수 있으며 수식내에서 사용될 때는 ++연산자의 위치에 따라 효과가 조금 다르다. 객체에 대해서도 마찬가지 규칙이 적용되어야 하는데 문제는 전위형이나 후위형이나 둘 다 사용되는 위치만 다를 뿐이지 연산자 함수의 이름은 operator ++로 동일하다는 것이다. 게다가 취하는 인수의 개수까지 같으므로 전위형의 ++ 연산자와 후위형의 ++ 연산자를 이름이나 인수 목록으로 구분할 수 없다.

그래서 C++ 표준위원들은 이 두 형식의 증가 연산자를 구분하기 위해 좀 어색하기는 하지만 더미 인수를 쓰는 방법을 쓰기로 결정했다. 전위형의 ++ 연산자 함수는 증가되는 객체 외에는 인수를 취하지 않으며 후위형의 ++ 연산자는 연산 대상인 객체 외에도 정수형의 더미 인수를 하나 더 취하기로 결정한 것이다. 당장 사용하지 않는 인수이지만 이 인수가 있음으로써 오버로딩이 성립되고 두 형식의 ++연산자 함수를 컴파일러가 구분할 수 있다.

컴파일러는 표준위원들이 정한 규칙대로 ++A 형태의 식을 컴파일할 때는 operator ++() 멤버 함수를 호출하고 A++ 형태의 식을 컴파일할 때는 operator ++(int) 멤버 함수를 찾는다. 아무리 표준이라 하더라도 이 부분은 다소 깔끔하지 못해 보이는데 이는 일종의 약속이기 때문에 우리는 이대로 외우고 규칙대로 ++ 연산자를 오버로딩해야 한다. 그래서 예제에서는 operator ++() 전위형 증가 멤버 연산자 함수와 operator ++(int dummy) 후위형 증가 멤버 연산자 함수를 같이 정의하고 있다. 후위형의 인수 목록에서 dummy는 어차피 자리만 차지하고 사용되지 않는 인수이므로 이름은 생략해도 상관없으며 int 타입만 남겨 둬도 된다.

전위형과 후위형은 효과가 다르기 때문에 본체와 리턴 타입에 있어 차이가 있다. 전위형의 증가 연산자는 객체의 sec을 1증가시키고 올림처리한 후 증가된 객체의 레퍼런스(Time &)를 리턴한다. 후위형인 경우는 일단 값을 먼저 평가한 후 증가시켜야 하므로 증가시키기 전의 객체를 지역변수 R에 백업해 놓고 값을 증가시킨 후 R을 리턴한다. 지역변수의 레퍼런스를 리턴할 수는 없으므로 후위형의 리턴타입은 Time이다. 값을 증가시키는 코드는 전위형의 ++ 연산자에 이미 작성되어 있으므로 ++*this를 호출하면 된다. 실행 결과는 다음과 같다.

 

1:1:2

1:1:2

1:1:3

1:1:2

 

전위형일 때(B=++A)는 증가된 값이 리턴되므로 A와 B가 모두 증가된 값을 가지지만 후위형으로 사용할 때(B=A++)는 증가되기 전의 A값이 리턴되므로 A만 1증가하고 B는 증가하기 전의 값을 대입받는다. 정수형의 후위 증감식과 효과가 동일하다. 만약 후위형의 증가 연산자를 정의하지 않고 A++ 후위 증가식을 사용하면 전위형을 대신 사용한다는 경고가 발생한다. 반대의 경우도 마찬가지인데 수식내에서 증가 연산자를 사용한다면 이 경고는 절대 무시할 수 없다. 사용자가 기대하는 효과가 다르기 때문이다. 전위형을 정의했으면 후위형도 반드시 정의해야 한다.

다음은 두 형식의 리턴 타입에 대해 생각해 보자. 전위형은 Time &를 리턴하고 후위형은 const Time을 리턴하는데 전위형이 레퍼런스를 리턴해야 하는 이유는 ++++A 같은 식이 가능해야 하기 때문이다. ++A가 먼저 평가되어 A가 1증가하고 다시 ++A가 실행되어 A를 한 번 더 증가시키기 위해 이 연산자의 리턴 타입이 레퍼런스여야 한다. 만약 전위형의 operator ++이 Time을 리턴한다면 ++++A 연산식이 어떻게 평가될 것인가 생각해 보자. 첫 번째 호출은 A에 대한 호출이므로 A가 1증가하지만 두 번째 이후부터는 리턴된 *this의 사본에 대한 호출이기 때문에 A는 한 번밖에 증가하지 않을 것이다.

후위형의 경우는 값을 먼저 평가한 후 증가해야 하므로 객체 자체를 리턴할 수 없으며 값만 리턴할 수 있다. 따라서 Time &를 리턴해서는 안되며 이렇게 할 경우 지역변수의 레퍼런스를 리턴한다는 경고가 발생할 것이다. 뿐만 아니라 리턴된 임시 객체를 변경하는 것은 의미가 없으며 A++++은 금지되어야 한다. 만약 후위형의 ++이 상수가 아닌 Time을 리턴할 경우 A++++은 적법한 문장이 되지만 실제로는 1밖에 증가하지 않아 오동작을 하는 것처럼 보일 것이다. 그러므로 아예 애초부터 꿈도 꾸지 못하도록 상수 객체를 리턴해야 한다.

전위형은 단순한 산술식 뿐인데 비해 후위형은 임시 객체 생성, 초기화, 전위형 ++ 호출 등 여러 가지 추가 처리가 필요하다. 그래서 증가시키는 동작만으로 본다면 전위형이 더 빠르고 효율적이며 객체를 단독으로 증가시킬 때는 가급적이면 전위형의 ++연산자를 쓰는 것이 유리하다. 수식내에서라면 물론 두 형태의 효과가 다르므로 적합한 형식을 사용해야 한다. 기본형의 경우도 물론 전위형이 더 유리하나 컴파일러는 단독으로 사용되는 증가 연산자는 전위형으로 바꿔서 호출하므로 i++이나 ++i나 동일하다. 단 기본형이라 하더라도 수식내에서 사용될 때는 이런 최적화를 하지 않는다.

증가 연산자는 두 가지 형식이 있어 오버로딩하기가 조금 까다로운데 다음 도표의 지침대로만 원형을 작성하면 된다. T형 클래스에 대해 멤버, 전역인 경우와 전위, 후위형인 경우 각각에 대해 ++ 연산자의 원형을 정리하였다. 외울 필요는 없고(사실 잘 외워지지도 않는다) 필요할 때마다 이 도표를 참조하도록 하자.

 

멤버 연산자 함수

전역 연산자 함수

전위형

T &T::operator ++()

T &operator ++(T &t)

후위형

T T::operator ++(int)

T operator ++(T &t, int)

Permalink 댓글 남기기

MFC Style

7월 4, 2006 at 8:02 오전 (Visual C++)

MFC Styles

컨트롤들은 윈도 어플리케이션에서 인터페이스들을 생성할 때 사용되는 유저 인터페이스 객체들이다. 대부분의 윈도 어플리케이션과 다이얼로그 박스들은, 단지, 프로그램의 기능 수행을 위해 적당한 방식으로 배치된, 컨트롤들의 집합이다. 효과적인 어플리케이션들을 빌드하기 위해서는, 윈도에서, 어떻게 컨트롤들을 사용하는지를 완전히 이해해야 한다. 단지 여섯 개의 기본적인 컨트롤들이 있다.- CStatic, CButton, CEdit, CList, CComboBox, CScrollBar -약간의 다양함을 보이면서 말이다. (윈도 95에 15개 정도의 강화된 컨트롤이 추가되었음도 알아두어라.) 각각의 컨트롤들이 무엇을 할 수 있는지, 어떻게 그 외형과 동작을 조정할 수 있는지, 어떻게 유저 이벤트에 따라서 알맞은 응답을 하게 할 수 있는지에 대한 이해가 필요하다. 메뉴와 다이얼로그에 대한 이해와 함께, 이런 지식들을 조합하여, 당신이 상상하는 윈도 어플리케이션을 만들 수 있는 능력을 갖출 수 있을 것이다. 튜토리얼에서 보여주었듯이 프로그램적으로 컨트롤을 만들 수도 있고, 다이얼로그 리소스 에디터를 이용해, 리소스 파일을 통해서 컨트롤을 만들 수도 있다. 다이얼로그 에디터가 훨씬 편리한 반면에, 이번 또는 다음 튜토리얼에서 보여줄, 프로그램적인 방식의 작업을 한다면, 일반적인 컨트롤들에 대한 이해를 얻는데 상당히 도움이 될 것이다.

가장 단순한 컨트롤, CStatic ,은 static 텍스트를 보여준다. CStatic 은 데이터 멤버를 가지고 있지 않고, 단지, 약간의 멤버 함수만을 갖고 있다. 생성자, Create 함수등이다. 이것은 유저 이벤트에 응답하지 않는다. 단순하기에, 윈도 컨트롤을 배우기 시작하기에 좋다.

이 튜토리얼에서는 어떻게 컨트롤들이 변형되고, 커스터마이징 되는 지를 이해하기 위해서, CStatic 클래스를 살펴볼 것이다. 다음 튜토리얼에서는 CButton 과, CScrollBar 클래스를, 이벤트 핸들링의 이해를 위해 살펴볼 것이다. 모든 컨트롤과, 클래스를 이해한다면, 완전한 어플리케이션을 만들 준비가 된 것이다.

The Basics

MFC의 CStatic 클래스는 static 텍스트 메시지들을 유저에게 보여준다. 이 메시지들은 정보만을 전달할 수 있다.(예로, 에러 메시지 다이얼로그의 텍스트), 또는 다른 컨트롤들을 구별하게 하는 조그마한 레이블 같은 것으로 이용될 수 있다. 윈도 어플리케이션의 파일 열기 다이얼로그를 열어보면, 여섯 개의 텍스트 레이블을 볼 수 있을 것이다. 다섯 개의 레이블은 리스트와, 텍스트 영역, 그리고, 체크 박스등을 나타내고, 변하지 않는 것들이다. 여섯 번째의 레이블은 현재의 디렉토리를 보여주고, 현재의 디렉토리가 바뀔 때마다, 변하는 것이다.

CStatic 객체는 몇가지 다른 형태를 갖는다. 레이블의 스타일을 변경하여, 사각형으로 보일 수도 있고, 경계선으로 보일 수도 있고, 아이콘으로 보일 수도 있다. CStatic 클래스의 사각 프레임 형태를 이용하면, 관련있는 컨트롤들과 그렇지 않은 컨트롤들을 그룹으로 분류하여 보여줄 수 있다.

CStatic 객체는 항상 어떤 부모 윈도에 대한 차일드 윈도이다. 전형적으로, 부모 윈도는 어플리케이션이나, 다이얼로그 박스를 위한 메인 윈도이다. 튜토리얼 2에서 얘기한 것 처럼, 두 줄의 코드로, static 컨트롤을 만든다.

CStatic *cs;

cs =
new
CStatic();
cs->Create(“hello world”,
                WS_CHILD|WS_VISIBLE|SS_CENTER,
                CRect(50,80, 150, 150),
                
this
);

이 두 줄의 생성 스타일은 MFC로 생성되는 컨트롤들의 전형이다. CStatic 의 인스턴스를 위해, new 를 호출하여, 메모리를 확보하고, 클래스의 생성자를 호출한다. 생성자는 클래스에 필요한 초기화를 한다. Create 함수는 윈도 레벨의 컨트롤을 생성하고, 스크린으로 보여준다.

Create 함수는 다섯 파라미터를 받는다. MFC의 헬프에 적혀있는 것 처럼 말이다. Visual C++의 Help 메뉴의 Search 옵션을 선택하고, Create 를 넣어보라. 리스트에서 CStatic::Create 를 선택할 수 있다. 양자택일로, CStatic 를 서치 박스에 입력해서, 그 오버뷰 페이지의 Members 버튼을 누르는 방법도 있겠다.

대개의 값들은 자신을 설명하고 있다. lpszText 파라미터는 레이블로 보여지는 텍스트를 명시하는 것이다. rect 파라미터는 위치와 사이즈, 텍스트의 모양을 조절한다. 부모 윈도 안에 보여질 때 말이다. 텍스트의 왼쪽 윗편 코너는 rect 파라미터의 윈쪽 윗편 코너로 정의된다. 사각형은 rect 파라미터의 넓이와 높이를 통해서 정의된다. pParentWnd 파라미터는 CStatic 컨트롤의 부모를 가리킨다. 이 컨트롤은 부모 윈도에 나타날 것이고, 컨트롤의 위치는 부모의 클라이언트 영역의 왼쪽 윗편 코너와 관계가 있게 될 것이다. nID 파라미터는 API 함수에서 컨트롤 ID로서 사용되는 정수 값이다. 우리는 다음 튜토리얼에서 이 파라미터의 예제들을 보게 될 것이다.

dwStyle 파라미터는 제일 중요한 파라미터이다. 컨트롤의 외형과 동작을 제어한다. 다음 섹션들에서 이 파라미터에 대해 자세하게 적을 것이다.

CStatic Styles

모든 컨트롤은 다양한 디스플레이 스타일을 갖는다. 스타일은, Create 함수에서 넘겨지는 dwStyle 파라미터를 이용하여, 생성할 때 정의한다. CStatic 컨트롤에서 유효한 상수들은 MFC 헬프에서 찾을 수 있고, (이전 섹션에서 얘기한 CStatic::Create 함수를 위한 이 페이지를 찾아라. 그리고, 페이지 상단 근처에 있는 Static Control Styles 링크를 클릭하라.) 아래에 대강 적어두었다.
 

Valid styles for the CStatic class-
 

Styles inherited from CWnd:
 

  • WS_CHILD Mandatory for CStatic.

  • WS_VISIBLE The control should be visible to the user.

  • WS_DISABLED The control should reject user events.

  • WS_BORDER The control’s text is framed by a border.
     

Styles native to CStatic:
 

  • SS_BLACKFRAME The control displays itself as a rectangular border. Color is the same as window frames.

  • SS_BLACKRECT The control displays itself as a filled rectangle. Color is the same as window frames.

  • SS_CENTER The text is center justified.

  • SS_GRAYFRAME The control displays itself as a rectangular border. Color is the same as the desktop.

  • SS_GRAYRECT The control displays itself as a filled rectangle. Color is the same as the desktop.

  • SS_ICON The control displays itself as an icon. The text string is used as the name of the icon in a resource file. The rect parameter controls only positioning.

  • SS_LEFT The text displayed is left justified. Extra text is word-wrapped.

  • SS_LEFTNOWORDWRAP The text is left justified, but extra text is clipped.

  • SS_NOPREFIX “&” characters in the text string indicate accelerator prefixes unless this attribute is used.

  • SS_RIGHT The text displayed is right justified. Extra text is word-wrapped.

  • SS_SIMPLE A single line of text is displayed left justified. Any CTLCOLOR messages must be ignored by the parent.

  • SS_USERITEM User-defined item.

  • SS_WHITEFRAME The control displays itself as a rectangular border. Color is the same as window backgrounds.

  • SS_WHITERECT The control displays itself as a filled rectangle. Color is the same as window backgrounds.


이 상수들은 두 개의 다른 뿌리로부터 왔다. “SS”(Static 스타일)상수는 단지
CStatic 컨트롤에만 적용된다. “WS”(Window 스타일)상수는 모든 윈도에 적용되며, 그러므로, CStatic 이 그 동작을 상속 받은, CWnd 객체에서 정의된다. CWnd::Create 함수를 MFC 문서에서 찾으면 된다. 위의 네 가지만이 CStatic 객체에 적용되는 것들이다.

CStatic 객체는 항상 최소한의 두 가지 스타일 상수를 가질 것이다. WS_CHILD 와 WS_VISIBLE 이 그것이다. 컨트롤은 다른 윈도의 차일드가 아니면, 생성되지 않는다. 그리고, WS_VISIBLE 로 설정되지 않으면, 보이지도 않는다. WS_DIABLED 는 레이블의 이벤트로의 응답을 제어한다. 레이블이 키입력이나, 마우스 클릭과 같은 이벤트에 반응이 없기에, 여분이라 할 수 있다.

다른 스타일 속성들은 선택 사항이며, 레이블의 외형을 제어한다. CStatic::Create 함수로의 스타일 속성들을 조정하여, 스크린에 static 객체를 어떻게 나타낼 지를 결정한다. 다음 섹션에서 다룰, 스타일 속성을 이용하여, CStatic 객체의 외형을 조정하는 것을 배우면, 스타일들에 대해 많이 이해할 수 있을 것이다.

CStatic Text Appearance

아래에 보여진 코드는 CStatic 객체의 동작을 이해하는 데에, 유용하다. 이것은 튜토리얼 2에서 본 것과 비슷하다. 그러나, CStatic 객체의 생성 부분을 조금 변화시킨 것이다. 튜토리얼 1의 설명으로 돌아가서, 이 코드를 컴파일 해보라.

//static1.cpp
#include <afxwin.h>

// Declare the application class
class CTestApp : public CWinApp
{
public:
        
virtual BOOL InitInstance();
};

// Create an instance of the application class
CTestApp TestApp;

// Declare the main window class
class CTestWindow : public CFrameWnd
{
        CStatic* cs;
public:
        CTestWindow();
};

// The InitInstance function is called
// once when the application first executes

BOOL CTestApp::InitInstance()
{
        m_pMainWnd =
new CTestWindow();
        m_pMainWnd->ShowWindow(m_nCmdShow);
        m_pMainWnd->UpdateWindow();
        
return TRUE;
}

// The constructor for the window class
CTestWindow::CTestWindow()
{
        CRect r;
        
// Create the window itself
        Create(NULL,
                ”CStatic Tests”,
                WS_OVERLAPPEDWINDOW,
                CRect(0,0,200,200));
        
// Get the size of the client rectangl e
        GetClientRect(&r);
        r.InflateRect(-20,-20);
        
// Create a static label
        cs =
new CStatic();
        cs->Create(“hello world”,
                WS_CHILD|WS_VISIBLE|WS_BORDER|SS_CENTER,
                r,
                
this );
}

라인 수와 함께 아래에 반복된, 윈도 생성자를 위한 함수 안의, 코드이다.

  CTestWindow::CTestWindow()
  {
          CRect r;
          
// Create the window itself
1         Create(NULL,
                  ”CStatic Tests”,
                  WS_OVERLAPPEDWINDOW,
                  CRect(0,0,200,200));
          
// Get the size of the client rectangl e
2         GetClientRect(&r);
3         r.InflateRect(-20,-20);
          
// Create a static label
4         cs =
new CStatic();
5         cs->Create(“hello world”,
                  WS_CHILD|WS_VISIBLE|WS_BORDER|SS_CENTER,
                  r,
                  
this );
  }

함수는 먼저 라인 1에서 윈도를 위해 CTestWindow::Create 함수를 불러낸다. 이것은 CFrameWnd 객체를 위한 Create 함수이다. CTestWindow 는 그 동작을 CFrameWnd 로부터 상속받았기 때문이다. 라인 1의 코드는 윈도가 200 X 200 픽셀 사이즈를 가져야 하고, 윈도의 왼쪽 윗편이 스크린의 0,0에 초기 위치를 가져야 한다는 것을 말한다. rectDefault 상수는 CRect 파라미터를 대신할 수 있다. 원한다면 말이다.

라인 2에서 코드는 파라미터 &r 을 넘기는, CTestWindow::GetClientRect 를 호출한다. GetClientRect 함수는 CWnd 클래스로부터 상속된다. (MFC 문서내의 함수를 찾을 때, 사이드 바를 보라.) 이 변수 r CRect 형이고, 함수의 초기 부분에서 로컬 변수로 선언되었다.

이 코드를 이해하려고 할 때, 두 가지 질문이 떠오른다.: 1) GetClientRect 함수가 뭘 하는가? 2) CRect 변수는 무엇을 하는가? 문제 1과 시작해보도록 하자. CWnd::GetClientRect 함수를 MFC 문서에서 찾을 때, 특정 윈도의 클라이언트 사각형 영역의 사이즈를 포함하는, CRect 구조체를 돌려준다는 것을 발견할 수 있을 것이다. 이 경우에는 &r 을 말이다. 이 주소는 CRect 의 위치를 가리킨다. CRect 형은 MFC에서 정의된 클래스이다. MFC 문서에서 클래스를 찾아보면, 사각형을 조정하기 위한 30개가 넘는 멤버 함수와 연산자를 정의하고 있음을 알 수 있을 것이다.

우리의 경우, 윈도의 중앙에 “Hello World”를 위치시키고자 한다. 그러므로, GetClientRect 를 클라이언트 영역을 얻기 위해 사용한다. 라인 3에서 CRect::InflateRect 를 호출한다. 대칭적으로 사각형의 사이즈를 증가시키거나 감소시키는 역할을 하는 함수 말이다.(CRect::DeflateRect도 보라.) 여기 우리는 모든 사이드의 20 픽셀씩을 감소시켰다. 그렇게 하지 않으면, 레이블을 둘러싼 보더가 윈도 프레임으로 혼합되어 버릴 것이다. 그리고, 우리는 그걸 볼 수 없을 것이다.

실제 CStatic 레이블은 라인 4와 5에서 생성된다. 스타일 속성들은, 레이블로 보여지는 단어들이, 중앙에 위치해야하고, 보더로 감싸져야하게, 기술되어 있다. 보더의 사이즈와 위치는 CRect 파라미터 r 에 의해서 정의된다.

스타일 속성을 이래저래 변형시켜서, CStatic 객체의 활용을 이해할 수 있을 것이다. 예로, 아래의 코드는 첫 번째 리스트에서의 CTestWindow 생성자 함수를 교체시킨 것을 포함하고 있다.

CTestWindow::CTestWindow()
{
        CRect r;
        
// Create the window itself
        Create(NULL,
                ”CStatic Tests”,
                WS_OVERLAPPEDWINDOW,
                CRect(0,0,200,200));

         // Get the size of the client rectangle
        GetClientRect(&r);
        r.InflateRect(-20,-20);

         // Create a static label
        cs =
new CStatic();
        cs->Create(“Now is the time for all good men to \
                come to the aid of their country”,
                WS_CHILD|WS_VISIBLE|WS_BORDER|SS_CENTER,
                r,
                
this );
}

위의 코드는 이전의 것과, 동일하다. 텍스트 스트링이 좀 길어진 것을 제외하고 말이다. 여기서 볼 수 있듯이, 코드를 실행해보면, CStatic 객체는 고정된 사각형 영역 내에서 텍스트를 랩하고, 각각의 라인을 중앙에 위치시킨다.

만약에 고정된 사각형 영역이 모든 라인의 텍스트를 포함하기에 너무 작다면, 텍스트는 유효한 공간에 맞게 필요한 만큼 잘릴 것이다. CStatic 객체의 이런 기능은 사각형 영역의 사이즈를 줄이거나, 스트링의 길이를 늘려서, 확인해 볼 수 있다.

지금까지 우리가 본 모든 코드에서는, SS_CENTER 스타일이 텍스트를 중앙에 위치시키기 위해 사용되었다. CStatic 객체는 왼쪽, 오른쪽으로의 정렬도 가능하게 한다. 왼쪽으로의 정렬은 SS_CENTER 속성 대신에, SS_LEFT 속성을 사용하면 된다. 오른쪽으로의 정렬은 왼쪽 보다는 오른쪽으로 단어들을 위치시킨다. 그리고, 이것은 SS_RIGHT 속성으로 가능하다.

다른 텍스트 속성도 유효하다. 워드 랩 기능을 해제하고, 종종, 컨트롤들을 설명하는 간단한 레이블로 사용된다. SS_LEFTNOWORDWRAP 스타일은 왼쪽 정렬을 시키고, 랩핑을 없앤다.

Rectangular Display Modes for CStatic

CStatic 객체는 두 개의 사각형 디스플레이 모드를 지원한다.: 속이 차있는 사각형과, 프레임 말이다. 주로 이 두 가지 스타일을 윈도 내에서 다른 컨트롤들을 그룹화하는데 사용할 것이다. 예를 들어, 몇몇의 연관이 있는 에디터 영역을 모아, 윈도에 검은 사각형 프레임을 넣을 것이다. 이런 사각형을 만들 때, 6가지 스타일을 선택할 수 있다.: SS_BLACKFRAME, SS_BLACKRECT, SS_GRAYFRAME, SS_GRAYRECT, SS_WHITEFRAME, SS_WHITERECT 이다. RECT 형은 속이 차있는 사각형이고, 반면에 FRAME은 보더이다. 색상 명은 약간 혼란스러울 수 있다. 예를 들면, SS_WHITERECT는 윈도 배경과 같은 색의 사각형을 보여준다. 이 색상이 기본 값으로 흰색을 사용한다고 해도, 유저는 제어판에서 색을 바꿀 수 있고, 사각형은 실제로 흰색이 아닐 수 있는 것이다. 어떤 머신에서는 말이다.

사각형과 프레임의 속성이 설정될 때, CStatic 의 텍스트 스트링은 무시된다. 빈 스트링이 넘겨지게 된다. 이전 코드에서 몇몇의 이런 스타일을 시도해보고, 결과를 관찰해 보라.

Fonts

CFont 객체를 생성하여 CStatic 객체의 폰트를 바꿀 수 있다. 그렇게 하는 것은, 어떻게 하나의 MFC 클래스가 다른 것과 함께, 컨트롤들의 동작을 변형해야 하는 몇몇 경우에 있어서, 동작할 수 있는지를 보여준다. MFC의 CFont 클래스는 특정한 윈도 폰트의 인스턴스 하나를 갖고 있다. 예를 들어, CFont 클래스의 하나의 인스턴스는 18 포인트의 Times 폰트를 갖고 있을 것이다. 다른 것이 10 포인트의 Courier 폰트를 가지고 있을 동안 말이다. static 레이블에 의해 사용되는 이 폰트는, CStatic CWnd 를 통해서 상속 받은 SetFont 함수를 통해서 변형할 수 있다.
아래의 코드는 폰트를 동작하는 데 필요한 코드를 보여준다.

CTestWindow::CTestWindow()
{
        CRect r;
        
// Create the window itself
        Create(NULL,
                ”CStatic Tests”,
                WS_OVERLAPPEDWINDOW,
                CRect(0,0,200,200));
        
// Get the size of the client rectangle
        GetClientRect(&r);
        r.InflateRect(-20,-20);
        
// Create a static label
        cs =
new CStatic();
        cs->Create(“Hello World”,
                WS_CHILD|WS_VISIBLE|WS_BORDER|SS_CENTER,
                r,
                
this );
        
// Create a new 36 point Arial font
        font =
new CFont;
        font->CreateFont(36,0,0,0,700,0,0,0,
                ANSI_CHARSET,OUT_DEFAULT_PRECIS,
                CLIP_DEFAULT_PRECIS,
                DEFAULT_QUALITY,
                DEFAULT_PITCH|FF_DONTCARE,
                ”arial”);
        
// Cause the label to use the new font
        cs->SetFont(font);
}

보통 그렇듯이, 위의 코드는, 윈도와 CStatic 객체를 생성하는 부분부터 시작한다. 다음, 코드는 CFont 형의 객체를 생성한다. 폰트 변수는 CTestWindow 클래스의 데이터 멤버로서 선언되어야 한다. “CFont *font” 라인과 함께 말이다. CFont::CreateFont 함수는 15개의 파라미터를 가지고 있다. (MFC 헬프를 보라.) 하지만, 대부분의 경우, 단지 세 개만이 중요하다. 예를 들면, 36은 폰트 사이즈 포인트를 의미하고, 700은 폰트의 굵기를 의미한다. (400은 “보통”, 700은 “굵게”, 1부터 1000까지 사용 가능. FW_NORMAL와 FW_BOLD 상수는 그 같은 의미를 갖고 있다. FW 상수를 API 헬프에서 읽어보아라.) 그리고, “arial”은 사용되는 폰트의 이름이다. 윈도는 다섯 개의 트루 타입 폰트와 함께 유통된다.(Arial, Courier New, Symbol, Times New Roman, Wingdings) 그리고, 어떤 머신이라도 이 폰트들이 존재함을 알 수 있을 것이다. 만약 폰트의 이름을 시스템에서 알 수 없는 것으로 사용을 한다면, CFont 클래스는 기본 폰트를 선택할 것이다. 이 튜토리얼에서 이용된 모든 예제에서 말이다.

CFont 클래스에 대해서, 좀 더 많은 정보를 원한다면, MFC 문서를 보라. API 온라인 헬프 파일에 좋은 오버뷰가 있다. “Fonts and Text Overview”를 찾아라.

SetFont 함수는 CWnd 클래스로부터 나온다. 이 시점에서 하나의 의문을 가질 것이다. ” CWnd 의 어느 함수가 CStatic 클래스에 적용되는지 어떻게 알지?” 경험에 의해 배워야 한다. 하루 30분씩, CWnd 안의 모든 함수를 읽어라. 많은 것을 얻을 뿐더러, 컨트롤들을 커스터마이징할 수 있는 많은 함수들을 찾을 수 있을 것이다. 다음 튜토리얼에서는 CWnd 클래스의 다른 Set 함수들을 볼 예정이다.

Conclusion

이 튜토리얼에서는
CStatic 객체의 많은 활용을 보았다. CWnd 로부터 파생된 Set 함수들을 남겨놓은 상태이고, 튜토리얼 4에서 얘기할 것이다.

Looking up functions in the Microsoft Documentation

Visual C++ 5.x에서 잘 모르는 함수들을 찾는 것은 매우 쉽다. 모든 MFC,SDK,윈도 API,C.C++ 표준 라이브러리 함수들이 같은 헬프 시스템에 모여있다. 만약 함수가 어디서 정의되었는지, 또는 어떤 구문을 사용하는지, 확신이 서지 않을 때는, 헬프 메뉴의 Search 옵션을 이용하라.

Compiling multiple executables

이 튜토리얼은 몇몇의 다른 예제 프로그램을 갖고 있다. 그것들을 컴파일하는데,두 개의 다른 방법이 있다. 첫 번째는 각각의 프로그램들을 각각의 디렉토리에 넣고, 각각을 위한 새 프로젝트를 만드는 것이다. 이 방법으로, 각각의 프로그램을 분리하여 컴파일하고, 동시에, 독립적으로 각각을 실행할 수 있다. 이 방법의 단점은 많은 양의 디스크 공간이 소모된다는 것이다.

두 번째 방법은 이 튜토리얼의 모든 실행 파일을 포함하는 새로운 디렉토리를 생성하는 것이다. 그 디렉토리에서 새로운 프로젝트 하나를 만들어라. 각각의 프로그램을 컴파일하기 위해서, 프로젝트를 수정할 수 있고, 그 소스 파일을 수정할 수 있다. 프로젝트를 재빌드할 때, 새로운 실행 파일은, 당신이 선택한 소스 파일을 반영할 것이다. 이 방법이 디스크 공간 낭비를 줄일 수 있고, 대개 추천하는 방법이다.

Permalink 댓글 남기기

CString – > char* 로…케스팅

6월 27, 2006 at 7:37 오전 (Visual C++)

CString strPP;

char * chNN = (LPSTR)(LPCSTR)strPP;

이렇게 해서 chNN을 CString의 포인터로 가져 옵니다.
(LPSTR)(LPCSTR)strPP 이 부분 에서 이유는 MSDN을 찾아보시면 아시겠지만 CString은 const char * 으로 만 받을수 있답니다.
그래서 (LPCTSTR)strPP 이런 형식으로도 사용합니다. 물론 const char *으로 return되겠죠.. 다시 char *을 필요하신다면

char * chNN = (char *)(LPCTSTR)strPP;

이런 식으로도 사용합니다.

===>(LPSTR)(LPCTSTR)str은 위험한듯..

1) 유니코드를 고려하지 않았을뿐 아니라

2) 위험하게 내부데이터를 접근합니다.

char *pstr = (LPSTR)(LPCTSTR)str;

이 줄은 UNICODE인경우..

char *pstr = (char *) (const wchar_t *) str;

입니다.
특히 wchar_t를 본래데이터형으로 보지 않을 경우

char *pstr = (char *)(const unsigned short *)str;

입니다..
뭐 일단 유니코드를 고려하지 않는다고 하더라도, CString이 제공하는 것은 const 가 붙은 형변환 밖에 없는데,
이걸 const 를 억지로 빼버리고는 마치 접근해서 고쳐도 되는 것처럼 보이게 되므로 .. 잘못사용하면 오류를 일으키며. 프로그램이 뻑.. 뻗을수도 있쬬 ^^ (const 변환은 받는 쪽에서 안고친다고 보고 CString이 자기 내부 데이터 주소를 살짝 보여주는 건데.. 그걸 char로 바꾸고 쓰려고 하면.. -.-;)

보통 이런 문제는 char[50] 에서 처럼 .. 특정 위치의 글자를 바로 접근할 수 있냐..란 의문에서 생기는데 이 문제는 CString만으로도 충분합니다. CString이 operator[] 를 제공하므로..
str[23] 이렇게 마치 char[] 처럼 사용하실수 있습니다. 물론 이때도 길이를 넘어서는 접근은 피해야 겠죠.. C/C++의 배열이 제공못하는 길이를 CString읜 GetLength()형태로 제공하니 얼마나 편합니까 ^^;;;

그래도 기존 라이브러리(void f(char *)가 있어서 변환이 필요하다면.. 추천하는 방법은

1) char data[50]; strcpy(data, str); f(data);  ///////// 유니코드에선 TCHAR data[50]; _tcscpy(data, str)

2) f(str.GetBuffer(50)); str.ReleaseBuffer();

CString  => char* 변환

char * ch;
CString *str;

1) ch = (LPSTR)(LPCSTR)str;

2) ch = str.GetBuffer(str.GetLength());
3) wsprintf( ch, “%s”, str);

char*  =>  CString 변환

1) str = (LPCSTR)(LPSTR)ch;
2) str = ch;

참고)

LPSTR 은 char* 입니다.

LPSTR : char stirng의 32비트 포인터, char* 와 같다.

LPCTSTR : Constant character String의 32비트 포인터

UINT : 32비트 unsigned형 정수
DWORD : unsigned long int형

BYTE : 8비트 unsigned 정수

참고 : CString을 const char* 형태로 변경 -> (LPTSTR)(LPCTSTR)CString

Permalink 댓글 남기기

char*, LPCTSTR, TCHAR 의 차이

6월 26, 2006 at 11:16 오전 (Visual C++)

어떠한 문자열을 처리하는 자료형은 보통 char, wchar, TCHAR 를 사용한다.
 
쉽게 보면,
char* => LPSTR 라고 생각하면 되고
const char* => LPCSTR 라고 할 수 있다.
 
가운데 'C' 는 const 의 의미다. 'LP' 는 long pointer 의 의미.
끝에 STR 이라는 의미는 NULL 포인터로 끝난다는 의미. 즉, 마지막 인덱스에는 항상 NULL 이 채워져 있어야 한다는 것.
그래야만 str 관련 함수들이 동작을 하는 것이다. strlen, strcpy, strcmp 등등.. 이것들이 src param 의 끝에 NULL 이 없으면 무지 당황하게 된다.
 
가운데 'T' 가 들어간다는 것은 TCHAR 이라는 의미.
그렇다면 왜 T를 쓸까??
 
영문은 1바이트.

그런데, 우리나라 말이나, 중국어 등 영문권이 아닌 제 3세계 언어를 표현하기 위해서는
2바이트가 필요하다.. 이를 위해서 MultiByte 를 사용하기도 하지만 MultiByte 는
어떤 글씨는 1바이트 이고 어떤 글씨는 2바이트여서 메모리 관리가 어렵다.
그래서 나온 것이 wchar 인데,

wchar 는 모든 글씨(영문 포함)가 2바이트로 구성이 된다.
당연히 wchar 는 일반 ascii 타입의 char 보다 메모리 공간이 2배 필요하다.
 
그런데 본론인 TCHAR 는 무엇이냐?
바로 ACSCII 타입의 일반 char 또는 wchar 라는 의미다.
TCHAR 을 사용하면 char 인지 wchar 인지 구별하지 않고 그냥 코딩을 하면 된다.
 
TCHAR tch = _T('A'); <=> char ch = 'A';
TCHAR tch = _T('A'); <=> wchar wch = 'A';
 
배열도 역시.. 그냥 숫자 인덱스로 원하는 글씨의 위치를 추적할 수 있다.
 
그럼 무엇으로 현재 프로젝트의 TCHAR가 char 인지 wchar 인지를 구별할까?
UNICODE 라는 precompile 상수.
 
해당 프로젝트 project -> settings 에 _DEBUG 등등 선언되어 있는데, 끝에다
UNICODE 라고 쓰고 컴파일 하면 앞으로 나오는 모든 TCHAR 타입은 wchar 으로 변환해서
컴파일을 시도하게 된다.
 
가운데 C 다음의 T 는 TCHAR 이라는 의미
 
당연히 상수로 변환을 했으므로 변수에 값을 대입할 수 없겠다.
( b[3] = _T('A'); <- syntax error  <=> const char str = "ABCD"; str[3] = 'E' ; <- 이때 에러와 같은 이치)
 
(TCHAR 을 나타는 매크로가 _T 이다. (한문자 _T(' '), 문장 _T(" ") )

Permalink 댓글 1개

LPSTR, LPCSTR, LPCTSTR…

6월 26, 2006 at 11:14 오전 (Visual C++)

LPSTR :  A 32-bit pointer to a character string.
         이 데이터 형같은 경우 예전에 우리가 사용하던 방식으로 선언하면
         char *
         와 같은 경우이죠. 유니코드를 지원하지 않기 때문에 각각의
         문자는 1바이트를 가집니다.

LPCSTR :  A 32-bit pointer to a constant character string.
         이 데이터 형같은 경우 예전에 우리가 사용하던 방식으로 선언하면
        const char *
         와 같은 경우이죠. const 형이 어떤건지는 아시죠? 이것역시
         유니코드를 지원하지 않기 때문에 각각의 문자는 1바이트를 가집니다.        

LPTSTR : A 32-bit pointer to a character string that is
           portable for Unicode and DBCS
          이 데이터 형같은 경우, 윈도우즈가 유니코드를 지원하면서 새로
          생긴형식으로 풀어쓰면 다음과 같습니다.
          TCHAR *
          이 형은 유니코드를 지원하기 때문에 각 문자가 2바이트를 가지게
          됩니다. 실제로 유니코드를 지원하는 프로그램을 작성하지 않는
          이상 거의 사용하지 않습니다.

LPCTSTR : A 32-bit pointer to a constant character string that
            is portable for Unicode and DBCS.
          이것 역시 윈도우즈가 유니코드를 지원하면서 새로 생긴형식으로
          풀어쓰면 다음과 같습니다.
         const TCHAR *
          마찬가지로 유니코드를 지원하기 때문에 한문자가 2바이트 크기를
          가지게 됩니다.

함수뒤에 const 를 붙이는건 클래스 멤버함수내에서 this 사용을 제한하기 위해서 사용합니다.
일반적으로 멤버 함수내에서 사용되는 this는 해당 클래스가 MyClass 인 경우에,

MyClass * const this;

와 같이 내부적으로 선언되어서 사용되는데, 함수의 뒤에 const를 붙이면

const MyClass * const this;

와 같이 속성이 바뀌어져서 사용됩니다. 즉 내부에 사용되는 데이터 멤버가 이 함수에서는 모조리
읽기 전용으로 바뀌어져 버리는거죠…

this 변수가 가리키는 멤버들이 모두 쓰기 불능으로 되어버리기 때문에… 즉, this는 우리가 직접
변경할수 없기 때문에 함수뒤에 const를 붙여 간접적으로 this를 const형으로 변경하는 기능을
구현한겁니다.

Permalink 댓글 남기기

Registry

6월 26, 2006 at 10:31 오전 (Visual C++)

이번시간 부터는 레지스트리에 대한 것에 대해 알아 보겠습니다. 혹시 레지스트리라는것을 들어 보았습니까? 윈도우즈에서는 레지스트리라는 것이 존재하는데 이것은 일종의 하드웨어적, 소프트웨어적인 정보를 저장하고 있는 매체입니다. 우리가 앞에서 배운 win.ini 파일에도 다양한 정보가 저장되어 있는데 사실 이것은 윈도우즈 3.1때와의 호환 때문에 존재하는 것이고 실제로 우리가 다루어야 할 것은 바로 이 레지스트리입니다. 음.. 예를들어서 win.ini 파일에 프로그램이 설치되어 있는 디렉토리 정보가 들어 있었죠? 아마 인스톨 쉴드라는 인스톨 툴로 인스톨 프로그램을 만들면 인스톨시에 win.ini 파일에 디렉토리 정보 저장 기능은 없을 겁니다.

그러나 레지스트리에 설치된 디렉토리를 저장할 수 있는 기능은 있죠. 바로 레지스트리에도 설치된 디렉토리 정보가 들어 있다는 말과도 같습니다. 그것 뿐만 아닙니다. PNP 하드웨어의 정보도 다 들어 있죠. 모뎀 포트를 자동으로 찾는다거나 하는 작업을 바로 이 레지스트리를 검색해서 할수 있는 것입니다. 여러분들 넷스케이프 사용해 봤습니까? 거기에 보면 홈 디렉토리를 지정할 수 있는 메뉴 아이템이 있을겁니다. MS 익스플로어의 요즘 버전은 제가 사용해 보지 않았지만 예전에 사용할 때에는 이 기능이 없더군요. 그래서 항상 홈 디렉토리가 마이크로 소프트사였습니다. 그런데 이 홈 디렉토리 정보가 레지스트리에 등록되어 있다는 것을 우연히 알게 되었습니다. 물론 그것을 바꿔서 홈 디렉토리를 바꾸었죠.

너무 서론이 길었죠? 그렇다면 이러한 레지스트리를 어떻게 원하는데로 바꿀 수 있을까요? 윈도우즈가 설치되어 있는 디렉토리에 보면 regedit.exe 파일이 있을 겁니다. 이 실행 파일을 실행시켜서 레지스트리 정보를 볼 수도 있고 또 수정할 수도 있습니다. 이 프로그램을 레지스트리 편집기라고도 하죠.

한번 실행해 보세요.

어때요? 크게 6개의 폴더가 보이죠? 각 커다란 폴더에는 의미가 있습니다.

HKEY_CLASSES_ROOT

이 하위에 있는 정보들은 문서의 형태, 파일연관상태에 대한 정보가 들어 있습니다. 실제로 하위 폴더를 보면 아래아 한글 문서의 확장자, 압축 파일 확장자들의 정보가 들어 있음을 확인할 수 있습니다.

HKEY_CURRENT_USER

각각 사용자에 따른 사용자 정보를 가지고 있습니다.

HKEY_LOCAL_MACHINE

하드웨어, 네트워크, 소프트웨어 정보가 이곳에 있습니다. 실제로 프로그램이 설치되어 있는 디렉토리, PNP 하드웨어 등록 정보들이 이곳에 있는 거죠.

HKEY_USERS

각각 사용자에 대한 시스템 정보를 가지고 있습니다.

HKEY_CURRENT_CONFIG

하드웨어 설정에 대한 정보값이 들어 있습니다.

HKEY_DYN_DATA

역시 하드웨어적인 정보값이 들어 있습니다.

자 각각에 대해 알아 보았으니 각 서브 폴더를 확인해 보시기 바랍니다. 폴더 옆에 보면 플러스 기호가 있는데 여기에 마우스의 왼쪽 버튼으로 클릭하면 마이너스 기호로 바뀌고 그곳에 속해 있는 서브 폴더를 보여주게 됩니다. 다시 클릭하면 감춰지죠. 서브 폴더를 보다보면 폴더 왼쪽에 아무 기호도 없는 폴더들이 있을 겁니다. 그것은 서브 폴더를 가지지 않음을 의미하는 것입니다. 그렇다면 서브 폴더를 가지지 않은 폴더에 클릭한 후에 오른쪽에 있는 창을 보십시요.

어때요? 네임에 어떤 이름이 있고 데이터에 값이 들어 있죠? 물론 아무 값도 가지지 않는 경우도 있습니다. 서브 풀더를 가지고 있는 것도 마찬가지입니다. 왼쪽에 있는 기호에 클릭하지 않고 바로 그 폴더를 클릭하면 네임과 데이터에 값이 표시될 것입니다.

자 새로운 용어를 알아 봅시다. 우리가 앞에서 언급한 폴더를 키라고 합니다. 각 키는 물론 서브 키를 가질 수 있고 각 키에는 네임과 데이터를 가집니다. 물론 하나의 키는 여러개의 네임과 데이터를 가질 수 있습니다. 이해가 가죠?

자 이제부터 우리는 기존에 등록되어 있는 키, 네임, 데이터를 읽는 것을 프로그램 상으로 어떻게 구현하는지 알아 볼 것입니다. 물론 등록하는 방법도 알아 볼 거구요.

그러면 시작해 봅시다.

먼저 등록되어 있는 정보를 읽어오는 방법부터 알아 봅시다. 처음에 정보를 가져올 키가 어떤 것인지 지정해서 열어 주어야 합니다.

LONG RegOpenKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    REGSAM  samDesired,
    PHKEY  phkResult
   );   

위 함수를 이용해서 레지스트리에 설정되어 있는 키를 열 수 있습니다. 첫번째 파라미터에 제일 상단에 있는 루트키를 지정하면 되는데 이 키는 레지스트리 편지기를 실행했을 때 볼 수 있는 값을 지정하면 되는 겁니다. 아래 값들 중 하나가 되겠군요.

HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_CURRENT_CONFIG
HKEY_DYN_DATA

두번째 파라미터에는 우리가 정보를 가져올 서브키를 지정해 주면 됩니다. 서브키 내에 또다른 서브키가 있으면 서브 디렉토리를 표시하던 식인 \\식으로 표시해 주면 됩니다. 세번째 파라미터에는 0을 지정하면 되고 네번째 파라미터에는 지정된 예약어를 지정해주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

마지막 파라미터에는 서브키를 열었을때 얻어진 키 핸들을 저장할 변수의 주소를 지정해 주면 됩니다.

자 이제 서브키를 열었으니 그 키가 가지는 데이터를 가져오기만 하면 될 겁니다.

LONG RegQueryValueEx(
    HKEY  hkey,
    LPTSTR  lpszValueName,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );

위 함수를 이용해서 데이터를 가져올 수 있습니다. 첫번째 파라미터에는 위 RegOpenKeyEx() 함수에서 얻은 키 핸들을 지정하면 되고 두번째 파라미터에는 가져올 데이터가 가지는 네임을 지정해 주면 됩니다. 네임이 Default로 되어 있으면 그냥 ""식으로 지정해 주면 됩니다. 세번째 파라미터에는 0을 지정해 주면되고 네번째 파라미터에는 데이터의 저장형태가 저장될 변수의 주소를 지정해주면 됩니다. 이 저장형태는 뒤에 가서 레지스트리에 정보를 기록할때 다시 설명드리겠습니다.

그리고 다섯번째 파라미터에는 읽어온 데이터가 저장될 주소를 지정하면 됩니다. 마지막 파라미터에는 이 읽어온 데이터의 크기가 저장될 변수의 주소를 지정하면 됩니다.

데이터를 읽어오는 과정이 어렵지 않죠? 키를 열어서 작업을 다 했으면 그 키를 닫아 주어야 합니다.

LONG RegCloseKey(
    HKEY  hkey
   );

위 함수를 이용해서 연 키를 닫을수 있습니다. 물론 새로 생성한 키도 위 함수를 이용해서 닫아주면 되죠.

그러면 이번에는 정보를 기록하는 방법에 대해 알아 봅시다. 마찬가지로 어렵지 않습니다. 먼저 새로운 키를 생성하는 과정이 필요한데 그것은 아래 함수를 이용해서 하면 됩니다.

LONG RegCreateKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    LPTSTR  lpszClass,
    DWORD  fdwOptions,
    REGSAM  samDesired,
    LPSECURITY_ATTRIBUTES  lpSecurityAttributes,
    PHKEY  phkResult,
    LPDWORD  lpdwDisposition
   );

첫번째 파라미터에는 제일 상단에 있는 루트키를 지정해 주면 되는데 이 루트키는 이미 RegOpenKeyEx() 함수때 설명드렸을 겁니다. 두번째 파라미터에는 서브키를 지정해 주면 됩니다. 세번째 파라미터에는 NULL을 지정하면 되고 네번째 파라미터에는 클래스 이름을 지정해 주면 됩니다. 보통 NULL을 지정하죠. 다섯번째 파라미터에는 지정된 예약어를 지정해 주면 되는데 이때 지정될 수 있는 예약어는 다음과 같습니다.

REG_OPTION_VOLATILE         메모리에만 저장됩니다.
REG_OPTION_NON_VOLATILE     파일에 저장됩니다.

첫번째 예약어를 사용하게 되면 메모리에만 저장되므로 시스템이 재시작되면 무효가 되나 두번째 예약어를 사용하게 되면 파일에 저장되므로 그 값이 계속 유효하게 됩니다. 어떤 차이인지 알겠죠?

여섯번째 파라미터에는 어세스 옵션을 지정해 주면 되는데 다음과 같은 예약어를 지정해 주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

앞에서 알아본 RegOpenKeyEx()함수의 예약어와 같죠? 일곱번째 파라미터에는 NULL을 지정하면 되고 여덟번째 파라미터에는 이 키에 대한 핸들을 기억할 키 변수의 핸들을 지정해 주면 됩니다. 마지막 파라미터에는 키에 대한 결과가 저장될 변수의 주소를 지정해 주면 됩니다.

이제 키를 생성하는 함수를 알아보았으니 원하는 네임에 데이터를 지정하는 함수를 알아 봅시다.

LONG RegSetValueEx(
    HKEY  hkey,
    LPCTSTR  lpszValueName,
    DWORD  dwReserved,
    DWORD  fdwType,
    CONST BYTE *  lpbData,
    DWORD  cbData
   );

위 함수를 이용해서 원하는 네임과 데이터를 지정해줄수 있는데 각 파라미터의 의미를 알아 봅시다.

첫번째 파라미터에는 RegCreateKeyEx()함수로 얻은 키 핸들을 지정해주면 되고 두번째 파라미터에는 네임을 지정해 주면됩니다. 세번째 파라미터에는 NULL을 지정해주면 되고 네번째 파라미터에는 데이터의 타입을 지정해주면 됩니다. 데이터의 타입은 아래의 예약어를 이용하면 됩니다.

REG_BINARY      바이너리 형태입니다.
REG_DWORD       더블워드 형태입니다.
REG_LINK        심볼릭 링크 형태입니다.
REG_NONE        아무 형태도 아닙니다.
REG_SZ          문자열의 형태입니다.

다섯번째 파라미터에는 데이터를 지정해주면 됩니다. 마지막 파라미터에는 데이터의 길이를 지정해 주면 되구요.

자 그러면 이번에는 키를 삭제하는 방법을 알아 봅시다.

먼저 지울 키를 RegOpenKeyEx()함수를 이용해서 핸들 형태로 반환을 받아야 합니다. 그래서 그 핸들을 이용해서 원하는 네임의 데이터를 삭제하면 되죠.

원하는 네임의 데이터를 지울 때에는 아래 함수를 사용하면 됩니다.

LONG RegDeleteValue(
    HKEY  hkey,
    LPTSTR  lpszValue
   );

첫번째 파라미터에 지울 네임이 속해 있는 키의 핸들을 지정해 주면 됩니다. 두번째 파라미터에는 지울 데이터의 네임을 지정해 주면 되구요.

이번에는 키 자체를 없애는 함수입니다.

LONG RegDeleteKey(
    HKEY  hkey,
    LPCTSTR  lpszSubKey
   );

첫번째 파라미터에는 최상위 루트 키의 이름을 지정해주면 되고 두번째 파라미터에는 그 안에 포함된 지울 서브키의 이름을 지정해 주면 됩니다. 사용방법이 간단하죠.

자 그러면 위에서 배운 함수들을 이용해서 실제로 프로그램을 만들어 봅시다.

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 47

안녕하세요……..돌팔이 황동준입니다……………..

이번시간에는 저번시간에 알아 본 함수들을 이용해서 프로그램을 만들어 보겠습니다.

아래 프로그램은 레지스트리에 원하는 키를 생성해서 거기에 데이터를 기록한 뒤 실제로 그 데이터를 가져오고 하는 기능을 가졌습니다. 물론 지우는 기능도 있구요. 앞부분에서 함수를 잘 이해한 분들은 그렇게 어렵지 않을 겁니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Get Registry", 200
        MENUITEM "&Delete Registry", 300
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
char* GetRegistry(void);
void DeleteRegistry(void);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        strcpy(szBuff, GetRegistry());
                        if(strcmp(szBuff, "ERROR"))
                            MessageBox(hWnd, szBuff, "", MB_OK);
                        else
                            MessageBox(hWnd, "Value not found!!", "", MB_OK);
                        break;

                    case 300 :

                        DeleteRegistry();
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

char* GetRegistry(void)
{
    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{
    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);
    RegDeleteValue(hKey, "Install");
    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");
    RegCloseKey(hKey);
}

자 프로그램을 실행시켜 첫번째 메뉴 아이템인 Set Registry를 선택해 보세요. 그리고 진짜로 그 키가 생성되었는지 레지스트리 편집기로 확인해 보시기 바랍니다. 어때요? 정말로 생성되어 있죠? 자 그러면 소스를 보도록 합시다.

제가 만든 세개의 함수 루틴만 보면 되겠네요.

BOOL SetRegistry(void)
{

이 함수는 새로운 키를 생성하는 역할을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);

HKEY_LOCAL_MACHINE 부분에 있는 Software내에 DORAN 20이라는 키를 생성하고 있는 구문입니다. 물론 Software라는 서브키가 존재하지 않으면 새로 생성합니다.

    if(lRet != ERROR_SUCCESS)
        return FALSE;

성공적으로 함수가 수행되면 ERROR_SUCESS라는 리터값이 발생된다는 것을 위 구문으로 짐작할 수 있을 겁니다.

    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));

Install이라는 네임에 c:\doran20이라는 데이터를 저장하는 구문입니다.

    if(lRet != ERROR_SUCCESS)
    {

역시 함수가 성공적으로 수행되면 ERROR_SUCCESS라는 리턴값이 발생되는군요.

        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);

생성한 키를 닫는 구문입니다.

    return TRUE;
}

char* GetRegistry(void)
{

이 함수는 원하는 서브키의 데이터를 가져오는 역할을 합니다.

    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

데이터를 가져올 서브키를 열고 있습니다.

    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);

Install이라는 네임에 해당하는 데이터를 가져오고 있습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{

이 함수는 첫번째 메뉴 아이템에 의해 생성된 키와 데이터를 삭제하는 역할을 합니다.

    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);

지울 키를 얻고 있습니다.

    RegDeleteValue(hKey, "Install");

Install 네임을 가지고 있는 데이터를 삭제하는 구문입니다.

    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");

서브 키를 없애는 구문입니다.

    RegCloseKey(hKey);
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 48

안녕하세요…………돌팔이 황동준입니다……………

이번시간에도 레지스트리에 대해 더 알아 보겠습니다. 이번에 알아 볼 것은 어떤 키에 속해있는 서브키를 어떻게 얻을 수 있는지 입니다. 물론 우리는
서브키에 속해 있는 네임에 해당하는 데이터를 얻는 방법은 압니다. 그러나 문제가 있죠. 바로 이 키에 어떤 서브키가 있는지 모른다면 할 수가 없습니다. 그렇죠? 그렇기 때문에 이 방법을 알아야 합니다. 좀더 들어간다면 어떤 서브키에 어떤 네임들이 있는지도 아는 방법이 있습니다. 그것도 우리가 지금 알아 볼 방법을 응용하면 됩니다. 자 그러면 시작해 봅시다.

먼저 크게 두단계로 나누어서 합니다. 첫번째 단계는 바로 원하는 키에 서브키가 몇개 있는지 개수를 알아내는 것입니다. 두번째 단계는 이 개수를 가지고 차례로 키 포인터를 이동시켜 서브키를 얻는 것입니다.

자 그러면 실제로 어떤 식으로 구현하는지 필요한 함수를 보도록 합시다. 먼저 서브키의 개수를 알아야 하는데 이때 필요한 함수의 첫번째 파라미터에 키 핸들을 지정해야 하는데 이 키 핸들은 앞에서 배운 RegOpenKeyEx()함수를 이용하면 됩니다. 그러면 키의 개수를 얻을 수 있는 함수를 보도록 합시다.

LONG RegQueryInfoKey (
    HKEY  hkey,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    LPDWORD  lpdwReserved,
    LPDWORD  lpcSubKeys,
    LPDWORD  lpcchMaxSubkey,
    LPDWORD  lpcchMaxClass,
    LPDWORD  lpcValues,
    LPDWORD  lpcchMaxValueName,
    LPDWORD  lpcbMaxValueData,
    LPDWORD  lpcbSecurityDescriptor,
    PFILETIME  lpftLastWriteTime
   );

첫번째 파라미터로 RegOpenKeyEx()함수에서 얻은 키 핸들을 지정하면 됩니다. 두번째 파라미터에는 클래스 이름이 저장될 버퍼의 주소를 세번째 파라미터에는 그 클래스 버퍼의 크기를 가지고 있는 변수의 주소를 지정해 주면 됩니다.

네번째 파라미터에는 0을 지정하면되고 다섯번째 파라미터인 lpcSubKeys에는 서브키의 개수가 저장될 변수의 주소를 지정해 주면 됩니다. 여섯번째 파라미터에는 서브키의 이름중 가장 긴 이름을 가진 서브키의 길이가 저장될 변수의 주소를 지정해주면 됩니다. 사실 이 값은 잘 쓰이지 않죠. 역시 일곱번째 파라미터도 서브키에 대한 클래스이름이 가장 긴 길이가 저장될 변수의 주소를 지정해주면 됩니다.

여덟번째 파라미터인 lpcValues에는 지정해준 키에 대한 데이터의 개수가 저장 될 변수의 주소를 지정해주면 되는데 우리가 지금 만들어볼 프로그램은 단지 서브키의 개수만 알아볼거니 사실 이 값이 필요없습니다. 그러나 다음 프로그램에서는 이 값을 이용할테니 그 의미를 알아 두시기 바랍니다.

아홉번째 파라미터에는 가장 긴 네임에 대한 길이가 저장될 변수의 주소를 지정해주면 되고 열번째 파라미터에는 가장긴 데이터의 길이가 저장될 변수의 주소를 지정해 주면 됩니다. 열한번째 파라미터에는 NULL을 지정하면 되고 마지막 파라미터에는 주어진 키에대한 시간정보를 저장할 구조체 변수의 주소를 지정해 주면 됩니다. 이 구조체는 이미 앞에서 알아보았고 또 어떻게 다루었는지도 알아 보았을 겁니다.

자 서브키나 데이터의 개수를 위 함수로 알아 냈으니 이번에는 실제로 그 서브키나 데이터가 무엇인지 알아내는 함수를 알아 봅시다.

LONG RegEnumKeyEx(
    HKEY  hkey,
    DWORD  iSubkey,
    LPTSTR  lpszName,
    LPDWORD  lpcchName,
    LPDWORD  lpdwReserved,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    PFILETIME  lpftLastWrite
   );

바로 위 함수를 이용해서 알아 낼 수 있습니다. 첫번째 파라미터에 알아낼 서브키가 속해있는 루트 키의 핸들을 지정해 주면 되고 두번째 파라미터에 서브키의 인덱스를 지정해주면 됩니다. 서브키의 인덱스는 0부터 시작합니다. 예를 들어 우리가 위의 RegQueryInfoKey() 함수로 서브키의 개수를 알아 내었다면 0부터 차례로 그 개수에서 1을 뺀값까지 이 파라미터로 지정해주면 되겠죠.

세번째 파라미터에는 해당하는 인텍스의 서브키가 들어갈 버퍼를 지정해 주면 되고 네번째 파라미터에는 그 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

다섯번째 파라미터에는 NULL을 지정해 주면 되고 여섯번째 파라미터에는 클래스 이름이 저장될 버퍼를 지정해주면 됩니다. 일곱번째 파라미터에는 클래스 이름이 저장 될 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

마지막 파라미터는 설명 드릴필요는 없겠군요. 앞의 RegQueryInfoKey() 함수의 마지막 파라미터와 의미가 같습니다.

자 그러면 실제로 이것을 이용한 프로그램을 만들어 봅시다. 아래 프로그램은 새로운 키를 생성하고 그 키에 포함된 서브키가 어떤것들인지 화면에 출력해 주는 기능을 가졌습니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey)

;
    return TRUE;
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 49

안녕하세요………….돌팔이 황동준입니다…………………

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

제가 만든 두개의 함수만 알아 보면 되겠죠?

BOOL SetRegistry(void)
{

이 함수는 우리가 어떤 서브키가 있는지 테스트해보기 위해 임시로 서브키를 만드는 기능을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

어떤 서브키를 만드는지 굳이 설명드릴 필요는 없죠? 바로 Install이라는 서브키를 만들고 Install이라는 네임에 c:\doran20이라는 데이터를 저장하고 있는 구문입니다.

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;

앞에서 만든 DORAN 20이라는 서브키에 다시 Version이라는 서브키를 생성하고 네임에 Version을 그 네임에 해당하는 데이터는 2.0을 지정하는 구문입니다.

}

앞에서 한부분이므로 이해가 안가는 부분은 없을 겁니다. 위 함수가 수행되면 DORAN 20이라는 서브키에 Install, Version이라는 서브키가 생성되어 있을 겁니다.

BOOL PrintSubRegistry(HWND hWnd)
{

이 함수는 DORAN 20 서브키에 속해 있는 서브키를 찾아서 화면에 출력해주는 역할을 합니다.

    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

    if(lRet != ERROR_SUCCESS)
        return FALSE;

알아 볼 서브키가 포함되어 있는 부모 키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

이번에 알아 볼것은 단지 서브키의 개수이므로 데이터의 개수가 저장될 파라미터에는 NULL을 지정하였습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;

서브키의 개수만큼 인덱스를 증가하기 위해 위 dwCount라는 변수를 사용하였습니다.

    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

인덱스에 해당하는 서브키를 얻고 있는 구문입니다.

        dwSubKeySize = 80;

위 함수가 수행되면 dwSubKeySize에 실제 크기가 저장되므로 이 값을 다시 초기화해주고 있습니다. 왜냐하면 위 함수를 사용할 때 버퍼의 크기가 저장된 변수의 주소를 지정하기 때문이죠.

        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);

얻은 서브키를 화면에 출력해 주는 구문입니다.

        dwCount++;

인덱스를 증가하고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문을 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

자 그러면 이번에는 좀더 진보적인 프로그램을 만들어 봅시다. 바로 서브키만 알아내는 것이 아니라 그 안에 속해있는 네임과 데이터도 알아내는 거죠.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);
        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);
        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {
            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);
            dwNameSize = dwValueSize = 80;
            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;
            dwValueCount++;
        }
        RegCloseKey(hSubKey);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey);
    return TRUE;

}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 50

안녕하세요…………..돌팔이 황동준입니다………….

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

이번에는 함수 하나만 알아보면 되겠네요.

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;

정보를 알아볼 서브키의 루크키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

서브키의 개수를 알아내고 있습니다.

    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

서브키를 알아내고 있는 구문입니다.

        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);

        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

알아낸 서브키를 열고 있는 구문입니다.

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);

그 서브키가 가지는 데이터의 개수를 얻고 있습니다.

        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {

데이터의 개수만큼 루프문을 반복하고 있습니다.

            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);

위 함수로 인덱스에 해당하는 네임과 데이터를 얻을 수 있는데 위에서 이 함수에 대해 설명드리지 않았군요.

LONG RegEnumValue(
    HKEY  hkey,
    DWORD  iValue,
    LPTSTR  lpszValue,
    LPDWORD  lpcchValue,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );   

첫번째 파라미터에는 얻을 네임이 포함되어 있는 서브키의 핸들을 지정해 주면됩니다. 두번째 파라미터에는 네임의 인덱스를 지정해 주면 되는데 역시 0부터 지정해주면 됩니다. 세번째 파라미터에는 네임이 저장될 버퍼를 네번째 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다. 다섯 번째 파라미터에는 NULL을 지정하면 되고 여섯번째 파라미터에는 데이터의 타입이 저장될 변수의 주소를 지정해 주면 됩니다. 이 데이터 타입은 앞에서 이미 알아 본 것입니다. 아래와 같은 예약어들이 함수 사용한 후에 저장되겠군요.

REG_BINARY
REG_DWORD
REG_LINK
REG_NONE
REG_SZ

그 의미는 이미 앞에서 설명드렸을 겁니다.

일곱번째 파라미터에는 데이터가 저장될 버퍼를 지정하면 되고 마지막 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다.

함수 설명을 했으니 계속해서 해당 루틴을 알아 보도록 하겠습니다.

            dwNameSize = dwValueSize = 80;

역시 길이를 초기화 해주고 있습니다.

            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;

얻은 네임과 데이터를 화면에 출력해 주고 있습니다.

            dwValueCount++;

네임에 대한 인덱스를 증가해주고 있습니다.

        }
        RegCloseKey(hSubKey);
        dwCount++;

서브키에 대한 인덱스를 증가해주고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문이 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

Permalink 댓글 남기기

Registry

6월 26, 2006 at 10:31 오전 (Visual C++)

이번시간 부터는 레지스트리에 대한 것에 대해 알아 보겠습니다. 혹시 레지스트리라는것을 들어 보았습니까? 윈도우즈에서는 레지스트리라는 것이 존재하는데 이것은 일종의 하드웨어적, 소프트웨어적인 정보를 저장하고 있는 매체입니다. 우리가 앞에서 배운 win.ini 파일에도 다양한 정보가 저장되어 있는데 사실 이것은 윈도우즈 3.1때와의 호환 때문에 존재하는 것이고 실제로 우리가 다루어야 할 것은 바로 이 레지스트리입니다. 음.. 예를들어서 win.ini 파일에 프로그램이 설치되어 있는 디렉토리 정보가 들어 있었죠? 아마 인스톨 쉴드라는 인스톨 툴로 인스톨 프로그램을 만들면 인스톨시에 win.ini 파일에 디렉토리 정보 저장 기능은 없을 겁니다.

그러나 레지스트리에 설치된 디렉토리를 저장할 수 있는 기능은 있죠. 바로 레지스트리에도 설치된 디렉토리 정보가 들어 있다는 말과도 같습니다. 그것 뿐만 아닙니다. PNP 하드웨어의 정보도 다 들어 있죠. 모뎀 포트를 자동으로 찾는다거나 하는 작업을 바로 이 레지스트리를 검색해서 할수 있는 것입니다. 여러분들 넷스케이프 사용해 봤습니까? 거기에 보면 홈 디렉토리를 지정할 수 있는 메뉴 아이템이 있을겁니다. MS 익스플로어의 요즘 버전은 제가 사용해 보지 않았지만 예전에 사용할 때에는 이 기능이 없더군요. 그래서 항상 홈 디렉토리가 마이크로 소프트사였습니다. 그런데 이 홈 디렉토리 정보가 레지스트리에 등록되어 있다는 것을 우연히 알게 되었습니다. 물론 그것을 바꿔서 홈 디렉토리를 바꾸었죠.

너무 서론이 길었죠? 그렇다면 이러한 레지스트리를 어떻게 원하는데로 바꿀 수 있을까요? 윈도우즈가 설치되어 있는 디렉토리에 보면 regedit.exe 파일이 있을 겁니다. 이 실행 파일을 실행시켜서 레지스트리 정보를 볼 수도 있고 또 수정할 수도 있습니다. 이 프로그램을 레지스트리 편집기라고도 하죠.

한번 실행해 보세요.

어때요? 크게 6개의 폴더가 보이죠? 각 커다란 폴더에는 의미가 있습니다.

HKEY_CLASSES_ROOT

이 하위에 있는 정보들은 문서의 형태, 파일연관상태에 대한 정보가 들어 있습니다. 실제로 하위 폴더를 보면 아래아 한글 문서의 확장자, 압축 파일 확장자들의 정보가 들어 있음을 확인할 수 있습니다.

HKEY_CURRENT_USER

각각 사용자에 따른 사용자 정보를 가지고 있습니다.

HKEY_LOCAL_MACHINE

하드웨어, 네트워크, 소프트웨어 정보가 이곳에 있습니다. 실제로 프로그램이 설치되어 있는 디렉토리, PNP 하드웨어 등록 정보들이 이곳에 있는 거죠.

HKEY_USERS

각각 사용자에 대한 시스템 정보를 가지고 있습니다.

HKEY_CURRENT_CONFIG

하드웨어 설정에 대한 정보값이 들어 있습니다.

HKEY_DYN_DATA

역시 하드웨어적인 정보값이 들어 있습니다.

자 각각에 대해 알아 보았으니 각 서브 폴더를 확인해 보시기 바랍니다. 폴더 옆에 보면 플러스 기호가 있는데 여기에 마우스의 왼쪽 버튼으로 클릭하면 마이너스 기호로 바뀌고 그곳에 속해 있는 서브 폴더를 보여주게 됩니다. 다시 클릭하면 감춰지죠. 서브 폴더를 보다보면 폴더 왼쪽에 아무 기호도 없는 폴더들이 있을 겁니다. 그것은 서브 폴더를 가지지 않음을 의미하는 것입니다. 그렇다면 서브 폴더를 가지지 않은 폴더에 클릭한 후에 오른쪽에 있는 창을 보십시요.

어때요? 네임에 어떤 이름이 있고 데이터에 값이 들어 있죠? 물론 아무 값도 가지지 않는 경우도 있습니다. 서브 풀더를 가지고 있는 것도 마찬가지입니다. 왼쪽에 있는 기호에 클릭하지 않고 바로 그 폴더를 클릭하면 네임과 데이터에 값이 표시될 것입니다.

자 새로운 용어를 알아 봅시다. 우리가 앞에서 언급한 폴더를 키라고 합니다. 각 키는 물론 서브 키를 가질 수 있고 각 키에는 네임과 데이터를 가집니다. 물론 하나의 키는 여러개의 네임과 데이터를 가질 수 있습니다. 이해가 가죠?

자 이제부터 우리는 기존에 등록되어 있는 키, 네임, 데이터를 읽는 것을 프로그램 상으로 어떻게 구현하는지 알아 볼 것입니다. 물론 등록하는 방법도 알아 볼 거구요.

그러면 시작해 봅시다.

먼저 등록되어 있는 정보를 읽어오는 방법부터 알아 봅시다. 처음에 정보를 가져올 키가 어떤 것인지 지정해서 열어 주어야 합니다.

LONG RegOpenKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    REGSAM  samDesired,
    PHKEY  phkResult
   );   

위 함수를 이용해서 레지스트리에 설정되어 있는 키를 열 수 있습니다. 첫번째 파라미터에 제일 상단에 있는 루트키를 지정하면 되는데 이 키는 레지스트리 편지기를 실행했을 때 볼 수 있는 값을 지정하면 되는 겁니다. 아래 값들 중 하나가 되겠군요.

HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_CURRENT_CONFIG
HKEY_DYN_DATA

두번째 파라미터에는 우리가 정보를 가져올 서브키를 지정해 주면 됩니다. 서브키 내에 또다른 서브키가 있으면 서브 디렉토리를 표시하던 식인 \\식으로 표시해 주면 됩니다. 세번째 파라미터에는 0을 지정하면 되고 네번째 파라미터에는 지정된 예약어를 지정해주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

마지막 파라미터에는 서브키를 열었을때 얻어진 키 핸들을 저장할 변수의 주소를 지정해 주면 됩니다.

자 이제 서브키를 열었으니 그 키가 가지는 데이터를 가져오기만 하면 될 겁니다.

LONG RegQueryValueEx(
    HKEY  hkey,
    LPTSTR  lpszValueName,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );

위 함수를 이용해서 데이터를 가져올 수 있습니다. 첫번째 파라미터에는 위 RegOpenKeyEx() 함수에서 얻은 키 핸들을 지정하면 되고 두번째 파라미터에는 가져올 데이터가 가지는 네임을 지정해 주면 됩니다. 네임이 Default로 되어 있으면 그냥 ""식으로 지정해 주면 됩니다. 세번째 파라미터에는 0을 지정해 주면되고 네번째 파라미터에는 데이터의 저장형태가 저장될 변수의 주소를 지정해주면 됩니다. 이 저장형태는 뒤에 가서 레지스트리에 정보를 기록할때 다시 설명드리겠습니다.

그리고 다섯번째 파라미터에는 읽어온 데이터가 저장될 주소를 지정하면 됩니다. 마지막 파라미터에는 이 읽어온 데이터의 크기가 저장될 변수의 주소를 지정하면 됩니다.

데이터를 읽어오는 과정이 어렵지 않죠? 키를 열어서 작업을 다 했으면 그 키를 닫아 주어야 합니다.

LONG RegCloseKey(
    HKEY  hkey
   );

위 함수를 이용해서 연 키를 닫을수 있습니다. 물론 새로 생성한 키도 위 함수를 이용해서 닫아주면 되죠.

그러면 이번에는 정보를 기록하는 방법에 대해 알아 봅시다. 마찬가지로 어렵지 않습니다. 먼저 새로운 키를 생성하는 과정이 필요한데 그것은 아래 함수를 이용해서 하면 됩니다.

LONG RegCreateKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    LPTSTR  lpszClass,
    DWORD  fdwOptions,
    REGSAM  samDesired,
    LPSECURITY_ATTRIBUTES  lpSecurityAttributes,
    PHKEY  phkResult,
    LPDWORD  lpdwDisposition
   );

첫번째 파라미터에는 제일 상단에 있는 루트키를 지정해 주면 되는데 이 루트키는 이미 RegOpenKeyEx() 함수때 설명드렸을 겁니다. 두번째 파라미터에는 서브키를 지정해 주면 됩니다. 세번째 파라미터에는 NULL을 지정하면 되고 네번째 파라미터에는 클래스 이름을 지정해 주면 됩니다. 보통 NULL을 지정하죠. 다섯번째 파라미터에는 지정된 예약어를 지정해 주면 되는데 이때 지정될 수 있는 예약어는 다음과 같습니다.

REG_OPTION_VOLATILE         메모리에만 저장됩니다.
REG_OPTION_NON_VOLATILE     파일에 저장됩니다.

첫번째 예약어를 사용하게 되면 메모리에만 저장되므로 시스템이 재시작되면 무효가 되나 두번째 예약어를 사용하게 되면 파일에 저장되므로 그 값이 계속 유효하게 됩니다. 어떤 차이인지 알겠죠?

여섯번째 파라미터에는 어세스 옵션을 지정해 주면 되는데 다음과 같은 예약어를 지정해 주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

앞에서 알아본 RegOpenKeyEx()함수의 예약어와 같죠? 일곱번째 파라미터에는 NULL을 지정하면 되고 여덟번째 파라미터에는 이 키에 대한 핸들을 기억할 키 변수의 핸들을 지정해 주면 됩니다. 마지막 파라미터에는 키에 대한 결과가 저장될 변수의 주소를 지정해 주면 됩니다.

이제 키를 생성하는 함수를 알아보았으니 원하는 네임에 데이터를 지정하는 함수를 알아 봅시다.

LONG RegSetValueEx(
    HKEY  hkey,
    LPCTSTR  lpszValueName,
    DWORD  dwReserved,
    DWORD  fdwType,
    CONST BYTE *  lpbData,
    DWORD  cbData
   );

위 함수를 이용해서 원하는 네임과 데이터를 지정해줄수 있는데 각 파라미터의 의미를 알아 봅시다.

첫번째 파라미터에는 RegCreateKeyEx()함수로 얻은 키 핸들을 지정해주면 되고 두번째 파라미터에는 네임을 지정해 주면됩니다. 세번째 파라미터에는 NULL을 지정해주면 되고 네번째 파라미터에는 데이터의 타입을 지정해주면 됩니다. 데이터의 타입은 아래의 예약어를 이용하면 됩니다.

REG_BINARY      바이너리 형태입니다.
REG_DWORD       더블워드 형태입니다.
REG_LINK        심볼릭 링크 형태입니다.
REG_NONE        아무 형태도 아닙니다.
REG_SZ          문자열의 형태입니다.

다섯번째 파라미터에는 데이터를 지정해주면 됩니다. 마지막 파라미터에는 데이터의 길이를 지정해 주면 되구요.

자 그러면 이번에는 키를 삭제하는 방법을 알아 봅시다.

먼저 지울 키를 RegOpenKeyEx()함수를 이용해서 핸들 형태로 반환을 받아야 합니다. 그래서 그 핸들을 이용해서 원하는 네임의 데이터를 삭제하면 되죠.

원하는 네임의 데이터를 지울 때에는 아래 함수를 사용하면 됩니다.

LONG RegDeleteValue(
    HKEY  hkey,
    LPTSTR  lpszValue
   );

첫번째 파라미터에 지울 네임이 속해 있는 키의 핸들을 지정해 주면 됩니다. 두번째 파라미터에는 지울 데이터의 네임을 지정해 주면 되구요.

이번에는 키 자체를 없애는 함수입니다.

LONG RegDeleteKey(
    HKEY  hkey,
    LPCTSTR  lpszSubKey
   );

첫번째 파라미터에는 최상위 루트 키의 이름을 지정해주면 되고 두번째 파라미터에는 그 안에 포함된 지울 서브키의 이름을 지정해 주면 됩니다. 사용방법이 간단하죠.

자 그러면 위에서 배운 함수들을 이용해서 실제로 프로그램을 만들어 봅시다.

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 47

안녕하세요……..돌팔이 황동준입니다……………..

이번시간에는 저번시간에 알아 본 함수들을 이용해서 프로그램을 만들어 보겠습니다.

아래 프로그램은 레지스트리에 원하는 키를 생성해서 거기에 데이터를 기록한 뒤 실제로 그 데이터를 가져오고 하는 기능을 가졌습니다. 물론 지우는 기능도 있구요. 앞부분에서 함수를 잘 이해한 분들은 그렇게 어렵지 않을 겁니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Get Registry", 200
        MENUITEM "&Delete Registry", 300
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
char* GetRegistry(void);
void DeleteRegistry(void);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        strcpy(szBuff, GetRegistry());
                        if(strcmp(szBuff, "ERROR"))
                            MessageBox(hWnd, szBuff, "", MB_OK);
                        else
                            MessageBox(hWnd, "Value not found!!", "", MB_OK);
                        break;

                    case 300 :

                        DeleteRegistry();
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

char* GetRegistry(void)
{
    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{
    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);
    RegDeleteValue(hKey, "Install");
    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");
    RegCloseKey(hKey);
}

자 프로그램을 실행시켜 첫번째 메뉴 아이템인 Set Registry를 선택해 보세요. 그리고 진짜로 그 키가 생성되었는지 레지스트리 편집기로 확인해 보시기 바랍니다. 어때요? 정말로 생성되어 있죠? 자 그러면 소스를 보도록 합시다.

제가 만든 세개의 함수 루틴만 보면 되겠네요.

BOOL SetRegistry(void)
{

이 함수는 새로운 키를 생성하는 역할을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);

HKEY_LOCAL_MACHINE 부분에 있는 Software내에 DORAN 20이라는 키를 생성하고 있는 구문입니다. 물론 Software라는 서브키가 존재하지 않으면 새로 생성합니다.

    if(lRet != ERROR_SUCCESS)
        return FALSE;

성공적으로 함수가 수행되면 ERROR_SUCESS라는 리터값이 발생된다는 것을 위 구문으로 짐작할 수 있을 겁니다.

    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));

Install이라는 네임에 c:\doran20이라는 데이터를 저장하는 구문입니다.

    if(lRet != ERROR_SUCCESS)
    {

역시 함수가 성공적으로 수행되면 ERROR_SUCCESS라는 리턴값이 발생되는군요.

        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);

생성한 키를 닫는 구문입니다.

    return TRUE;
}

char* GetRegistry(void)
{

이 함수는 원하는 서브키의 데이터를 가져오는 역할을 합니다.

    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

데이터를 가져올 서브키를 열고 있습니다.

    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);

Install이라는 네임에 해당하는 데이터를 가져오고 있습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{

이 함수는 첫번째 메뉴 아이템에 의해 생성된 키와 데이터를 삭제하는 역할을 합니다.

    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);

지울 키를 얻고 있습니다.

    RegDeleteValue(hKey, "Install");

Install 네임을 가지고 있는 데이터를 삭제하는 구문입니다.

    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");

서브 키를 없애는 구문입니다.

    RegCloseKey(hKey);
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 48

안녕하세요…………돌팔이 황동준입니다……………

이번시간에도 레지스트리에 대해 더 알아 보겠습니다. 이번에 알아 볼 것은 어떤 키에 속해있는 서브키를 어떻게 얻을 수 있는지 입니다. 물론 우리는
서브키에 속해 있는 네임에 해당하는 데이터를 얻는 방법은 압니다. 그러나 문제가 있죠. 바로 이 키에 어떤 서브키가 있는지 모른다면 할 수가 없습니다. 그렇죠? 그렇기 때문에 이 방법을 알아야 합니다. 좀더 들어간다면 어떤 서브키에 어떤 네임들이 있는지도 아는 방법이 있습니다. 그것도 우리가 지금 알아 볼 방법을 응용하면 됩니다. 자 그러면 시작해 봅시다.

먼저 크게 두단계로 나누어서 합니다. 첫번째 단계는 바로 원하는 키에 서브키가 몇개 있는지 개수를 알아내는 것입니다. 두번째 단계는 이 개수를 가지고 차례로 키 포인터를 이동시켜 서브키를 얻는 것입니다.

자 그러면 실제로 어떤 식으로 구현하는지 필요한 함수를 보도록 합시다. 먼저 서브키의 개수를 알아야 하는데 이때 필요한 함수의 첫번째 파라미터에 키 핸들을 지정해야 하는데 이 키 핸들은 앞에서 배운 RegOpenKeyEx()함수를 이용하면 됩니다. 그러면 키의 개수를 얻을 수 있는 함수를 보도록 합시다.

LONG RegQueryInfoKey (
    HKEY  hkey,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    LPDWORD  lpdwReserved,
    LPDWORD  lpcSubKeys,
    LPDWORD  lpcchMaxSubkey,
    LPDWORD  lpcchMaxClass,
    LPDWORD  lpcValues,
    LPDWORD  lpcchMaxValueName,
    LPDWORD  lpcbMaxValueData,
    LPDWORD  lpcbSecurityDescriptor,
    PFILETIME  lpftLastWriteTime
   );

첫번째 파라미터로 RegOpenKeyEx()함수에서 얻은 키 핸들을 지정하면 됩니다. 두번째 파라미터에는 클래스 이름이 저장될 버퍼의 주소를 세번째 파라미터에는 그 클래스 버퍼의 크기를 가지고 있는 변수의 주소를 지정해 주면 됩니다.

네번째 파라미터에는 0을 지정하면되고 다섯번째 파라미터인 lpcSubKeys에는 서브키의 개수가 저장될 변수의 주소를 지정해 주면 됩니다. 여섯번째 파라미터에는 서브키의 이름중 가장 긴 이름을 가진 서브키의 길이가 저장될 변수의 주소를 지정해주면 됩니다. 사실 이 값은 잘 쓰이지 않죠. 역시 일곱번째 파라미터도 서브키에 대한 클래스이름이 가장 긴 길이가 저장될 변수의 주소를 지정해주면 됩니다.

여덟번째 파라미터인 lpcValues에는 지정해준 키에 대한 데이터의 개수가 저장 될 변수의 주소를 지정해주면 되는데 우리가 지금 만들어볼 프로그램은 단지 서브키의 개수만 알아볼거니 사실 이 값이 필요없습니다. 그러나 다음 프로그램에서는 이 값을 이용할테니 그 의미를 알아 두시기 바랍니다.

아홉번째 파라미터에는 가장 긴 네임에 대한 길이가 저장될 변수의 주소를 지정해주면 되고 열번째 파라미터에는 가장긴 데이터의 길이가 저장될 변수의 주소를 지정해 주면 됩니다. 열한번째 파라미터에는 NULL을 지정하면 되고 마지막 파라미터에는 주어진 키에대한 시간정보를 저장할 구조체 변수의 주소를 지정해 주면 됩니다. 이 구조체는 이미 앞에서 알아보았고 또 어떻게 다루었는지도 알아 보았을 겁니다.

자 서브키나 데이터의 개수를 위 함수로 알아 냈으니 이번에는 실제로 그 서브키나 데이터가 무엇인지 알아내는 함수를 알아 봅시다.

LONG RegEnumKeyEx(
    HKEY  hkey,
    DWORD  iSubkey,
    LPTSTR  lpszName,
    LPDWORD  lpcchName,
    LPDWORD  lpdwReserved,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    PFILETIME  lpftLastWrite
   );

바로 위 함수를 이용해서 알아 낼 수 있습니다. 첫번째 파라미터에 알아낼 서브키가 속해있는 루트 키의 핸들을 지정해 주면 되고 두번째 파라미터에 서브키의 인덱스를 지정해주면 됩니다. 서브키의 인덱스는 0부터 시작합니다. 예를 들어 우리가 위의 RegQueryInfoKey() 함수로 서브키의 개수를 알아 내었다면 0부터 차례로 그 개수에서 1을 뺀값까지 이 파라미터로 지정해주면 되겠죠.

세번째 파라미터에는 해당하는 인텍스의 서브키가 들어갈 버퍼를 지정해 주면 되고 네번째 파라미터에는 그 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

다섯번째 파라미터에는 NULL을 지정해 주면 되고 여섯번째 파라미터에는 클래스 이름이 저장될 버퍼를 지정해주면 됩니다. 일곱번째 파라미터에는 클래스 이름이 저장 될 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

마지막 파라미터는 설명 드릴필요는 없겠군요. 앞의 RegQueryInfoKey() 함수의 마지막 파라미터와 의미가 같습니다.

자 그러면 실제로 이것을 이용한 프로그램을 만들어 봅시다. 아래 프로그램은 새로운 키를 생성하고 그 키에 포함된 서브키가 어떤것들인지 화면에 출력해 주는 기능을 가졌습니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey)

;
    return TRUE;
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 49

안녕하세요………….돌팔이 황동준입니다…………………

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

제가 만든 두개의 함수만 알아 보면 되겠죠?

BOOL SetRegistry(void)
{

이 함수는 우리가 어떤 서브키가 있는지 테스트해보기 위해 임시로 서브키를 만드는 기능을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

어떤 서브키를 만드는지 굳이 설명드릴 필요는 없죠? 바로 Install이라는 서브키를 만들고 Install이라는 네임에 c:\doran20이라는 데이터를 저장하고 있는 구문입니다.

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;

앞에서 만든 DORAN 20이라는 서브키에 다시 Version이라는 서브키를 생성하고 네임에 Version을 그 네임에 해당하는 데이터는 2.0을 지정하는 구문입니다.

}

앞에서 한부분이므로 이해가 안가는 부분은 없을 겁니다. 위 함수가 수행되면 DORAN 20이라는 서브키에 Install, Version이라는 서브키가 생성되어 있을 겁니다.

BOOL PrintSubRegistry(HWND hWnd)
{

이 함수는 DORAN 20 서브키에 속해 있는 서브키를 찾아서 화면에 출력해주는 역할을 합니다.

    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

    if(lRet != ERROR_SUCCESS)
        return FALSE;

알아 볼 서브키가 포함되어 있는 부모 키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

이번에 알아 볼것은 단지 서브키의 개수이므로 데이터의 개수가 저장될 파라미터에는 NULL을 지정하였습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;

서브키의 개수만큼 인덱스를 증가하기 위해 위 dwCount라는 변수를 사용하였습니다.

    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

인덱스에 해당하는 서브키를 얻고 있는 구문입니다.

        dwSubKeySize = 80;

위 함수가 수행되면 dwSubKeySize에 실제 크기가 저장되므로 이 값을 다시 초기화해주고 있습니다. 왜냐하면 위 함수를 사용할 때 버퍼의 크기가 저장된 변수의 주소를 지정하기 때문이죠.

        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);

얻은 서브키를 화면에 출력해 주는 구문입니다.

        dwCount++;

인덱스를 증가하고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문을 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

자 그러면 이번에는 좀더 진보적인 프로그램을 만들어 봅시다. 바로 서브키만 알아내는 것이 아니라 그 안에 속해있는 네임과 데이터도 알아내는 거죠.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);
        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);
        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {
            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);
            dwNameSize = dwValueSize = 80;
            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;
            dwValueCount++;
        }
        RegCloseKey(hSubKey);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey);
    return TRUE;

}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 50

안녕하세요…………..돌팔이 황동준입니다………….

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

이번에는 함수 하나만 알아보면 되겠네요.

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;

정보를 알아볼 서브키의 루크키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

서브키의 개수를 알아내고 있습니다.

    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

서브키를 알아내고 있는 구문입니다.

        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);

        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

알아낸 서브키를 열고 있는 구문입니다.

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);

그 서브키가 가지는 데이터의 개수를 얻고 있습니다.

        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {

데이터의 개수만큼 루프문을 반복하고 있습니다.

            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);

위 함수로 인덱스에 해당하는 네임과 데이터를 얻을 수 있는데 위에서 이 함수에 대해 설명드리지 않았군요.

LONG RegEnumValue(
    HKEY  hkey,
    DWORD  iValue,
    LPTSTR  lpszValue,
    LPDWORD  lpcchValue,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );   

첫번째 파라미터에는 얻을 네임이 포함되어 있는 서브키의 핸들을 지정해 주면됩니다. 두번째 파라미터에는 네임의 인덱스를 지정해 주면 되는데 역시 0부터 지정해주면 됩니다. 세번째 파라미터에는 네임이 저장될 버퍼를 네번째 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다. 다섯 번째 파라미터에는 NULL을 지정하면 되고 여섯번째 파라미터에는 데이터의 타입이 저장될 변수의 주소를 지정해 주면 됩니다. 이 데이터 타입은 앞에서 이미 알아 본 것입니다. 아래와 같은 예약어들이 함수 사용한 후에 저장되겠군요.

REG_BINARY
REG_DWORD
REG_LINK
REG_NONE
REG_SZ

그 의미는 이미 앞에서 설명드렸을 겁니다.

일곱번째 파라미터에는 데이터가 저장될 버퍼를 지정하면 되고 마지막 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다.

함수 설명을 했으니 계속해서 해당 루틴을 알아 보도록 하겠습니다.

            dwNameSize = dwValueSize = 80;

역시 길이를 초기화 해주고 있습니다.

            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;

얻은 네임과 데이터를 화면에 출력해 주고 있습니다.

            dwValueCount++;

네임에 대한 인덱스를 증가해주고 있습니다.

        }
        RegCloseKey(hSubKey);
        dwCount++;

서브키에 대한 인덱스를 증가해주고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문이 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

Permalink 댓글 남기기

Registry

6월 26, 2006 at 10:31 오전 (Visual C++)

이번시간 부터는 레지스트리에 대한 것에 대해 알아 보겠습니다. 혹시 레지스트리라는것을 들어 보았습니까? 윈도우즈에서는 레지스트리라는 것이 존재하는데 이것은 일종의 하드웨어적, 소프트웨어적인 정보를 저장하고 있는 매체입니다. 우리가 앞에서 배운 win.ini 파일에도 다양한 정보가 저장되어 있는데 사실 이것은 윈도우즈 3.1때와의 호환 때문에 존재하는 것이고 실제로 우리가 다루어야 할 것은 바로 이 레지스트리입니다. 음.. 예를들어서 win.ini 파일에 프로그램이 설치되어 있는 디렉토리 정보가 들어 있었죠? 아마 인스톨 쉴드라는 인스톨 툴로 인스톨 프로그램을 만들면 인스톨시에 win.ini 파일에 디렉토리 정보 저장 기능은 없을 겁니다.

그러나 레지스트리에 설치된 디렉토리를 저장할 수 있는 기능은 있죠. 바로 레지스트리에도 설치된 디렉토리 정보가 들어 있다는 말과도 같습니다. 그것 뿐만 아닙니다. PNP 하드웨어의 정보도 다 들어 있죠. 모뎀 포트를 자동으로 찾는다거나 하는 작업을 바로 이 레지스트리를 검색해서 할수 있는 것입니다. 여러분들 넷스케이프 사용해 봤습니까? 거기에 보면 홈 디렉토리를 지정할 수 있는 메뉴 아이템이 있을겁니다. MS 익스플로어의 요즘 버전은 제가 사용해 보지 않았지만 예전에 사용할 때에는 이 기능이 없더군요. 그래서 항상 홈 디렉토리가 마이크로 소프트사였습니다. 그런데 이 홈 디렉토리 정보가 레지스트리에 등록되어 있다는 것을 우연히 알게 되었습니다. 물론 그것을 바꿔서 홈 디렉토리를 바꾸었죠.

너무 서론이 길었죠? 그렇다면 이러한 레지스트리를 어떻게 원하는데로 바꿀 수 있을까요? 윈도우즈가 설치되어 있는 디렉토리에 보면 regedit.exe 파일이 있을 겁니다. 이 실행 파일을 실행시켜서 레지스트리 정보를 볼 수도 있고 또 수정할 수도 있습니다. 이 프로그램을 레지스트리 편집기라고도 하죠.

한번 실행해 보세요.

어때요? 크게 6개의 폴더가 보이죠? 각 커다란 폴더에는 의미가 있습니다.

HKEY_CLASSES_ROOT

이 하위에 있는 정보들은 문서의 형태, 파일연관상태에 대한 정보가 들어 있습니다. 실제로 하위 폴더를 보면 아래아 한글 문서의 확장자, 압축 파일 확장자들의 정보가 들어 있음을 확인할 수 있습니다.

HKEY_CURRENT_USER

각각 사용자에 따른 사용자 정보를 가지고 있습니다.

HKEY_LOCAL_MACHINE

하드웨어, 네트워크, 소프트웨어 정보가 이곳에 있습니다. 실제로 프로그램이 설치되어 있는 디렉토리, PNP 하드웨어 등록 정보들이 이곳에 있는 거죠.

HKEY_USERS

각각 사용자에 대한 시스템 정보를 가지고 있습니다.

HKEY_CURRENT_CONFIG

하드웨어 설정에 대한 정보값이 들어 있습니다.

HKEY_DYN_DATA

역시 하드웨어적인 정보값이 들어 있습니다.

자 각각에 대해 알아 보았으니 각 서브 폴더를 확인해 보시기 바랍니다. 폴더 옆에 보면 플러스 기호가 있는데 여기에 마우스의 왼쪽 버튼으로 클릭하면 마이너스 기호로 바뀌고 그곳에 속해 있는 서브 폴더를 보여주게 됩니다. 다시 클릭하면 감춰지죠. 서브 폴더를 보다보면 폴더 왼쪽에 아무 기호도 없는 폴더들이 있을 겁니다. 그것은 서브 폴더를 가지지 않음을 의미하는 것입니다. 그렇다면 서브 폴더를 가지지 않은 폴더에 클릭한 후에 오른쪽에 있는 창을 보십시요.

어때요? 네임에 어떤 이름이 있고 데이터에 값이 들어 있죠? 물론 아무 값도 가지지 않는 경우도 있습니다. 서브 풀더를 가지고 있는 것도 마찬가지입니다. 왼쪽에 있는 기호에 클릭하지 않고 바로 그 폴더를 클릭하면 네임과 데이터에 값이 표시될 것입니다.

자 새로운 용어를 알아 봅시다. 우리가 앞에서 언급한 폴더를 키라고 합니다. 각 키는 물론 서브 키를 가질 수 있고 각 키에는 네임과 데이터를 가집니다. 물론 하나의 키는 여러개의 네임과 데이터를 가질 수 있습니다. 이해가 가죠?

자 이제부터 우리는 기존에 등록되어 있는 키, 네임, 데이터를 읽는 것을 프로그램 상으로 어떻게 구현하는지 알아 볼 것입니다. 물론 등록하는 방법도 알아 볼 거구요.

그러면 시작해 봅시다.

먼저 등록되어 있는 정보를 읽어오는 방법부터 알아 봅시다. 처음에 정보를 가져올 키가 어떤 것인지 지정해서 열어 주어야 합니다.

LONG RegOpenKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    REGSAM  samDesired,
    PHKEY  phkResult
   );   

위 함수를 이용해서 레지스트리에 설정되어 있는 키를 열 수 있습니다. 첫번째 파라미터에 제일 상단에 있는 루트키를 지정하면 되는데 이 키는 레지스트리 편지기를 실행했을 때 볼 수 있는 값을 지정하면 되는 겁니다. 아래 값들 중 하나가 되겠군요.

HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_CURRENT_CONFIG
HKEY_DYN_DATA

두번째 파라미터에는 우리가 정보를 가져올 서브키를 지정해 주면 됩니다. 서브키 내에 또다른 서브키가 있으면 서브 디렉토리를 표시하던 식인 \\식으로 표시해 주면 됩니다. 세번째 파라미터에는 0을 지정하면 되고 네번째 파라미터에는 지정된 예약어를 지정해주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

마지막 파라미터에는 서브키를 열었을때 얻어진 키 핸들을 저장할 변수의 주소를 지정해 주면 됩니다.

자 이제 서브키를 열었으니 그 키가 가지는 데이터를 가져오기만 하면 될 겁니다.

LONG RegQueryValueEx(
    HKEY  hkey,
    LPTSTR  lpszValueName,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );

위 함수를 이용해서 데이터를 가져올 수 있습니다. 첫번째 파라미터에는 위 RegOpenKeyEx() 함수에서 얻은 키 핸들을 지정하면 되고 두번째 파라미터에는 가져올 데이터가 가지는 네임을 지정해 주면 됩니다. 네임이 Default로 되어 있으면 그냥 ""식으로 지정해 주면 됩니다. 세번째 파라미터에는 0을 지정해 주면되고 네번째 파라미터에는 데이터의 저장형태가 저장될 변수의 주소를 지정해주면 됩니다. 이 저장형태는 뒤에 가서 레지스트리에 정보를 기록할때 다시 설명드리겠습니다.

그리고 다섯번째 파라미터에는 읽어온 데이터가 저장될 주소를 지정하면 됩니다. 마지막 파라미터에는 이 읽어온 데이터의 크기가 저장될 변수의 주소를 지정하면 됩니다.

데이터를 읽어오는 과정이 어렵지 않죠? 키를 열어서 작업을 다 했으면 그 키를 닫아 주어야 합니다.

LONG RegCloseKey(
    HKEY  hkey
   );

위 함수를 이용해서 연 키를 닫을수 있습니다. 물론 새로 생성한 키도 위 함수를 이용해서 닫아주면 되죠.

그러면 이번에는 정보를 기록하는 방법에 대해 알아 봅시다. 마찬가지로 어렵지 않습니다. 먼저 새로운 키를 생성하는 과정이 필요한데 그것은 아래 함수를 이용해서 하면 됩니다.

LONG RegCreateKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    LPTSTR  lpszClass,
    DWORD  fdwOptions,
    REGSAM  samDesired,
    LPSECURITY_ATTRIBUTES  lpSecurityAttributes,
    PHKEY  phkResult,
    LPDWORD  lpdwDisposition
   );

첫번째 파라미터에는 제일 상단에 있는 루트키를 지정해 주면 되는데 이 루트키는 이미 RegOpenKeyEx() 함수때 설명드렸을 겁니다. 두번째 파라미터에는 서브키를 지정해 주면 됩니다. 세번째 파라미터에는 NULL을 지정하면 되고 네번째 파라미터에는 클래스 이름을 지정해 주면 됩니다. 보통 NULL을 지정하죠. 다섯번째 파라미터에는 지정된 예약어를 지정해 주면 되는데 이때 지정될 수 있는 예약어는 다음과 같습니다.

REG_OPTION_VOLATILE         메모리에만 저장됩니다.
REG_OPTION_NON_VOLATILE     파일에 저장됩니다.

첫번째 예약어를 사용하게 되면 메모리에만 저장되므로 시스템이 재시작되면 무효가 되나 두번째 예약어를 사용하게 되면 파일에 저장되므로 그 값이 계속 유효하게 됩니다. 어떤 차이인지 알겠죠?

여섯번째 파라미터에는 어세스 옵션을 지정해 주면 되는데 다음과 같은 예약어를 지정해 주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

앞에서 알아본 RegOpenKeyEx()함수의 예약어와 같죠? 일곱번째 파라미터에는 NULL을 지정하면 되고 여덟번째 파라미터에는 이 키에 대한 핸들을 기억할 키 변수의 핸들을 지정해 주면 됩니다. 마지막 파라미터에는 키에 대한 결과가 저장될 변수의 주소를 지정해 주면 됩니다.

이제 키를 생성하는 함수를 알아보았으니 원하는 네임에 데이터를 지정하는 함수를 알아 봅시다.

LONG RegSetValueEx(
    HKEY  hkey,
    LPCTSTR  lpszValueName,
    DWORD  dwReserved,
    DWORD  fdwType,
    CONST BYTE *  lpbData,
    DWORD  cbData
   );

위 함수를 이용해서 원하는 네임과 데이터를 지정해줄수 있는데 각 파라미터의 의미를 알아 봅시다.

첫번째 파라미터에는 RegCreateKeyEx()함수로 얻은 키 핸들을 지정해주면 되고 두번째 파라미터에는 네임을 지정해 주면됩니다. 세번째 파라미터에는 NULL을 지정해주면 되고 네번째 파라미터에는 데이터의 타입을 지정해주면 됩니다. 데이터의 타입은 아래의 예약어를 이용하면 됩니다.

REG_BINARY      바이너리 형태입니다.
REG_DWORD       더블워드 형태입니다.
REG_LINK        심볼릭 링크 형태입니다.
REG_NONE        아무 형태도 아닙니다.
REG_SZ          문자열의 형태입니다.

다섯번째 파라미터에는 데이터를 지정해주면 됩니다. 마지막 파라미터에는 데이터의 길이를 지정해 주면 되구요.

자 그러면 이번에는 키를 삭제하는 방법을 알아 봅시다.

먼저 지울 키를 RegOpenKeyEx()함수를 이용해서 핸들 형태로 반환을 받아야 합니다. 그래서 그 핸들을 이용해서 원하는 네임의 데이터를 삭제하면 되죠.

원하는 네임의 데이터를 지울 때에는 아래 함수를 사용하면 됩니다.

LONG RegDeleteValue(
    HKEY  hkey,
    LPTSTR  lpszValue
   );

첫번째 파라미터에 지울 네임이 속해 있는 키의 핸들을 지정해 주면 됩니다. 두번째 파라미터에는 지울 데이터의 네임을 지정해 주면 되구요.

이번에는 키 자체를 없애는 함수입니다.

LONG RegDeleteKey(
    HKEY  hkey,
    LPCTSTR  lpszSubKey
   );

첫번째 파라미터에는 최상위 루트 키의 이름을 지정해주면 되고 두번째 파라미터에는 그 안에 포함된 지울 서브키의 이름을 지정해 주면 됩니다. 사용방법이 간단하죠.

자 그러면 위에서 배운 함수들을 이용해서 실제로 프로그램을 만들어 봅시다.

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 47

안녕하세요……..돌팔이 황동준입니다……………..

이번시간에는 저번시간에 알아 본 함수들을 이용해서 프로그램을 만들어 보겠습니다.

아래 프로그램은 레지스트리에 원하는 키를 생성해서 거기에 데이터를 기록한 뒤 실제로 그 데이터를 가져오고 하는 기능을 가졌습니다. 물론 지우는 기능도 있구요. 앞부분에서 함수를 잘 이해한 분들은 그렇게 어렵지 않을 겁니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Get Registry", 200
        MENUITEM "&Delete Registry", 300
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
char* GetRegistry(void);
void DeleteRegistry(void);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        strcpy(szBuff, GetRegistry());
                        if(strcmp(szBuff, "ERROR"))
                            MessageBox(hWnd, szBuff, "", MB_OK);
                        else
                            MessageBox(hWnd, "Value not found!!", "", MB_OK);
                        break;

                    case 300 :

                        DeleteRegistry();
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

char* GetRegistry(void)
{
    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{
    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);
    RegDeleteValue(hKey, "Install");
    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");
    RegCloseKey(hKey);
}

자 프로그램을 실행시켜 첫번째 메뉴 아이템인 Set Registry를 선택해 보세요. 그리고 진짜로 그 키가 생성되었는지 레지스트리 편집기로 확인해 보시기 바랍니다. 어때요? 정말로 생성되어 있죠? 자 그러면 소스를 보도록 합시다.

제가 만든 세개의 함수 루틴만 보면 되겠네요.

BOOL SetRegistry(void)
{

이 함수는 새로운 키를 생성하는 역할을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);

HKEY_LOCAL_MACHINE 부분에 있는 Software내에 DORAN 20이라는 키를 생성하고 있는 구문입니다. 물론 Software라는 서브키가 존재하지 않으면 새로 생성합니다.

    if(lRet != ERROR_SUCCESS)
        return FALSE;

성공적으로 함수가 수행되면 ERROR_SUCESS라는 리터값이 발생된다는 것을 위 구문으로 짐작할 수 있을 겁니다.

    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));

Install이라는 네임에 c:\doran20이라는 데이터를 저장하는 구문입니다.

    if(lRet != ERROR_SUCCESS)
    {

역시 함수가 성공적으로 수행되면 ERROR_SUCCESS라는 리턴값이 발생되는군요.

        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);

생성한 키를 닫는 구문입니다.

    return TRUE;
}

char* GetRegistry(void)
{

이 함수는 원하는 서브키의 데이터를 가져오는 역할을 합니다.

    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

데이터를 가져올 서브키를 열고 있습니다.

    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);

Install이라는 네임에 해당하는 데이터를 가져오고 있습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{

이 함수는 첫번째 메뉴 아이템에 의해 생성된 키와 데이터를 삭제하는 역할을 합니다.

    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);

지울 키를 얻고 있습니다.

    RegDeleteValue(hKey, "Install");

Install 네임을 가지고 있는 데이터를 삭제하는 구문입니다.

    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");

서브 키를 없애는 구문입니다.

    RegCloseKey(hKey);
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 48

안녕하세요…………돌팔이 황동준입니다……………

이번시간에도 레지스트리에 대해 더 알아 보겠습니다. 이번에 알아 볼 것은 어떤 키에 속해있는 서브키를 어떻게 얻을 수 있는지 입니다. 물론 우리는
서브키에 속해 있는 네임에 해당하는 데이터를 얻는 방법은 압니다. 그러나 문제가 있죠. 바로 이 키에 어떤 서브키가 있는지 모른다면 할 수가 없습니다. 그렇죠? 그렇기 때문에 이 방법을 알아야 합니다. 좀더 들어간다면 어떤 서브키에 어떤 네임들이 있는지도 아는 방법이 있습니다. 그것도 우리가 지금 알아 볼 방법을 응용하면 됩니다. 자 그러면 시작해 봅시다.

먼저 크게 두단계로 나누어서 합니다. 첫번째 단계는 바로 원하는 키에 서브키가 몇개 있는지 개수를 알아내는 것입니다. 두번째 단계는 이 개수를 가지고 차례로 키 포인터를 이동시켜 서브키를 얻는 것입니다.

자 그러면 실제로 어떤 식으로 구현하는지 필요한 함수를 보도록 합시다. 먼저 서브키의 개수를 알아야 하는데 이때 필요한 함수의 첫번째 파라미터에 키 핸들을 지정해야 하는데 이 키 핸들은 앞에서 배운 RegOpenKeyEx()함수를 이용하면 됩니다. 그러면 키의 개수를 얻을 수 있는 함수를 보도록 합시다.

LONG RegQueryInfoKey (
    HKEY  hkey,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    LPDWORD  lpdwReserved,
    LPDWORD  lpcSubKeys,
    LPDWORD  lpcchMaxSubkey,
    LPDWORD  lpcchMaxClass,
    LPDWORD  lpcValues,
    LPDWORD  lpcchMaxValueName,
    LPDWORD  lpcbMaxValueData,
    LPDWORD  lpcbSecurityDescriptor,
    PFILETIME  lpftLastWriteTime
   );

첫번째 파라미터로 RegOpenKeyEx()함수에서 얻은 키 핸들을 지정하면 됩니다. 두번째 파라미터에는 클래스 이름이 저장될 버퍼의 주소를 세번째 파라미터에는 그 클래스 버퍼의 크기를 가지고 있는 변수의 주소를 지정해 주면 됩니다.

네번째 파라미터에는 0을 지정하면되고 다섯번째 파라미터인 lpcSubKeys에는 서브키의 개수가 저장될 변수의 주소를 지정해 주면 됩니다. 여섯번째 파라미터에는 서브키의 이름중 가장 긴 이름을 가진 서브키의 길이가 저장될 변수의 주소를 지정해주면 됩니다. 사실 이 값은 잘 쓰이지 않죠. 역시 일곱번째 파라미터도 서브키에 대한 클래스이름이 가장 긴 길이가 저장될 변수의 주소를 지정해주면 됩니다.

여덟번째 파라미터인 lpcValues에는 지정해준 키에 대한 데이터의 개수가 저장 될 변수의 주소를 지정해주면 되는데 우리가 지금 만들어볼 프로그램은 단지 서브키의 개수만 알아볼거니 사실 이 값이 필요없습니다. 그러나 다음 프로그램에서는 이 값을 이용할테니 그 의미를 알아 두시기 바랍니다.

아홉번째 파라미터에는 가장 긴 네임에 대한 길이가 저장될 변수의 주소를 지정해주면 되고 열번째 파라미터에는 가장긴 데이터의 길이가 저장될 변수의 주소를 지정해 주면 됩니다. 열한번째 파라미터에는 NULL을 지정하면 되고 마지막 파라미터에는 주어진 키에대한 시간정보를 저장할 구조체 변수의 주소를 지정해 주면 됩니다. 이 구조체는 이미 앞에서 알아보았고 또 어떻게 다루었는지도 알아 보았을 겁니다.

자 서브키나 데이터의 개수를 위 함수로 알아 냈으니 이번에는 실제로 그 서브키나 데이터가 무엇인지 알아내는 함수를 알아 봅시다.

LONG RegEnumKeyEx(
    HKEY  hkey,
    DWORD  iSubkey,
    LPTSTR  lpszName,
    LPDWORD  lpcchName,
    LPDWORD  lpdwReserved,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    PFILETIME  lpftLastWrite
   );

바로 위 함수를 이용해서 알아 낼 수 있습니다. 첫번째 파라미터에 알아낼 서브키가 속해있는 루트 키의 핸들을 지정해 주면 되고 두번째 파라미터에 서브키의 인덱스를 지정해주면 됩니다. 서브키의 인덱스는 0부터 시작합니다. 예를 들어 우리가 위의 RegQueryInfoKey() 함수로 서브키의 개수를 알아 내었다면 0부터 차례로 그 개수에서 1을 뺀값까지 이 파라미터로 지정해주면 되겠죠.

세번째 파라미터에는 해당하는 인텍스의 서브키가 들어갈 버퍼를 지정해 주면 되고 네번째 파라미터에는 그 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

다섯번째 파라미터에는 NULL을 지정해 주면 되고 여섯번째 파라미터에는 클래스 이름이 저장될 버퍼를 지정해주면 됩니다. 일곱번째 파라미터에는 클래스 이름이 저장 될 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

마지막 파라미터는 설명 드릴필요는 없겠군요. 앞의 RegQueryInfoKey() 함수의 마지막 파라미터와 의미가 같습니다.

자 그러면 실제로 이것을 이용한 프로그램을 만들어 봅시다. 아래 프로그램은 새로운 키를 생성하고 그 키에 포함된 서브키가 어떤것들인지 화면에 출력해 주는 기능을 가졌습니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey)

;
    return TRUE;
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 49

안녕하세요………….돌팔이 황동준입니다…………………

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

제가 만든 두개의 함수만 알아 보면 되겠죠?

BOOL SetRegistry(void)
{

이 함수는 우리가 어떤 서브키가 있는지 테스트해보기 위해 임시로 서브키를 만드는 기능을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

어떤 서브키를 만드는지 굳이 설명드릴 필요는 없죠? 바로 Install이라는 서브키를 만들고 Install이라는 네임에 c:\doran20이라는 데이터를 저장하고 있는 구문입니다.

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;

앞에서 만든 DORAN 20이라는 서브키에 다시 Version이라는 서브키를 생성하고 네임에 Version을 그 네임에 해당하는 데이터는 2.0을 지정하는 구문입니다.

}

앞에서 한부분이므로 이해가 안가는 부분은 없을 겁니다. 위 함수가 수행되면 DORAN 20이라는 서브키에 Install, Version이라는 서브키가 생성되어 있을 겁니다.

BOOL PrintSubRegistry(HWND hWnd)
{

이 함수는 DORAN 20 서브키에 속해 있는 서브키를 찾아서 화면에 출력해주는 역할을 합니다.

    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

    if(lRet != ERROR_SUCCESS)
        return FALSE;

알아 볼 서브키가 포함되어 있는 부모 키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

이번에 알아 볼것은 단지 서브키의 개수이므로 데이터의 개수가 저장될 파라미터에는 NULL을 지정하였습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;

서브키의 개수만큼 인덱스를 증가하기 위해 위 dwCount라는 변수를 사용하였습니다.

    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

인덱스에 해당하는 서브키를 얻고 있는 구문입니다.

        dwSubKeySize = 80;

위 함수가 수행되면 dwSubKeySize에 실제 크기가 저장되므로 이 값을 다시 초기화해주고 있습니다. 왜냐하면 위 함수를 사용할 때 버퍼의 크기가 저장된 변수의 주소를 지정하기 때문이죠.

        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);

얻은 서브키를 화면에 출력해 주는 구문입니다.

        dwCount++;

인덱스를 증가하고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문을 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

자 그러면 이번에는 좀더 진보적인 프로그램을 만들어 봅시다. 바로 서브키만 알아내는 것이 아니라 그 안에 속해있는 네임과 데이터도 알아내는 거죠.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);
        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);
        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {
            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);
            dwNameSize = dwValueSize = 80;
            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;
            dwValueCount++;
        }
        RegCloseKey(hSubKey);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey);
    return TRUE;

}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 50

안녕하세요…………..돌팔이 황동준입니다………….

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

이번에는 함수 하나만 알아보면 되겠네요.

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;

정보를 알아볼 서브키의 루크키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

서브키의 개수를 알아내고 있습니다.

    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

서브키를 알아내고 있는 구문입니다.

        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);

        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

알아낸 서브키를 열고 있는 구문입니다.

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);

그 서브키가 가지는 데이터의 개수를 얻고 있습니다.

        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {

데이터의 개수만큼 루프문을 반복하고 있습니다.

            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);

위 함수로 인덱스에 해당하는 네임과 데이터를 얻을 수 있는데 위에서 이 함수에 대해 설명드리지 않았군요.

LONG RegEnumValue(
    HKEY  hkey,
    DWORD  iValue,
    LPTSTR  lpszValue,
    LPDWORD  lpcchValue,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );   

첫번째 파라미터에는 얻을 네임이 포함되어 있는 서브키의 핸들을 지정해 주면됩니다. 두번째 파라미터에는 네임의 인덱스를 지정해 주면 되는데 역시 0부터 지정해주면 됩니다. 세번째 파라미터에는 네임이 저장될 버퍼를 네번째 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다. 다섯 번째 파라미터에는 NULL을 지정하면 되고 여섯번째 파라미터에는 데이터의 타입이 저장될 변수의 주소를 지정해 주면 됩니다. 이 데이터 타입은 앞에서 이미 알아 본 것입니다. 아래와 같은 예약어들이 함수 사용한 후에 저장되겠군요.

REG_BINARY
REG_DWORD
REG_LINK
REG_NONE
REG_SZ

그 의미는 이미 앞에서 설명드렸을 겁니다.

일곱번째 파라미터에는 데이터가 저장될 버퍼를 지정하면 되고 마지막 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다.

함수 설명을 했으니 계속해서 해당 루틴을 알아 보도록 하겠습니다.

            dwNameSize = dwValueSize = 80;

역시 길이를 초기화 해주고 있습니다.

            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;

얻은 네임과 데이터를 화면에 출력해 주고 있습니다.

            dwValueCount++;

네임에 대한 인덱스를 증가해주고 있습니다.

        }
        RegCloseKey(hSubKey);
        dwCount++;

서브키에 대한 인덱스를 증가해주고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문이 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

Permalink 댓글 남기기

Registry

6월 26, 2006 at 10:31 오전 (Visual C++)

이번시간 부터는 레지스트리에 대한 것에 대해 알아 보겠습니다. 혹시 레지스트리라는것을 들어 보았습니까? 윈도우즈에서는 레지스트리라는 것이 존재하는데 이것은 일종의 하드웨어적, 소프트웨어적인 정보를 저장하고 있는 매체입니다. 우리가 앞에서 배운 win.ini 파일에도 다양한 정보가 저장되어 있는데 사실 이것은 윈도우즈 3.1때와의 호환 때문에 존재하는 것이고 실제로 우리가 다루어야 할 것은 바로 이 레지스트리입니다. 음.. 예를들어서 win.ini 파일에 프로그램이 설치되어 있는 디렉토리 정보가 들어 있었죠? 아마 인스톨 쉴드라는 인스톨 툴로 인스톨 프로그램을 만들면 인스톨시에 win.ini 파일에 디렉토리 정보 저장 기능은 없을 겁니다.

그러나 레지스트리에 설치된 디렉토리를 저장할 수 있는 기능은 있죠. 바로 레지스트리에도 설치된 디렉토리 정보가 들어 있다는 말과도 같습니다. 그것 뿐만 아닙니다. PNP 하드웨어의 정보도 다 들어 있죠. 모뎀 포트를 자동으로 찾는다거나 하는 작업을 바로 이 레지스트리를 검색해서 할수 있는 것입니다. 여러분들 넷스케이프 사용해 봤습니까? 거기에 보면 홈 디렉토리를 지정할 수 있는 메뉴 아이템이 있을겁니다. MS 익스플로어의 요즘 버전은 제가 사용해 보지 않았지만 예전에 사용할 때에는 이 기능이 없더군요. 그래서 항상 홈 디렉토리가 마이크로 소프트사였습니다. 그런데 이 홈 디렉토리 정보가 레지스트리에 등록되어 있다는 것을 우연히 알게 되었습니다. 물론 그것을 바꿔서 홈 디렉토리를 바꾸었죠.

너무 서론이 길었죠? 그렇다면 이러한 레지스트리를 어떻게 원하는데로 바꿀 수 있을까요? 윈도우즈가 설치되어 있는 디렉토리에 보면 regedit.exe 파일이 있을 겁니다. 이 실행 파일을 실행시켜서 레지스트리 정보를 볼 수도 있고 또 수정할 수도 있습니다. 이 프로그램을 레지스트리 편집기라고도 하죠.

한번 실행해 보세요.

어때요? 크게 6개의 폴더가 보이죠? 각 커다란 폴더에는 의미가 있습니다.

HKEY_CLASSES_ROOT

이 하위에 있는 정보들은 문서의 형태, 파일연관상태에 대한 정보가 들어 있습니다. 실제로 하위 폴더를 보면 아래아 한글 문서의 확장자, 압축 파일 확장자들의 정보가 들어 있음을 확인할 수 있습니다.

HKEY_CURRENT_USER

각각 사용자에 따른 사용자 정보를 가지고 있습니다.

HKEY_LOCAL_MACHINE

하드웨어, 네트워크, 소프트웨어 정보가 이곳에 있습니다. 실제로 프로그램이 설치되어 있는 디렉토리, PNP 하드웨어 등록 정보들이 이곳에 있는 거죠.

HKEY_USERS

각각 사용자에 대한 시스템 정보를 가지고 있습니다.

HKEY_CURRENT_CONFIG

하드웨어 설정에 대한 정보값이 들어 있습니다.

HKEY_DYN_DATA

역시 하드웨어적인 정보값이 들어 있습니다.

자 각각에 대해 알아 보았으니 각 서브 폴더를 확인해 보시기 바랍니다. 폴더 옆에 보면 플러스 기호가 있는데 여기에 마우스의 왼쪽 버튼으로 클릭하면 마이너스 기호로 바뀌고 그곳에 속해 있는 서브 폴더를 보여주게 됩니다. 다시 클릭하면 감춰지죠. 서브 폴더를 보다보면 폴더 왼쪽에 아무 기호도 없는 폴더들이 있을 겁니다. 그것은 서브 폴더를 가지지 않음을 의미하는 것입니다. 그렇다면 서브 폴더를 가지지 않은 폴더에 클릭한 후에 오른쪽에 있는 창을 보십시요.

어때요? 네임에 어떤 이름이 있고 데이터에 값이 들어 있죠? 물론 아무 값도 가지지 않는 경우도 있습니다. 서브 풀더를 가지고 있는 것도 마찬가지입니다. 왼쪽에 있는 기호에 클릭하지 않고 바로 그 폴더를 클릭하면 네임과 데이터에 값이 표시될 것입니다.

자 새로운 용어를 알아 봅시다. 우리가 앞에서 언급한 폴더를 키라고 합니다. 각 키는 물론 서브 키를 가질 수 있고 각 키에는 네임과 데이터를 가집니다. 물론 하나의 키는 여러개의 네임과 데이터를 가질 수 있습니다. 이해가 가죠?

자 이제부터 우리는 기존에 등록되어 있는 키, 네임, 데이터를 읽는 것을 프로그램 상으로 어떻게 구현하는지 알아 볼 것입니다. 물론 등록하는 방법도 알아 볼 거구요.

그러면 시작해 봅시다.

먼저 등록되어 있는 정보를 읽어오는 방법부터 알아 봅시다. 처음에 정보를 가져올 키가 어떤 것인지 지정해서 열어 주어야 합니다.

LONG RegOpenKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    REGSAM  samDesired,
    PHKEY  phkResult
   );   

위 함수를 이용해서 레지스트리에 설정되어 있는 키를 열 수 있습니다. 첫번째 파라미터에 제일 상단에 있는 루트키를 지정하면 되는데 이 키는 레지스트리 편지기를 실행했을 때 볼 수 있는 값을 지정하면 되는 겁니다. 아래 값들 중 하나가 되겠군요.

HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USERS
HKEY_CURRENT_CONFIG
HKEY_DYN_DATA

두번째 파라미터에는 우리가 정보를 가져올 서브키를 지정해 주면 됩니다. 서브키 내에 또다른 서브키가 있으면 서브 디렉토리를 표시하던 식인 \\식으로 표시해 주면 됩니다. 세번째 파라미터에는 0을 지정하면 되고 네번째 파라미터에는 지정된 예약어를 지정해주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

마지막 파라미터에는 서브키를 열었을때 얻어진 키 핸들을 저장할 변수의 주소를 지정해 주면 됩니다.

자 이제 서브키를 열었으니 그 키가 가지는 데이터를 가져오기만 하면 될 겁니다.

LONG RegQueryValueEx(
    HKEY  hkey,
    LPTSTR  lpszValueName,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );

위 함수를 이용해서 데이터를 가져올 수 있습니다. 첫번째 파라미터에는 위 RegOpenKeyEx() 함수에서 얻은 키 핸들을 지정하면 되고 두번째 파라미터에는 가져올 데이터가 가지는 네임을 지정해 주면 됩니다. 네임이 Default로 되어 있으면 그냥 ""식으로 지정해 주면 됩니다. 세번째 파라미터에는 0을 지정해 주면되고 네번째 파라미터에는 데이터의 저장형태가 저장될 변수의 주소를 지정해주면 됩니다. 이 저장형태는 뒤에 가서 레지스트리에 정보를 기록할때 다시 설명드리겠습니다.

그리고 다섯번째 파라미터에는 읽어온 데이터가 저장될 주소를 지정하면 됩니다. 마지막 파라미터에는 이 읽어온 데이터의 크기가 저장될 변수의 주소를 지정하면 됩니다.

데이터를 읽어오는 과정이 어렵지 않죠? 키를 열어서 작업을 다 했으면 그 키를 닫아 주어야 합니다.

LONG RegCloseKey(
    HKEY  hkey
   );

위 함수를 이용해서 연 키를 닫을수 있습니다. 물론 새로 생성한 키도 위 함수를 이용해서 닫아주면 되죠.

그러면 이번에는 정보를 기록하는 방법에 대해 알아 봅시다. 마찬가지로 어렵지 않습니다. 먼저 새로운 키를 생성하는 과정이 필요한데 그것은 아래 함수를 이용해서 하면 됩니다.

LONG RegCreateKeyEx(
    HKEY  hkey,
    LPCTSTR  lpszSubKey,
    DWORD  dwReserved,
    LPTSTR  lpszClass,
    DWORD  fdwOptions,
    REGSAM  samDesired,
    LPSECURITY_ATTRIBUTES  lpSecurityAttributes,
    PHKEY  phkResult,
    LPDWORD  lpdwDisposition
   );

첫번째 파라미터에는 제일 상단에 있는 루트키를 지정해 주면 되는데 이 루트키는 이미 RegOpenKeyEx() 함수때 설명드렸을 겁니다. 두번째 파라미터에는 서브키를 지정해 주면 됩니다. 세번째 파라미터에는 NULL을 지정하면 되고 네번째 파라미터에는 클래스 이름을 지정해 주면 됩니다. 보통 NULL을 지정하죠. 다섯번째 파라미터에는 지정된 예약어를 지정해 주면 되는데 이때 지정될 수 있는 예약어는 다음과 같습니다.

REG_OPTION_VOLATILE         메모리에만 저장됩니다.
REG_OPTION_NON_VOLATILE     파일에 저장됩니다.

첫번째 예약어를 사용하게 되면 메모리에만 저장되므로 시스템이 재시작되면 무효가 되나 두번째 예약어를 사용하게 되면 파일에 저장되므로 그 값이 계속 유효하게 됩니다. 어떤 차이인지 알겠죠?

여섯번째 파라미터에는 어세스 옵션을 지정해 주면 되는데 다음과 같은 예약어를 지정해 주면 됩니다.

KEY_ALL_ACCESS              아래 모든 예약어를 만족합니다.
KEY_CREATE_LINK             다른 키로부터의 링크를 허락합니다.
KEY_CREATE_SUB_KEY          서브키의 생성을 허락합니다.
KEY_ENUMERATE_SUB_KEYS      서브키의 보여줌을 허락합니다.
KEY_EXECUTE                 서브키의 데이터를 가져오는 것을 허락합니다.
KEY_QUERY_VALUE             서브키의 데이터를 가져오는 것을 허락합니다.
KEY_READ                    서브키의 데이터를 가져오는 것을 허락합니다.
KEY_SET_VALUE               서브키의 데이터를 지정하는 것을 허락합니다.
KEY_WRITE                   서브키의 데이터를 지정하는 것을 허락합니다.

앞에서 알아본 RegOpenKeyEx()함수의 예약어와 같죠? 일곱번째 파라미터에는 NULL을 지정하면 되고 여덟번째 파라미터에는 이 키에 대한 핸들을 기억할 키 변수의 핸들을 지정해 주면 됩니다. 마지막 파라미터에는 키에 대한 결과가 저장될 변수의 주소를 지정해 주면 됩니다.

이제 키를 생성하는 함수를 알아보았으니 원하는 네임에 데이터를 지정하는 함수를 알아 봅시다.

LONG RegSetValueEx(
    HKEY  hkey,
    LPCTSTR  lpszValueName,
    DWORD  dwReserved,
    DWORD  fdwType,
    CONST BYTE *  lpbData,
    DWORD  cbData
   );

위 함수를 이용해서 원하는 네임과 데이터를 지정해줄수 있는데 각 파라미터의 의미를 알아 봅시다.

첫번째 파라미터에는 RegCreateKeyEx()함수로 얻은 키 핸들을 지정해주면 되고 두번째 파라미터에는 네임을 지정해 주면됩니다. 세번째 파라미터에는 NULL을 지정해주면 되고 네번째 파라미터에는 데이터의 타입을 지정해주면 됩니다. 데이터의 타입은 아래의 예약어를 이용하면 됩니다.

REG_BINARY      바이너리 형태입니다.
REG_DWORD       더블워드 형태입니다.
REG_LINK        심볼릭 링크 형태입니다.
REG_NONE        아무 형태도 아닙니다.
REG_SZ          문자열의 형태입니다.

다섯번째 파라미터에는 데이터를 지정해주면 됩니다. 마지막 파라미터에는 데이터의 길이를 지정해 주면 되구요.

자 그러면 이번에는 키를 삭제하는 방법을 알아 봅시다.

먼저 지울 키를 RegOpenKeyEx()함수를 이용해서 핸들 형태로 반환을 받아야 합니다. 그래서 그 핸들을 이용해서 원하는 네임의 데이터를 삭제하면 되죠.

원하는 네임의 데이터를 지울 때에는 아래 함수를 사용하면 됩니다.

LONG RegDeleteValue(
    HKEY  hkey,
    LPTSTR  lpszValue
   );

첫번째 파라미터에 지울 네임이 속해 있는 키의 핸들을 지정해 주면 됩니다. 두번째 파라미터에는 지울 데이터의 네임을 지정해 주면 되구요.

이번에는 키 자체를 없애는 함수입니다.

LONG RegDeleteKey(
    HKEY  hkey,
    LPCTSTR  lpszSubKey
   );

첫번째 파라미터에는 최상위 루트 키의 이름을 지정해주면 되고 두번째 파라미터에는 그 안에 포함된 지울 서브키의 이름을 지정해 주면 됩니다. 사용방법이 간단하죠.

자 그러면 위에서 배운 함수들을 이용해서 실제로 프로그램을 만들어 봅시다.

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 47

안녕하세요……..돌팔이 황동준입니다……………..

이번시간에는 저번시간에 알아 본 함수들을 이용해서 프로그램을 만들어 보겠습니다.

아래 프로그램은 레지스트리에 원하는 키를 생성해서 거기에 데이터를 기록한 뒤 실제로 그 데이터를 가져오고 하는 기능을 가졌습니다. 물론 지우는 기능도 있구요. 앞부분에서 함수를 잘 이해한 분들은 그렇게 어렵지 않을 겁니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Get Registry", 200
        MENUITEM "&Delete Registry", 300
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
char* GetRegistry(void);
void DeleteRegistry(void);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        strcpy(szBuff, GetRegistry());
                        if(strcmp(szBuff, "ERROR"))
                            MessageBox(hWnd, szBuff, "", MB_OK);
                        else
                            MessageBox(hWnd, "Value not found!!", "", MB_OK);
                        break;

                    case 300 :

                        DeleteRegistry();
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

char* GetRegistry(void)
{
    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{
    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);
    RegDeleteValue(hKey, "Install");
    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");
    RegCloseKey(hKey);
}

자 프로그램을 실행시켜 첫번째 메뉴 아이템인 Set Registry를 선택해 보세요. 그리고 진짜로 그 키가 생성되었는지 레지스트리 편집기로 확인해 보시기 바랍니다. 어때요? 정말로 생성되어 있죠? 자 그러면 소스를 보도록 합시다.

제가 만든 세개의 함수 루틴만 보면 되겠네요.

BOOL SetRegistry(void)
{

이 함수는 새로운 키를 생성하는 역할을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", NULL,
        NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);

HKEY_LOCAL_MACHINE 부분에 있는 Software내에 DORAN 20이라는 키를 생성하고 있는 구문입니다. 물론 Software라는 서브키가 존재하지 않으면 새로 생성합니다.

    if(lRet != ERROR_SUCCESS)
        return FALSE;

성공적으로 함수가 수행되면 ERROR_SUCESS라는 리터값이 발생된다는 것을 위 구문으로 짐작할 수 있을 겁니다.

    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));

Install이라는 네임에 c:\doran20이라는 데이터를 저장하는 구문입니다.

    if(lRet != ERROR_SUCCESS)
    {

역시 함수가 성공적으로 수행되면 ERROR_SUCCESS라는 리턴값이 발생되는군요.

        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);

생성한 키를 닫는 구문입니다.

    return TRUE;
}

char* GetRegistry(void)
{

이 함수는 원하는 서브키의 데이터를 가져오는 역할을 합니다.

    HKEY hKey;
    LONG lRet;
    DWORD dwByte, dwType;
    static char szBuff[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

데이터를 가져올 서브키를 열고 있습니다.

    if(lRet != ERROR_SUCCESS)
        return "ERROR";
    lRet = RegQueryValueEx(hKey, "Install", 0, &dwType,
        (unsigned char *)szBuff, &dwByte);

Install이라는 네임에 해당하는 데이터를 가져오고 있습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return "ERROR";
    }
    RegCloseKey(hKey);
    return szBuff;
}

void DeleteRegistry(void)
{

이 함수는 첫번째 메뉴 아이템에 의해 생성된 키와 데이터를 삭제하는 역할을 합니다.

    HKEY hKey;

    RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0, KEY_ALL_ACCESS,
        &hKey);

지울 키를 얻고 있습니다.

    RegDeleteValue(hKey, "Install");

Install 네임을 가지고 있는 데이터를 삭제하는 구문입니다.

    RegDeleteKey(HKEY_LOCAL_MACHINE, "Software\\DORAN 20");

서브 키를 없애는 구문입니다.

    RegCloseKey(hKey);
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 48

안녕하세요…………돌팔이 황동준입니다……………

이번시간에도 레지스트리에 대해 더 알아 보겠습니다. 이번에 알아 볼 것은 어떤 키에 속해있는 서브키를 어떻게 얻을 수 있는지 입니다. 물론 우리는
서브키에 속해 있는 네임에 해당하는 데이터를 얻는 방법은 압니다. 그러나 문제가 있죠. 바로 이 키에 어떤 서브키가 있는지 모른다면 할 수가 없습니다. 그렇죠? 그렇기 때문에 이 방법을 알아야 합니다. 좀더 들어간다면 어떤 서브키에 어떤 네임들이 있는지도 아는 방법이 있습니다. 그것도 우리가 지금 알아 볼 방법을 응용하면 됩니다. 자 그러면 시작해 봅시다.

먼저 크게 두단계로 나누어서 합니다. 첫번째 단계는 바로 원하는 키에 서브키가 몇개 있는지 개수를 알아내는 것입니다. 두번째 단계는 이 개수를 가지고 차례로 키 포인터를 이동시켜 서브키를 얻는 것입니다.

자 그러면 실제로 어떤 식으로 구현하는지 필요한 함수를 보도록 합시다. 먼저 서브키의 개수를 알아야 하는데 이때 필요한 함수의 첫번째 파라미터에 키 핸들을 지정해야 하는데 이 키 핸들은 앞에서 배운 RegOpenKeyEx()함수를 이용하면 됩니다. 그러면 키의 개수를 얻을 수 있는 함수를 보도록 합시다.

LONG RegQueryInfoKey (
    HKEY  hkey,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    LPDWORD  lpdwReserved,
    LPDWORD  lpcSubKeys,
    LPDWORD  lpcchMaxSubkey,
    LPDWORD  lpcchMaxClass,
    LPDWORD  lpcValues,
    LPDWORD  lpcchMaxValueName,
    LPDWORD  lpcbMaxValueData,
    LPDWORD  lpcbSecurityDescriptor,
    PFILETIME  lpftLastWriteTime
   );

첫번째 파라미터로 RegOpenKeyEx()함수에서 얻은 키 핸들을 지정하면 됩니다. 두번째 파라미터에는 클래스 이름이 저장될 버퍼의 주소를 세번째 파라미터에는 그 클래스 버퍼의 크기를 가지고 있는 변수의 주소를 지정해 주면 됩니다.

네번째 파라미터에는 0을 지정하면되고 다섯번째 파라미터인 lpcSubKeys에는 서브키의 개수가 저장될 변수의 주소를 지정해 주면 됩니다. 여섯번째 파라미터에는 서브키의 이름중 가장 긴 이름을 가진 서브키의 길이가 저장될 변수의 주소를 지정해주면 됩니다. 사실 이 값은 잘 쓰이지 않죠. 역시 일곱번째 파라미터도 서브키에 대한 클래스이름이 가장 긴 길이가 저장될 변수의 주소를 지정해주면 됩니다.

여덟번째 파라미터인 lpcValues에는 지정해준 키에 대한 데이터의 개수가 저장 될 변수의 주소를 지정해주면 되는데 우리가 지금 만들어볼 프로그램은 단지 서브키의 개수만 알아볼거니 사실 이 값이 필요없습니다. 그러나 다음 프로그램에서는 이 값을 이용할테니 그 의미를 알아 두시기 바랍니다.

아홉번째 파라미터에는 가장 긴 네임에 대한 길이가 저장될 변수의 주소를 지정해주면 되고 열번째 파라미터에는 가장긴 데이터의 길이가 저장될 변수의 주소를 지정해 주면 됩니다. 열한번째 파라미터에는 NULL을 지정하면 되고 마지막 파라미터에는 주어진 키에대한 시간정보를 저장할 구조체 변수의 주소를 지정해 주면 됩니다. 이 구조체는 이미 앞에서 알아보았고 또 어떻게 다루었는지도 알아 보았을 겁니다.

자 서브키나 데이터의 개수를 위 함수로 알아 냈으니 이번에는 실제로 그 서브키나 데이터가 무엇인지 알아내는 함수를 알아 봅시다.

LONG RegEnumKeyEx(
    HKEY  hkey,
    DWORD  iSubkey,
    LPTSTR  lpszName,
    LPDWORD  lpcchName,
    LPDWORD  lpdwReserved,
    LPTSTR  lpszClass,
    LPDWORD  lpcchClass,
    PFILETIME  lpftLastWrite
   );

바로 위 함수를 이용해서 알아 낼 수 있습니다. 첫번째 파라미터에 알아낼 서브키가 속해있는 루트 키의 핸들을 지정해 주면 되고 두번째 파라미터에 서브키의 인덱스를 지정해주면 됩니다. 서브키의 인덱스는 0부터 시작합니다. 예를 들어 우리가 위의 RegQueryInfoKey() 함수로 서브키의 개수를 알아 내었다면 0부터 차례로 그 개수에서 1을 뺀값까지 이 파라미터로 지정해주면 되겠죠.

세번째 파라미터에는 해당하는 인텍스의 서브키가 들어갈 버퍼를 지정해 주면 되고 네번째 파라미터에는 그 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

다섯번째 파라미터에는 NULL을 지정해 주면 되고 여섯번째 파라미터에는 클래스 이름이 저장될 버퍼를 지정해주면 됩니다. 일곱번째 파라미터에는 클래스 이름이 저장 될 버퍼의 크기가 저장된 변수의 주소를 지정해 주면 됩니다.

마지막 파라미터는 설명 드릴필요는 없겠군요. 앞의 RegQueryInfoKey() 함수의 마지막 파라미터와 의미가 같습니다.

자 그러면 실제로 이것을 이용한 프로그램을 만들어 봅시다. 아래 프로그램은 새로운 키를 생성하고 그 키에 포함된 서브키가 어떤것들인지 화면에 출력해 주는 기능을 가졌습니다.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey)

;
    return TRUE;
}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 49

안녕하세요………….돌팔이 황동준입니다…………………

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

제가 만든 두개의 함수만 알아 보면 되겠죠?

BOOL SetRegistry(void)
{

이 함수는 우리가 어떤 서브키가 있는지 테스트해보기 위해 임시로 서브키를 만드는 기능을 합니다.

    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

어떤 서브키를 만드는지 굳이 설명드릴 필요는 없죠? 바로 Install이라는 서브키를 만들고 Install이라는 네임에 c:\doran20이라는 데이터를 저장하고 있는 구문입니다.

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;

앞에서 만든 DORAN 20이라는 서브키에 다시 Version이라는 서브키를 생성하고 네임에 Version을 그 네임에 해당하는 데이터는 2.0을 지정하는 구문입니다.

}

앞에서 한부분이므로 이해가 안가는 부분은 없을 겁니다. 위 함수가 수행되면 DORAN 20이라는 서브키에 Install, Version이라는 서브키가 생성되어 있을 겁니다.

BOOL PrintSubRegistry(HWND hWnd)
{

이 함수는 DORAN 20 서브키에 속해 있는 서브키를 찾아서 화면에 출력해주는 역할을 합니다.

    HDC hDC;
    HKEY hKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwSubKeySize = 80;
    FILETIME FileTime;
    static char szSubKey[80];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);

    if(lRet != ERROR_SUCCESS)
        return FALSE;

알아 볼 서브키가 포함되어 있는 부모 키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

이번에 알아 볼것은 단지 서브키의 개수이므로 데이터의 개수가 저장될 파라미터에는 NULL을 지정하였습니다.

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;

서브키의 개수만큼 인덱스를 증가하기 위해 위 dwCount라는 변수를 사용하였습니다.

    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

인덱스에 해당하는 서브키를 얻고 있는 구문입니다.

        dwSubKeySize = 80;

위 함수가 수행되면 dwSubKeySize에 실제 크기가 저장되므로 이 값을 다시 초기화해주고 있습니다. 왜냐하면 위 함수를 사용할 때 버퍼의 크기가 저장된 변수의 주소를 지정하기 때문이죠.

        hDC = GetDC(hWnd);
        TextOut(hDC, 0, 20*dwCount, szSubKey, strlen(szSubKey));
        ReleaseDC(hWnd, hDC);

얻은 서브키를 화면에 출력해 주는 구문입니다.

        dwCount++;

인덱스를 증가하고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문을 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

자 그러면 이번에는 좀더 진보적인 프로그램을 만들어 봅시다. 바로 서브키만 알아내는 것이 아니라 그 안에 속해있는 네임과 데이터도 알아내는 거죠.

MyMenu MENU
BEGIN
    POPUP "&Registry"
    BEGIN
        MENUITEM "&Set Registry", 100
        MENUITEM "&Print Registry", 200
    END
END

#include <windows.h>
#include <string.h>
#include <stdio.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL SetRegistry(void);
BOOL PrintSubRegistry(HWND hWnd);

int WINAPI WinMain
(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszArg, int nCmdShow)
{
        static char szAppName[] = "Registry Example";
        HWND hWnd;
        MSG msg;
        WNDCLASS WndClass;

        WndClass.style = CS_HREDRAW|CS_VREDRAW;
        WndClass.lpfnWndProc = WndProc;
        WndClass.cbClsExtra = 0;
        WndClass.cbWndExtra = 0;
        WndClass.hInstance = hInstance;
        WndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
        WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
        WndClass.lpszMenuName = "MyMenu";
        WndClass.lpszClassName = szAppName;
        if(!RegisterClass(&WndClass))
                return FALSE;

        hWnd = CreateWindow(
                szAppName,
                szAppName,
                WS_OVERLAPPEDWINDOW,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                CW_USEDEFAULT,
                NULL,
                NULL,
                hInstance,
                NULL
        );

        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);

        while(GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        return msg.wParam;
}

LRESULT CALLBACK
WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
        static char szBuff[80];

        switch(message)
        {
            case WM_COMMAND :

                switch(LOWORD(wParam))
                {
                    case 100 :

                        if(!SetRegistry())
                            MessageBox(hWnd, "Create Fail", "Error", MB_OK);
                        break;

                    case 200 :

                        if(!PrintSubRegistry(hWnd))
                            MessageBox(hWnd, "Print Fail", "Error", MB_OK);
                        break;
                }
                return 0;

            case WM_DESTROY :

                PostQuitMessage(0);
                return 0;
        }
        return DefWindowProc(hWnd, message, wParam, lParam);
}

BOOL SetRegistry(void)
{
    HKEY hKey;
    DWORD dwState;
    LONG lRet;

    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Install",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Install", NULL, REG_SZ,
        (unsigned char *)"c:\\doran20", strlen("c:\\doran20"));
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    lRet = RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20\\Version",
        NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey,
            &dwState);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegSetValueEx(hKey, "Version", NULL, REG_SZ,
        (unsigned char *)"2.0", strlen("2.0"));

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    RegCloseKey(hKey);
    return TRUE;
}

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;
    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);
    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }
    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);
        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);
        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);
        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {
            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);
            dwNameSize = dwValueSize = 80;
            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;
            dwValueCount++;
        }
        RegCloseKey(hSubKey);
        dwCount++;
    } while(dwCount<dwSubKeyNumber);
    RegCloseKey(hKey);
    return TRUE;

}

오늘은 여기까지 끝~~~~~~~~~~~~~~~~~~~~~~~~~~

 

  윈도우 32비트 프로그래밍 50

안녕하세요…………..돌팔이 황동준입니다………….

이번시간에는 저번시간에 만든 프로그램을 분석해 보겠습니다.

이번에는 함수 하나만 알아보면 되겠네요.

BOOL PrintSubRegistry(HWND hWnd)
{
    HDC hDC;
    HKEY hKey, hSubKey;
    LONG lRet;
    DWORD dwSubKeyNumber, dwCount, dwValueNumber, dwValueCount,
        dwSubKeySize = 80;
    DWORD dwNameSize = 80, dwValueSize = 80;
    FILETIME FileTime;
    int n=0;
    static char szSubKey[80], szName[80], szValue[80];
    static char szBuff[80], szPrint[256];

    lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\DORAN 20", 0,
        KEY_ALL_ACCESS, &hKey);
    if(lRet != ERROR_SUCCESS)
        return FALSE;

정보를 알아볼 서브키의 루크키를 여는 구문입니다.

    lRet = RegQueryInfoKey(hKey, NULL, 0, 0, &dwSubKeyNumber, NULL, NULL,
        NULL, NULL, NULL, NULL, &FileTime);

    if(lRet != ERROR_SUCCESS)
    {
        RegCloseKey(hKey);
        return FALSE;
    }

서브키의 개수를 알아내고 있습니다.

    dwCount = 0;
    do
    {
        RegEnumKeyEx(hKey, dwCount, szSubKey, &dwSubKeySize, NULL,
            NULL, 0, &FileTime);

서브키를 알아내고 있는 구문입니다.

        dwSubKeySize = 80;
        strcpy(szBuff, "Software\\DORAN 20");
        strcat(szBuff, "\\");
        strcat(szBuff, szSubKey);

        lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuff, 0, KEY_ALL_ACCESS,
            &hSubKey);

알아낸 서브키를 열고 있는 구문입니다.

        lRet = RegQueryInfoKey(hSubKey, NULL, 0, 0, NULL, NULL, NULL,
            &dwValueNumber, NULL, NULL, NULL, &FileTime);

그 서브키가 가지는 데이터의 개수를 얻고 있습니다.

        dwValueCount = 0;
        while(dwValueNumber > dwValueCount)
        {

데이터의 개수만큼 루프문을 반복하고 있습니다.

            RegEnumValue(hSubKey, dwValueCount, szName, &dwNameSize, NULL,
                NULL, (unsigned char *)szValue, &dwValueSize);

위 함수로 인덱스에 해당하는 네임과 데이터를 얻을 수 있는데 위에서 이 함수에 대해 설명드리지 않았군요.

LONG RegEnumValue(
    HKEY  hkey,
    DWORD  iValue,
    LPTSTR  lpszValue,
    LPDWORD  lpcchValue,
    LPDWORD  lpdwReserved,
    LPDWORD  lpdwType,
    LPBYTE  lpbData,
    LPDWORD  lpcbData
   );   

첫번째 파라미터에는 얻을 네임이 포함되어 있는 서브키의 핸들을 지정해 주면됩니다. 두번째 파라미터에는 네임의 인덱스를 지정해 주면 되는데 역시 0부터 지정해주면 됩니다. 세번째 파라미터에는 네임이 저장될 버퍼를 네번째 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다. 다섯 번째 파라미터에는 NULL을 지정하면 되고 여섯번째 파라미터에는 데이터의 타입이 저장될 변수의 주소를 지정해 주면 됩니다. 이 데이터 타입은 앞에서 이미 알아 본 것입니다. 아래와 같은 예약어들이 함수 사용한 후에 저장되겠군요.

REG_BINARY
REG_DWORD
REG_LINK
REG_NONE
REG_SZ

그 의미는 이미 앞에서 설명드렸을 겁니다.

일곱번째 파라미터에는 데이터가 저장될 버퍼를 지정하면 되고 마지막 파라미터에는 이 버퍼의 크기가 저장되어 있는 변수의 주소를 지정해 주면 됩니다.

함수 설명을 했으니 계속해서 해당 루틴을 알아 보도록 하겠습니다.

            dwNameSize = dwValueSize = 80;

역시 길이를 초기화 해주고 있습니다.

            sprintf(szPrint, "%s : %s", szName, szValue);
            hDC = GetDC(hWnd);
            TextOut(hDC, 0, 20*n, szPrint, strlen(szPrint));
            ReleaseDC(hWnd, hDC);
            n++;

얻은 네임과 데이터를 화면에 출력해 주고 있습니다.

            dwValueCount++;

네임에 대한 인덱스를 증가해주고 있습니다.

        }
        RegCloseKey(hSubKey);
        dwCount++;

서브키에 대한 인덱스를 증가해주고 있습니다.

    } while(dwCount<dwSubKeyNumber);

서브키의 개수만큼 루프문이 돌고 있습니다.

    RegCloseKey(hKey);
    return TRUE;
}

Permalink 댓글 남기기

Next page »