You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Win32 API的PrintDlg返回选中打印机的信息?

问题:PrintDlg函数仅返回默认打印机信息,无法获取选中打印机的信息

我正在测试一个使用Win32 API打印发票的动态链接库,目前发票绘制正常,但PrintDlg函数仅返回默认打印机的信息。请问有没有办法让PrintDlg函数返回选中打印机的信息?

精简版调用代码

//Variable declarations
PRINTDLGA *tmpprintdlg = 0;
char *tmpstr = 0;
char *tmpstr2 = 0;
char *tmpstr3 = 0;
DEVNAMES *tmpdevnames = 0;
DEVMODEA *tmpdevmode = 0;
HDC tmphdc = 0;
int ret = 0;
int ret2 = 0;

//Show the print dialog.
ret = PrntShowPrintDialog(GLB_HWndMainDialog, 
                          &tmpprintdlg);
if((ret != TSUCCESS) && \
   (ret != FALSE)){
    //Get the error string.
    PrntGetErrorString(&tmpstr);
    MessageBox(NULL, 
               tmpstr, 
               "Error", 
               MB_OK);
    return FALSE;
} //EndIf

//Check if the user cancelled the print.
if(ret == FALSE){
    //Returns
    return FALSE;
} //EndIf

//Get the selected printer.
tmpdevnames = (DEVNAMES*)GlobalLock(tmpprintdlg->hDevNames);

//Use the members in the device names pointer
//to get information about the printer.

//Set tmpn to zero.
tmpn = 0;

//Get the name of the printer driver.
tmpn = tmpdevnames->wDriverOffset;
tmpstr = (char*)(((char*)tmpdevnames) + tmpn);

//Get the name of the printer.
tmpn = tmpdevnames->wDeviceOffset;
tmpstr2 = (char*)(((char*)tmpdevnames) + tmpn);

//Get the name of the printer port.
tmpn = tmpdevnames->wOutputOffset;
tmpstr3 = (char*)(((char*)tmpdevnames) + tmpn);

//Get the device mode for the printer.
if(tmpprintdlg->hDevMode != NULL){
    tmpdevmode = (DEVMODEA*)GlobalLock(tmpprintdlg->hDevMode);
} //EndIf 

//Create a device context for the user selected
//printer.
tmphdc = CreateDCA(tmpstr, 
                   tmpstr2, 
                   tmpstr3, 
                   tmpdevmode);
if(tmphdc == NULL){
    //Get the last Win32 error.
    ret2 = GetLastError();
    //Create an error string.
    PrntCreateErrorString(ret2, \
              TCREATEFAILED, \
              "CreateDC", \
              &tmpstr);
    //Show the error string.
    MessageBox(NULL, \
           tmpstr, \
           "Error", \
           MB_OK);
    //Returns
    return FALSE;
}

//Unlock the tmpdevnames memory.
GlobalUnlock(tmpdevnames);

//Unlock the tmpdevmode memory.
if(tmpdevmode != NULL){
    GlobalUnlock(tmpdevmode);
} //EndIf

PrntShowPrintDialog函数实现

TRESULT PrntShowPrintDialog(_IN HWND inobj, \
                            _INOUT PRINTDLGA **outobj){

 //Variable declarations
 PRINTDLGA *tmpprintdlg = 0;
 HWND tmphndwindow = 0;
 HINSTANCE tmphinst = 0;
 int ret = 0;
 int ret2 = 0;

 //Allocate memory to one PRINTDLGA object.
 tmpprintdlg = (PRINTDLGA*)calloc(1024, sizeof(char));
 if(tmpprintdlg == NULL){
  //Create an error string and set the
  //global static variable.
  PrntCreateErrorString(0, 
                        TMEMALLOCFAILED, 
                        "calloc", 
                        NULL);
  //Returns
  return TMEMALLOCFAILED;
 } //EndIf

 //Set some members in the PRINTDLGA object.
 tmpprintdlg->lStructSize = sizeof(PRINTDLGA);
 tmpprintdlg->hwndOwner = tmphndwindow;
 tmpprintdlg->hDevMode = NULL;
 tmpprintdlg->hDevNames = NULL;
 tmpprintdlg->hDC = NULL;
 tmpprintdlg->Flags = PD_ALLPAGES | \
              PD_ENABLEPRINTHOOK | \
              PD_ENABLESETUPHOOK | \
              PD_NONETWORKBUTTON | \
              PD_NOSELECTION | \
              PD_COLLATE | \
              PD_RETURNDC;
 tmpprintdlg->nMinPage = 0;
 tmpprintdlg->nMaxPage = 200;
 tmpprintdlg->nCopies = 1;
 tmpprintdlg->hInstance = tmphinst;
 tmpprintdlg->lCustData = NULL;
 tmpprintdlg->lpfnPrintHook = PrntDlgPrintProc;
 tmpprintdlg->lpfnSetupHook = PrntDlgSetupProc;
 tmpprintdlg->lpPrintTemplateName = NULL;
 tmpprintdlg->lpSetupTemplateName = NULL;
 tmpprintdlg->hPrintTemplate = NULL;
 tmpprintdlg->hSetupTemplate = NULL;

 //Show the print dialog box. This
 //is a modal dialog.
 ret = PrintDlg(tmpprintdlg);

 //Returns
 *outobj = tmpprintdlg;
 if(ret == 0){
  return FALSE;
 }else{
  return TSUCCESS;
 } //EndIf

} //End PrntShowPrintDialog

补充说明

钩子过程的两个回调函数PrntDlgPrintProc和PrntDlgSetupProc是仅返回TSUCCESS的哑函数。

  • 选择前:对话框显示默认打印机信息
  • 选择后:已在对话框中选中其他打印机,但代码获取到的仍为默认打印机信息

问题原因及解决方法

核心问题

  1. 父窗口句柄未正确传递:PrntShowPrintDialog中tmphndwindow初始化为0,未使用传入的inobj作为hwndOwner,导致对话框上下文异常,无法保存用户选择。
  2. 钩子函数返回值错误:Win32对话框钩子需返回系统约定值(如FALSE让系统处理默认逻辑),自定义的TSUCCESS会干扰对话框流程,导致用户选择不被保存。
  3. 内存分配不规范:用calloc(1024, sizeof(char))分配结构,虽当前足够,但更规范的方式是直接分配sizeof(PRINTDLGA)大小内存。

修正步骤

  1. 传递正确的父窗口句柄:
    在PrntShowPrintDialog中,将tmpprintdlg->hwndOwner = tmphndwindow;改为:

    tmpprintdlg->hwndOwner = inobj;
    
  2. 修正钩子函数返回值:
    将两个哑钩子函数返回值改为FALSE,让系统处理默认消息流程:

    UINT_PTR CALLBACK PrntDlgPrintProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        return FALSE;
    }
    
    UINT_PTR CALLBACK PrntDlgSetupProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
        return FALSE;
    }
    

    若不需要钩子功能,可直接移除PD_ENABLEPRINTHOOK和PD_ENABLESETUPHOOK标志,避免不必要干扰。

  3. 规范内存分配:
    将tmpprintdlg = (PRINTDLGA*)calloc(1024, sizeof(char));改为:

    tmpprintdlg = (PRINTDLGA*)calloc(1, sizeof(PRINTDLGA));
    

验证修正

修改后,PrintDlg调用完成后,tmpprintdlg->hDevNames和tmpprintdlg->hDevMode会正确指向用户选中打印机对应的DEVNAMES和DEVMODE结构,后续通过GlobalLock获取的信息即为选中打印机的内容。


内容的提问来源于stack exchange,提问作者Thema Guishard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 00:22:18