Help with Win32: PostQuitMessage(0) doesn't post WM_QUIT apparently, because the message loop is not exited.

Ruby The Roobster michaeleverestc79 at gmail.com
Fri Aug 13 16:18:06 UTC 2021


On Friday, 13 August 2021 at 03:05:22 UTC, Mike Parker wrote:
> On Friday, 13 August 2021 at 00:30:59 UTC, Ruby The Roobster 
> wrote:
>
>>
>> When I run the program and close the window, the program still 
>> runs in background mode.  I don't know why this happens nor 
>> how to fix it.  Does anybody know what's going on?
>
> frame beat me to it, but it may well be that you're getting -1. 
> [The documentation][1] says that a window that has already been 
> destroyed will result in the `hWnd` parameter being invalid, 
> which will cause the function to return -1.
>
>
> [1]: 
> https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getmessage#return-value

After rewriting the program, I figured out that the bug was still 
there. Here is the function that causes it:
```d
public class Entity	{
	package:
		ulong id;
		wchar[] name;
		static ulong nextid;
		Point centre;
		Skeleton skeleton;
	public:
		this(ulong id, inout(wchar)[] name,Point centre, Skeleton 
skeleton)	{
			assert(init == true, "Error: dutils.entity has not been 
initialized yet. Call dutils.entity.initialize() to initialize 
the library.");
			this.id = id;
			this.name.length = name.length;
			for(uint i = 0; i < name.length; i++)	{
				this.name[i] = name[i];
			}
			this.nextid = this.id + 1;
			this.centre = centre;
			if(!skeleton.init)	{
				this.skeleton = skeleton;
			}
			entitytable.length +=1;
			entitytable[cast(uint)id] = this;
		}

		~this()	{
			entitytable.length -= 1;
			this.nextid -=1;
			did(cast(uint)this.id);
		}

		debug immutable(char)[] f() @property	{
			import std.format : format;
			return format("ID: %s, Name: %s, NextID: %s, Centre: %s, 
Skeleton: %s", this.id, this.name, this.nextid, this.centre, 
this.skeleton);
		}

		ulong Id() @property	{
			return id;
		}
}
```

Context for this: I am creating a module of my  own, and this is 
a class contained in the module.  You will notice that after 
calling this class' constructor anywhere in a Win32 API program, 
that the program doesn't close after the window is closed.

Here are the files:

entity.d:
```d
module dutils.entity;

debug { package import core.sys.windows.winuser; } //For 
MessageBoxA in debug messages...

public struct Skeleton	{
		public Face[] faces;
		package bool init = false;
		public this(Face[] faces)	{
			this.faces.length = faces.length;
			for(uint i = 0; i < faces.length; i++)	{
				this.faces[i] = faces[i];
			}
		}
		package this(bool init)	{
			this.init = init;
		}
		public void opAssign(Skeleton rhs)	{
			this.faces.length = rhs.faces.length;
			for(uint i; i < rhs.faces.length; i++)	{
				this.faces[i] = rhs.faces[i];
			}
			this.init = rhs.init;
		}
}

public struct Face	{
	Point[] points;
	Point centre;
	void opAssign(Face rhs)	{
		this.points.length = rhs.points.length;
		for(uint i = 0; i < rhs.points.length; i++)	{
			this.points[i] = rhs.points[i];
		}
		this.centre = rhs.centre;
	}
}

public struct Point	{
	real x;
	real y;
	real z;
	void opAssign(Point rhs)	{
		this.x = rhs.x;
		this.y = rhs.y;
		this.z = rhs.z;
	}
}

public class Entity	{
	package:
		ulong id;
		wchar[] name;
		static ulong nextid;
		Point centre;
		Skeleton skeleton;
	public:
		this(ulong id, inout(wchar)[] name,Point centre, Skeleton 
skeleton)	{
			assert(init == true, "Error: dutils.entity has not been 
initialized yet. Call dutils.entity.initialize() to initialize 
the library.");
			this.id = id;
			this.name.length = name.length;
			for(uint i = 0; i < name.length; i++)	{
				this.name[i] = name[i];
			}
			this.nextid = this.id + 1;
			this.centre = centre;
			if(!skeleton.init)	{
				this.skeleton = skeleton;
			}
			entitytable.length +=1;
			entitytable[cast(uint)id] = this;
		}

		~this()	{
			entitytable.length -= 1;
			this.nextid -=1;
			did(cast(uint)this.id);
		}

		debug immutable(char)[] f() @property	{
			import std.format : format;
			return format("ID: %s, Name: %s, NextID: %s, Centre: %s, 
Skeleton: %s", this.id, this.name, this.nextid, this.centre, 
this.skeleton);
		}

		ulong Id() @property	{
			return id;
		}
}

public class ImmobileEntity : Entity	{ //Same as Entity, but with 
a different constructor...
	public:
		this(inout(wchar)[] name, Point centre, Skeleton skeleton)
			in	{
				assert(skeleton.init == false, "Error: Member Skeleton.init 
must always be set to false.");
			}
			do	{
				super(this.nextid, name, centre, skeleton);
			}
}

public class MobileEntity : Entity	{ //Same as entity, but has a 
move feature(and a speed in miliseconds per whole number moved)...
	package:
		uint speed;
	public:
		this(inout(wchar)[] name, Point centre, Skeleton skeleton, uint 
speed)
			in	{
				assert(skeleton.init == false, "Error: Member Skeleton.init 
must always be set to false.");
			}
			do	{
				super(this.nextid, name, centre, skeleton);
				this.speed = speed;
			}

		void move(Point newpos)	{ //Distance between one point and 
another
			import core.thread;
			import core.math : sqrt;
			real temp = sqrt(((newpos.x - this.centre.x)^^2) + ((newpos.y 
- this.centre.y)^^2) + ((newpos.z - this.centre.z)^^2));
			temp *= speed;
			uint time = cast(uint) temp;
			Thread.sleep(dur!("msecs")(time));
			this.centre = newpos;
		}
}
package bool init;
package Entity entity;
public Entity[] entitytable;

public void initialize()	{
	entitytable.length = 0;
	init = true;
	entity = new Entity(0, "BaseEntityInitialized at id0"w, 
Point(0,0,0), Skeleton(true));
}

public void terminate()	{
	for(uint i = 0; i < entitytable.length; i++)	{
		destroy(entitytable[i]);
	}
	entitytable.length = 0;
}

package void did(uint currid)	{
	for(uint i = currid; i < entitytable.length; i++)	{
		entitytable[i].id -=1;
	}
}
debug	{
	public void DebugInfo()	{ //Debug messages...
		for(uint i = 0; i < entitytable.length; i++)	{
			MessageBoxA(null,cast(const(char)*)entitytable[i].f,"Test", 
MB_ICONINFORMATION | MB_OK);
		}
	}
}
```
test.d
```d
import core.sys.windows.windef;
import core.runtime;
import core.sys.windows.wingdi;
import core.sys.windows.winuser;
import entity;
bool exit = false;
extern(Windows)
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR 
lpCmdLine, int nCmdShow)	{
	int ret;
	try	{
		Runtime.initialize();
		ret = prog(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
		Runtime.terminate();
	}
	catch(Throwable o)	{
		MessageBoxA(null, "Error","Error", MB_OK | MB_ICONERROR);
	}
	debug MessageBoxA(null, "HERE!", "HERE!", MB_OK);
	return ret;
}

int prog(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR 
lpCmdLine, int CmdShow)	{
	WNDCLASSW wndclass;
	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = &WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = cast(HBRUSH)GetStockObject(GRAY_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = cast(const(wchar)*)"Test"w;
	if(!RegisterClassW(&wndclass))	{
		throw new Exception("F");
	}
	HWND hwnd = CreateWindowW(cast(const(wchar)*)"Test"w, 
cast(const(wchar)*)"Test Program"w, WS_OVERLAPPEDWINDOW, 
CW_USEDEFAULT, CW_USEDEFAULT, 900, 900, NULL, NULL, hInstance, 
NULL);
	ShowWindow(hwnd, CmdShow);
	UpdateWindow(hwnd);
	MSG msg;
	while((GetMessage(&msg,NULL,0,0)))	{
		if(exit)	{
			break;
		}
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		debug MessageBoxA(null, "HERE!", "HERE!", MB_OK);
	}
	return msg.wParam;
}

extern(Windows)
LRESULT WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM 
lparam) nothrow	{
	switch(msg)	{
		case WM_CREATE:
			try	{
				entity.initialize();
				Skeleton pyrskel;
				Face[4] pyrfaces;
				Point[3][2] pyrpoints;
				pyrpoints[0][0] = Point(0, 0, 1);
				pyrpoints[0][1] = Point(-1, 0, -1);
				pyrpoints[0][2] = Point(1, 0, -1);
				pyrpoints[1][0] = Point(0, 0.75, -0.5);
				pyrpoints[1][1] = Point(-1, 0, 0.5);
				pyrpoints[1][2] = Point(1, 0, -0.5);
				for(uint i = 0;i < 3;i++)	{
					pyrfaces[i].points.length = 3;
					for(uint j = 0;j < 3;j++)	{
						pyrfaces[i].points[j] = pyrpoints[1][j];
					}
				}
				for(uint i = 0;i < 3;i++)	{
					pyrfaces[3].points.length = 3;
					pyrfaces[3].points[i] = pyrpoints[0][i];
				}
				pyrskel = Skeleton(pyrfaces);
				auto Pyramid = new Entity(entitytable[0].Id+1,"Pyramid"w, 
Point(0,0,0), pyrskel);
			}
			catch(Throwable e)	{
				MessageBoxA(hwnd,"ERROR", "ERROR", MB_OK | MB_ICONERROR);
				PostQuitMessage(1);
			}
			return 0;
			break;
		case WM_CLOSE:
			try	{
				entity.terminate();
				DestroyWindow(hwnd);
			}
			catch(Throwable e)	{
				MessageBoxA(hwnd, "ERROR", "ERROR", MB_OK | MB_ICONERROR);
			}
			DestroyWindow(hwnd);
			exit = true;
			break;
		case WM_DESTROY:
			exit = true;
			PostQuitMessage(0);
			break;
		default:
		return DefWindowProc(hwnd,msg,wparam,lparam);
	}
		return 0;
}
```
Sorry for the inconvenience and longness of this post.

P.S: The issue isn't GetMessage(), I can confirm that.


More information about the Digitalmars-d-learn mailing list