2012-07-20 92 views
0

我想打印到使用TextOut函数的Win32屏幕,当我发送一个WM_PAINT消息时,我是一个ASM编码器,并且为了理解C++上的类型转换而奋斗了很多。打印到Win32屏幕

理论上我需要一个全局缓冲区来存储我的字符串,并在我得到一条WM_PAINT消息时逐一显示这些行,问题是我读过的,使用全局变量并不好。那么有没有另一种打印屏幕的方法,而不使用全局缓存来存储我的字符串?

而且,这里是我的另一个问题:

PrintLines *PaintArray[MAX_PRINT_LINES]; 
int CurrentLine; 

    void Print(HWND hWnd, int rgb, const char* string, ...) 
    { 
     MSG msg; 
     char buff[MAX_LINE_CHARS]; 
     if (CurrentLine >= MAX_PRINT_LINES) 
     { 
      CurrentLine = 0; 
      memset (*PaintArray, NULL, sizeof PaintArray); 
      InvalidateRect(hWnd, NULL, TRUE); 
     } 
     va_list argList; 
     va_start(argList, string); 
     PaintArray[CurrentLine]->stringlen = vsprintf(buff, string, argList); 
     va_end (argList); 
     PaintArray[CurrentLine]->rgb = rgb; 
     CurrentLine+=1; 
     msg.hwnd = hWnd; 
     msg.message = WM_PAINT; 
     DispatchMessage(&msg); 
    } 

当调试调用vsprintf中,我看到:

00412AD3 8B15 98B34100 MOV EDX,DWORD PTR DS:[CurrentLine] 
00412AD9 8B0495 20B34100 MOV EAX,DWORD PTR DS:[EDX*4+PaintArray] 
00412AE0 50    PUSH EAX 
00412AE1 FF15 6CC44100 CALL DWORD PTR DS:[<&MSVCR110D.vsprintf>>; MSVCR110.vsprintf 

EDX * 4 +偏移PainArray告诉我它的数组指向我想要的实际类数组(这很好),但是当调试它的初始化为0.我怎么能初始化它?

而且我怎么可以让编译器做到这一点:

MOV Edx, CurrentLine 
Imul Edx, Edx, SizeOf PrintLines 
Add Edx, Offset PaintArray 

的感谢!

回答

-1

香港专业教育学院做关于此问题的代码是正确的一个错误:

PrintLines *PaintArray[MAX_PRINT_LINES]; 
int CurrentLine; 

void Print(HWND hWnd, int rgb, const char* string, ...) 
{ 
    MSG msg; 
    if (CurrentLine >= MAX_PRINT_LINES) 
    { 
     CurrentLine = 0; 
     memset (*PaintArray, NULL, sizeof PaintArray); 
     InvalidateRect(hWnd, NULL, TRUE); 
    } 
    va_list argList; 
    va_start(argList, string); 
    PaintArray[CurrentLine]->stringlen = vsprintf(PaintArray[CurrentLine]->string, string, argList); 
    va_end (argList); 
    PaintArray[CurrentLine]->rgb = rgb; 
    CurrentLine+=1; 
    msg.hwnd = hWnd; 
    msg.message = WM_PAINT; 
    DispatchMessage(&msg); 
} 
+0

这是正确的工作功能,或只是在您的问题中的代码的“编辑”?如果这是正确的工作功能,那么你应该解释问题是什么,也许接受你自己的答案。如果只是编辑问题中的代码,则应编辑您的问题,并删除此“答案”。 – 2012-07-20 06:34:14

+0

@JoachimPileborg - 这是他的问题中的代码编辑。是的,他应该编辑他的问题并替换代码。 – phonetagger 2012-07-20 17:58:37

0

OK ......现在用您的评论(见下文)一起工作,我的回答将有希望更有用你,我完全用我的新答案替换它:

首先,你的PaintArray []缓冲区是一个指针数组,而不是一个PrintLines对象数组。您访问它们的方式(使用 - >运算符)也将它们视为指针,以便编译。但是你永远不会分配真正的PrintLines对象来分配给缓冲区,因此当你在调试器中调用vsprintf()时会发现NULL。

在我看来,这真的没有需要动态分配这些PrintLines对象,所以你也可以定义PaintArray作为打印行对象的数组,并用它做(分配,这是):

PrintLines PaintArray[MAX_PRINT_LINES]; // note this is an array of objects, not pointers 

...但随后在您访问它们时,您需要使用点运算符而不是箭头运算符。我不知道是否/为什么需要在“if(CurrentLine> = MAX_PRINT_LINES)”中将这些对象清零,并且我不确定InvalidateRect()调用会为您做什么。我也不确定为什么你不能只有一个全局的PrintLines对象,你不断重用;当Print()被再次调用,或者你正在运行多个线程时,它不会被完成吗?如果您正在运行多个线程,那么我对您的“if(CurrentLine> = MAX_PRINT_LINES)”东西的正确性怀疑,除非您通过某种同步机制以某种方式调用Print()知道所有缓冲的消息已完成发送再次调用Print()的时间....其他代码是否将CurrentLine设置为零,或者是Print()CurrentLine变量的唯一用户?如果Print()是它的唯一用户,那么(1),你可以使它成为一个局部静态变量而不是全局的,(2),我认为你有一些真正的正确性问题:要么你真的不需要这些东西的数组,否则当CurrentLine回到0时,你不应该全部清除它们。

一个结构良好的非平凡C++程序将其组件明确地与私有公共(如果它在继承层次结构中是受保护的)接口隔离开来。这是通过类来完成的。类是与它们绑定的“方法”(函数)的数据结构。当你将你的代码分解成小的,有凝聚力的单元,并且把你的整体项目建立为一个可以协同工作的对象集合,而不是把你的项目建立成单一的代码混乱的时候,事情会非常成功。从一个背景来看,这对你来说将是一个巨大的飞跃。我知道,我开始使用asm进行编程......对于Intel 8051,然后是8088,然后是Motorola 68K和PowerPC 850/860,并带有一点Sparc,以达到良好的效果。从asm到C的步骤很少,如果你只是用C++程序风格编程,那也不是什么大的飞跃,但是如果你希望作为程序员能够成为市场,那么你真的需要跨越到面向对象节目。现在有OO狂热者会提倡以严格的OO风格进行编程,但也有很多项目将其大部分组件作为对象实现,但其主要监督/控制代码在程序代码中实现,仅使用对象...这可能是一个非常好的开始,如果你能做到这一点的飞跃。

就这一点而言,我编写了一个代码版本,将您的缓冲区封装在环形缓冲区类中。这假设你实际需要的只是一个看似永无止境的PrintLines对象的缓冲区(只要消费者保持不变,因此环形缓冲区不会填满)。如果你正在尝试学习C++,我建议你开始将有凝聚力的概念封装成类,这些类一旦实现了&调试,就有助于减少其他代码中与未正确使用原始数据相关的未来错误的可能性。下面的代码是作为一个结构体来实现的,所有的静态数据都是静态方法,这对于OO编程来说有点不寻常,但在这种情况下,你永远不需要这些“对象”中的一个以上(实际上在这种情况下, t甚至有一个PaintBuffers“对象”,你只需要有一堆静态数据&方法)。有些人会主张在这种情况下使用“单身”模式,但这是通过面向对象的优势,而你并不真的需要这种模式。这让你更接近面向对象思维,并且更容易直接从asm代码访问(几乎)。我希望它对你有用。

#include <stdio.h> 
    #include <stdarg.h> 

    // ================================== This would go in a .h file..... 

    struct PrintLines // My own personal stand-in for whatever a "PrintLines" object is 
    { 
    int rgb; 
    int stringlen; 
    char string[400]; 
    }; 

    class PaintBuffers // encapsulates a circular buffer of PrintLines objects 
    { 
    public: // Public data: Anyone can have direct access to this stuff.... 
    static const unsigned int maxPrintLines = 4; // formerly #define MAX_PRINT_LINES 

    private: // Private data: Only this class's methods can access this stuff.... 
    static PrintLines PaintArray[maxPrintLines]; // note these are real objects, not pointers 
    static unsigned int producerIdx; // for data coming into this class 
    static unsigned int consumerIdx; // for data going out of this class 

    public: // Public methods: Anyone can call these methods.... 
    static int numUsedBuffers() { return (producerIdx-consumerIdx) % maxPrintLines; } 
       // Side note, but important: The % above gives us what we want only if the 
       // lhs (left-hand-side) is positive. One way to ensure that is by simply 
       // treating the terms as unsigned; even though subtracting a larger 
       // number from a smaller "wraps around" to a very large number, after 
       // the % operation we still get what we want, so there's no need to 
       // compute the absolute value of a signed subtraction if we just make 
       // them unsigned (or cast them as unsigned) in the first place. 
    static int numFreeBuffers() { return maxPrintLines - numUsedBuffers(); } 

    // Producer calls this: Get the next 'write' buffer (to write into it) 
    static PrintLines* getWriteBuf() 
    { 
     if (numFreeBuffers() > 1) // The >1 implements the "always keep one slot open" 
     {       // solution to the full/empty ambiguity problem, thus 
           // there will ALWAYS be at least one unused buffer. 
           // There are alternative solutions that allow use of 
           // that one last buffer, but none which results in 
           // more efficient code. 
      PrintLines* ret = &PaintArray[producerIdx]; 
      producerIdx = (producerIdx+1) % maxPrintLines; 
      // ...Note that if maxPrintLines is a power-of-2 (smart programmers only make 
      // circular buffers that are sized as powers-of-2), the compiler will 
      // automatically turn that % operation into an equivalent & for efficiency. 
      return ret; 
     } 
     else 
     { 
      return NULL; // Tell the caller there's no more buffer space. 
     } 
    } 

    // Consumer calls this: Get the next 'read' buffer (to read data from it) 
    static PrintLines* getReadBuf() 
    { 
     if (numUsedBuffers() > 0) 
     { 
      PrintLines* ret = &PaintArray[consumerIdx]; 
      consumerIdx = (consumerIdx+1) % maxPrintLines; 
      return ret; 
     } 
     else 
     { 
      return NULL; // Tell the caller there's no data available. 
     } 
    } 
    }; 

    // Because you can't (easily) call a C++ name-mangled function from assembly, 
    // I'll define a "C"-linkage interface to the PaintBuffers class below. Once 
    // your whole ASM project is ported to C++, you can blow the ASM interface away. 
    extern "C" int PaintBuffers_numUsedBuffers(); 
    extern "C" int PaintBuffers_numFreeBuffers(); 
    extern "C" PrintLines* PaintBuffers_getWriteBuf(); 
    extern "C" PrintLines* PaintBuffers_getReadBuf(); 


    // ================================== This would go in a .cpp file..... 

    // In the .h file, we declared that there are such functions (somewhere), now 
    // we need to actually define them.... 
    extern "C" int PaintBuffers_numUsedBuffers() { return PaintBuffers::numUsedBuffers(); } 
    extern "C" int PaintBuffers_numFreeBuffers() { return PaintBuffers::numFreeBuffers(); } 
    extern "C" PrintLines* PaintBuffers_getWriteBuf() { return PaintBuffers::getWriteBuf(); } 
    extern "C" PrintLines* PaintBuffers_getReadBuf() { return PaintBuffers::getReadBuf(); } 

    // In the .h file, we declared that there are such variables (somewhere), now 
    // we need to actually define them.... 
    PrintLines PaintBuffers::PaintArray[PaintBuffers::maxPrintLines]; 
    unsigned int PaintBuffers::producerIdx=0; 
    unsigned int PaintBuffers::consumerIdx=0; 

    // Note that all of the PaintBuffers class's methods were defined inline in the 
    // class itself. You could also just declare them there (in the class definition), 
    // and define them here in a .cpp file. 


    void Print(/*HWND hWnd,*/ int rgb, const char* string, ...) 
    { 
    PrintLines* PaintObject = PaintBuffers::getWriteBuf(); 
    if (!PaintObject) // Is it NULL? 
    { // What should we do if there is no more buffer space??? 
     return; // I guess just do nothing... Lost message. 
    } 
    // TODO: Is this needed somehow?.... InvalidateRect(hWnd, NULL, TRUE); 
    // MSG msg; 
    va_list argList; 
    va_start(argList, string); 
    PaintObject->stringlen = vsnprintf(PaintObject->string, sizeof(PaintObject->string)-1, string, argList); 
    va_end (argList); 
    PaintObject->rgb = rgb; 
    // msg.hwnd = hWnd; 
    // msg.message = WM_PAINT; 
    // DispatchMessage(&msg); 
    } 


    void Consume() // ...my stand-in for whatever your consumer is (still in ASM?) 
    { 
    PrintLines* PaintObject = PaintBuffers::getReadBuf(); 
    if (PaintObject) // Was it non-NULL? 
    { 
     printf("Consume(): Got \"%s\"\n", PaintObject->string); 
    } 
    else // This is only here to show that we did get NULL..... 
    { 
     printf("Consume(): Got NULL! (no buffers with data in them)\n"); 
    } 
    } 


    int main() 
    { 
    Consume(); 
    Consume(); 
    Print(0x11111111, "The %dst message.", 1); 
    Print(0x11111111, "The %dnd message.", 2); 
    Consume(); 
    Consume(); 
    Print(0x11111111, "The %drd message.", 3); 
    Print(0x11111111, "The %dth message.", 4); 
    Consume(); 
    Print(0x11111111, "The %dth message.", 5); 
    Consume(); 
    Consume(); 
    Consume(); 
    Consume(); 
    Consume(); 
    Print(0x11111111, "The %dth message.", 6); 
    Print(0x11111111, "The %dth message.", 7); 
    Print(0x11111111, "The %dth message.", 8); 
    Print(0x11111111, "The %dth message.", 9); // ...will be lost (no more buffer space) 
    Print(0x11111111, "The %dth message.", 10); // ...will be lost (no more buffer space) 
    Print(0x11111111, "The %dth message.", 11); // ...will be lost (no more buffer space) 
    Consume(); 
    Consume(); 
    Consume(); 
    Consume(); 
    Consume(); 
    Consume(); 
    Consume(); 
    } 
+0

我正试图移植我在ASM(MASM)编译器中创建的源代码;这个想法是有一个字符串的缓冲区,当WM_PAINT消息通过DispatchMessage获得时,会得到字符串的缓冲区。问题是,如果我在“本地”(堆栈缓冲区)中执行,那么使用TextOut处理和打印消息的函数将无法访问本地缓冲区(因为它在函数结束后被清除)。所以我需要一个全局缓冲区。问题是我不知道如何声明一个PrintLines类的全局数组......或者至少得到数组位置的地址。 – ffenix 2012-07-20 04:03:10