'3. Implementation/Windows API'에 해당되는 글 40건

  1. 2008.11.06 Testing for the End of a File
  2. 2008.10.24 Using Timer Queues
  3. 2008.09.30 Just Enough Assembly Language to Get By
  4. 2008.09.21 HowTo: Export C++ classes from a DLL
  5. 2008.09.18 포기된 뮤텍스 (Abandoned Mutex & WAIT_ABANDONED)
  6. 2008.09.04 InterProcess Communications (IPC)
  7. 2008.08.27 WAIT_ABANDONED_0 에 대해서..
  8. 2008.08.21 ANSI 와 UNICODE 사이의 변환 클래스
  9. 2008.08.20 Win32에서 TRACE 매크로 문
  10. 2008.08.11 Whats the difference between Control.Invalidate, Control.Update and Control.Refresh?
  11. 2008.08.10 정규 DLL Implicit, Explicit 링킹에 대한 몇가지 잡담
  12. 2008.07.15 ActiveX와 Application에서 Mutex 문제
  13. 2008.07.07 About Mouse
  14. 2008.07.06 About Keyboard
  15. 2008.07.05 About Windows
  16. 2008.07.05 Visual C++ Debugging 관련 MSDN 정리
  17. 2008.07.05 좌표공간과 변환
  18. 2008.07.05 크리티컬 섹션 초간단 클래스
  19. 2008.07.05 정규 DLL Implicit, Explicit 링킹에 대한 몇가지 잡담
  20. 2008.07.05 Using Layered Windows
2008. 11. 6. 08:50

Testing for the End of a File

The ReadFile function checks for the end-of-file condition (eof) differently for synchronous and asynchronous read operations. When a synchronous read operation reaches the end of a file, ReadFile returns TRUE and sets the variable pointed to by lpNumberOfBytesRead to zero. An asynchronous read operation can encounter the end of a file during the initiating call to ReadFile or during subsequent asynchronous operation.

The following example test for the end of a file during a synchronous read operation.

// Attempt a synchronous read operation
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL);

// Check for eof
if (bResult &&  nBytesRead == 0, ) 
{
    // At the end of the file
}

The test for end-of-file during an asynchronous read operation is more difficult. There are three end-of-file indicators for asynchronous read operations:

  • ReadFile returns FALSE and GetLastError returns ERROR_HANDLE_EOF.
  • ReadFile returns FALSE and GetLastError returns ERROR_IO_PENDING.
  • GetOverlappedResult returns FALSE and GetLastError returns ERROR_HANDLE_EOF.

The following example shows how to test for an end-of-file during an asynchronous read operation:

// Attempt to initiate an asynchronous read operation.
bResult = ReadFile(hFile, &inBuffer, nBytesToRead, &nBytesRead, NULL);

// Check if there was a problem.
if (!bResult) 
{
    switch (dwError = GetLastError()) 
    {
        case ERROR_HANDLE_EOF: 
        // At the end of the file.
            break;
        case ERROR_IO_PENDING: 
        // I/O pending.
           break;
    }
}

// Check on an asynchronous read operation.
bResult = GetOverlappedResult(hFile, &gOverlapped, &nBytesRead, TRUE);

// Check if there was a problem.
if (!bResult) 
{
    switch (dwError = GetLastError()) 
    {
        case ERROR_HANDLE_EOF:
        // At the end of the file
    }
}

From : MSDN
2008. 10. 24. 21:53

Using Timer Queues


The following example creates a timer routine that will be executed by a timer-queue thread after a 10 second delay. First, the code uses the CreateEvent function to create an event object that is signaled when the timer-queue thread completes. Then it creates a timer queue and a timer-queue timer, using the CreateTimerQueue and CreateTimerQueueTimer functions, respectively. The code uses the WaitForSingleObject function to determine when the timer routine has completed. Finally, the code calls DeleteTimerQueue to clean up.

For more information on the timer routine, see WaitOrTimerCallback.

 

#include <windows.h>

#include <stdio.h>

 

HANDLE gDoneEvent;

 

VOID CALLBACK TimerRoutine(PVOID lpParam, BOOL TimerOrWaitFired)

{

    if (lpParam == NULL)

    {

        printf("TimerRoutine lpParam is NULL\n");

    }

    else

    {

        // lpParam points to the argument; in this case it is an int

 

        printf("Timer routine called. Parameter is %d.\n",

                *(int*)lpParam);

    }

 

    SetEvent(gDoneEvent);

}

 

int main()

{

    HANDLE hTimer = NULL;

    HANDLE hTimerQueue = NULL;

    int arg = 123;

 

    // Use an event object to track the TimerRoutine execution

    gDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

    if (!gDoneEvent)

    {

        printf("CreateEvent failed (%d)\n", GetLastError());

        return 1;

    }

 

    // Create the timer queue.

    hTimerQueue = CreateTimerQueue();

    if (!hTimerQueue)

    {

        printf("CreateTimerQueue failed (%d)\n", GetLastError());

        return 2;

    }

 

    // Set a timer to call the timer routine in 10 seconds.

    if (!CreateTimerQueueTimer(

        &hTimer, hTimerQueue, TimerRoutine, &arg , 10000, 0, 0))

    {

        printf("CreateTimerQueueTimer failed (%d)\n", GetLastError());

        return 3;

    }

 

    // TODO: Do other useful work here

 

    printf("Call timer routine in 10 seconds...\n");

 

    // Wait for the timer-queue thread to complete using an event

    // object. The thread will signal the event at that time.

 

    if (WaitForSingleObject(gDoneEvent, INFINITE) != WAIT_OBJECT_0)

        printf("WaitForSingleObject failed (%d)\n", GetLastError());

 

    // Delete all timers in the timer queue.

    if (!DeleteTimerQueue(hTimerQueue))

        printf("DeleteTimerQueue failed (%d)\n", GetLastError());

 

    return 0;

}

 

FROM : MSDN

2008. 9. 30. 22:37

Just Enough Assembly Language to Get By

At a recent lunch troika of MSJ columnists (Paul DiLascia, John Robbins, and me), we were commenting on how so few of today's programmers are skilled in what was essential knowledge just a few years ago. For instance, we all agreed that many programmers lack even a basic understanding of assembly language. In the idealized world presented by most language vendors, coding is so easy that there are no bugs to speak of. And if there ever was a bug, you'd certainly be able to find it easily. No need to resort to messy instruction-by-instruction code slogging, no sir.
Contrast that utopian vision with your own experience. How many times have you been in your debugger stepping through somebody else's code in assembly language because there's no source available? This is especially annoying when some third-party component blows up and you're assigned to track down the problem. Even when debugging your own code, knowing a little assembly language can help you figure out why your high-level language code isn't working the way you think it should. Just put the debugger into mixed source/assembly mode and observe how the compiler translated your code into machine instructions.
Paul DiLascia observed that there's a big difference between programming in assembler and knowing just enough to get by in a pinch while debugging. He jokingly suggested an "assembly language survival guide" that would cover just enough to debug the most common situations. Sounds like a darn good idea to me, so this column presents "Matt's Just Enough Assembly Language to Get By." Think of it as a cram course in Intel x86 assembly language, with all of the esoteric stuff omitted. Afterward, I'll show the assembler code for a typical procedure, and show how its operations can be inferred by the instructions I've covered.
Before jumping into the various instructions and instruction sequences, let me add a couple of prefaces and warnings. First, I'm going to describe only 32-bit Intel code. If you're still stuck programming in 16-bit land, my sympathies. Second, different compilers from different vendors generate different code. However, what I describe here should apply to all compilers (including Visual Basic
®
5.0 when generating native code.)
Third, don't be surprised if you encounter instructions and instruction sequences that aren't mentioned below. Most compilers use only a small fraction of the instruction set available to them (at least on the Intel platform). But many compilers support inlining of raw assembly language. This allows assembly language gurus to use CPU instructions that the compiler isn't aware of. An inline assembler may be used to optimize a particular sequence, or it may be used to get at CPU-specific instructions such as the timers available on Pentium-class CPUs. In addition to inline assembly code, don't forget that programmers sometimes write entire source modules in assembly language—hard to believe, isn't it?
Just as most 32-bit compilers use only a small fraction of the available instructions, they also use only a subset of the registers of the CPU. Since so much of what I'll describe depends on the registers, a quick review of the commonly used Intel x86 register set is in order. In
Figure 1, all registers are 32 bits except where noted. "Multipurpose" means the register can hold any arbitrary 32-bit value (for example, literal values, addresses, and bit flags).

 

Figure 1    Common Intel x86 Registers

 

EAX

Multipurpose. Return values from a function are usually stored in EAX. Low 16 bits are referenced as AX. AX can be further subdivided into AL (the low 8 bits), and AH (the upper 8 bits of AX).

EBX

Multipurpose. Low 16 bits are referenced as BX. BX can be further subdivided into BL (the low 8 bits), and BH (the upper 8 bits of BX).

ECX

Multipurpose. Often used as a counter, for example, to hold the number of loop iterations that should be performed. Low 16 bits are referenced as CX. CX can be further subdivided into CL (the low 8 bits), and CH (the upper 8 bits of CX).

EDX

Multipurpose. Low 16 bits are referenced as DX. DX can be further subdivided into DL (the low 8 bits), and DH (the upper 8 bits of DX).

ESI

Multipurpose. In certain operations that move or compare memory, ESI contains the source address. Low 16 bits are referenced as SI.

EDI

Multipurpose. In certain operations that move or compare memory, EDI contains the destination address. Low 16 bits are referenced as DI.

ESP

Stack pointer. Implicitly changed by PUSH, POP, CALL, and RET instructions.

EBP

Base pointer. Usually points to the current stack frame for a procedure. Procedure parameters are usually at positive offsets from EBP (for example, EBP+8). Local variables are usually at negative offsets (for example, EBP-16). Sometimes, optimizing compilers won't use a stack frame, and use EBP as a multipurpose register.

EFLAGS

Rarely directly referenced. Instead, instructions implicitly set or clear bitfields within the EFLAGS register to represent a certain state. For example, when the result of a mathematical operation is zero, the Zero flag is toggled on in the EFLAGS register. The conditional jump instructions make use of the EFLAGS register.

FS

16-bit. Under Win32, the FS register points to a data structure with information pertaining to the current thread. FS is a segment register (segment registers are beyond the scope of this discussion). Intel CPUs have six segment registers, but the operating system sets them up and maintains them. Win32 compilers only need to explicitly refer to the FS segment register, which is used for things like structured exception handling and thread local storage.


In addition to being familiar with the registers, it's essential to understand how instruction arguments are used. With the exception of a few obscure cases, all instructions take zero, one, or two arguments. Instructions that take zero or one arguments don't require explanation. For instructions that take two arguments, the first argument is usually the destination, while the second is the source. For example, the "ADD EAX,ESI" instruction adds the contents of the ESI (the source) to EAX. The result is stored in EAX (the destination). Put another way, the first argument is the one that's modified as a result of the instruction.
A basic knowledge of how instructions reference memory is also vital. Some instructions implicitly reference memory. For example, PUSH EAX pushes the current value of the EAX register onto the stack. Where's the stack? It's whatever the ESP register is currently pointing to. Likewise, instructions like SCASB require that the ESI and/or EDI registers contain the address of the memory location you want to use.
Other instructions use arguments to explicitly state the address to be used. You can usually tell this by the presence of square brackets in the instruction. For example, "MOV EBX,[00401234]" reads from the address 0x00401234. Another form of addressing uses registers and possibly offsets. For example, in "MOV EBX,[ECX]", the ECX register contains an address (also known as a pointer by C++ users). The instruction "MOV EBX,[EBP+8]" reads from the address calculated by adding 8 to the contents of the EBP register.
Intel CPUs have a very formal definition for allowable forms of instruction addresses. It's complex enough to make most people's heads swim. If you know what a modR/M byte is, or know how S-I-B addressing works, then you already know more than this column can teach you. In the "Just Enough to Get By" guide, the preceding paragraph should be enough.
With the theory part over with, let's now look at the most common instructions and instruction sequences. I've grouped them into several categories rather than sorting them alphabetically. As you'll see, some instructions are used in multiple categories.

 

Procedure Entry and Exit


These instructions are automatically inserted by the compiler to create a standard method for accessing parameters and local variables. This method is called a stack frame, as in "frame of reference." In fact, the Intel CPU dedicates the EBP register to maintaining a stack frame. For this group of instructions, it's especially important to note that not every procedure will use exactly the same sequence, and that certain things may be omitted entirely.

Sequence PUSH EBP / MOV EBP,ESP / SUB ESP,XX
Purpose Sets up the EBP stack frame for a new procedure
Examples

  PUSH    EBP

  MOV     EBP, ESP

  SUB     ESP, 24

Description "PUSH EBP" saves the previous frame pointer on the stack. "MOV EBP,ESP" sets the EBP register to the same value as the stack pointer (ESP). "SUB ESP,XX" creates space for local variables below the EBP frame.
In optimized code, you may see this sequence interspersed with other instructions (for example, "PUSH ESI"). Since "PUSH EBP" and "MOV EBP,ESP" both use the EBP register, a processor with multiple pipelines would ordinarily need to stall one of the pipelines. By interspersing other instructions that don't use the EBP register, the processor can do more work in the same amount of time.

Instruction ENTER
Purpose Sets up the EBP stack frame for a new procedure
Examples

  ENTER 8, 0 ; Sets up stack frame with

             ; 8 bytes of local variables

Description The ENTER instruction first became available on the 80286 processor. It was intended to replace the "PUSH EBP / MOV EBP,ESP / SUB ESP,XX" sequence with a single, smaller instruction. On current processors the ENTER instruction is slower than the three-instruction sequence, so ENTER is rarely used.

Sequence MOVE ESP,EBP / POP EBP
Purpose Removes the EBP stack frame before leaving a procedure
Description The "MOV ESP,EBP" instruction bumps up the stack pointer past any space allocated for local variables on the stack. "POP EBP" restores the stack frame pointer to point at the previous EBP frame. This sequence is normally followed by a return instruction to return control to the calling procedure.

Instruction LEAVE
Purpose Removes the EBP stack frame before leaving
Description The LEAVE instruction is the inverse of the ENTER instruction. It can also be used to remove a frame set up by the "PUSH EBP / MOV EBP,ESP" sequence. The LEAVE instruction is only 1 byte long, which is smaller than the longer "MOV ESP,EBP / POP EBP" sequence. Unlike the ENTER instruction, there's no performance penalty for using it, so some compilers use LEAVE.

Instruction PUSH register
Purpose Saves the previous values of register variables
Examples

  PUSH EBX

  PUSH ESI

  PUSH EDI

Description Sometimes compilers use a general-purpose register to hold the value of parameters or local variables. This can be more efficient than storing the same value in memory. These are commonly known as register variables. The EBX, ESI, and EDI registers are most often used as register variables.
The convention most compilers use is that register variable values are preserved across procedure calls. If the compiler decides to use register variables in a procedure, it is responsible for preserving the value of the registers that it alters (typically, EBX, ESI, and EDI). Typically, compilers preserve these register values on the stack as part of setting up the procedure's stack frame. If the compiler uses only one or two of the aforementioned registers, it needs to preserve only those registers.

Instruction POP register
Purpose Restores the previous values of register variables
Examples

  POP EDI

  POP ESI

  POP EBX

Description In preparing to return from a procedure, the register variable registers need to be restored to their previous values. These instructions remove a value from the stack and place it into the designated register.

 

Accessing Variables


The Intel CPU has many instructions that work with variables, which are just locations in memory. For example, you can add or subtract from a variable representing a counter. Likewise, a variable may contain a pointer to something. There are just too many instructions to describe here, and in most cases the instruction name gives a good clue about what the instruction is doing. However, I will show how variables of different storage classes appear in assembly language.

Instruction instruction [global]
Purpose Global/static variables
Examples

  MOV EAX,[00401234]

  MOV [00401238],ESI

  PUSH [77852432]

  ADD [00620428],00001000

Description When you see an instruction that includes an actual machine address inside the square brackets, it's accessing memory that was declared as either a global or static variable. These addresses are known at program load time, so the instruction contains the actual memory address to read or write.

Instruction instruction [parameter]
Purpose Procedure parameters and this pointers
Examples

  MOV ESI,[EBP+14]

  MOV [ESP+30],EAX

  ADD [EBP+0C],2

  OR  [ESP+20],00000010

Description Parameters to procedures are usually passed on the thread's stack. Since these values are pushed before the procedure call and before the called procedure sets up its stack frame, the parameters appear at positive offsets from the stack frame base pointer (EBP). Just about any instruction that makes reference to memory above EBP (for example, "[EBP+8]") is making use of a procedure parameter. The advantage of using EBP for accessing parameters is that EBP doesn't change throughout the lifetime of a procedure. This makes it easier to keep track of the procedure's parameters.
Prior to the 80386, the only effective way to access parameters was with the base pointer register. The 386 added the ability to access memory just as easily with displacements from the stack pointer (ESP) register. Thus, optimized code can dispense with setting up an EBP frame and still reference parameters by using positive offsets from ESP. For example, "ADD [ESP+20],4" adds four to whatever DWORD is at [ESP+20]. From a debugging standpoint, using ESP to access parameters is inconvenient. Since ESP can change during a procedure, a given parameter may be at different offsets from ESP at different points in a procedure's code.
One last word on parameters. In C++, the this pointer of a member function is really a hidden parameter. Usually the this pointer is the last parameter pushed on the stack before the call. In Visual Basic, the self-referential me is the same thing as the C++ this pointer.

Instruction instruction [local]
Purpose Local Variables
Examples

  MOV ESI,[EBP-14]

  MOV [EBP-30],EAX

  SUB [ESP],2

  AND [ESP+4],00000010

Description From the vantage point of an assembly instruction, local variables aren't much different than parameters when an EBP frame is used. The only distinction is that local variables are at negative offsets from the EBP stack frame. You can get an idea of how big the sandbox for local variables will be by examining the "SUB ESP,XX" instruction near the beginning of the procedure.
Things do get messy when the compiler decides to omit an EBP frame. When this happens, the compiler addresses both local variables and parameters as positive offsets from the ESP register. There's no good way to tell a local apart from a parameter in this situation except to find out how much space the procedure has allocated for locals (see above). If the offset is less than the space allocated, it's a local. Otherwise, it's probably a parameter.

Instruction LEA variable
Purpose Load Effective Address
Examples

  LEA EAX,[ESP+14]

  LEA EDX,[EBP-24]

Description Despite the square brackets, LEA doesn't actually read memory or dereference a pointer. Instead, it loads the first operand with an address specified by the second parameter. For example, "LEA EAX,[ESP+14]" takes the current value of the ESP register, adds 14 to it, and puts the result in EAX.
LEA's primary use is to obtain the address of local variables and parameters. For example, in C++, if you use the & operator on a local variable or parameter, the compiler will likely generate an LEA instruction. As another example, "LEA EAX,[EBP-8]" loads EAX with the address of the local variable at EBP-8.
A less obvious use of LEA is as a fast multiplication. For example, multiplying a value by 5 is relatively expensive. Using "LEA EAX,[EAX*4+EAX]" turns out to be faster than the MUL instruction. The LEA instruction uses hardwired address generation tables that makes multiplying by a select set of numbers very fast (for example, multiplying by 3, 5, and 9). Twisted, but true.

 

Calling Procedures

Instruction CALL location
Purpose Transfer control to another procedure
Examples

  CALL 00682568

  CALL [00401234]

  CALL ESI

  CALL [EAX+24]

Description The CALL instruction doesn't need much explanation in itself. It pushes the address of the instruction following it onto the stack, then transfers control to the address given by the argument. The various ways of specifying a target address are worth mentioning, however.
The simplest form of the CALL instruction is when the argument contains the destination address as an immediate value (for example, "CALL 00682568"). This type of call is almost always to another location within the same module (EXE or DLL). Slightly more complicated is when the CALL instruction indirects through an address (for example, "CALL [00401234]"). You'll see this form of CALL instruction when calling a function imported from another module. It's also seen when calling through a function pointer stored in a global variable.
Two other forms of CALL instruction use registers as part of their address. If just a register name is specified (for example, "CALL ESI"), the CPU transfers to whatever address is in the register. If a register is used within brackets, perhaps with an additional displacement ("CALL [EAX+24]"), the instruction is calling through a table of function addresses. Where would these come from? You may know these tables by the more familiar name of vtables. In the preceding instruction example, the sixth member function is being called. (24 divided by the size of a DWORD is 6.)

Instruction PUSH value
Purpose Places a parameter onto the stack in preparation for calling procedure
Examples

  PUSH [00405234]    ; Push a global variable

  PUSH [EBP+C]       ; Push a parameter

  PUSH [EBP-14]      ; Push a local variable

  PUSH EAX           ; Push whatever is in EAX

  PUSH 12345678      ; Push an immediate value.

Description When it comes to passing parameters, all variations of the PUSH instruction are used by the compiler. Global variables, local variables, parameters, the results of a calculation, and immediate values can all be passed with a single instruction. When you see a sequence of PUSH instructions prior to a CALL instruction, the odds are good that the PUSHes are putting the parameters onto the stack.
As mentioned earlier, if a member function or method is being called, the this or me pointer is usually passed last. In some cases, the this pointer is passed in the ECX register instead. You can identify when this occurs by looking for code that initializes the ECX register and then does nothing with it before the CALL instruction.

Instruction RET
Purpose Return from a procedure call
Examples

  RET

  RET 8

Description The RET instruction returns from a procedure call. It simply pops whatever value is currently at [ESP] into the EIP (instruction pointer) register. The "RET XX" form does the same thing, and then adds XX to the ESP value. This is how __stdcall procedures clear parameters off the stack before returning to their caller. (Most Win32® APIs are __stdcall based.) By dividing the number of cleared bytes by four (the size of a DWORD), you can usually figure out how many parameters a procedure takes. For instance, a procedure that returns with a "RET 8" instruction takes two parameters.
Functions that return an integer or pointer value usually return the value in the EAX register. By examining what's in EAX before executing the RET instruction, you can see the function's return value.

Instruction ADD ESP, value
Purpose Removes parameters off the stack
Examples

  ADD ESP,24

Description When calling procedures that don't remove parameters before returning, it's up to the calling function to remove its parameters. This is the case with cdecl functions, which is the default for C and C++ code. The "ADD ESP,XX" function bumps up the stack pointer so that any passed parameters are below the resulting ESP.
If the function doesn't take a variable number of parameters, the "ADD ESP,XX" instruction gives insight to how many parameters the called procedure accepts. (See the description above for "RET XX".) If the called procedure takes a variable number of parameters (like printf and wsprintf do), the "ADD ESP,XX" instruction tells you how many parameters were passed for that particular CALL.

 

Flow Control


In the context of this column, flow control means code that affects which portions of a program's code are subsequently executed. At the simplest level, this means conditional execution (colloquially known as if statements). More complex flow control sequences such as while loops and for statements are usually built from the lower-level if statement constructs. In one case though (the LOOP instruction), the processor has built-in knowledge of these higher-level language constructs.
Before I get to these instruction sequences, let me highlight two things that can easily trip you up. For starters, the term "Jcc" is used as a stand-in for any of the 16 conditional jump instructions. The cc means condition code.
More insidiously, there are several sets of Jcc instructions that are aliases for one another. For example, JZ (Jump if Zero flag set) is the same instruction as JE (Jump if Equal). Likewise, JNZ (Jump if Zero flag NOT set) is the same instruction as JNE (Jump if Not Equal). Unfortunately, some disassemblers use the JZ/JNZ form, while others use the JE/JNE form. Is this confusing? Yes! The moral of the story: be prepared to mentally substitute an aliased form of the instruction if it makes the code easier to understand.

Sequence CMP value, value / Jcc location
Purpose Compare two values, and branch accordingly

Examples

  CMP    EAX,2
  JE     10036728
 
  CMP    [EBP+20],1000
  JNE    00427824

Description The CMP instruction is used when two values are to be compared. The CMP instruction sets or clears a variety of flags, including the Zero, Sign, and Overflow flags. From this, a variety of Jcc instructions can then be used to branch accordingly. Most often, the JE and JNE instructions follow a CMP instruction.
The following C++ code sequence would be implemented with a CMP / JNE sequence:

 

if ( MyVariable == 2 )
 {
     // Whatever code you want
 }

If the CMP instruction determines that MyVariable isn't 2, the flag will be set so that the JNE instruction that follows will skip over the code in curly brackets.

Sequence TEST value, value / Jcc location
Purpose Determine if a bit is set, and branch accordingly
Examples

  TEST    EAX,EAX

  JNZ     00400124

 

  TEST    EDX,00400024

  JZ      77f85624

Description The TEST instruction does a logical AND of the two arguments, which sets or clears the Zero flag in the EFLAGS register. The next instruction (JZ or JNZ) does a jump to the target address if the Zero flag is set or cleared, depending on the instruction used. If the JZ/JNZ doesn't jump, execution continues at the following instruction.
This sequence is typically used to test one or more bits as part of an if statement. For example, this C++ code could be implemented using a "TEST / JZ" sequence.

 

if ( MyVariable & 0x00400024 )
 {
     // Whatever code you want
 }

If MyVariable has any of the same bitfields set as in the value 0x00400024, the Zero flag won't be set. This prevents the JZ instruction from jumping, and execution falls into the code in the curly brackets.

Instruction JMP location
Purpose Transfer control to some other location
Examples

  JMP 10047820

Description The unconditional JMP instruction occurs in at least three scenarios. The first is as part of an if/else clause. At the end of the code generated for the if clause, a JMP instruction transfers control past all the code in the else clause. Consider this code snippet:

 

if ( MyVariable == 2 )
 {
     // some code
     // JMP past "else" code
 }
 else
 {
     // some other code
 }

The second place where JMP instructions crop up is as part of a loop. At the end of the loop's code, some code sequence determines if it's time to break out of the loop. If the loop isn't finished, a JMP instruction transfers control back to the beginning of the loop's code.
The third scenario where you'll see JMP instructions is when a procedure has a common exit sequence. That is, no matter how many return statements there are in the procedure, there's only one spot in the code that cleans up the stack frame and returns. In this situation, a return statement in the middle of the procedure's code is implemented as a JMP to the common exit sequence code.
It's also possible that you'll encounter a JMP instruction from a goto statement. Fortunately, most programmers don't bother with goto's anymore. Finally, if you see a JMP instruction that simply jumps to the next instruction, you're probably in code that wasn't compiled with optimizations enabled.

Instructions LOOP, LOOPZ, LOOPNZ
Purpose Purpose Jump back to the beginning of a loop's code, if conditions are right
Examples

  LOOP     00401234

  LOOPZ    65432108

Description The LOOP instruction uses the contents of the ECX register as a counter. Each invocation of the LOOP instruction decrements the ECX register. In the simplest case, the LOOP instruction branches back to the beginning of the instruction sequence if ECX isn't zero. The LOOPZ and LOOPNZ only branch if ECX is nonzero, and the Zero flag in EFLAGS is set accordingly.
The C++ for loop construct can be implemented with the LOOP instruction if the number of iterations is known ahead of time. Before executing the actual code inside the loop, ECX is loaded with the number of iterations. At the end of the code inside the loop is a LOOP instruction. After the specified number of iterations, ECX becomes zero and the LOOP instruction doesn't branch.

 

Bitwise Manipulation


The bitwise instructions are used to turn individual bits on and off in a value. The value can be a global variable, a local variable, a parameter, or a register. Here, I'll show the two most common instructions, AND and OR. There's also an XOR instruction, but it's less commonly used.

Instruction AND value,bitfield
Purpose Performs a logical AND of the bitfields of two operands
Examples

  AND    EAX,00001000

  AND    [ESI+4],00000004

Description Unlike the TEST instruction (see above), the AND instruction actually modifies the destination operand. For example, in C++, the statement

 

MyVar &= 0x00010001;

could be implemented as:

 

AND [MyVar],00010001h

The AND instruction is also used to turn off particular bitfields. To do this, the desired bits to be turned off are set to the off (zero) state in the source operand. All of the bits to be left alone are set to true in the source operand.

Instruction OR value,bitfield
Purpose Performs a logical OR of the bitfields of the two operands
Examples

  OR EDX,10101010

  OR [EBP+24],00080000

Description The OR instruction is used to turn on one or more bits in the destination operand. For example, the value of WS_VISIBLE is 0x10000000. The following C++ statement

 

wndStyle |= WS_VISIBLE;

would translate to something like this:

 

OR [wndStyle],10000000h

String Manipulation


The string instructions allow sequences of consecutive memory locations to be processed without branching after every operation. The instructions are able to do this because the CPU dedicates two registers (ESI and EDI) to point at the source and/or destination locations. After every operation, these registers are incremented or decremented based upon the CPU's direction flag. In the real world, the registers are rarely decremented, so from here on out I'll just say "incremented."
When combined with the REP / REPE / REPNE class of instruction prefixes, very powerful code sequences can be implemented using only a single instruction. For example, with one string instruction, you can find the end of a null-terminated string (some setup and assembly required). In addition, the code executes much faster than if it were implemented as a series of instructions in a loop.

Instruction SCASB, SCASW, SCASD
Purpose Scan for a particular BYTE, WORD, or DWORD
Examples

  REPNE    SCASB

  REPZ     SCASD

Description These instructions scan consecutive locations in memory looking for a particular BYTE, WORD, or DWORD. Alternatively, they can be used to find the first occurrence of a value that's different from a target value. The BYTE, WORD, or DWORD target value is placed in the AL, AX, or EAX register. Each iteration of SCASx compares the contents of the AL/AX/EAX register to the memory pointed at by the EDI register, and sets EFLAGS accordingly. Afterwards, EDI is incremented.
To search for the zero byte in an ANSI string, the AL register should be set to zero and EDI should be set to the beginning of the string. The ECX register is set to the maximum number of bytes to search. Finally, the REPNE SCASB instruction executes. The REPNE prefix causes the SCASB instruction to execute until one of two conditions is met. If ECX is zero, no zero byte was found in the entire string. Alternatively, a zero byte was found, and EDI points to the next byte in memory.

Instruction CMPSB, CMBSW, CMPSD
Purpose Compares two strings in memory
Examples

REPE CMPSB

Description These instructions are used to compare the BYTEs, WORDs, or DWORDs pointed to by the ESI and EDI registers with the EFLAGS set appropriately after the comparison. Each iteration of the CMPSx instruction causes the ESI and EDI registers to increment by the appropriate amount (one, two, or four bytes).
It's not hard to see how the C++ memcmp function could be implemented by using the REPE prefix with the CMPSx instructions. The REPE prefix causes the CMPSx instruction to keep iterating while the two memory locations are equal and ECX is nonzero. The memcmp function could be implemented using "REPE CMPSB", although optimized code will use "REPE CMPSD" for the bulk of the string and "REPE CMPSB" for the last three or fewer bytes.

Instruction MOVSB, MOVSW, MOVSD
Purpose Moves BYTEs, WORDs, or DWORDs from the source string to the destination string
Examples

REP MOVSD

Description The MOVSx instructions copy memory pointed to by ESI into the memory pointed at by EDI. After each iteration, ESI and EDI are incremented. Typically, MOVSx is used with the REP prefix to copy a predetermined number of BYTEs, WORDs, or DWORDs. The number to copy is specified in the ECX register. The C++ memcpy function can be implemented using "REP MOVSB".

Instruction STOSB, STOSW, STOSD
Purpose Sets a series of BYTEs, WORDs, or DWORDs to a specified value
Examples

  REP STOSB

Description The STOSx instructions copy the value in AL, AX, or EAX into the memory pointed to by the EDI register. Typically, STOSB is used with the REP prefix to copy the number of bytes specified in the ECX register. The C++ memset function can be implemented with "REP STOSB", or by a combination of "REP STOSD" and "REP STOSB".

 

Miscellaneous


In this final group are random instructions that you'll often encounter. Of the list, "XOR EAX,EAX" is most prevalent.

Instruction XOR register, register
Purpose Sets a register's value to zero
Examples

  XOR EAX,EAX

Description Using the XOR instruction to zero out a register takes less space than the equivalent MOV instruction. For example, "MOV EAX,0" takes five bytes, while "XOR EAX,EAX" uses only two bytes. Is using XOR twisted? Yes. But after years of stepping through assembly code, you too will automatically substitute "zero out the register" when you see this instruction.

Instruction MOVZX DWORD value, byte or word value
Purpose Copies an unsigned value into a larger type
Examples

  MOVZX EAX,BYTE PTR [EBP+8]

  MOVZX EAX,WORD PTR [00451234]

Description In most languages, a value of a smaller type can be copied into or used in place of a larger type. For example, in C++ an unsigned char can be copied into an unsigned short (aka a WORD). Likewise, an unsigned short can be used where an unsigned long is expected. The compiler uses MOVZX (move with zero extend) to convert the smaller type into a larger type. In C++, BYTEs can be converted to WORDs or DWORDS, and WORDs can be converted to DWORDs.

Instruction MOVSX DWORD value, byte or word value
Purpose Copies a signed value into a larger type
Examples

  MOVSX EAX,BYTE PTR [EBP+8]

  MOVSX EAX,WORD PTR [77f81234]

Description In most languages, a value of a smaller type can be copied into or used in place of a larger type. For example, in C++ a char can be copied into a short. Likewise, a short can be used where a long is expected. The compiler uses MOVSX (move with sign bit extend) to convert the smaller type into a larger type. In C++, chars can be converted to shorts or longs, and shorts can be converted to longs.

Instructions: MOV EAX,FS:[0], MOV FS:[0],ESP
Purpose Establish a new structured exception handling frame
Examples

  MOV     EAX,FS:[00000000]

  Push    EAX

  MOV     FS:[00000000h],ESP

Description In Win32, the FS register points to the Thread Environment Block (TEB). A data structure unique for each thread, the TEB contains values that the system uses to control the thread. At offset 0 in the TEB is a pointer to the first node in the structured exception handling chain. When you see code that uses FS:[0], it's usually setting up or tearing down a try block.

Instruction MOV EAX,FS:[18]
Purpose Makes a linear pointer to the TEB
Examples

  MOV EAX,FS:[18]

  MOV EAX,[EAX+24]

Description The TEB is always pointed to by the FS register. To make code portable, it's helpful to use a flat, linear address for the TEB. The TEB's linear address can be found at offset 0x18 in the TEB. Code that reads from FS:[18] is preparing to read some other value from the TEB. Step through all three instructions in GetCurrentThreadId under Windows NT® to see this for yourself.

Instruction MOV ECX,FS:[2C]
Purpose Makes a pointer to the Thread Local Storage (TLS) array
Examples

  ECX,DWORD PTR FS:[0000002C]

  EDX,DWORD PTR [ECX+EAX*4]

Description At offset 0x2C in the TEB is a pointer to the TLS array for the thread. This array contains 64 DWORDs, each corresponding to a particular index value that would be passed to TlsGetValue. Code that uses FS:[2C] is using TLS.

 

To The Code!


To show many of the instructions and sequences that I've described, I wrote the InstructionDemo program. A quick look at the source code in Figure 2 shows that the two functions don't do anything worthwhile. But the code is well commented, pointing out the particular instruction or instruction sequence it's designed to produce.

 

Figure 2   InstructionDemo.CPP

 

//==========================================

 // Matt Pietrek

 // Microsoft Systems Journal, February 1998

 // Program: InstructionDemo.CPP

 // FILE: InstructionDemo.CPP

 //==========================================

 #define WIN32_LEAN_AND_MEAN

 #include <windows.h>

 #include <stdlib.h>

 #include <stdio.h>

 

 // Force these functions inline (/O2 would normally do this

 #pragma intrinsic( memset, strlen, strcmp )

 

 __declspec(thread) int tlsVariable = 0; // Make a thread local variable

 

 int g_myGlobalVariable;                 // Make a global variable

 

 void MySubProcedure( void );

 

 int main( int argc, char *argv[] )

 {

     char szBuffer[128];

     char *pszString = "Hello";

     unsigned long   localUnsignedLong = 2;

     unsigned char   localUnsignedChar = 2;

     long            localSignedLong = 2;

     char            localSignedChar = 2;

     int             i;

 

     g_myGlobalVariable = 0x12345678;        // Assignment to global

            

     localSignedLong = localSignedChar;      // signed type promotion

 

     // Conditional execution   

     if ( localUnsignedLong == 2 )

         localSignedLong = 1;

     else

         localSignedLong = 2;   

 

     // Using TEST

     if ( localUnsignedLong & 0x00040008 )

         i = 3;

 

     // AND'ing off bitfields

     localUnsignedLong &= 0x01020304;

 

     // OR'ing on bitfields 

     localSignedLong |= 0x05060708;

 

     // LOOP code   

     for ( i = 0; i < 4; i++ )

         localUnsignedLong += i;

 

     // Procedure invocation

     printf( "%u %u %08X %s", localUnsignedLong, argc, &argc, szBuffer );

 

     // Using STOSD / STOSB 

     memset( szBuffer, 0, sizeof(szBuffer) );

 

     // Using SCASB

     i = strlen( szBuffer );

 

     MySubProcedure( );

        

     return 0;

 }

 

 void MySubProcedure( void )

 {

     tlsVariable = 2;

    

     // Use of try/except code

     __try

     {

         g_myGlobalVariable = 2;

     }

     __except( EXCEPTION_EXECUTE_HANDLER )

     {

         g_myGlobalVariable = 4;

     }

 }

 


I compiled InstructionDemo.CPP with the following command line:

 

CL InstructionDemo.CPP

I then disassembled the relevant parts of the executable and annotated the listing. Above each instruction or sequence is the C++ code responsible for it (see Figure 3). This is similar to what the Developer Studio IDE does when you select "Go To Disassembly" in the source window. Many of the instructions don't need explanation, but it's worthwhile to point out a few things.

 

Figure 3   InstructionDemo Mixed Source and Assembly

 

int main( int argc, char *argv[] )

{

401000:    PUSH       EBP

401001:    MOV        EBP,ESP

401003:    SUB        ESP,00000098

401009:    PUSH       EDI

    char *pszString = "Hello";

40100A:    MOV        DWORD PTR [EBP-0000008C],00406030

 

    unsigned long     localUnsignedLong = 2;

401014:    MOV        DWORD PTR [EBP-00000088],00000002

 

    unsigned char     localUnsignedChar = 2;

40101E:    MOV        BYTE PTR [EBP-00000094],02

 

    long              localSignedLong = 2;

401025:    MOV         DWORD PTR [EBP-00000084],00000002

 

    char              localSignedChar = 2;

40102F:    MOV        BYTE PTR [EBP-00000098],02

 

    g_myGlobalVariable = 0x12345678;        // Assignment to global

401036:    MOV        DWORD PTR [004088E8],12345678

 

    localSignedLong = localSignedChar;      // signed type promotion

401040:    MOVSX      EAX,BYTE PTR [EBP-00000098]

401047:    MOV        DWORD PTR [EBP-00000084],EAX

 

    // Conditional execution   

    if ( localUnsignedLong == 2 )

40104D:    CMP        DWORD PTR [EBP-00000088],02

401054:    JNE        00401062

 

        localSignedLong = 1;

401056:    MOV        DWORD PTR [EBP-00000084],00000001

    else

401060:    JMP        0040106C

 

        localSignedLong = 2;   

401062:    MOV        DWORD PTR [EBP-00000084],00000002

 

    // Using TEST

    if ( localUnsignedLong & 0x00040008 )

40106C:    MOV        ECX,DWORD PTR [EBP-00000088]

401072:    AND        ECX,00040008

401078:    TEST       ECX,ECX

40107A:    JE         00401086

 

        i = 3;

40107C:    MOV        DWORD PTR [EBP-00000090],00000003

 

    // AND'ing off bitfields

    localUnsignedLong &= 0x01020304;

401086:    MOV        EDX,DWORD PTR [EBP-00000088]

40108C:    AND        EDX,01020304

401092:    MOV        DWORD PTR [EBP-00000088],EDX

 

    // OR'ing on bitfields   

    localSignedLong |= 0x05060708;

401098:    MOV        EAX,DWORD PTR [EBP-00000084]

40109E:    OR         EAX,05060708

4010A3:    MOV        DWORD PTR [EBP-00000084],EAX

 

    // LOOP code   

    for ( i = 0; i < 4; i++ )

4010A9:    MOV        DWORD PTR [EBP-00000090],00000000

4010B3:    JMP        004010C4

 

4010B5:    MOV        ECX,DWORD PTR [EBP-00000090]

4010BB:    ADD        ECX,01

4010BE:    MOV        DWORD PTR [EBP-00000090],ECX

4010C4:    CMP        DWORD PTR [EBP-00000090],04

4010CB:    JNL        004010E1

 

        localUnsignedLong += i;

4010CD:    MOV        EDX,DWORD PTR [EBP-00000088]

4010D3:    ADD        EDX,DWORD PTR [EBP-00000090]

4010D9:    MOV        DWORD PTR [EBP-00000088],EDX

4010DF:    JMP        004010B5

 

    // Procedure invocation

    printf( "%u %u %08X %s", localUnsignedLong, argc, &argc, szBuffer );

4010E1:    LEA        EAX,[EBP-80]

4010E4:    PUSH       EAX

4010E5:    LEA        ECX,[EBP+08]

4010E8:    PUSH       ECX

4010E9:    MOV        EDX,DWORD PTR [EBP+08]

4010EC:    PUSH       EDX

4010ED:    MOV        EAX,DWORD PTR [EBP-00000088]

4010F3:    PUSH       EAX

4010F4:    PUSH       00406038

4010F9:    CALL       004011C0

4010FE:    ADD        ESP,14

 

    // Using STOSD / STOSB   

    memset( szBuffer, 0, sizeof(szBuffer) );

401101:    MOV        ECX,00000020

401106:    XOR        EAX,EAX

401108:    LEA        EDI,[EBP-80]

40110B:    REP        STOSD

 

    // Using SCASB

    i = strlen( szBuffer );

40110D:    LEA        EDI,[EBP-80]

401110:    OR         ECX,FF

401113:    XOR        EAX,EAX

401115:    REPNE      SCASB

401117:    NOT        ECX

401119:    ADD        ECX,FF

40111C:    MOV        DWORD PTR [EBP-00000090],ECX

 

    MySubProcedure( );

401122:    CALL       0040112E

 

    return 0;

401127:    XOR        EAX,EAX

 

}

401129:    POP        EDI

40112A:    MOV        ESP,EBP

40112C:    POP        EBP

40112D:    RET

 

void MySubProcedure( void )

{

40112E:    PUSH       EBP

40112F:    MOV        EBP,ESP

401131:    PUSH       FF

401133:    PUSH       00405058

401138:    PUSH       004012F8

40113D:    MOV        EAX,FS:[00000000]

401143:    PUSH       EAX

401144:    MOV        DWORD PTR FS:[00000000],ESP

40114B:    SUB        ESP,08

40114E:    PUSH       EBX

40114F:    PUSH       ESI

401150:    PUSH       EDI

401151:    MOV        DWORD PTR [EBP-18],ESP

 

    tlsVariable = 2;

401154:    MOV        EAX,[004088EC]

401159:    MOV        ECX,DWORD PTR FS:[0000002C]

401160:    MOV        EDX,DWORD PTR [ECX+EAX*4]

401163:    MOV        DWORD PTR [EDX+00000004],00000002

 

    __try

    {

40116D:    MOV        DWORD PTR [EBP-04],00000000

 

        g_myGlobalVariable = 2;

401174:    MOV        DWORD PTR [004088E8],00000002

40117E:    MOV        DWORD PTR [EBP-04],FFFFFFFF

401185:    JMP        004011A1

 

    __except( EXCEPTION_EXECUTE_HANDLER )

401187:    MOV        EAX,00000001

40118C:    RET

 

40118D:    MOV        ESP,DWORD PTR [EBP-18]

 

        g_myGlobalVariable = 4;   

401190:    MOV        DWORD PTR [004088E8],00000004

 

    }

40119A:    MOV        DWORD PTR [EBP-04],FFFFFFFF

 

}

4011A1:    MOV        ECX,DWORD PTR [EBP-10]

4011A4:    MOV        DWORD PTR FS:[00000000],ECX

4011AB:    POP        EDI

4011AC:    POP        ESI

4011AD:    POP        EBX

4011AE:    MOV        ESP,EBP

4011B0:    POP        EBP

4011B1:    RET


First, examine the instructions at offset 0x401000. They're establishing the stack frame for the procedure, including creation of space below the frame for local variables. If you look throughout the procedure, you won't see the EBX and ESI registers used, so the stack frame only preserves the EDI register.
After a whole bunch of variable initialization instructions, notice that the signed type promotion (char to long) at offset 0x401040 requires two instructions. This is because (in the general case) the Intel architecture doesn't allow one instruction to reference two memory addresses. Therefore, the assignment must go through a register that acts as an intermediate location.
Also interesting is the if statement starting at offset 0x40104D. After the code that executes when the expression evaluates to TRUE, note the JMP instruction at offset 0x0x401060. This JMP instruction makes the CPU skip over all the code for the else clause. A bit later (at offset 0x40106C), another if statement uses the TEST instruction to see if bitfields are set. In that sequence, the compiler treats the ECX register as a private, unnamed local variable.
Examining the for loop at offset 0x4010A9 is interesting because of the way the compiler orders the initialization, termination condition, and post-iteration code. The MOV instruction at 0x4010A9 performs the initialization, and then control JMPs past the post-iteration code to get to the termination condition code. The termination condition code looks very similar to an if statement. If you understand what the code is doing here, you can see how a for statement could be rewritten using if and goto statements.
Starting at offset 0x4010E1, the code begins pushing parameters on the stack in preparation for calling printf. It's important to realize that the parameters are passed right to left. Note that there are two distinct LEA instructions. The first calculates the address of the szBuffer array, while the second calculates the address of the argc parameter. After the call to printf at offset 0x4010F9, the code cleans all the pushed parameters off the stack with the "ADD ESP,14" instruction.
In the MySubProcedure code starting at offset 0x40112E, the stack frame setup is considerably more complex than the prior procedure's. The instructions like "PUSH 00405058" and "MOV EAX,FS:[00000000]" are building a frame for the structured exception handler code that results from using __try. Also, this time the stack frame setup code preserves all the register variable registers (EBX, ESI, and EDI).
At offset 0x401154, the code modifies the TLS variable called tlsVariable. The "MOV ECX,DWORD PTR FS:[0000002C]" instruction loads the ECX register with a pointer to the array of 64 DWORDs that each thread uses for TLS. The next instruction uses an advanced addressing form to index into the array and read the slot corresponding to a particular TLS index. ECX contains the pointer to the array, while EAX contains the TLS index. The code multiplies EAX by four (the size of a DWORD), and adds it to the TLS array pointer.

Wrap-up


In the real world, you will no doubt encounter instructions beyond what I've described here. But now you should be familiar with most of the commonly used registers and how memory is addressed. You should be able to tell a local variable apart from a parameter. You should also be able to distinguish these type classes from global and TLS variables.
Beyond the basic theory, I've also shown a reasonably large subset of the instructions that Win32 compilers generate. It's unlikely that my introduction will enable you to start writing your code in MASM. Still, with this working knowledge, you can be more confident when your debugger takes you to dark, scary places in other people's code, especially when even the dim light of source code isn't available.

Read more about assembly language in the June 1998 installment of Under the Hood.

Have a question about programming in Windows? Send it to Matt at mpietrek@tiac.com

From : http://www.microsoft.com/msj/0298/hood0298.aspx


2008. 9. 21. 05:46

HowTo: Export C++ classes from a DLL





Contents

Introduction

Dynamic-Link libraries (DLL) are an integrated part of the Windows platform from its very beginning. DLLs allow encapsulation of a piece of functionality in a standalone module with an explicit list of C functions that are available for external users. In 1980’s, when Windows DLLs were introduced to the world, the only viable option to speak to broad development audience was C language. So, naturally, Windows DLLs exposed their functionality as C functions and data. Internally, a DLL may be implemented in any language, but in order to be used from other languages and environments, a DLL interface should fall back to the lowest common denominator – the C language.

Using the C interface does not automatically mean that a developer should give up object oriented approach. Even the C interface can be used for true object oriented programming, though it may be a tedious way of doing things. Unsurprisingly, the second most used programming language in the world, namely C++, could not help but to fall prey to the temptation of a DLL. However, opposite to the C language, where the binary interface between a caller and a callee is well-defined and widely accepted, in the C++ world, there is no recognized application binary interface (ABI). In practice, it means that binary code that is generated by a C++ compiler is not compatible with other C++ compilers. Moreover, the binary code of the same C++ compiler may be incompatible with other versions of this compiler. All this makes exporting C++ classes from a DLL quite an adventure.

The purpose of this article is to show several methods of exporting C++ classes from a DLL module. The source code demonstrates different techniques of exporting the imaginary Xyz object. The Xyz object is very simple, and has only one method: Foo.

Here is the diagram of the object Xyz:

Xyz

int Foo(int)

The implementation of the Xyz object is inside a DLL, which can be distributed to a wide range of clients. A user can access Xyz functionality by:

  • Using pure C
  • Using a regular C++ class
  • Using an abstract C++ interface

The source code consists of two projects:

  • XyzLibrary – a DLL library project
  • XyzExecutable – a Win32 console program that uses "XyzLibrary.dll"

The XyzLibrary project exports its code with the following handy macro:

#if defined(XYZLIBRARY_EXPORT) // inside DLL

#   define XYZAPI   __declspec(dllexport)

#else // outside DLL

#   define XYZAPI   __declspec(dllimport)

#endif  // XYZLIBRARY_EXPORT

The XYZLIBRARY_EXPORT symbol is defined only for the XyzLibrary project, so the XYZAPI macro expands into __declspec(dllexport) for the DLL build and into __declspec(dllimport) for the client build.

C Language Approach

Handles

The classic C language approach to object oriented programming is the usage of opaque pointers, i.e., handles. A user calls a function that creates an object internally, and returns a handle to that object. Then, the user calls various functions that accept the handle as a parameter and performs all kinds of operations on the object. A good example of the handle usage is the Win32 windowing API that uses an HWND handle to represent a window. The imaginary Xyz object is exported via a C interface, like this:

typedef tagXYZHANDLE {} * XYZHANDLE;

 

// Factory function that creates instances of the Xyz object.

XYZAPI XYZHANDLE APIENTRY GetXyz(VOID);

 

// Calls Xyz.Foo method.

XYZAPI INT APIENTRY XyzFoo(XYZHANDLE handle, INT n);

// Releases Xyz instance and frees resources.

XYZAPI VOID APIENTRY XyzRelease(XYZHANDLE handle);

 

// APIENTRY is defined as __stdcall in WinDef.h header.

Here is an example of how a client's C code might look like:

#include “XyzLibrary.h”

 

...

 

/* Create Xyz instance. */

XYZHANDLE hXyz = GetXyz();

 

if(hXyz)

{

    /* Call Xyz.Foo method. */

    XyzFoo(hXyz, 42);

 

    /* Destroy Xyz instance and release acquired resources. */

    XyzRelease(hXyz);

 

    /* Be defensive. */

    hXyz = NULL;

}

With this approach, a DLL must provide explicit functions for object creation and deletion.

Calling Conventions

It is important to remember to specify the calling convention for all exported functions. Omitted calling convention is a very common mistake that many beginners do. As long as the default client's calling convention matches that of the DLL, everything works. But, once the client changes its calling convention, it goes unnoticed by the developer until runtime crashes occur. The XyzLibrary project uses the APIENTRY macro, which is defined as __stdcall in the "WinDef.h" header file.

Exception Safety

No C++ exception is allowed to cross over the DLL boundary. Period. The C language knows nothing about C++ exceptions, and cannot handle them properly. If an object method needs to report an error, then a return code should be used.

Advantages

  • A DLL can be used by the widest programming audience possible. Almost every modern programming language supports interoperability with plain C functions.
  • C run-time libraries of a DLL and a client are independent of each other. Since resource acquisition and freeing happens entirely inside a DLL module, a client is not affected by a DLL's choice of CRT.

Disadvantages

  • The responsibility of calling the right methods on the right instance of an object rests on the user of a DLL. For example, in the following code snippet, the compiler won't be able to catch the error:

·         /* void* GetSomeOtherObject(void) is declared elsewhere. */

·         XYZHANDLE h = GetSomeOtherObject();

·          

·         /* Oops! Error: Calling Xyz.Foo on wrong object intance. */

XyzFoo(h, 42);

  • Explicit function calls are required in order to create and destroy object instances. This is especially annoying for deletion of an instance. The client function must meticulously insert a call to XyzRelease at all points of exit from a function. If the developer forgets to call XyzRelease, then resources are leaked because the compiler doesn't help to track the lifetime of an object instance. Programming languages that support destructors or have a garbage collector may mitigate this problem by making a wrapper over the C interface.
  • If object methods return or accept other objects as parameters, then the DLL author has to provide a proper C interface for these objects, too. The alternative is to fall back to the lowest common denominator, that is the C language, and use only built-in types (like int, double, char*, etc.) as return types and method parameters.

C++ Naive Approach: Exporting a Class

Almost every modern C++ compiler that exists on the Windows platform supports exporting a C++ class from a DLL. Exporting a C++ class is quite similar to exporting C functions. All that a developer is required to do is to use the __declspec(dllexport/dllimport) specifier before the class name if the whole class needs to be exported, or before the method declarations if only specific class methods need to be exported. Here is a code snippet:

// The whole CXyz class is exported with all its methods and members.

//

class XYZAPI CXyz

{

public:

    int Foo(int n);

};

 

// Only CXyz::Foo method is exported.

//

class CXyz

{

public:

    XYZAPI int Foo(int n);

};

There is no need to explicitly specify a calling convention for exporting classes or their methods. By default, the C++ compiler uses the __thiscall calling convention for class methods. However, due to different naming decoration schemes that are used by different compilers, the exported C++ class can only be used by the same compiler and by the same version of the compiler. Here is an example of a naming decoration that is applied by the MS Visual C++ compiler:



Notice how the decorated names are different from the original C++ names. Following is a screenshot of the same DLL module with name decoration deciphered by the
Dependency Walker tool:



Only the MS Visual C++ compiler can use this DLL now. Both the DLL and the client code must be compiled with the same version of MS Visual C++ in order to ensure that the naming decoration scheme matches between the caller and the callee. Here is an example of a client code that uses the
Xyz object:

#include “XyzLibrary.h”

 

...

// Client uses Xyz object as a regular C++ class.

CXyz xyz;

xyz.Foo(42);

As you can see, the usage of an exported class is pretty much the same as the usage of any other C++ class. Nothing special.

Important: Using a DLL that exports C++ classes should be considered no different than using a static library. All rules that apply to a static library that contains C++ code are fully applicable to a DLL that exports C++ classes.

What You See Is Not What You Get

A careful reader must have already noticed that the Dependency Walker tool showes an additional exported member, that is the CXyz& CXyz::operator =(const CXyz&) assignment operator. What we see is our C++ money at work. According to the C++ Standard, every class has four special member functions:

  • Default constructor
  • Copy constructor
  • Destructor
  • Assignment operator (operator =)

If the author of a class does not declare and does not provide an implementation of these members, then the C++ compiler declares them, and generates an implicit default implementation. In the case of the CXyz class, the compiler decided that the default constructor, copy constructor, and the destructor are trivial enough, and optimized them out. However, the assignment operator survived optimization and got exported from a DLL.

Important: Marking the class as exported with the __declspec(dllexport) specifier tells the compiler to attempt to export everything that is related to the class. It includes all class data members, all class member functions (either explicitly declared, or implicitly generated by the compiler), all base classes of the class, and all their members. Consider:

class Base

{

    ...

};

 

class Data

{

    ...

};

 

// MS Visual C++ compiler emits C4275 warning about not exported base class.

class __declspec(dllexport) Derived :

    public Base

{

    ...

 

private:

    Data m_data;    // C4251 warning about not exported data member.

};

In the above code snippet, the compiler will warn you about the not exported base class and the not exported class of the data member. So, in order to export a C++ class successfully, a developer is required to export all the relevant base classes and all the classes that are used for the definition of the data members. This snowball exporting requirement is a significant drawback. That is why, for instance, it is very hard and tiresome to export classes that are derived from STL templates or to use STL templates as data members. An instantiation of an STL container like std::map<>, for example, may require tens of additional internal classes to be exported.

Exception Safety

An exported C++ class may throw an exception without any problem. Because of the fact that the same version of the same C++ compiler is used both by a DLL and its client, C++ exceptions are thrown and caught across DLL boundaries as if there were no boundaries at all. Remember, using a DLL that exports C++ code is the same as using a static library with the same code.

Advantages

  • An exported C++ class can be used in the same way as any other C++ class.
  • An exception that is thrown inside a DLL can be caught by the client without any problem.
  • When only small changes are made in a DLL module, no rebuild is required for other modules. This can be very beneficial for big projects where huge amounts of code are involved.
  • Separating logical modules in a big project into DLL modules may be seen as the first step towards true module separation. Overall, it is a rewarding activity that improves the modularity of a project.

Disadvantages

  • Exporting C++ classes from a DLL does not prevent very tight coupling between an object and its user. The DLL should be seen as a static library with respect to code dependencies.
  • Both client code and a DLL must link dynamically with the same version of CRT. It is necessary in order to enable correct bookkeeping of CRT resources between the modules. If a client and DLL link to different versions of CRT, or link with CRT statically, then resources that have been acquired in one instance of the CRT will have been freed in a different instance of the CRT. It will corrupt the internal state of the CRT instance that attempts to operate on foreign resources, and most likely will lead to crash.
  • Both the client code and the DLL must agree on the exception handling/propagating model, and use the same compiler settings with respect to C++ exceptions.
  • Exporting a C++ class requires exporting everything that is related to this class: all its base classes, all classes that are used for the definition of data members, etc.

C++ Mature Approach: Using an Abstract Interface

A C++ abstract interface (i.e., a C++ class that contains only pure virtual methods and no data members) tries to get the best of both worlds: a compiler independent clean interface to an object, and a convenient object oriented way of method calls. All that is required to do is to provide a header file with an interface declaration and implement a factory function that will return the newly created object instances. Only the factory function has to be declared with the __declspec(dllexport/dllimport) specifier. The interface does not require any additional specifiers.

// The abstract interface for Xyz object.

// No extra specifiers required.

struct IXyz

{

    virtual int Foo(int n) = 0;

    virtual void Release() = 0;

};

 

// Factory function that creates instances of the Xyz object.

extern “C” XYZAPI IXyz* APIENTRY GetXyz();

In the above code snippet, the factory function GetXyz is declared as extern “C”. It is required in order to prevent the mangling of the function name. So, this function is exposed as a regular C function, and can be easily recognized by any C-compatible compiler. This is how the client code looks like, when using an abstract interface:

#include “XyzLibrary.h”

 

...

IXyz* pXyz = ::GetXyz();

 

if(pXyz)

{

    pXyz->Foo(42);

 

    pXyz->Release();

    pXyz = NULL;

}

C++ does not provide a special notion for an interface as other programming languages do (for example, C# or Java). But it does not mean that C++ cannot declare and implement interfaces. The common approach to make a C++ interface is to declare an abstract class without any data members. Then, another separate class inherits from the interface and implements interface methods, but the implementation is hidden from the interface clients. The interface client neither knows nor cares about how the interface is implemented. All it knows is which methods are available and what they do.

How This Works

The idea behind this approach is very simple. A member-less C++ class that consisting of pure virtual methods only is nothing more than a virtual table, i.e., an array of function pointers. This array of function pointers is filled within a DLL with whatever an author deems necessary to fill. Then, this array of pointers is used outside of a DLL to call the actual implementation. Bellow is the diagram that illustrates the IXyz interface usage.


Click on the image to view the full sized diagram in a new window:




The above diagram shows the
IXyz interface that is used both by the DLL and the EXE modules. Inside the DLL module, the XyzImpl class inherits from the IXyz interface, and implements its methods. Method calls in the EXE module invoke the actual implementation in the DLL module via a virtual table.

Why This Works With Other Compilers

The short explanation is: because COM technology works with other compilers. Now, for the long explanation. Actually, using a member-less abstract class as an interface between modules is exactly what COM does in order to expose COM interfaces. The notion of a virtual table, as we know it in the C++ language, fits nicely into the specification of the COM standard. This is not a coincidence. The C++ language, being the mainstream development language for at least over a decade now, has been used extensively with COM programming. It is thanks to natural support for object oriented programming in the C++ language. It is not surprising at all that Microsoft has considered the C++ language as the main heavy-duty instrument for industrial COM development. Being the owner of the COM technology, Microsoft has ensured that the COM binary standard and their own C++ object model implementation in the Visual C++ compiler do match, with as little overhead as possible.

No wonder that other C++ compiler vendors jumped on the bandwagon and implemented the virtual table layout in their compilers in the same way as Microsoft did. After all, everybody wanted to support COM technology, and to be compatible with the existing solution from Microsoft. A hypothetical C++ compiler that fails to support COM efficiently is doomed to oblivion in the Windows market. That is why ,nowadays, exposing a C++ class from a DLL via an abstract interface will work reliably with every decent C++ compiler on the Windows platform.

Using a Smart Pointer

In order to ensure proper resource release, an abstract interface provides an additional method for the disposal of an instance. Calling this method manually can be tedious and error prone. We all know how common this error is in the C world where the developer has to remember to free the resources with an explicit function call. That's why typical C++ code uses RAII idiom generously with the help of smart pointers. The XyzExecutable project uses the AutoClosePtr template, which is provided with the example. The AutoClosePtr template is the simplest implementation of a smart pointer that calls an arbitrary method of a class to destroy an instance instead of operator delete. Here is a code snippet that demonstrates the usage of a smart pointer with the IXyz interface:

#include “XyzLibrary.h”

#include “AutoClosePtr.h”

 

...

typedef AutoClosePtr<IXyz, void, &IXyz::Release> IXyzPtr;

 

IXyzPtr ptrXyz(::GetXyz());

 

if(ptrXyz)

{

    ptrXyz->Foo(42);

}

 

// No need to call ptrXyz->Release(). Smart pointer

// will call this method automatically in the destructor.

Using a smart pointer will ensure that the Xyz object is properly released, no matter what. A function can exit prematurely because of an error or an internal exception, but the C++ language guarantees that destructors of all local objects will be called upon the exit.

Exception Safety

In the same way as a COM interface is not allowed to leak any internal exception, the abstract C++ interface cannot let any internal exception to break through DLL boundaries. Class methods should use return codes to indicate an error. The implementation for handling C++ exceptions is very specific to each compiler, and cannot be shared. So, in this respect, an abstract C++ interface should behave as a plain C function.

Advantages

  • An exported C++ class can be used via an abstract interface, with any C++ compiler.
  • C run-time libraries of a DLL and a client are independent of each other. Since resource acquisition and freeing happens entirely inside a DLL module, a client is not affected by a DLL's choice of CRT.
  • True module separation is achieved. The resulting DLL module can be redesigned and rebuilt without affecting the rest of the project.
  • A DLL module can be easily converted to a full-fledged COM module, if required.

Disadvantages

  • An explicit function call is required to create a new object instance and to delete it. A smart pointer can spare a developer of the latter call, though.
  • An abstract interface method cannot return or accept a regular C++ object as a parameter. It has be either a built-in type (like int, double, char*, etc.) or another abstract interface. It is the same limitation as for COM interfaces.

What About STL Template Classes?

The Standard C++ Library containers (like vector, list, or map) and other templates were not designed with DLL modules in mind. The C++ Standard is silent about DLLs because this is a platform specific technology, and it is not necessarily present on other platforms where the C++ language is used. Currently, the MS Visual C++ compiler can export and import instantiations of STL classes which a developer explicitly marks with the __declspec(dllexport/dllimport) specifier. The compiler emits a couple of nasty warnings, but it works. However, one must remember that exporting STL template instantiations is in no way different from exporting regular C++ classes, with all accompanying limitations. So, there is nothing special about STL in that respect.

Summary

The article discussed different methods of exporting a C++ object from a DLL module. Detailed description is given of the advantages and disadvantages for each method. Exception safety considerations are outlined. The following conclusions are made:

  • Exporting an object as a set of plain C functions has an advantage of being compatible with the widest range of development environments and programming languages. However, a DLL user is required to use outdated C techniques or to provide additional wrappers over the C interface in order to use modern programming paradigms.
  • Exporting a regular C++ class is no different than providing a separate static library with the C++ code. The usage is very simple and familiar; however, there is a tight coupling between the DLL and its client. The same version of the same C++ compiler must be used, both for the DLL and its client.
  • Declaring an abstract member-less class and implementing it inside a DLL module is the best approach to export C++ objects, so far. This method provides a clean, well-defined object oriented interface between the DLL and its client. Such a DLL can be used with any modern C++ compiler on the Windows platform. The usage of an interface in conjunction with smart pointers is almost as easy as the usage of an exported C++ class.

The C++ programming language is a powerful, versatile, and flexible development instrument.

양식의

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

양식의 아래

 
출처 : http://www.codeproject.com/KB/cpp/howto_export_cpp_classes.aspx

2008. 9. 18. 16:49

포기된 뮤텍스 (Abandoned Mutex & WAIT_ABANDONED)

한 쓰레드가 뮤텍스를 소유하면 이 뮤텍스를 기다리는 스레드들은 모두 블록 상태가 된다. 그런데 만약 뮤텍스를 소유한 스레드가 어떠한 이유로 뮤텍스의 소유를 풀지 못하게 되면 어떻게 될까? 구조적 예외가 발생한 경우나 아니면 Exit Thread 로 스레드를 종료했거나 외부에서 TerminateThread로 스레드를 강제로 죽였을 경우 이런 현상이 발생할 수 있다. 크리티컬 섹션의 경우라면 블록된 스레드를 깨울 수 있는 방법이 없지만 뮤텍스의 경우는 한 가지 안전 장치가 있다.

뮤텍스는 자신을 소유한 스레드가 누구인지를 기억하고 있는데 시스템은 뮤텍스의 소유 스레드가 뮤텍스를 풀지않고 종료되었을 경우 강제로 뮤텍스를 신호상태로 만들어 준다. 이때의 뮤텍스를 포기된 뮤텍스(Abandoned Mutex)라고 한다. 뮤텍스가 포기되면 대기중인 스레드중 하나가 뮤텍스를 가지게 될 것이다. 이 때 이 스레드는 Wait 계열 함수(WaitForSingleObject, WaitForMultipleObjects 등)의 리턴값으로 WAIT_ABANDONED 값을 전달받음으로써 이 뮤텍스가 정상적인 방법으로 신호상태가 된 것이 아니라 포기된 것임을 알 수 있다.

포기된 뮤텍스를 받았다는 것은 스레드 코드에 뭔가 버그가 있다는 뜻이다. 관련 스레드가 정상 종료하지 못했음을 알 수 있는데 이때 뮤텍스에 의해 보고되는 공유 자원의 상태는 알 수가 없다. 이런 상태로 인해 포기된 뮤텍스를 받은 스레드가 정상적으로 동작할 수 없다면 일종의 예외 처리를 해 주어야 하되 실행에 별 이상이 없다면 그냥 실행해도 될 것이다. 포기된 뮤텍스를 어떻게 처리할 것인가는 프로그래머에게 달린 문제이다.

참고 : Windows API 정복, 가남사 참고

앞으로 뮤텍스의 소유/해제에 각별히 신경쓰자.
2008. 9. 4. 00:57

InterProcess Communications (IPC)

InterProcess Communications (IPC)

 

번역 : Codemuri

 

Microsoft Windows OS 는 애플리케이션간에 커뮤니케이션과 데이터 공유를 위한 여러 매커니즘을 제공한다. 이러한 매커니즘을 묶어, Interprocess Communications (IPC)라고 부른다. 일부는 프로세스간의 작업 분배를 수월하게 해주며, 또 일부는 네트워크 상에 있는 컴퓨터들 사이의 작업 분배를 수월하게 해준다.

 

일반적으로, 애플리케이션은 클라이언트와 서버로 분류하여 IPC를 사용할 수 있다. 클라이언트는 어떤 다른 애플리케이션이나 프로세스에 서비스를 요청하는 애플리케이션 또는 프로세스이다. 서버는 클라이언트의 요청에 응답하는 애플리케이션 또는 프로세스이다. 많은 애플리케이션은 상황에 따라 클라이언트와 서버 둘다로 동작한다. 예를 들면, 워드 프로세스 애플리케이션은 서버로서 동작하는 스프레드 쉬트 애플리케이션에게 양산 비용의 요약 테이블을 요청하기 위해 클라이언트로서 동작할 수도 있다. , 스프레드 쉬트 애플리케이션은 자동화된 자재 제어(inventory control) 애플리케이션에게 최근의 자재 수준(inventory level)을 요청하기 위해 클라이언트로 동작할 수도 있다.

 

IPC가 만약 애플리케이션에 유용하다고 결정한 후에는, 어떤 IPC 수단을 사용할 것인지를 결정해야 한다. 하나의 애플리케이션이 여러 개의 IPC 매커니즘을 사용할 지도 모른다. 이 물음에 대한 답변은 애플리케이션이 애플리케이션이 하나를 사용할지 또는 그 이상의 IPC 매커니즘을 사용할지를 결정짓는다.

 

l  애플리케이션은 네트워크상에 있는 다른 컴퓨터에서 실행하고 있는 애플리케이션과 통신할수 있어야 하는가, 또는 로컬 컴퓨터에 있는 애플리케이션과 통신하는 것 만으로 충분한가?

l  애플리케이션은 다른 OS (16 비트 윈도우 또는 UNIX와 같은)에서 실행될 수도 있는 다른 컴퓨터상의 애플리케이션과 통신할 수 있어야 하는가?

l  애플리케이션의 사용자가 통신할 다른 애플리케이션을 선택할 수 있어야 하는가, 또는 애플리케이션은 통신할 파트너를 암시적으로 찾을 수 있는가?

l  애플리케이션은 다른 애플리케이션에게 cut-and-paste 연산을 허용하는 것과 같은, 일반적인 방법으로 다른 애플리케이션과 통신해야만 하는가? 또는 통신 요구사항(communications requirements)이 특정 애플리케이션과 제한된 범위의 상호작용으로 한정되어야 하는가?

l  성능이 애플리케이션에 있어 치명적인 요소인가? 모든 IPC 매커니즘은 약간의 오버헤드를 포함하고 있다.

l  애플리케이션은 GUI 애플리케이션인가, 또는 콘솔 애플리케이션인가? 일부 IPC 매커니즘은 GUI 애플리케이션에서만 동작한다.

 

윈도우에서는 아래의 IPC 매커니즘을 지원한다.

 

l  클립보드 (Clipboard)

l  COM

l  Data Copy

l  DDE

l  File mapping

l  Mailslots

l  Pipes

l  RPC

l  Windows Sockets

 

클립보드를 IPC로 사용하기

 

클립보드는 애플리케이션간에 데이터를 공유하기 위한 중심 저장소로 동작한다. 사용자가 한 애플리케이션에서 cut 또는 copy 연산을 실행할 때, 애플리케이션은 선택된 데이터를 하나 또는 그이상의 표준적인 데이터(Standard) 또는 애플리케이션이 정의한 포맷으로 클립보드에 넣는다. 그러면 다른 애플리케이션은 클립보드에서 애플리케이션이 이해할 수 있는 형태의 포맷을 선택하여 데이터를 가져올 수 있다. 클립보드는 애플리케이션이 그 데이터 포맷에 대해 동의만을 필요로 하는 매우 느슨하게 묶여진 교환 방법이다. (역자 주 : 이 문맥은 아마 클립보드에 다양한 형태의 포맷으로 데이터를 저장해 놓으면, 그 데이터를 클립보드에서 가져가는 애플리케이션이 이해할 수 있는 데이터만을 가져간다는 것을 의미하는 것 같다. 예로, 만약 워드에서 텍스트를 선택하여 복사를 하여 메모장에 복사하면 단지 텍스트만을 가져가고, 다른 워드 프로세스에 복사를 하면 텍스트 뿐만 아니라 폰트와 색상도 함께 복사해 갈 수 있는 경우와 같다. , 그림판과 같은 경우에는 텍스트를 이미지로 바꿀 수 없으므로 복사를 수행하지 않는다. – 텍스트 박스에서 편집중은 제외). 애플리케이션은 같은 컴퓨터상에 존재하거나 네트워크상에 있는 다른 컴퓨터에 존재할 수 있다.

 

Key Point : 모든 애플리케이션은 클립보드에 다른 애플리케이션이 이해할 수 있는 형태의 데이터 포맷을 지원해야 한다. 예를 들면, 텍스트 에디터 또는 워드 프로세서는 최소한 순수한 텍스트 형태로 클립보드 데이터를 복사하거나 붙여 넣을 수 있어야 한다.

 

COM IPC로 사용하기

 

OLE를 사용하는 애플리케이션은 복합 문서(compound document)를 다룬다. – , 문서는 많은 다른 애플리케이션으로부터 얻은 데이터로 구성된다. OLE는 데이터 편집을 위해 다른 애플리케이션을 호출하는 것을 쉽게 해 주는 서비스를 제공한다. 예를 들면, OLE를 사용하는 워드 프로세서는 스프레드쉬트로부터 얻은 그래프를 포함할 수 있다. 사용자는 편집을 위해 내장된 차트를 선택함으로써 워드프로세서 내에서 스프레드 쉬트를 자동적으로 시작할 수 있다. OLE는 스프레드 쉬트를 시작하고 그래프를 편집하는 것을 다룬다. 사용자가 스프레드 쉬트를 종료할 때, 그래프는 원본 워드 프로세서 문서내에서 업데이트 될 것이다. 스프레드 쉬트는 워드 프로세서의 확장인 것 처럼 보인다.

 

OLE의 기초는 Component Object Model(COM)이다. COM을 사용하는 소프트웨어 컴포넌트는 아주 다양한 다른 컴포넌트들과 통신할 수 있다, 심지어 아직 작성되지 않은 컴포넌트들과도. 컴포넌트는 객체와 클라이언트로서 상호작용한다. 분산 COM (DCOM)은 네트워크를 넘어서 동작하기 위해 COM 프로그래밍 모델을 확장한다.

 

Key Point : OLE는 복합 문서를 지원하고 선택될 때, 자동적으로 데이터 편집을 위해 다른 애플리케이션이 시작되는 내장된 또는 연결된 데이터를 포함하는 것을 가능하게 한다. 이것은 애플리케이션이 OLE를 사용하는 모든 애플리케이션에게 확장될 수 있도록 한다. COM 객체는 인터페이스로 알려진 하나 또는 여러 개의 함수를 통해서 객체의 데이터에 대한 접근을 제공한다.

 

Data Copy IPC로 사용하기

 

데이터 복사는 WM_COPYDATA 메시지를 사용하여 다른 애플리케이션에게 정보를 보내도록 한다. 이 방법은 보내는 애플리케이션과 받는 애플리케이션간의 협력을 필요로 한다. 받는 애플리케이션은 정보의 포맷을 알아야만 하고 송신자를 식별할 수 있어야 한다. 보내는 애플리케이션은 어떤 포인터에 의해 참조되는 메모리를 수정할 수 없다.

 

Key Point : 데이터 복사는 윈도우 메시지를 사용하여 다른 애플리케이션에게 정보를 빠르게 전송하는데 사용할 수 있다.

 

DDE IPC로 사용하기

 

DDE(Dynamic Data Exchange)는 애플리케이션이 다양한 형태의 포맷으로 데이터를 교환하도록 하는 프로토콜이다. 애플리케이션은 one-time 데이터 교환을 위해서나 또는 하나의 데이터를 새로운 데이터가 이용할 수 있게 될 때 다른 데이터로 업데이트하는 것과 같은 지속적인 데이터 교환을 위해서 DDE를 사용할 수 있다.

 

DDE에 사용되는 데이터 포맷은 클립보드에 사용되는 것과 같다. DDE는 클립보드 매커니즘의 확장의 하나로 볼 수 있다. 클립보드는 거의 항상 붙여넣기 명령을 메뉴로부터 선택하는 것과 같은 사용자 명령에 대한 one-time 응답을 위해서 사용된다. DDE도 보통 사용자 명령에 의해 초기화 되지만, 종종 사용자 상호작용 없이 지속적으로 동작한다. 좀더 강하게 묶여진 통신 요구사항을 가진 애플리케이션간에 특별한 목적의 IPC를 위해 사용자 정의형 DDE 데이터 포맷을 정의할 수 있다.

 

DDE 교환은 같은 컴퓨터상에 실행하고 있는 애플리케이션 간이나 네트워크상에 있는 다른 컴퓨터들 간에 일어날 수 있다.

 

Key Point : DDE는 최신의 기술들만큼 효과적이지 않다. 그러나, 다른 다른 IPC 매커니즘이 적합하지 않거나 또는 DDE만을 지원하는 기존의 애플리케이션과 상호작용해야 하는 경우에는 DDE를 여전히 사용할 수 있다.

 

File Mapping IPC로 사용하기

 

파일 맵핑은 프로세스를 파일의 컨텐츠로서 다루도록 한다. 마치 그 프로세스의 주소 공간에 있는 메모리 블락인 것 처럼. 프로세스는 파일의 콘텐츠를 조사하거나 수정하기 위해 간단한 포인터 연산을 사용할 수 있다. 두개 또는 그이상의 프로세스가 같은 파일 매핑에 접근 할 때, 각 프로세스는 자신의 주소 공간의 메모리에 대한 포인터를 얻는다. 그리고 그 포인터는 파일의 내용을 읽거나 수정하기 위해서 사용할 수 있다. 프로세스는 멀티태스킹 환경에서 데이터 오염(data corruption)을 방지하기 위해서는 세마포어와 같은 동기화 객체를 반드시 사용해야 한다.

 

프로세스간에 이름지어진 공유 메모리 (named shared memory)를 제공하기 위해 특별한 파일 맵핑을 사용할 수 있다. 만약 파일 맵핑 객체를 생성할 때 system swapping file을 명시한다면, 그 파일 맵핑 객체는 공유 메모리 블락으로 다루어 진다. 다른 프로세스는 같은 파일-맵핑 객체를 열어서 같은 메모리 블락에 접근할 수 있다.

 

파일 맵핑은 매우 효과적이고 또한 인증되지 않은 데이터 오염을 방지하는데 도움을 줄 수 있는 OS에 의해 지원되는 보안 특성을 제공한다. 파일 맵핑은 로컬 컴퓨터상에 있는 프로세스간에만 사용될 수 있다. 네트워크상에서는 사용될 수 없다.

 

Key Point : 파일 맵핑은 같은 컴퓨터상에 있는 두개 또는 그 이상의 프로세스들이 데이터를 공유하기 위한 효과적인 방법중의 하나이다, 그러나 프로세스간에 동기화를 제공해야만 한다.

 

Mailslot IPC로 사용하기

 

Mailslot 은 단방향 통신을 제공한다. mailslot을 생성하는 모든 프로세스는 mailslot 서버이다. mailslot 클라이언트라 불리는 다른 프로세스는 메시지를 자신의 mailslot에 작성함으로써 메시지를 mailslot 서버에 보낸다. 들어오는 메시지는 항상 mailslot에 추가된다. mailslotmailslot 서버가 그 메시지를 읽을 때 까지 그 메시지를 저장한다. 하나의 프로세스는 mailslot 서버가 될 수도 있고, mailslot 클라이언트가 될 수도 있다, 그래서 양방향 통신은 다중 mailslot을 사용함으로써 가능하다.

 

mailslot 클라이언트는 로컬 컴퓨터상에 있는 mailslot, 다른 컴퓨터에 있는 mailslot, 또는 특정 네트워크 도메인에 있는 모든 컴퓨터상에 있는 같은 이름의 모든 mailslot에 메시지를 보낼 수 있다. 어떤 도메인에 있는 모든 mailslot에 대한 메시지 브로드 캐스트는 400 바이트 이상이 될 수 없다, 반면에 하나의 mailslot에 보내어 지는 메시지는 mailslot이 생성될 때 mailslot 서버에 의해 명시된 최대 메시지 크기에 의해서만 제한된다.

 

Key Point : Mailslot은 애플리케이션이 짧은 메시지를 보내거나 받기 위한 쉬운 방법을 제공한다. 한 네트워크 도메인에 있는 모든 컴퓨터에 메시지를 브로드캐스트하는 능력을 또한 제공한다.

 

Pipe IPC로 사용하기

 

양방향 토신을 위한 두가지 형태의 파이프가 있다. 익명 파이프(anonymous pipe)와 이름있는 파이프(Named pipe). 익명 파이프는 관련된 프로세스들이 서로에게 정보를 전송하게끔 한다. 일반적으로, 익명 파이프는 자식 프로세스의 표준 입출력을 재정하기 위해 사용된다, 그렇게 함으로써 자식 프로세스는 부모 프로세스와 데이터를 교환할 수 있다. 양방향으로 데이터를 교환하기 위해서는, 반드시 두개의 익명 파이프를 생성해야 한다. 부모 프로세스는 자신의 write 핸들을 사용하여 데이터를 파이프에 작성한다, 반면에 자식 프로세스는 자신의 read 핸들을 사용하여 파이프로부터 그 데이터를 읽는다. 비슷하게, 자식 프로세스는 데이터를 다른 파이프에 작성하고 부모 프로세스는 그 파이프로부터 데이터를 읽는다. 익명 파이프는 네트워크 상에나 연관되지 않은(unrelated) 프로세스 사이에 사용될 수 없다.

 

이름있는 파이프는 관련 없는 프로세스 사이에 그리고 다른 컴퓨터에 있는 프로세스 사이에 데이터를 전송하는 데 사용된다. 일반적으로, 이름있는 파이프 서버는 이름있는 파이프를 잘알려진 이름으로 또는 클라이언트와 통신하기로 되어 있는 이름을 가지고 생성한다. 그 파이프의 이름을 알고 있는 이름있는 파이프 클라이언트 프로세스는 이름있는 파이프 서버 프로세스가 명시한 접근 제한을 가지고 그 파이프를 열 수 있다. 서버와 클라이언트가 파이프로 연결된 후에, 그들은 파이프상에서 읽기와 쓰기 연산을 수행함으로써 데이터를 교환할 수 있다.

 

Key Point : 익명 파이프는 같은 컴퓨터에 있는 자식 프로세스와 표준 입출력을 재정하기 위한 효율적인 방법을 제고한다. 이름있는 파이프는 같은 컴퓨터상에 또는 다른 네트워크상에 존재하는 두개의 프로세스사이에 데이터를 전송하는 간단한 프로그래밍 인터페이스를 제공한다.

 

RPC IPC로 사용하기

 

RPC는 애플리케이션이 원격으로 함수를 호출하도록 한다. 그리하여, RPC IPC가 함수를 호출하는데 있어 가능한한 쉽게 만들어 준다. RPC는 단일 컴퓨터 또는 네워크상에 있는 다른 컴퓨터에 있는 프로세스사이에 동작한다.

 

윈도우에 의해 제공되는 RPCOpen Software Foundation(OSF) Distributed Computing Environment(DCE)에 따른다. 이것은 RPC를 사용하는 애플리케이션은 DCE를 지원하는 다른 OS에서 실행하고 있는 애플리케이션과 통신할 수 있다는 것을 의미한다. RPC는 다른 하드웨어 아키텍처와 다른 4 바이트-순서로 된 환경 사이에 데이터 변환을 자동적으로 책임을 진다.

 

RPC 클라이언트와 서버는 강하게 결합되어 있지만 높은 성능을 여전히 유지한다. 시스템은 RPC를 사용함으로써 다른 OS 사이의 클라이언트/서버 관계를 용이하게 만들 수 있다.

 

Key Point : RPC는 다른 OS사이에 통신을 위한 자동 데이터 변환을 지원하는 함수-수준의 인터페이스의 하나이다. PRC를 사용함으로써, 고성능의, 강하게 결합된 분산 애플리케이션을 생성할 수 있다.

 

Windows Socket IPC로 사용하기

 

윈도우 소켓은 프로토콜-독립적인 인터페이스의 하나이다. It takes advantage of the communication capabilities of the underlying protocols. (???) 윈도우 소켓 2에서, 소켓 핸들은 선택적으로 표준 파일 입출력 함수를 가지고 파일 핸들처럼 사용될 수 있다.

 

윈도우 소켓은 Berkeley Software Distribution (BSD)에 의해 처음으로 보급된 소켓에 기초를 두고 있다. 윈도우 소켓을 사용하는 애플리케이션은 다른 시스템과 소켓 구현을 가지고 통신할 수 있다. 그러나, 모든 전송 서비스 공급자가 모든 가능한 옵션을 지원하지는 않는다.

 

Key Point : 윈도우 소켓은 현재의 그리고 새로운 네트워크 능력을 지원할 수 있는 프로토콜 독립적인 인터페이스이다.

 

참고 : http://msdn.microsoft.com/en-us/library/aa365574(VS.85).aspx#base.using_rpc_for_ipc

2008. 8. 27. 21:15

WAIT_ABANDONED_0 에 대해서..

공유메모리를 이용하여 프로세스간 데이터 통신을 갖추었다.

1. A, B 프로세스간에 공유하는 메모리를 생성하고
2. 그 메모리에 대한 공통의 뷰를 생성
3. 그리고 동기화를 위한 뮤텍스와 이벤트 등등
4. 그리고 데이터의 동기화를 위한 프로세스 내부 큐를 생성

위와같은 기능을 하는 동기화 클래스를 만들었다.

이때, 동기화를 위해서 각 프로세스간에 이름있는(Named) 뮤텍스를 가지고 있는데,

이때 다음과 같은 문제점이 발생하였다.

A라는 프로세스는 공유메모리를 읽기 모드로 열어서 B 프로세스가 보내는 데이터를 수신하고 있다.

B 프로세스는 공유메모리를 쓰기모드로 오픈하여 데이터를 송신하기로 했다.
근데 B 프로세스에서 약간의 디버깅을 하다가 약간의 코드가 추가되었는데, (삽질)

// 대략적인 거라 문법은 무시하자.
// 일단 쓰레드를 생성하고 쓰레드 내에서 공유메모리 객체를 연다는 의미이다.

void a()
{
    CreateThread(ThreadProc)
}

void ThreadProc()
{
    ShreadMemory.Open()
    return 0;
}


와 같이 임시 쓰레드를 생성한 후 쓰레드내에서 공유메모리 객체를 오픈하였는데, 이 공유메모리 객체 내부에서 뮤텍스를 생성한다.

이때, 공유메모리 객체를 읽기 모드로 WaitFor 계열 함수로 대기하고 있는 A 프로세스에서

WAIT_ABANDONED_0

가 발생하였다. ABANDONED의 사전적 뜻은 "버림받은, 유기된"의 뜻으로 생성된 뮤텍스가 버림받았다고?

생각컨데, 뮤텍스를 생성한 쓰레드가 종료되는 순간 그 뮤텍스를 Close 하는 것과는 별개로 쓰레드 컨텍스트가 사라지는 순간 그 뮤텍스 자체도 ABANDONED 되어 버리는 것 같다.


지금껏 WaitFor 게열 함수를 사용할때 WAIT_OBJECT_0 + n 매크로와 WAIT_TIMEOUT 정도만 고려해서 작성하였는데 실제로 WAIT_ABANDONED_0도 고려해서 설계를 해야 된다는 것을 느꼈다.


Keyword :
WaitForSingleObject; WaitForMultipleObjects; Mutex; Event; Thread; Semaphore;
2008. 8. 21. 11:39

ANSI 와 UNICODE 사이의 변환 클래스

For a simple string conversion between ANSI and Unicode on a Pocket PC I have also written some C++ classes:
  • CTtoA for conversion from LPCTSTR to LPCSTR
  • CTtoW for conversion from LPCTSTR to LPCWSTR
  • CAtoT for conversion from LPCSTR to LPCTSTR
  • CWtoT for conversion from LPCWSTR to LPCTSTR
  • CAtoW for conversion from LPCSTR to LPCWSTR
  • CWtoA for conversion from LPCWSTR to LPCSTR

//////////////////////////////////////////////////////////////////////////
//
//  strconv.h - Some useful classes for string conversion.
//
//  Copyright (c) 2004-2007 by Daniel Strigl.
//
//  This code may be used in compiled form in any way you desire.
//  This file may be redistributed unmodified by any means PROVIDING
//  it is not sold for profit without the authors written consent,
//  and providing that this notice and the authors name is included.
//  By the way, if the source code in this file is used in any
//  application I would appreciate and enjoy hearing about them.
//
//  This file is provided "as is" with no expressed or implied warranty.
//  The author accepts no liability for any damage, in any form, caused
//  by this code. Use it at your own risk and as with all code expect
//  bugs! It's been tested, but I'm not perfect ;-)
//
//  Please use the contact page of my blog to report any
//  bug, comment or suggestion.
//
//  Contact:
//  --------
//
//    Daniel Strigl [http://geekswithblogs.net/dastblog]
//
//  History:
//  --------
//
//    21 Jul 2004 - Initial version.
//    20 Nov 2006 - Added class CAtoT, CWtoT, CAtoW and CWtoA.
//
//////////////////////////////////////////////////////////////////////////

#if !defined(_STRCONV_H_)
#define _STRCONV_H_

//////////////////////////////////////////////////////////////////////////
//
//  CTtoA
//
//      LPCTSTR to LPCSTR conversion.
//
class CTtoA
{
    LPSTR m_psz;

public:
    CTtoA(LPCTSTR ptsz) : m_psz(NULL)
    {
        ASSERT(ptsz != NULL);
#if defined(_UNICODE) // for UNICODE build
        const int size = WideCharToMultiByte(CP_ACP, 0, ptsz, -1,
            NULL, 0, NULL, NULL);
#ifdef _DEBUG
        if (size == 0) {
            ASSERT(!_T("Function \"WideCharToMultiByte\" failed."));
        }
#endif // _DEBUG
        ASSERT(size > 0);
        m_psz = (LPSTR) malloc(sizeof(CHAR)*size);
        ASSERT(m_psz != NULL);
#ifdef _DEBUG
        memset(m_psz, 0, sizeof(CHAR)*size);
#endif // _DEBUG
        VERIFY(WideCharToMultiByte(CP_ACP, 0, ptsz, -1,
            m_psz, size, NULL, NULL) != 0);
#else // for non-UNICODE build
        m_psz = _strdup(ptsz);
        ASSERT(m_psz != NULL);
#endif // defined(_UNICODE)
    }

    CTtoA(const CTtoA& rhs) : m_psz(_strdup(rhs.m_psz))
    {
        ASSERT(m_psz != NULL);
    }

    CTtoA& operator=(const CTtoA& rhs)
    {
        if (this != &rhs)
        {
            free(m_psz);
            m_psz = _strdup(rhs.m_psz);
            ASSERT(m_psz != NULL);
        }
        return *this;
    }

    ~CTtoA()
    {
        free(m_psz);
    }

    size_t GetLength() const { return strlen(m_psz); }

    operator LPCSTR() const { return m_psz; }
};

//////////////////////////////////////////////////////////////////////////
//
//  CTtoW
//
//      LPCTSTR to LPCWSTR conversion.
//
class CTtoW
{
    LPWSTR m_pwsz;

public:
    CTtoW(LPCTSTR ptsz) : m_pwsz(NULL)
    {
        ASSERT(ptsz != NULL);
#if defined(_UNICODE) // for UNICODE build
        m_pwsz = _wcsdup(ptsz);
        ASSERT(m_pwsz != NULL);
#else // for non-UNICODE build
        const int size = MultiByteToWideChar(CP_ACP, 0, ptsz, -1, NULL, 0);
#ifdef _DEBUG
        if (size == 0) {
            ASSERT(!_T("Function \"MultiByteToWideChar\" failed."));
        }
#endif // _DEBUG
        ASSERT(size > 0);
        m_pwsz = (LPWSTR) malloc(sizeof(WCHAR)*size);
        ASSERT(m_pwsz != NULL);
#ifdef _DEBUG
        memset(m_pwsz, 0, sizeof(WCHAR)*size);
#endif // _DEBUG
        VERIFY(MultiByteToWideChar(CP_ACP, 0, ptsz, -1, m_pwsz, size) != 0);
#endif // defined(_UNICODE)
    }

    CTtoW(const CTtoW& rhs) : m_pwsz(_wcsdup(rhs.m_pwsz))
    {
        ASSERT(m_pwsz != NULL);
    }

    CTtoW& operator=(const CTtoW& rhs)
    {
        if (this != &rhs)
        {
            free(m_pwsz);
            m_pwsz = _wcsdup(rhs.m_pwsz);
            ASSERT(m_pwsz != NULL);
        }
        return *this;
    }

    ~CTtoW()
    {
        free(m_pwsz);
    }

    size_t GetLength() const { return wcslen(m_pwsz); }

    operator LPCWSTR() const { return m_pwsz; }
};

//////////////////////////////////////////////////////////////////////////
//
//  CAtoT
//
//      LPCSTR to LPCTSTR conversion.
//
class CAtoT
{
    LPTSTR m_ptsz;

public:
    CAtoT(LPCSTR psz) : m_ptsz(NULL)
    {
        ASSERT(psz != NULL);
#if defined(_UNICODE) // for UNICODE build
        const int size = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
#ifdef _DEBUG
        if (size == 0) {
            ASSERT(!_T("Function \"MultiByteToWideChar\" failed."));
        }
#endif // _DEBUG
        ASSERT(size > 0);
        m_ptsz = (LPWSTR) malloc(sizeof(WCHAR)*size);
        ASSERT(m_ptsz != NULL);
#ifdef _DEBUG
        memset(m_ptsz, 0, sizeof(WCHAR)*size);
#endif // _DEBUG
        VERIFY(MultiByteToWideChar(CP_ACP, 0, psz, -1, m_ptsz, size) != 0);
#else // for non-UNICODE build
        m_ptsz = _strdup(psz);
        ASSERT(m_ptsz != NULL);
#endif // defined(_UNICODE)
    }

    CAtoT(const CAtoT& rhs) : m_ptsz(_tcsdup(rhs.m_ptsz))
    {
        ASSERT(m_ptsz != NULL);
    }

    CAtoT& operator=(const CAtoT& rhs)
    {
        if (this != &rhs)
        {
            free(m_ptsz);
            m_ptsz = _tcsdup(rhs.m_ptsz);
            ASSERT(m_ptsz != NULL);
        }
        return *this;
    }

    ~CAtoT()
    {
        free(m_ptsz);
    }

    size_t GetLength() const { return _tcslen(m_ptsz); }

    operator LPCTSTR() const { return m_ptsz; }
};

//////////////////////////////////////////////////////////////////////////
//
//  CWtoT
//
//      LPCWSTR to LPCTSTR conversion.
//
class CWtoT
{
    LPTSTR m_ptsz;

public:
    CWtoT(LPCWSTR pwsz) : m_ptsz(NULL)
    {
        ASSERT(pwsz != NULL);
#if defined(_UNICODE) // for UNICODE build
        m_ptsz = _wcsdup(pwsz);
        ASSERT(m_ptsz != NULL);
#else // for non-UNICODE build
        const int size = WideCharToMultiByte(CP_ACP, 0, pwsz, -1,
            NULL, 0, NULL, NULL);
#ifdef _DEBUG
        if (size == 0) {
            ASSERT(!_T("Function \"WideCharToMultiByte\" failed."));
        }
#endif // _DEBUG
        ASSERT(size > 0);
        m_ptsz = (LPSTR) malloc(sizeof(CHAR)*size);
        ASSERT(m_ptsz != NULL);
#ifdef _DEBUG
        memset(m_ptsz, 0, sizeof(CHAR)*size);
#endif // _DEBUG
        VERIFY(WideCharToMultiByte(CP_ACP, 0, pwsz, -1,
            m_ptsz, size, NULL, NULL) != 0);
#endif // defined(_UNICODE)
    }

    CWtoT(const CWtoT& rhs) : m_ptsz(_tcsdup(rhs.m_ptsz))
    {
        ASSERT(m_ptsz != NULL);
    }

    CWtoT& operator=(const CWtoT& rhs)
    {
        if (this != &rhs)
        {
            free(m_ptsz);
            m_ptsz = _tcsdup(rhs.m_ptsz);
            ASSERT(m_ptsz != NULL);
        }
        return *this;
    }

    ~CWtoT()
    {
        free(m_ptsz);
    }

    size_t GetLength() const { return _tcslen(m_ptsz); }

    operator LPCTSTR() const { return m_ptsz; }
};

//////////////////////////////////////////////////////////////////////////
//
//  CAtoW
//
//      LPCSTR to LPCWSTR conversion.
//
class CAtoW
{
    LPWSTR m_pwsz;

public:
    CAtoW(LPCSTR psz) : m_pwsz(NULL)
    {
        ASSERT(psz != NULL);
        const int size = MultiByteToWideChar(CP_ACP, 0, psz, -1, NULL, 0);
#ifdef _DEBUG
        if (size == 0) {
            ASSERT(!_T("Function \"MultiByteToWideChar\" failed."));
        }
#endif // _DEBUG
        ASSERT(size > 0);
        m_pwsz = (LPWSTR) malloc(sizeof(WCHAR)*size);
        ASSERT(m_pwsz != NULL);
#ifdef _DEBUG
        memset(m_pwsz, 0, sizeof(WCHAR)*size);
#endif // _DEBUG
        VERIFY(MultiByteToWideChar(CP_ACP, 0, psz, -1, m_pwsz, size) != 0);
    }

    CAtoW(const CAtoW& rhs) : m_pwsz(_wcsdup(rhs.m_pwsz))
    {
        ASSERT(m_pwsz != NULL);
    }

    CAtoW& operator=(const CAtoW& rhs)
    {
        if (this != &rhs)
        {
            free(m_pwsz);
            m_pwsz = _wcsdup(rhs.m_pwsz);
            ASSERT(m_pwsz != NULL);
        }
        return *this;
    }

    ~CAtoW()
    {
        free(m_pwsz);
    }

    size_t GetLength() const { return wcslen(m_pwsz); }

    operator LPCWSTR() const { return m_pwsz; }
};

//////////////////////////////////////////////////////////////////////////
//
//  CWtoA
//
//      LPCWSTR to LPCSTR conversion.
//
class CWtoA
{
    LPSTR m_psz;

public:
    CWtoA(LPCWSTR pwsz) : m_psz(NULL)
    {
        ASSERT(pwsz != NULL);
        const int size = WideCharToMultiByte(CP_ACP, 0, pwsz, -1,
            NULL, 0, NULL, NULL);
#ifdef _DEBUG
        if (size == 0) {
            ASSERT(!_T("Function \"WideCharToMultiByte\" failed."));
        }
#endif // _DEBUG
        ASSERT(size > 0);
        m_psz = (LPSTR) malloc(sizeof(CHAR)*size);
        ASSERT(m_psz != NULL);
#ifdef _DEBUG
        memset(m_psz, 0, sizeof(CHAR)*size);
#endif // _DEBUG
        VERIFY(WideCharToMultiByte(CP_ACP, 0, pwsz, -1,
            m_psz, size, NULL, NULL) != 0);
    }

    CWtoA(const CWtoA& rhs) : m_psz(_strdup(rhs.m_psz))
    {
        ASSERT(m_psz != NULL);
    }

    CWtoA& operator=(const CWtoA& rhs)
    {
        if (this != &rhs)
        {
            free(m_psz);
            m_psz = _strdup(rhs.m_psz);
            ASSERT(m_psz != NULL);
        }
        return *this;
    }

    ~CWtoA()
    {
        free(m_psz);
    }

    size_t GetLength() const { return strlen(m_psz); }

    operator LPCSTR() const { return m_psz; }
};

//////////////////////////////////////////////////////////////////////////

#endif // !defined(_STRCONV_H_)


The usage of the classes are realy simple, here is an example:

...

#include "strconv.h"

BOOL IsWireless(LPCTSTR pszAdapter)
{
    ...
}

int _tmain(int argc, _TCHAR* argv[])
{
    char szAdapter[64];

    ...

    BOOL bIsWireless = IsWireless(CAtoT(szAdapter));

    ...

    return 0;
}

출처 : http://www.pocketpcdn.com/forum/viewtopic.php?t=11603
2008. 8. 20. 11:27

Win32에서 TRACE 매크로 문

아래 코드를 추가하면 Win32에서도 MFC에서 사용하는 TRACE를 똑같이 사용할 수 있다.
주의할점은 버퍼 사이즈 제한이 있다는 점.

#ifndef TRACE
#include <strsafe.h>
#include <assert.h>
#if defined(_DEBUG) || defined(FORCE_XTRACE)
#define TRACE_BUF_SIZE 4096
#define TRACE          _DbgPrintf
#ifndef ASSERT
#define ASSERT   assert
#endif
inline void __cdecl _DbgPrintf(LPCTSTR str, ...)
{
 TCHAR   buff[TRACE_BUF_SIZE];
 DWORD   dwError;
 va_list    ap;
 dwError = GetLastError();
 va_start(ap, str);
 StringCbVPrintf(buff, sizeof buff, str, ap);
 va_end(ap);
 OutputDebugString(buff);
 SetLastError(dwError);   
}
#else
#define TRACE 1 ? (void) 0 : _DbgPrintf
inline void _DbgPrintf(const char *str, ...) {}
#endif
#endif


2008. 8. 11. 10:04

Whats the difference between Control.Invalidate, Control.Update and Control.Refresh?

Before discussing each one of the above functions, let’s look at how winforms controls paint.

 

Windows controls paint is response to WM_PAINT messages. This message is sent when UpdateWindow or RedrawWindow is called, or by the DispatchMessage function when the application gets a WM_PAINT through the message queue. On getting the WM_PAINT message, the control paints its background and then the foreground if necessary. Double-buffering and transparency is honored while painting and then the OnPaint event is fired to give the user a chance to perform his custom painting.

 

With this background, let’s look at the above mentioned three functions in more detail,

 

Control.Invalidate( ) / Control.Invalidate(bool) / Control.Invalidate(Rectangle) / Control.Invalidate(Rectangle, bool) / Control.Invalidate(Region) / Control.Invalidate(Region, bool)

 

            The bool parameter denotes whether the user wants to invalidate the child controls of the control on which he is calling Invalidate. The Rectangle parameter are the bounds to invalidate and the region parameter is the region to invalidate. All the overloads essentially end up calling one of the RedrawWindow, InvaliateRect or InvalidateRgn functions. If RedrawWindow is called then this may result in a WM_PAINT message being posted to the application message queue (to invalidate the child controls).

            The important thing to note here is that these functions only “invalidate” or “dirty” the client area by adding it to the current update region of the window of the control. This invalidated region, along with all other areas in the update region, is marked for painting when the next WM_PAINT message is received. As a result you may not see your control refreshing (and showing the invalidation) immediately (or synchronously).

 

Control.Update()

 

Update function calls the UpdateWindow function which updates the client area of the control by sending WM_PAINT message to the window (of the control) if the window's update region is not empty. This function sends a WM_PAINT directly to WNDPROC() bypassing the application message queue.

Thus, if the window update region is previously “invalidated” then calling “update” would immediately "update" (and cause repaint) the invalidation.

 

Control.Refresh()

 

            By now, you might have guessed what Refresh( ) would be doing. Yes, it calls Invalidate(true) to invalidate the control and its children and then calls Update( ) to force paint the control so that the invalidation is synchronous.

출처 : http://blogs.msdn.com/subhagpo/archive/2005/02/22/378098.aspx

2008. 8. 10. 18:44

정규 DLL Implicit, Explicit 링킹에 대한 몇가지 잡담

아래 함수를 일단 링크하는 법에 대해서 알아보자.

==============================================================================
1. DEF 파일을 사용하여 EXPORT
==============================================================================

// 헤더 파일
#ifdef __cplusplus
extern "C" {          // 반드시 C 형태로 선언해야 한다.
#endif  
        void WINAPI func();    // WINAPI는 FAR PASCAL의 매크로로 CALLBACK과 같다.
#ifdef __cplusplus
}
#endif

// 소스 파일
void WINAPI func()
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState());   // DLL에서 MFC를 사용한다면..

        TRACE("Hello dll\n");
}

// DEF 파일
EXPORTS
    ; 명시적 내보내기를 여기에 사용할 수 있습니다.
    func        @2

와 같이 한 후,

Dll을 사용하는 프로그램에서,

프로젝트 종속성을 지정하여 위에서 만든 DLL의 라이브러리를 링크하든,
어쨋든 라이브러리 링크가 필요하다.

그리고, 위 DLL 생성시 선언한 헤더파일을 include 하든, 아래와 같이 선언하면 DLL에서
export 한 함수를 사용할 수 있다.

extern "C" void WINAPI func(void);

void CUseDllDlg::OnBnClickedButton1()
{
        // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
        func();
}

==============================================================================
2. __decl(dllexport) 사용하기
==============================================================================

// 헤더 파일
#define DLLEXPORT        __declspec(dllexport)

#ifdef __cplusplus
extern "C" {
#endif  

        DLLEXPORT void WINAPI func();

#ifdef __cplusplus
}
#endif

// 소스파일
... 1과  동일

DEF 파일은 건들지 않고,

extern "C" void WINAPI func(void);

void CUseDllDlg::OnBnClickedButton1()
{
        // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
        func();
}

또는

extern "C" __declspec(dllimport) void WINAPI func(void);

void CUseDllDlg::OnBnClickedButton1()
{
        // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
        func();
}

와 같이 사용할 수 있다.

만약 헤더 파일을 사용한다면,
아래와 같이 정의하면,,좀더 편하게 import, export 할 수 있다.

#ifdef _EXPORTING
   #define CLASS_DECLSPEC    __declspec(dllexport)
#else
   #define CLASS_DECLSPEC    __declspec(dllimport)
#endif

==============================================================================
3. EXPLICIT LINKING
==============================================================================
위 두방법은 모두 implicit 링킹으로 DLL과 함께 생성된 LIB 파일이랑 함수 선언이 반드시
필요하다. 이제 이런 Lib와 헤더파일이 필요없는 Explicit 링킹을 해보자.

LoadLiblary("test.dll")
GetProAddress(...)

를 이용하여 함수 포인터를 얻어서 사용하면 된다.

==============================================================================
확장 DLL에 대해서 또 알아보자.
==============================================================================


==> 확장 DLL은 MFC 프로젝트에서만 이용할 수 있으며, (위 3가지 방법은 아무데서나 사용가능)
      클래스를 사용할 수 있기 때문에, MFC만으로 개발하는 프로젝트에서는 확장 DLL을 사용하는 것이
      좀더 편리할 것이다.


==============================================================================
FAR에 관해서
==============================================================================
Any data pointers used in the API are explicit FAR pointers. Again, FAR, is not really necessary for Win32, but is

useful if you plan to compile the code for 16-bit Windows sometime in the future


==> 첨부는 정규 DLL 예제(DLLScreenCap.zip)와 확장 DLL 예제(dllhusk.zip)

2008. 7. 15. 07:49

ActiveX와 Application에서 Mutex 문제


문제 : MainThread에서 Mutext생성 후 wait 계열 함수로 소유권을 요청하고, 메인쓰레드에서 ActiveX를 생성하면, ActiveX 내에서 다시 동기화를 위해 Mutex를 생성한 후 소유권을 요청하는 데, 이 때 ActiveX 역시 소유권을 가짐. 의도하는 바는 ActiveX랑 메인쓰레드랑 동기화를 하려고 했으나, 아래 예제에서는 동기화가 되지 않음.


아래 첨부를 해결하자! (2003 빌드)



원인 :

ActiveX를 생성하는 Main Thread랑 ActiveX 컨트롤이 같은 쓰레드이므로 Wait 계열 함수에서 항상 뮤텍스에 대한 소유권을 가짐

해결 :

MainThread에서 CreateThread 함수를 이용하여 쓰레드를 생성하고 그 쓰레드 내에서 뮤텍스를 생성하면 메인쓰레드에서 Wait 계열 함수로 소유권을 요청해도 소유권이 반환되지 않으므로 정상적인 동기화가 동작함.
2008. 7. 7. 00:49

About Mouse

Mouse Cursor
A single-pixel point called the hot spot, a point that the system tracks and recognizes as the position of the cursor.

The window need not be active or have the keyboard focus to receive a mouse message.

You can use the SystemParametersInfo function with the SPI_GETMOUSE or SPI_SETMOUSE flag to retrieve or set mous speed.

Mouse Capture
An application can chage this behavior by using the SetCapture function to route mouse messages to a specific window. The window receives all mouse messages until the application calls the ReleaseCapture function or specifies another capture window, or until the user clicks a window created by another thread.

When the mouse capture changes, the system sends a WM_CAPTUECHANGED message to the window that is losing the mouse capture.

GetCapture
To determine whether one of its window has captured the mouse

Mouse ClickLock
This feature lets a user lock down the primary mouse button after a single click.

Mouse Configuration
An application can determine whether the system includes a mouse by passing the SM_MOUSEPRESENT value to the GetSystemMetrics function

Mouse Messages
The system converts mouse input events into messages and posts them to the appropriate thread's message queue. When mouse message are posted faster than a thread can process them, the system discards all but the most recent mouse message.

Client Area Mouse Messages
An application can call the TrackMouseEvent function to have the system send two other messages. It posts the WM_MOUSEHOVER message when the cursor hovers over the client area for a certain time period. It posts the WM_MOUSELEAVE message when the cursor leaves the client area.

Message Parameters
lParam
Indicates the position of the cursor hot spot. The low-order word indicates the x-coordinate of the hot spot, and the high-order word indicates the y-coordinate. The coordinates are specified in client coordinates.

wParam
Indicates the status of the other mouse buttons and the CTRL and SHIFT keys at the time of the mouse event.
The wParam parameter can be a combination of the following values.
Value Meaning
MK_CONTROL The CTRL key is down.
MK_LBUTTON The left mouse button is down.
MK_MBUTTON The middle mouse button is down.
MK_RBUTTON The right mouse button is down.
MK_SHIFT The SHIFT key is down.
MK_XBUTTON1 Windows 2000/Windows XP: the first X button is down.
MK_XBUTTON2 Windows 2000/Windows XP: the second X button is down.


Double-Click Messages
The system generates a double-click message when the user clicks a mouse button twice in quick succession.
When the user clicks a button, the system establishes a rectangle centered around the cursor hot spot. It also marks the time at which the click occurred.
When the user clicks the same button a second time, the system determines whether the hot spot is still within the rectangle and calculates the time elapsed since the first click.
If the hot spot is still within the rectangle and the elapsed time does not exceed the double-click time-out value, the system generates a double-click message.

An application can get and set double-click time-out values by using the GetDoubleClickTime and SetDoubleClickTime functions,
Alternatively the application can set the double-click-time-out-value by using the SPI_SETDOUBLECLICKTIME flag with the SystemParametersInfo function. It can also set te size of the rectangle that the system uses to detect double clicks by passing the SPI_SETDOUBLECLKWIDTH and SPI_SETDOUBLECLKHEIGHT flags to SystemParametersInfo.
Note, however, that setting the double-click-time-out vlaue and rectangle affects all applications.

An application-defined window does not, by default, receive double-click messages. Because of the system overhead involved in generating double-click messages, these messages are gnerated only for windows belonging to classes that have the CS_DBLCLKS class style.

A double-click message is always the third message in a four-message series.
For example, double-clicking the left mouse button generates the following message sequences:

1. WM_LBUTTONDOWN
2. WM_LBUTTONUP
3. WM_LBUTTONDBLCLK
4. WM_LBUTTONUP

Because a window always receives a button-down message before receiving a double-click message, an application typically uses a double-click message to extend a task it began during a button-down message.

Nonclient Area Mouse Messages
A window's nonclient area consists of its border, menu bar, title bar, scroll bar, window menu, minimize button, and maximize button.

A window must pass nonclient area mouse message to the DefWindowProc function to take advantage of the built-in mouse interface.

There is a corresponding nonclient area mouse message for each client area mouse message.

lParam
Unlike coordinates of client area mouse messages, the coordinaes are specified in screen coordinates rather than client coordinates.

wParam
contains a hit-test value, a value that indicates where in the nonclient area the mouse event occurred.

The WM_NCHITTEST Message
Whenever a mouse event occurs, the system sends a WM_NCHITTEST message to either the window that contains the cursor hot spot or the window that has captured the mouse. The system uses this message to determine whether to send a client area or nonclient area mouse message.
An application that must receive mouse movement and mouse button messages must pass the WM_NCHITTEST message to the DefWindowProc function.

The lParam parameter of the WM_NCHITTEST message contains the screen coordinates of the cursor hot spot. The DefWindowProc function examines the coordinates and returns a hit-test value that indicates the location of the hot spot. Te hit-test value can be one of the following values.

Value Location of hot spot
HTBORDER                                        In the border of a window that does not have a sizing border.
HTBOTTOM In the lower-horizontal border of a window.
HTBOTTOMLEFT In the lower-left corner of a window border.
HTBOTTOMRIGHT In the lower-right corner of a window border.
HTCAPTION In a title bar.
HTCLIENT In a client area.
HTCLOSE In a Close button.
HTERROR On the screen background or on a dividing line between windows (same as HTNOWHERE, except that the DefWindowProc function produces a system beep to indicate an error).
HTGROWBOX In a size box (same as HTSIZE).
HTHELP In a Help button.
HTHSCROLL In a horizontal scroll bar.
HTLEFT In the left border of a window.
HTMENU In a menu.
HTMAXBUTTON In a Maximize button.
HTMINBUTTON In a Minimize button.
HTNOWHERE On the screen background or on a dividing line between windows.
HTREDUCE In a Minimize button.
HTRIGHT In the right border of a window.
HTSIZE In a size box (same as HTGROWBOX).
HTSYSMENU In a System menu or in a Close button in a child window.
HTTOP In the upper-horizontal border of a window.
HTTOPLEFT In the upper-left corner of a window border.
HTTOPRIGHT In the upper-right corner of a window border.
HTTRANSPARENT In a window currently covered by another window in the same thread.
HTVSCROLL In the vertical scroll bar.
HTZOOM In a Maximize button.

Mouse Sonar
This feature briefly shows several concentric circles around the pointer when the user presses and releases the CTRL key.

SystemPrametersInfo
SPI_GETMOUSESONAR
SPI_SETMOUSESONAR

Mouse Vanish
This feature hides the pointer when the user is typing. The mouse pointer reappears when the user moves the mouse.

SstemParametersInfo
SPI_GETMOUSEVANISH
SPI_SETMOUSEVANISH

The Mouse Wheel
When an application processes the WM_MOUSEWHEEL message, it must return zero. When an application processes the MSH_MOUSEWHEEL message, it must return TRUE.

Detecting a Mouse with a Wheel
To determine if a mouse with a wheel is connected, call GetSystemMetrics(SM_MOUSEWHEELPRESENT)

ClipCursor()
To confine the cursor to the client area during the line drawing operation.

Processing a Double Click Message
To receive double-click messages, a window must belong to a window class that has the CS_DBLCLKS class style.

2008. 7. 6. 23:59

About Keyboard

Keyboard Input Model

Scan Code
Assigned to each key on a keyboard s a unique value
a device-dependent identifier for the key on the keyboard

Virtual-Key-Code
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 key board 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 trhread'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 Focus and Activation

GetFocus()
determine which of its windows (if any) currently has the keyboard focus

SetFocus()
A thread can give the keybord focus to one of its windows by calling this.

The concept of keyboard focus is related to that of the active window.

Active Window
The active window is the top-level window the user is currently working with.

SetActiveWindow()
A thread can activate a top-level window by using this

GetActiveWindow()
Determine whether a top-level window it created is active by using this

When one window is deactivated and another activated, the system sends the WM_ACTIVE message.
The low-order word of the wParam parameter is zero,
if the window is being deactivated and nonzero if it is being activated.

BlockInput() & SendInput()
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 wihle input is blocked will change the asynchronous keyboard input-state table.

Keystroke Messages
WM_KEYDOWN
WM_SYSKEYDOWN
WM_KEYUP
WM_SYSKEYUP

System and Nonsystem Keystrokes
The system makes a distinction between system keystrokes and nonsystem keystrokes.

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.

System Keystroke messages are primarily for use by the system rather than by an application.

Nonsystem Keystroke message are for use by application windows; the DefWindowProc function does nothing with them.

Virtual-Key codes Described
The wParam parameter of a keystroke message contains the virtual-key code of the key that was pressed or released.

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.

사용자 삽입 이미지

An applicatoin 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
Determine whether a keystroke message represents more than one keystroke.
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.

Scan Code
The scan code is the value that the keyboard hardware generates when the user presses a key.
a device dependent value

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 cluster to the left of the numeric keypad;
  the NUM LOCK key; the BRAEK(CTRL+PAUSE) key;
  the PRINT SCRN key; and divide(/) and ENTER keys in the numeric keypad.

The extended-key flag is set if the key is an extended key.

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
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.

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_KEUP and WM_SYSKEYUP messages.

Character Messages
To retrieve character codes, an application must include the TranslateMessage function in its thread message loop. TranslateMessage passes a WM_KEYDOWN or WM_SYSKEDOWN 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).

Nontsystem Character Messages
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.

Note the WM_CHAR uses 16-bit Unicode transformation format (UTF) while WM_UNICHAR uses UTF-32.

The wParam parameter of all character messages contains the character code of the character key that was pressed.

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.

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 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. If the subsequent keystroke generates a character that cannot be combined with a diacritic, the system generates two WM_CHAR mesages.

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 LT ke)

Key Status
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.

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 message. These functions include MapVirtualKey, ToAscii, ToUnicode and VkKeyScan.

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.

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.

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 typicall used to define a hot key that activates a window;

Simulating Input
To simulate an uninterruped series of user input events, use the SendInput function.

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.

If the return 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.

Translating Character Messages

MSG msg;
BOOL bRet;

while (( bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0) 
{
    if (bRet == -1);
    {
        // handle the error and possibly exit
    }
    else
    { 
        if (TranslateAccelerator(hwndMain, haccl, &msg) == 0) 
        { 
            TranslateMessage(&msg); 
            DispatchMessage(&msg); 
        } 
    } 
}

2008. 7. 5. 23:01

About Windows

윈도우에 대해서 기본적인 지식을 제공함

MSDN 2003 정리 자료 (영문)

키워드 정리 :
DesktopWindow; Client Area; Nonclient Area; GetDesktopWindow; SystemParametersInfo; SetWindowLong;
CreateWindowEx; GetWindowLong; WindowFromPoint; ChildWindowFromPoint; ChildWindowFromPointEx;
GetSystemMetrics; SM_CXMIN; SM_CYMIN; AdjustWindowRect; AdjustWindowRectEx; EnumWindows;
HWND; FindWindow; FindWindowEx; IsWindow; HWND_BROADCAST; HWND_DESKTOP; MapWindowPoints; CreateMutex; GetLastError; SW_SHOWDEFAULT; IsWindowUnicode; RegisterClass;
WM_NCCREATE; CREATESTRUCT; WM_PARENTNOTIFY; WM_CREATE; EnumThreadWindows;
GetWindowThreadProcessId; ShowWindowAsync; WS_CLIPSIBLING, WS_CLIPCHILDREN;  SetParent; GetParent; Descendant Window; IsChild; EnumChildWindows; Layered Window; WS_EX_TRANSPARENT; HWND_MESSAGE; Foreground Window; Background Window; GetForegroundWindow; SetForegroundWindow; LockSetForegroundWindow; AllowSetForegroundWindow; BroadcastSystemMessage; Owned Window; GW_OWNER; Z-Order; topmost window; WS_EX_TOPMOST; BringWindowtoTop; GetTopWindow; DeferWindowPos; GetTopWindow; GetNextWindow; SetActiveWindow; GetActiveWdinow; Active Window; WM_ACTIVEAPP, WM_ACTIVE; Disabled Window; EnableWindow; WM_ENABLE; IsEnabledWindow; Hidden Window; WM_SHOWWINDOW; IsWindowVisibile; ShowOwnedPopups; ArrangeIconicWindows; OpenIcon; ShowWindow; SetWindowPlacement; IsZoomed; IsIconic; GetWindowPlacement; WM_QUERYOPEN; WM_GETMINMAXINFO; WS_THICKFRAME; WM_SYSCOMMAND; ScreenToClient; MapWindowPoints; GetClientRect; CascadeWindow; tileWindow; WM_WINDOWPOSCHANGING; WM_WINDOWPOSCHANGED; WM_NCCALCSIZE; WM_CLOSE;


출처 : ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1042/winui/winui/windowsuserinterface/windowing/windows/aboutwindows.htm

2008. 7. 5. 22:57

Visual C++ Debugging 관련 MSDN 정리

<Contents>

삽입한 코드 디버깅        3
최적화된 코드 디버깅        3
디버그 빌드 구성에서 최적화를 설정하는 방법        3
DebugBreak        3
Assertion        3
CRT Assertion        4
printf 바꾸기        5
Heap 손상 확인        5
포인터 유효성 확인        5
Memory Block 확인        6
MFC Assertion        6
MFC ASSERT_VALID 및 CObject::AssertValid        6
논리 오류 찾기        9
결과 확인        9
오류 조건 테스트        9
Memory Leak Detection and Isolation        9
메모리 누수 탐지 기능 사용        10
_CrtSetDbgFlag        11
CRT 보고서 모드 설정        11
메모리 블록 형식 해석        11
메모리 할당 번호에 중단점 설정        11
조사식 창에서 메모리 할당 중단점을 설정        12
메모리 상태 비교        12
메모리 누수 및 잘못된 양수        13
ATL Debugging        14
COM+ 1.0 구성 요소 디버깅        14
QueryInterface 호출 디버깅        15
참조 횟수 추적        16
MFC Debugging 기술        16
AfxDebugBreak        16
TRACE 매크로        16
MFC의 메모리 누수 탐지        16
메모리 할당 추적        16
메모리 진단 사용        17
메모리 스냅샷 보기        18
메모리 통계 보기        19
개체 덤프        19
개체 덤프 해석        21
개체 덤프 사용자 지정        22
MFC 디버그 빌드 크기 줄이기        24
선택한 모듈의 디버그 정보로 FMC 응용 프로그램 빌드        24
CRT 디버깅 기술        26
CRT 디버그 라이브러리 사용        26
보고서 매크로        26
CRT 디버그 힙        27
메모리 관리 및 디버그 힙        27
디버그 힙의 블록 형식        29
디버그 힙        30
C++에서 디버그 힙 사용        32
힙 상태 보고 함수        33
힙 할당 요청 추적        34
디버그 후크 함수 작성        35
클라이언트 블록 후크 함수        35
할당 후크 함수        36
할당 후크 및 C 런타임 메모리 할당        37
보고서 후크 함수        38
Visual C++ 디버깅에 대한 질문과 대답        38
포인터가 메모리 주소를 손상시키는지 어떻게 알 수 있습니까?        38
포인터가 변경된 위치를 어떻게 찾을 수 있습니까?        39
함수를 수백 번 호출하는 경우 어떤 호출이 실패했습니까?        39
프로그램에서 단계별로 실행하는 경우 어떻게 포커스를 유지할 수 있습니까?        39
전경 프로그램을 디버깅 하는 동안 디버거 창을 어떻게 사용합니까?        39
NT 기호가 있는 Windows API 함수를 어떻게 디버깅 할 수 있습니까?        39
References        40


2008. 7. 5. 22:40

좌표공간과 변환

MSND에 나오는 문서내용을 정리


좌표공간그 그 공간간의 변환을 문서로 정리함. (영문)

키워드 정리 :

Coordinates Spaces and Transformation; World-space; Page-space; Device-space; Physical Device;
Translation; Scaling; Rotation; Shear; Reflection; SetWorldTransform; MM_ANISOTROPIC; M_ISOTROPIC;
MM_HIENGILSH; MM_LOENGLISH; MM_HIMETRIC; MM_LOMETRIC; MM_TWIPS; MM_TEST; Viewpost;
OffsetWindowOrgEx; OffsetViewportOrgEx; LPtoDP; DPtoLP; GM_ADVANCED; SetGraphicsMode;
SetWorldTransform;


2008. 7. 5. 22:33

크리티컬 섹션 초간단 클래스

class CCriticalSection
{
public:
    void Lock() { EnterCriticalSection(&m_sec); }
    void Unlock() { LeaveCriticalSection(&m_sec); }
    CCriticalSection() { InitializeCriticalSection(&m_sec); }
    ~CCriticalSection() { DeleteCriticalSection(&m_sec); }
    CRITICAL_SECTION m_sec;
};


생성자에서 바로 EnterCriticalSection 하는게 좀 걸리긴 하지만...
저 함수가 실패할리는 없겠지...

출처가 MSDN이라 그냥 써도 될듯함.

From MSDN :
2008. 7. 5. 22:30

정규 DLL Implicit, Explicit 링킹에 대한 몇가지 잡담

아래 함수를 일단 링크하는 법에 대해서 알아보자.

==============================================================================
1. DEF 파일을 사용하여 EXPORT
==============================================================================

// 헤더 파일
#ifdef __cplusplus
extern "C" {          // 반드시 C 형태로 선언해야 한다.
#endif  
        void WINAPI func();    // WINAPI는 FAR PASCAL의 매크로로 CALLBACK과 같다.
#ifdef __cplusplus
}
#endif

// 소스 파일
void WINAPI func()
{
        AFX_MANAGE_STATE(AfxGetStaticModuleState());   // DLL에서 MFC를 사용한다면..

        TRACE("Hello dll\n");
}

// DEF 파일
EXPORTS
    ; 명시적 내보내기를 여기에 사용할 수 있습니다.
    func        @2

와 같이 한 후,

Dll을 사용하는 프로그램에서,

프로젝트 종속성을 지정하여 위에서 만든 DLL의 라이브러리를 링크하든,
어쨋든 라이브러리 링크가 필요하다.

그리고, 위 DLL 생성시 선언한 헤더파일을 include 하든, 아래와 같이 선언하면 DLL에서
export 한 함수를 사용할 수 있다.

extern "C" void WINAPI func(void);

void CUseDllDlg::OnBnClickedButton1()
{
        // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
        func();
}

==============================================================================
2. __decl(dllexport) 사용하기
==============================================================================

// 헤더 파일
#define DLLEXPORT        __declspec(dllexport)

#ifdef __cplusplus
extern "C" {
#endif  

        DLLEXPORT void WINAPI func();

#ifdef __cplusplus
}
#endif

// 소스파일
... 1과  동일

DEF 파일은 건들지 않고,

extern "C" void WINAPI func(void);

void CUseDllDlg::OnBnClickedButton1()
{
        // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
        func();
}

또는

extern "C" __declspec(dllimport) void WINAPI func(void);

void CUseDllDlg::OnBnClickedButton1()
{
        // TODO: 여기에 컨트롤 알림 처리기 코드를 추가합니다.
        func();
}

와 같이 사용할 수 있다.

만약 헤더 파일을 사용한다면,
아래와 같이 정의하면,,좀더 편하게 import, export 할 수 있다.

#ifdef _EXPORTING
   #define CLASS_DECLSPEC    __declspec(dllexport)
#else
   #define CLASS_DECLSPEC    __declspec(dllimport)
#endif

==============================================================================
3. EXPLICIT LINKING
==============================================================================
위 두방법은 모두 implicit 링킹으로 DLL과 함께 생성된 LIB 파일이랑 함수 선언이 반드시
필요하다. 이제 이런 Lib와 헤더파일이 필요없는 Explicit 링킹을 해보자.

LoadLiblary("test.dll")
GetProAddress(...)

를 이용하여 함수 포인터를 얻어서 사용하면 된다.

==============================================================================
확장 DLL에 대해서 또 알아보자.
==============================================================================


==> 확장 DLL은 MFC 프로젝트에서만 이용할 수 있으며, (위 3가지 방법은 아무데서나 사용가능)
      클래스를 사용할 수 있기 때문에, MFC만으로 개발하는 프로젝트에서는 확장 DLL을 사용하는 것이
      좀더 편리할 것이다.


==============================================================================
FAR에 관해서
==============================================================================
Any data pointers used in the API are explicit FAR pointers. Again, FAR, is not really necessary for Win32, but is

useful if you plan to compile the code for 16-bit Windows sometime in the future


==> 첨부는 정규 DLL 예제(DLLScreenCap.zip)와 확장 DLL 예제(dllhusk.zip)


2008. 7. 5. 21:40

Using Layered Windows

Using Layered Windows

To have a dialog box come up as a translucent window, first create the dialog as usual. Then, on WM_INITDIALOG, set the layered bit of the window's extended style and call SetLayeredWindowAttributes with the desired alpha value. The code might look like this:

// Set WS_EX_LAYERED on this window 
SetWindowLong(hwnd, 
              GWL_EXSTYLE, 
              GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);

// Make this window 70% alpha
SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);

Note that the third parameter of SetLayeredWindowAttributes is a value that ranges from 0 to 255, with 0 making the window completely transparent and 255 making it completely opaque. This parameter mimics the more versatile BLENDFUNCTION of the AlphaBlend function.

To make this window completely opaque again, remove the WS_EX_LAYERED bit by calling SetWindowLong and then ask the window to repaint. Removing the bit is desired to let the system know that it can free up some memory associated with layering and redirection. The code might look like this:

// Remove WS_EX_LAYERED from this window styles
SetWindowLong(hwnd, 
              GWL_EXSTYLE,
              GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);

// Ask the window and its children to repaint
RedrawWindow(hwnd, 
             NULL, 
             NULL, 
             RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN);