当我发送WM_PAINT消息时,我想使用TextOut函数打印到win32屏幕,我是ASM编码器,并且我一直在为了解C ++上的类型转换而奋斗。
理论上我需要一个全局缓冲区来存储我的字符串并在我收到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 + Offset PainArray 告诉我它是一个指向我想要的实际类数组的数组(哪个好),但是在调试时将其初始化为0.如何初始化它?
另外我如何让编译器执行此操作:
MOV Edx, CurrentLine
Imul Edx, Edx, SizeOf PrintLines
Add Edx, Offset PaintArray
谢谢!
答案 0 :(得分:0)
好的......现在您的评论(下方)可以使用,我的答案对您来说更有用,因此我完全用我的新答案取而代之:
首先,您的PaintArray []缓冲区是一个指针数组,而不是PrintLines对象的数组。您访问它们的方式(使用 - &gt;运算符)也将它们视为指针,因此它会进行编译。但是你永远不会分配用于分配给缓冲区的真正的PrintLines对象,因此当你在调试器中打破vsprintf()调用时,你会找到NULL。
在我看来,确实没有必要动态分配这些PrintLines对象,因此您可以将PaintArray定义为PrintLine对象的数组并完成它(分配,即):
PrintLines PaintArray[MAX_PRINT_LINES]; // note this is an array of objects, not pointers
...但是当你访问它们时,你需要使用点运算符,而不是箭头运算符。我不知道/为什么你需要在“if(CurrentLine&gt; = MAX_PRINT_LINES)”中将这些对象清零,而且我不确定InvalidateRect()调用对你有什么作用。我也不确定为什么你不能只使用一个可以重复使用的全局PrintLines对象;不会在Print()被再次调用时使用,或者你在运行多个线程吗?如果你正在运行多个线程,那么我怀疑你的“if(CurrentLine&gt; = MAX_PRINT_LINES)”的正确性,除非你以某种方式通过某种同步机制调用Print()。知道在你再次调用Print()时所有缓冲的消息都已完成发送....其他一些代码会将CurrentLine设置为零,还是Print()是CurrentLine变量的唯一用户?如果Print()是它的唯一用户,那么(1),你可以使它成为局部静态变量而不是全局变量,(2),我认为你有一些真正的正确性问题:要么你真的不需要这些东西的数组,否则你不应该在CurrentLine回到0时清除它们。
一个结构良好的非平凡的C ++程序将其组件与公共和&amp; private(和protected,如果它在继承层次结构中)接口。这是通过课程完成的。类是与它们相关联的“方法”(函数)的数据结构。当你将代码分解成小的,有凝聚力的单元,并将整个项目构建为一起工作的对象集合,而不是将项目构建为单一的混乱代码时,事情就会非常顺利。来自asm背景,这对你来说将是一次重大飞跃。我知道,我开始编程asm ...对于英特尔8051,然后是8088,然后是摩托罗拉68K,以及PowerPC 850/860,有一点Sparc可以用来测量。从asm到C的步骤很少,如果你只是用C ++中的程序风格编程,这也不是一个大的飞跃,但如果你希望作为一个程序员有市场,你真的需要跨越面向对象节目。现在有OO狂热者主张以严格的OO风格进行编程,但是也有很多项目将其大部分组件实现为对象,但是他们的主要监督/控制代码是在程序代码中实现的,只是简单地使用对象......如果你能做到这一点,那可能是一个非常好的开始。
在这种情况下,我编写了一个代码版本,将缓冲区封装在一个环形缓冲区类中。这假设您实际需要的只是一个看似永无止境的PrintLines对象的缓冲区(只要消费者保持不变,因此环形缓冲区不会填满)。如果您正在尝试学习C ++,我建议您开始将内聚概念封装为类,这些类一旦实现并且已经实现。调试,有助于降低与不正确使用原始数据相关的其他代码中未来错误的可能性。下面的代码实现为包含所有静态数据和结构的结构。静态方法,这对于OO编程来说有点不寻常,但在这种情况下,你永远不需要多个这些“对象”(实际上在这种情况下你甚至不会有一个PaintBuffers“对象”,你只需要类和一堆静态数据和方法)。有些人会提倡在这种情况下使用“单身”模式,但是这会超过OO边缘而你并不需要这样做。这使您更接近OO思维,并且更容易直接从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();
}
答案 1 :(得分:-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);
}