【Win32多线程】创建线程、获取线程执行状态、退出线程、错误处理

VC++多线程控制动态图标:Win32 API实现与线程同步详解 多线程编程是提升桌面应用响应性和用户体验的核心技术,其原理在于将耗时任务与用户界面操作分离到不同执行流,避免主线程阻塞导致的界面卡顿。在Windows平台,通过Win32 API的CreateThread函数创建工作线程,结合事件、临界区等同步机制,可以实现高效的后台任务处理。这一技术对于需要实时反馈的应用场景价值显著,例如文件传输、数据计算或网络请求过程中的状态指示。本文聚焦于利用VC++和Win32线程模型,实现一个由工作线程驱动的动态图标控件,详细解析了从线程创建、自定义消息通信到双缓冲绘图消除闪烁的 阅读详情

产生一个线程:

HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD描述施行与这一新线程的security属性,NULL表示使用缺省值,在windows 95中忽略该参数
  DWORD dwStackSize,                        // initial stack size新线程拥有的堆栈大小,0表示缺省大小,1MB
  LPTHREAD_START_ROUTINE lpStartAddress,    // thread function  函数指针
  LPVOID lpParameter,                       // thread argument  传递到线程函数的参数
  DWORD dwCreationFlags,                    // creation option  允许产生一个暂时挂起的线程,默认为立即运行
  LPDWORD lpThreadId                        // thread identifier  //新的线程ID会被传回到这里
);



第一个使用线程范例:
//多线程出现的执行混乱问题


#define WIN32_LEAN_AND_MEAN
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>


DWORD WINAPI ThreadFunc(LPVOID); //线程标准函数形式
//#define  WINAPI _stdcall


int main()
{
  HANDLE hThrd;
  DWORD threadId;
  int i;
  for(i=0;i<5;i++)
  {
	  hThrd=CreateThread(NULL,0,ThreadFunc,(LPVOID)i,0,&threadId);
     //返回一个核心对象hThrd
	  if(hThrd)
	  {
		  printf("Thread Start %d\n",i);
		  CloseHandle(hThrd);
	  }
  }


  Sleep(2000); //等待这些线程完成,不加这句,主线程将结束,其他线程将无法完成
  return EXIT_SUCCESS;
}


DWORD WINAPI ThreadFunc(LPVOID n)
{
  int i;
  for(i=0;i<10;i++)
  {
	  printf("%d%d%d%d%d%d%d%d\n",n,n,n,n,n,n,n,n);
  }
  return 0;
}




核心对象:CreateThread()返回的handle被称为一个核心对象(kernel object).其和所谓的GDI对象,如画笔、画刷或DC差不多,前者由KERNEL32.DLL管理,后者由GDI32.DLL管理。所谓handle就是一个


指针,指向操作系统内存空间的某样东西,那东西不允许你直接 取得。
Win32核心对象清单:
1.进程(processes),线程(threads),文件(files),事件(events),信号量(semaphores),互斥器(mutexes),管道(Pipes,分为named和anonymous两种)
这些核心对象可以用来整合许多的线程或进程。
GDI对象和核心对象的主要区别:GDI的对象有单一拥护者,不是进程就是线程。核心对象可以有一个以上的拥有者,甚至可以跨进程。


BOOL CloseHandle(
  HANDLE hObject   // handle to object
);

不关闭可能导致资源泄露。不可以依赖“因线程的结束而清理所有被这一线程产生的核心对象”。许多对象,如文件,是被进程所拥有,而非线程拥有,在进程结束之前不能够清理它们。

线程对象与线程的不同:
线程的handle是指向“线程核心对象”,而不是指向线程本身。当调用CloseHandle()并给一个线程handle时候,就是吧引用计数减1.如果该值变为0,对象会自动被操作系统销毁。

判断线程是否结束:
BOOL GetExitCodeThread(
  HANDLE hThread,      // handle to the thread
  LPDWORD lpExitCode   // termination status
);


如果线程结束,结束代码会被放在lpExitCode参数中带回来。如果没有结束,lpExitCode的值是STILL_ACTIVE.
如果成功,返回True,否则返回False;
GetExitCodeThread将传回线程函数ThreadFunc的返回值。
GetExitCodeThread等待一个线程的结束,但这并不是最好的方法


第二个线程范例:
// 示例GetExitCodeThread的用法,获取线程的状态
#define WIN32_LEAN_AND_MEAN
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<conio.h>
DWORD WINAPI ThreadFunc(LPVOID); //线程标准函数形式


int main()
{
	
  HANDLE hThrd1;
  HANDLE hThrd2;
  DWORD exitCode1=0;
  DWORD exitCode2=0;


  DWORD threadId;


  hThrd1=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId);


  if(hThrd1)
	  printf("Thread 1 launched \n");


  hThrd2=CreateThread(NULL,0,ThreadFunc,(LPVOID)2,0,&threadId);
  if(hThrd2)
	  printf("Thread 2 launched \n");


  for(;;)
  {
	  printf("Press any key to exit..\n");
	  getch();
	  	  //GetExitCodeThread等待一个线程的结束,但这并不是最好的方法
	  GetExitCodeThread(hThrd1,&exitCode1); //GetExitCodeThread将传回线程函数ThreadFunc的返回值
	  GetExitCodeThread(hThrd2,&exitCode2);


	  if(exitCode1==STILL_ACTIVE)
		  puts("Thread 1 is still running ...");
	  if(exitCode2==STILL_ACTIVE)
		  puts("Thread 2 is still running ...");


	  if(exitCode1!=STILL_ACTIVE&&exitCode2!=STILL_ACTIVE)
		  break;




  }
  CloseHandle(hThrd1);
  CloseHandle(hThrd2);
  printf("Thread 1 returned %d\n",exitCode1);
  printf("Thread 2 returned %d\n",exitCode2);


 return  EXIT_SUCCESS;






}
DWORD WINAPI ThreadFunc(LPVOID n)
{
	Sleep((DWORD)n*1000*2);
	return (DWORD)n*2;
}


结束一个线程:

前面是靠线程函数的结束而结束线程。有时候需要更强制性的手法结束一个线程要用ExitThread();
VOID ExitThread(
  DWORD dwExitCode   // exit code for this thread 指示此线程之结束代码
);



它可以在任何时候被调用并且绝对不会返回。任何代码若放在此行之下,保证不会被执行。


如果线程还在运行而我的程序的结束了,会怎么样??
结束主线程:程序启动后就执行一个线程称为主线程,主线程有两个特点,第一,负责GUI程序中的主消息循环。第二,这一线程的结束(不论是因为返回或因为调用了ExitThread())会使程序中的所有


线程都被强迫结束,程序也因此结束。其他线程没有机会做清理工作。故在main或WinMain结束之前,总是先等待所有的线程都结束。
建议在主线程中不要调用ExitThread();


第三个线程范例:怎么结束一个线程
#define WIN32_LEAN_AND_MEAN
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
DWORD WINAPI ThreadFunc(LPVOID); //线程标准函数形式
void AnotherFunc(void);


int main()
{
 HANDLE hThrd;
 DWORD exitCode=0;
 DWORD threadId;
 hThrd=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId);


  if(hThrd)
	  printf("Thread launched.. \n");


  for(;;)
  {
	  bool rc;
	  rc=GetExitCodeThread(hThrd,&exitCode);
	  if(rc&&exitCode!=STILL_ACTIVE)
		  break;
  }


  CloseHandle(hThrd);
  printf("Thread  returned %d\n",exitCode);
   return  EXIT_SUCCESS;




}


DWORD WINAPI ThreadFunc(LPVOID n)
{
  printf("Thread running ..\n");
  AnotherFunc(); //调用一个函数,退出该线程
  return 0;
}


void AnotherFunc()
{
printf("About to exit Thread ..\n");
ExitThread(4);
printf("This will never print ..\n"); //这一行不会被打印
}






/****************错误处理*********************/
可以调用GetLastError获得描述
什么是MTVERIFY?
它是一个宏,这个宏内部记录并解释Win32 GetLastError()的结果。如果Win32函数失败,MTVERIFY()会打印一段简短的文字说明。


如何使用MTVERIFY的范例:
错误处理函数
//示例错误处理
#define WIN32_LEAN_AND_MEAN
#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
//////////////////MTVERIFY宏进入////////////
#include "MtVerify.h"
DWORD WINAPI ThreadFunc(LPVOID);


int main()
{
  HANDLE hThrd;
 DWORD exitCode=0;
 DWORD threadId;
 MTVERIFY(hThrd=CreateThread(NULL,0,ThreadFunc,(LPVOID)1,0,&threadId));


if(hThrd)
	  printf("Thread launched.. \n");


MTVERIFY(CloseHandle(hThrd));


for(;;)
  {
	  bool rc;
	  MTVERIFY(rc=GetExitCodeThread(hThrd,&exitCode));
	  if(rc&&exitCode!=STILL_ACTIVE)
		  break;


  }
printf("Thread  returned %d\n",exitCode);
	return EXIT_SUCCESS;


}


DWORD WINAPI ThreadFunc(LPVOID n)
{
  printf("Thread running ..\n");
  return 0;
}




/**  
* MtVerify.h  
*  
* Error handling for applications in  
* "Multitheading Applications in Win32"  
*  
* The function PrintError() is marked as __inline so that it can be  
* included from one or more C or C++ files without multiple definition  
* errors. For the examples in this book, this works fine.  
* To use the PrintError() in an application, it should be taken out,  
* placed in its own source file, and the "__inline" declaration removed  
* so the function will be globally available.  
*/  
#pragma comment( lib, "USER32" )   
#include <stdlib.h>   
#include <crtdbg.h>   
#define MTASSERT(a) _ASSERTE(a)   
// 宏定义 __FILE__ 与__LINE__都是预处理符号提供错误信息的描述   
// 如果a返回FALSE就执行PrintError函数   
#define MTVERIFY(a) if (!(a)) PrintError(#a,__FILE__,__LINE__,GetLastError())   
__inline void PrintError(LPSTR linedesc, LPSTR filename, int lineno, DWORD errnum)   
{   
	LPSTR lpBuffer;   
	char errbuf[256];   
#ifdef _WINDOWS   
	char modulename[MAX_PATH];   
#else // _WINDOWS   
	DWORD numread;   
#endif // _WINDOWS   
	// 把从GetLastError()返回的错误码转化为错误信息    
	FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER   
		| FORMAT_MESSAGE_FROM_SYSTEM,   
		NULL,   
		errnum,   
		LANG_NEUTRAL,   
		(LPTSTR)&lpBuffer,   
		0,   
		NULL );   
	wsprintfA(errbuf, "\nThe following call failed at line %d in %s:\n\n"  
		" %s\n\nReason: %s\n", lineno, filename, linedesc, lpBuffer);   
	// 如果是console程序就输出信息到控制台上
#ifndef _WINDOWS   
	WriteFile(GetStdHandle(STD_ERROR_HANDLE), errbuf, strlen(errbuf), &numread, FALSE );   
	// 等待3秒钟是为了使用者看到出错信息   
	Sleep(3000);   
	// 如果是窗口程序就一弹出对话框的形式输出错误信息
#else   
	// 当前exe文件的全路径   
	GetModuleFileName(NULL, modulename, MAX_PATH);   
	// 置弹出窗口在最上层以免被忽略   
	MessageBox(NULL, errbuf, modulename, MB_ICONWARNING|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);   
#endif   
	// 把结束代码EXIT_FAILURE 交给操作系统
	exit(EXIT_FAILURE);   
}  



多线程综合实例:后台打印,建立一个Win32 Application 

/*多线程后台打印范例*/


/******************************/
/*
 * 
 *
 * Sample code for "Multithreading Applications in Win32"
 * This is from Chapter 2, Listing 2-3
 *
 * Demonstrates background printing
 */

#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include "resource.h"
#include "MtVerify.h"

//
// Macro definitions
//
#define WM_SHOWBITMAP   WM_APP

#define MAX_PRINT_JOBS  64

//
// Structures
//
typedef struct
{   // Information passed to background thread for printing
    HWND hDlg;
    HWND hWndParent;
    HDC hDc;
    BOOL bPrint;    // TRUE if printing;
    char szText[256];
} ThreadPrintInfo;

//
// Global variables
//
HANDLE hInst;
HBITMAP gbmpDisplay;
RECT gDisplayRect;

int gNumPrinting = 0;

// Handle to each created thread
HANDLE gPrintJobs[64]; //保存已经创建的线程

// Height of bitmap returned by DrawText
int iHeight;

// HWND of the dialog so other threads can find it.
HWND hDlgMain;


//
// Function declarations
//
int APIENTRY WinMain(HINSTANCE hInstance/*当前程序运行实例的句柄,每个程序可以运行多个实例*/,
				   HINSTANCE hPrevInstance, /*当前实例前一个实例的句柄 ,win32下为NULL*/
				   LPSTR lpCmdLine, /*指定传递给命令行的参数*/
				   int nCmdShow/*指定程序窗口应该如何显示,如最大化等*/);
LRESULT CALLBACK MainWndProc(HWND hWnd/*标志接受消息的具体窗口*/, unsigned msg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam);
void PrintDlg_OnCommand(HWND hDlg, int id, HWND hwndCtl, UINT codeNotify);
void PrintDlg_OnPaint(HWND hwnd);
void PrintText(HWND hwndParent, char *pszText);
void PrintToDisplay(HWND hwndParent, char *pszText);
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
DWORD WINAPI BackgroundPrintThread(LPVOID pVoid);


///////////////////////////////////////////////////////////
//
//      WinMain
//
// Main entry point of application. This will be a
// dialog based app, not a normal window, so this
// routine acts a little differently than "normal".
//
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
    MSG     msg;
    HWND    hWnd;
    WNDCLASS wc;
	int index;

    hInst = hInstance;
   //1.设计一个窗口类
    if (!hPrevInstance)
    {
        memset(&wc, 0, sizeof(wc));
        wc.lpfnWndProc  = MainWndProc;
        wc.hInstance    = hInstance;
        wc.hIcon        = LoadIcon (hInstance, "GenIco");
        wc.hCursor      = LoadCursor(NULL,IDC_ARROW);
        wc.hbrBackground= GetSysColorBrush(COLOR_BACKGROUND);
        wc.lpszMenuName = "PRINTING_MENU";
        wc.lpszClassName= "PrintDlgClass";
        if (!RegisterClass(&wc))
            return FALSE;
    }

//2.创建窗口,并返回创建成功后的窗口句柄

    hWnd = CreateWindow(
        "PrintDlgClass",
        "Background Printing",
        WS_OVERLAPPED|WS_CAPTION|WS_MINIMIZEBOX|WS_SYSMENU,
        CW_USEDEFAULT, // At this point we do not want to
        0,             //  show the window until we know
        0,             //  how big the Dialog Box is so
        0,             //  that we can fit the main window
        NULL,          //  around it.
        NULL,
        hInstance,
        NULL);

	    //创建打印对话框
    hDlgMain = CreateDialog(hInst,
                    MAKEINTRESOURCE(IDD_PRINT),
                    hWnd, PrintDlgProc);


	//3.显示及刷新窗口
    ShowWindow(hWnd, nCmdShow);
    ShowWindow(hDlgMain, SW_SHOW);
    //4.消息循环

	while (GetMessage(&msg, NULL, 0, 0))
	{	// Get Next message in queue
		if(hDlgMain == NULL || !IsDialogMessage(hDlgMain,&msg))
		{
			TranslateMessage(&msg); /* Translate virtual key codes */
			DispatchMessage(&msg);	/* Dispatches message to window */
		}
	} // end while

	// Wait for all threads to terminate. The Window will
    // have already disappeared by this point.
	//等待各个线程结束
	for (index = 0; index < gNumPrinting; index++)
	{
		DWORD status;
		do 
		{	// Wait for thread to terminate
			GetExitCodeThread(gPrintJobs[index], &status);
			Sleep(10);
		} while (status == STILL_ACTIVE);

	} // end for

	return (msg.wParam);  /* Returns the value from PostQuitMessage */
}


//主窗口回调函数
LRESULT CALLBACK MainWndProc(HWND hWnd, unsigned msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        break;

    case WM_COMMAND:

        switch (wParam)
        {
        case IDM_ABOUT:
            DialogBox(hInst, "AboutBox", hWnd, (DLGPROC)About);
            break;
        case IDM_EXIT:
            PostQuitMessage(0);
            break;
        default:
            return (DefWindowProc(hWnd, msg, wParam, lParam));
        }

    case WM_SETFOCUS:
        // ensure that the Dialog Box has the focus
        SetFocus(hDlgMain);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:
        return DefWindowProc(hWnd, msg, wParam, lParam);

    }
    return 0;
}


LRESULT CALLBACK PrintDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_CLOSE:
        DestroyWindow(hDlg);
        hDlgMain = NULL;
        break;
        
    case WM_DESTROY:
        return TRUE;
        break;

    case WM_SHOWBITMAP:
        if (gbmpDisplay)
            DeleteObject(gbmpDisplay);

        gDisplayRect = *(RECT*)wParam;
        gbmpDisplay = (HBITMAP) lParam;
        InvalidateRect(hDlgMain, NULL, TRUE);
        break;

    HANDLE_MSG(hDlg, WM_INITDIALOG, PrintDlg_OnInitDialog);
    HANDLE_MSG(hDlg, WM_COMMAND, PrintDlg_OnCommand);
    HANDLE_MSG(hDlg, WM_PAINT, PrintDlg_OnPaint);

    default:
        return (FALSE);
    }

    return 0;
}

BOOL PrintDlg_OnInitDialog(HWND hwndDlg, HWND hwndFocus, LPARAM lParam)
{
    RECT rect;

    // Size parent to fit this dialog
    GetWindowRect(hwndDlg, &rect); 
    SetWindowPos(GetParent(hwndDlg),NULL,
        0,0,
        rect.right-rect.left,
        rect.bottom-rect.top+GetSystemMetrics(SM_CYMENU)
            +GetSystemMetrics(SM_CYCAPTION),
        SWP_NOMOVE | SWP_NOZORDER);

    return TRUE;
}

void PrintDlg_OnCommand(HWND hDlg, int id,HWND hwndCtl, UINT codeNotify)
{
    char szText[256];

    switch (id)
    {
    case IDC_PRINT:
        GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
        PrintText(hDlg, szText);
        break;

    case IDC_DISPLAY:
        GetDlgItemText(hDlg, IDC_EDIT_TEXT, szText, 256);
        PrintToDisplay(hDlg, szText);
        break;

    case IDCANCEL:
    case IDM_EXIT:
        PostMessage(GetParent(hDlg),WM_DESTROY,
                        (WPARAM)0, (LPARAM)0);
        DestroyWindow(hDlgMain);
        hDlgMain = NULL;
        break;
        
    default:
        break;
    }
}

void PrintDlg_OnPaint( HWND hwnd )
{
    PAINTSTRUCT paint;
    HWND hwndCtrl;
	HDC hdc;
    HDC hDcMem;
    HBITMAP bmpOld;
    RECT rect;
    POINT point;

	if (!gbmpDisplay)
		return;

    hwndCtrl = GetDlgItem(hwnd, IDC_OUTPUT);

    hdc = BeginPaint(hwnd, &paint);

    GetWindowRect(hwndCtrl, &rect);
    point = *((POINT *)&rect);
    ScreenToClient(hwnd, &point);

    hDcMem = CreateCompatibleDC(NULL);
    bmpOld = SelectObject(hDcMem, gbmpDisplay);

    // Copy bitmap to screen
    MTVERIFY( BitBlt(hdc, point.x+10, point.y+40,
        gDisplayRect.right-gDisplayRect.left, gDisplayRect.bottom-gDisplayRect.top,
        hDcMem, iHeight, 0, SRCCOPY) );

    SelectObject(hDcMem, bmpOld);
    DeleteDC(hDcMem);

    EndPaint(hwnd, &paint);
}

//
// Asks user which printer to use, then creates
// background printing thread.
//
void PrintText(HWND hwndParent, char *pszText)
{
    ThreadPrintInfo *pInfo;
    HANDLE hThread;
    DWORD dwThreadId;
    int result;
    DOCINFO docInfo;

    PRINTDLG dlgPrint;

    // Put up Common Dialog for Printing and get hDC.
    memset(&dlgPrint, 0, sizeof(PRINTDLG));
    dlgPrint.lStructSize = sizeof(PRINTDLG);
    dlgPrint.hwndOwner = hwndParent;
    dlgPrint.Flags = PD_ALLPAGES | PD_USEDEVMODECOPIES
           | PD_NOPAGENUMS | PD_NOSELECTION | PD_RETURNDC;
    dlgPrint.hInstance = hInst;
    if (!PrintDlg(&dlgPrint))
        return;

    // Initialize Printer device
    docInfo.cbSize = sizeof(DOCINFO);
    docInfo.lpszDocName = "Background Printing Example";
    docInfo.lpszOutput = NULL;
    docInfo.lpszDatatype = NULL;
    docInfo.fwType = 0;
    result = StartDoc(dlgPrint.hDC, &docInfo);
    result = StartPage(dlgPrint.hDC);

    pInfo = HeapAlloc(GetProcessHeap(),
                      HEAP_ZERO_MEMORY,
                      sizeof(ThreadPrintInfo));
    pInfo->hDlg = hwndParent;
    pInfo->hWndParent = hwndParent;
    pInfo->hDc = dlgPrint.hDC;
    pInfo->bPrint = TRUE;
    strcpy(pInfo->szText, pszText);

    MTVERIFY( hThread = CreateThread(NULL, 0,
        BackgroundPrintThread, (LPVOID)pInfo,
        0, &dwThreadId ));

	// keep track of all background printing threads
    gPrintJobs[gNumPrinting++] = hThread;
}

//
// Shows output on the dialog box.
//
void PrintToDisplay(HWND hwndParent, char *pszText)
{
    ThreadPrintInfo *pInfo;
    DWORD dwThreadId;
    HANDLE hThread;

    pInfo = HeapAlloc(GetProcessHeap(),
                      HEAP_ZERO_MEMORY,
                      sizeof(ThreadPrintInfo));
    pInfo->hDlg = hwndParent;
    pInfo->hWndParent = GetDlgItem(hwndParent, IDC_OUTPUT);
	pInfo->hDc = GetDC(pInfo->hWndParent);
    pInfo->bPrint = FALSE;
    strcpy(pInfo->szText, pszText);

    MTVERIFY( hThread = CreateThread(NULL, 0,
                                     BackgroundPrintThread,
                                     (LPVOID)pInfo,
                                     0, &dwThreadId ));

	// keep track of all background printing threads
    gPrintJobs[gNumPrinting++] = hThread;
}



//---------------------------------------------------------
// About Box Handling
//---------------------------------------------------------

LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {
        case WM_COMMAND:
            if (LOWORD(wParam) == IDOK
                || LOWORD(wParam) == IDCANCEL)
            {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            break;

        default:
            return (DefWindowProc(hDlg, message, wParam, lParam));
    }

    return FALSE;
}


//---------------------------------------------------------
// Background Printing Code后台线程打印函数
//---------------------------------------------------------

DWORD WINAPI BackgroundPrintThread(LPVOID pVoid)
{
    ThreadPrintInfo *pInfo = (ThreadPrintInfo*) pVoid; 
    RECT rect;
    RECT rectMem;
    HDC hDcMem;
    HBITMAP bmpMem;
    HBITMAP bmpOld;
    int x, y;
    int counter = 0;
    int nHeight;
    HFONT hFont;
    HFONT hFontOld;

    // Get dimensions of paper into rect
    rect.left = 0;
    rect.top = 0;
    rect.right =  GetDeviceCaps(pInfo->hDc, HORZRES);
    rect.bottom = GetDeviceCaps(pInfo->hDc, VERTRES);

    nHeight = -MulDiv(36, GetDeviceCaps(pInfo->hDc, LOGPIXELSY), 72);

    // Create Font
    hFont = CreateFont(nHeight, 0, 
        0, 0, FW_DONTCARE, 
        FALSE, FALSE, FALSE, 
        ANSI_CHARSET, 
        OUT_TT_PRECIS, 
        CLIP_DEFAULT_PRECIS,
        PROOF_QUALITY, 
        VARIABLE_PITCH,
        NULL);
    MTASSERT( hFont != 0);

    // Draw into memory device context
    hDcMem = CreateCompatibleDC(pInfo->hDc);
    hFontOld = SelectObject(hDcMem, hFont);
    iHeight = DrawText(hDcMem, pInfo->szText, -1,  &rect, DT_LEFT | DT_NOPREFIX | DT_WORDBREAK | DT_CALCRECT);
    rectMem = rect;
    rectMem.left = rect.left + iHeight;
    rectMem.right = rect.right + (iHeight*2);
    bmpMem = CreateCompatibleBitmap(hDcMem,
                                    rectMem.right, rect.bottom);
    bmpOld = SelectObject(hDcMem, bmpMem);
    OffsetRect(&rect, iHeight, 0); 
    DrawText(hDcMem, pInfo->szText, -1,  &rect,
             DT_LEFT | DT_NOPREFIX | DT_WORDBREAK);

    // Italicize bitmap. We use GetPixel and
    // SetPixel because they are horribly inefficient,
    // thereby causing the thread to run for awhile.
    for (y = 0; y < iHeight; y++)
    {   // Italicize line y
        for (x = rectMem.right; x > iHeight; x--)
        {   // Move specified pixel to the right.
            COLORREF color;
            int offset;
            offset = y - iHeight;
            color = GetPixel(hDcMem, x + offset, y);
            if (color != 0)
                counter++;
            SetPixel(hDcMem, x, y, color);
        } // end for x
    } // end for y
    MTASSERT( counter > 0);

    // Copy bitmap of italicized text from memory to device
    if (pInfo->bPrint)
    {
        BitBlt(pInfo->hDc, 50, 50, rectMem.right-rect.left, rectMem.bottom-rect.top,
            hDcMem, iHeight, 0, SRCCOPY);
    }

    SelectObject(hDcMem, hFontOld);
    SelectObject(hDcMem, bmpOld);
    DeleteDC(hDcMem);

    if (!pInfo->bPrint)
    {
        // We can't just write to the global variable where the
        // bitmap is kept or we might overwrite the work of
        // another thread, thereby "losing" a bitmap

        // Also, if we used PostMessage instead of SendMessage, then
        // the rectangle could have been deleted (it's on the stack)
        // by the time the main message loop is reached.
        SendMessage(pInfo->hDlg, WM_SHOWBITMAP, (WPARAM)&rectMem, (LPARAM) bmpMem);
    }

    if (pInfo->bPrint)
    {   // Finish printing
        int result;

        result = EndPage(pInfo->hDc);
        MTASSERT (result != SP_ERROR);
        result = EndDoc(pInfo->hDc);
        MTASSERT (result != SP_ERROR);
        DeleteDC(pInfo->hDc);
        // If we are printing, we are done with the bitmap.
        DeleteObject(bmpMem);
    } 
    else
    {
        ReleaseDC(pInfo->hWndParent, pInfo->hDc);
    }

    // free data structure passed in.
    HeapFree(GetProcessHeap(), 0, pInfo);

    return 0;
}


多线程应该遵循下面原则:
1.各个线程的数据要分离开来,避免使用全局变量
2.不要在线程之间共享GDI对象
3.让主线程处理GUI界面
4.要等其他线程结束在结束程序







VC++实现工业级RS232/RS485串口通信:从Win32 API到多线程封装 串口通信作为设备间数据交换的基础物理层协议,其核心在于通过特定的电气标准(如RS232的点对点电压传输、RS485的差分信号总线)实现稳定可靠的数据传输。在Windows平台下,其技术实现主要依托Win32 API,通过CreateFile、ReadFile、WriteFile等函数进行端口操作,并利用重叠I/O(Overlapped I/O)模型实现异步非阻塞,以提升程序响应能力与CPU效率。这对于构建高可靠性的工业控制、嵌入式调试及自动化设备软件具有关键价值。在实际应用场景中,开发者常需处理RS485的 阅读详情

相关推荐

VC++串口通信:工业级SerialPort类设计与多线程避坑指南

串口通信作为设备与计算机之间基础的数据传输方式,其核心在于通过串行接口实现稳定可靠的字节流交换。在Windows平台下,串口通信通常基于文件I/O模型,通过CreateFile、ReadFile、WriteFile等API直接操作硬件端口,这要求开发者深入理解异步I/O、重叠操作和线程同步等底层机制。对于工业控制、数据采集等对实时性和稳定性要求极高的场景,采用VC++配合Win32 API能够提供接近硬件的控制力与微秒级响应能力,这是高级语言封装库难以比拟的优势。本文聚焦于构建一个健壮的SerialPort

weixin_30411819的博客 290

Win32系统编程 创建线程

线程是附属在进程上的执行实体,是代码的执行流程,一个进程可以包含多个线程,但一个进程至少要包含一个线程。使用的函数CreateThread函数CreateThread是有返回值的,返回的为当前进程的句柄。

woaijianji的博客 461

Windows多线程编程入门:CreateThread API原理与实战详解

多线程编程是现代软件开发中提升程序性能、实现并发处理的核心技术。其基本原理在于允许一个进程内同时运行多个执行流,共享进程资源但拥有独立的栈空间和程序计数器,从而充分利用多核CPU的计算能力。在Windows平台进行系统级开发时,理解线程的底层创建与管理机制至关重要,这直接关系到程序的稳定性与性能优化。CreateThread作为Win32 API中最基础的线程创建函数,提供了对线程生命周期的精细控制,是深入理解Windows线程模型和同步机制的关键入口。通过掌握线程函数签名、参数传递、句柄管理及同步对象(如

weixin_30718391的博客 381

Win32创建线程

这里被0加到1000那里的while循环坑惨了,找半天不知道为什么第二个文本框为什么不懂,原来是dwTime作为循环参数,但是它本身是0,因此循环不进行。被调用完毕之后,堆栈被摧毁,我们通过&num传参给线程函数后,参数的位置已经被销毁,所以传参就会失败。向线程函数并不是我们调用的,是我们在创建线程时,把线程函数的指针传参给线程,系统调用的线程函数。线程句柄就是一块令牌,有了令牌就可以进行使用,但是没有令牌也并不能影响线程的生命。线程ID是身份证明,具有唯一性,因此系统进行线程调度的时候需要用到。

2301_77816603的博客 788

C++网络编程实战:从TCP Socket到多线程聊天室开发

网络编程是现代软件开发的核心技能之一,其基础在于理解TCP/IP协议栈和Socket编程模型。TCP协议通过面向连接、可靠传输的特性,确保了数据在网络中的有序、完整交付,这使其成为实时通信、文件传输等场景的首选。在工程实践中,多线程模型是处理并发连接的经典方案,通过为每个客户端创建独立线程,可以充分利用多核CPU资源,直观地管理客户端会话。然而,多线程环境也引入了数据竞争和资源同步的挑战,需要借助互斥锁等机制保护共享状态。基于C++实现网络聊天工具,正是将Socket API、多线程编程、消息协议设计等知识

weixin_33416697的博客 249

C++多线程编程:深入理解std::thread::join()原理与Windows实践

在多线程编程中,线程同步是确保程序正确执行的核心机制。其基本原理是通过协调不同线程的执行顺序,避免数据竞争和资源冲突,从而保证程序的稳定性和可靠性。从技术价值看,有效的线程同步能提升资源利用率、简化并发逻辑,是实现高性能计算和响应式系统的关键。在C++开发中,std::thread::join()函数正是实现线程同步的重要工具,它通过阻塞调用线程来等待目标线程完成,确保线程生命周期的有序管理。特别是在Windows平台开发中,理解join()的底层实现与Win32线程句柄管理,对于防止资源泄露和提升代码健壮

weixin_33785108的博客 344

windows编程 第11课 win32线程

错误代码分析:当线程A执行g_value++时,如果线程切换时间正好是在线程A将值保存到g_value之前,线程B继续执行g_value++,那么当线程A再次被切换回来之后,会将原来线程A保存的值保存到g_value上,线程B进行的加法操作被覆盖。事件和信号量都解决线程同步问题,但是信号量具有计数功能,可以对资源数量进行控制,比如限制http服务器的访问客户数量。操作系统将CPU的执行时间划分成时间片,依次根据时间片执行不同的线程。类似于事件,解决通知的相关问题。线程轮询:线程A->线程B-> 线程A。

qq_35984700的博客 338

C++集成大漠插件:COM原理、免注册调用与多线程实践指南

组件对象模型(COM)是Windows平台实现二进制组件复用的核心技术标准,它通过接口与实现分离的机制,允许不同语言编写的模块相互调用。理解COM原理是进行Windows自动化开发的基础,其核心在于通过虚函数表(vtable)进行方法调度和引用计数管理生命周期。这一技术价值在于实现了跨语言、跨进程的组件交互,极大提升了代码复用性和系统模块化程度。在自动化测试、游戏辅助和办公流程自动化等应用场景中,开发者常需集成如大漠插件这类功能强大的COM组件来实现屏幕操作、图像识别等复杂功能。本文聚焦于C++项目中集成大

weixin_30607659的博客 381

Windows下线程的创建与使用(win32-API)

在Windows环境下,C语言可以通过调用Win32 API中的`CreateThread`函数来创建和管理线程。`CreateThread`函数允许你指定线程的入口点(即线程函数)、线程的优先级、堆栈大小等参数。

1991

Windows WIN32 API安全封装:语义分层与错误域隔离设计

WIN32 API是Windows桌面应用的底层基石,但其C风格接口、LastError时序敏感性、HANDLE资源管理复杂性,极易引发Access Violation、句柄泄漏和错误码误判等生产级问题。本文从ABI兼容性、线程安全错误上下文、资源生命周期解耦三大原理切入,解析如何构建超越简单函数映射的现代封装体系——通过Raw ABI桥接层捕获结构化异常、Semantic Adapter层实现`file_handle`零开销RAII与异步关闭、Domain Facade层提供业务语义接口。技术价值在于将`

weixin_33755557的博客 342

C++多线程TCP文件传输:实现高效稳定的大文件同步与实时进度显示

在网络编程中,TCP协议是构建可靠数据传输的基石,它通过三次握手、流量控制和拥塞避免等机制确保数据的完整性与顺序。多线程技术则能有效利用多核CPU资源,将计算或I/O任务并行化,从而显著提升程序吞吐量。结合两者,多线程TCP文件传输方案应运而生,其核心价值在于解决大文件同步场景下的效率与稳定性问题。通过将文件逻辑分块,并由多个工作线程并行发送,可以充分利用网络带宽,尤其在高延迟环境中优势明显。同时,引入应用层确认机制和状态同步器,确保了传输的可靠性。其实时进度显示功能,通过独立的进度收集器定期采样全局状态,

weixin_34043301的博客 388

Win32_Windows线程

一 Windows 线程   1 Windows线程    Windows进程中可以执行代码的实体,Windows系统可以调度的执行代码,一个进程中      至少有一个或多个线程,每个线程是进程的一个任务分支。   2 线程的特点    2.1 每个线程有一个ID    2.2 每个线程有自己的安全属性    2.3 每个线程有自己的内存栈   3 进程和线程多任务    多进程...

weixin_30533797的博客 134

C++实战:构建多线程网络文件传输工具,掌握现代C++工程实践

网络编程与并发处理是构建高性能服务器应用的核心技术。其原理在于通过Socket实现跨网络进程通信,并利用多线程或事件驱动模型处理并发请求,以提升系统吞吐量和资源利用率。这项技术的工程价值在于能够支撑高并发、低延迟的应用场景,如在线聊天、文件传输和实时数据交换服务。理解其概念和原理后,通过实践项目能将知识内化为解决实际问题的能力。本文以C++17为基础,结合RAII资源管理、智能指针和现代线程库,深入探讨如何实现一个基于Reactor模式和线程池的多线程网络文件传输工具。项目涉及Socket封装、可靠数据传输

weixin_30243533的博客 412

Win32 C++多线程编程:五种通信方式、死锁预防与性能优化实战

并发编程是现代软件开发的核心技术,旨在通过同时执行多个任务来提升程序性能与响应能力。其基本原理在于利用多核处理器,通过进程与线程的协作来并行处理任务。在Windows平台进行C++开发时,深入理解并正确应用线程间通信机制具有极高的技术价值,它直接关系到后台服务、桌面应用及游戏引擎等性能敏感型程序的稳定性与效率。Win32 API提供了一套原生且强大的多线程与进程间通信工具集,包括临界区、互斥体、事件、信号量和互锁函数等。这些机制是构建健壮并发程序架构的基础,尤其适用于需要与系统底层紧密结合或进行深度性能优化

weixin_30660027的博客 434

libmemcached-win32:Windows原生Memcached客户端实战指南

Memcached客户端是C/C++服务接入分布式缓存的核心组件,其跨平台兼容性直接决定系统稳定性。在Windows环境下,标准POSIX实现常因socket错误码映射失准、线程模型冲突及DNS解析阻塞等问题导致连接失败或运行时崩溃。libmemcached-win32通过Win32 API原生重构——包括WSAStartup显式初始化、WSAEventSelect异步连接、CRITICAL_SECTION线程同步等关键技术,解决了errno/WSAGetLastError混用、pthreads-win32

weixin_34228387的博客 324

libcurl实战指南:从HTTPS传输到多线程的C/C++网络编程

在C/C++网络编程中,libcurl作为一款跨平台、支持多协议的网络传输库,为开发者处理HTTP/HTTPS数据交换提供了高效解决方案。理解其全局初始化流程、easy handle生命周期以及基于回调函数的数据读写机制,是掌握文件上传下载、HTTP POST提交等核心功能的基础。借助curl-config工具可快速获取编译与链接参数,通过curl_version_info运行时检测SSL特性,能有效规避环境差异导致的传输失败。多线程场景下,严格隔离handle并遵循全局初始化边界,是保证程序健壮性的关键。

weixin_42523693的博客 243

《Win32多线程程序设计》笔录

读后感: 这本书是一本Win32下多线程的抗鼎之作,其涵盖了多线程编程的方方面面。我阅读的原因在于期望能了解原汁原味的Win32下多线程编程。而我以前只是使用BCB的TThread等对象进行Win32多线程快速开发。阅读后的确对于Win32下的许多概念清楚明了不少,但也产生许多新的疑问。但我知道,这些疑问许多并不能在这本书中获得求解。在.Net下的Windows多线程开发已经有了新的发展,需要我...

weixin_30332241的博客 132

保姆级教程:在树莓派5B上用AI Kit跑YOLOv8,实测134fps的完整配置流程

本文提供了一份详细的树莓派5B搭配AI Kit部署YOLOv8的教程,从硬件组装到模型转换、优化和部署,最终实现134FPS的高帧率推理性能。通过Hailo-8L AI加速模块和HEF模型转换,这套方案为边缘AI计算提供了强大支持,适用于智能监控、无人机视觉导航等应用场景。

weixin_28725407的博客 446

Linux系统篇39——线程(四) pthread库的由来和线程的创建与等待

库的由来:内核只给 clone 不给线程调用,用户只要线程——pthread 库在用户层封装轻量级进程,Linux 的线程是用户级线程;跨平台靠各 OS 各写底层、条件编译、对外同一接口创建:pthread_create 四参数,输出型 tid、输入型 attr、函数指针入口、void* 传参;入口函数返回的 void* 就是线程的返回值通道;出错看返回值不看 errno等待:pthread_join 拿两样东西——等哪个线程、收退出状态;

2502_94387000的博客 489

Eclipse在Mac上报错JNI_CreateJavaVM?可能是你的JDK架构搞错了(附Adoptium JDK下载指南)

本文深入解析了Mac(尤其是Apple Silicon芯片)上Eclipse启动时出现“JNI_CreateJavaVM”错误的根本原因,即JDK架构(aarch64与x86_64)不匹配。文章提供了详细的诊断步骤,并推荐通过下载并配置Eclipse Adoptium提供的aarch64版本JDK来彻底解决问题,同时附上清晰的配置指南。

weixin_29065659的博客 321

0009 用VisualStudio设计Max工具

这次分享个独家专利^)^:在Visual Studio中设计3dsMax脚本工具界面。 某次做原型设计时,需求改来改去,实在懒得搞GUI;虽然也尝试在Axure RP里画原型,但还是Visual Studio更顺手,也更容易测试一些交互,就直接在VS里做了。后来突发奇想,想实验一把能否直接用到Max里去,结果没想到成功了,效果还挺好。发现了新大陆,记录一下。   开始: 在VS里新建W...

whwst的专栏 769
上一篇: 图的存储与遍历
下一篇: 【Win32多线程】如何避免线程等待浪费CPU时间,降低对系统资源的冲击?
xiaoding133
博客等级 码龄19年 415粉丝 159原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值