Cách in 2 mặt ở nhiều sheet trong cùng 1 file excel

Liên hệ QC

darkjoee

Thành viên mới
Tham gia
24/4/20
Bài viết
11
Được thích
7
Gửi các Anh/Chị !

em có 1 file excel, trong đó có khoảng 300 sheet.... Trong mỗi sheet lại có khoảng 1 ~ 5 trang.

mong muốn của em là in đồng loạt 300 sheet này với các yêu cầu sau:
1. Sheet nào có 2 trang trở lên thì in 2 mặt.
2. Qua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. (kiểu như là không được in sheet 1 mặt trước, sheet 2 mặt sau của cùng 1 tờ giấy)

em có làm rồi nhưng hay gặp lỗi sau:
1. Chỉ in được 2 mặt ở sheet mà mình đang có con trỏ chuột, các sheet sau mặc dù có 2 trang nhưng cũng chỉ in ra 1 mặt.
2. Có trường hợp em lại in sheet 1 được 1 mặt, sheet 2 lại là trang sau của sheet 1...

Anh/Chị nào có cách xin chỉ giúp.
em cảm ơn
 
Gửi các Anh/Chị !

em có 1 file excel, trong đó có khoảng 300 sheet.... Trong mỗi sheet lại có khoảng 1 ~ 5 trang.

mong muốn của em là in đồng loạt 300 sheet này với các yêu cầu sau:
1. Sheet nào có 2 trang trở lên thì in 2 mặt.
2. Qua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. (kiểu như là không được in sheet 1 mặt trước, sheet 2 mặt sau của cùng 1 tờ giấy)

em có làm rồi nhưng hay gặp lỗi sau:
1. Chỉ in được 2 mặt ở sheet mà mình đang có con trỏ chuột, các sheet sau mặc dù có 2 trang nhưng cũng chỉ in ra 1 mặt.
2. Có trường hợp em lại in sheet 1 được 1 mặt, sheet 2 lại là trang sau của sheet 1...

Anh/Chị nào có cách xin chỉ giúp.
em cảm ơn
Đề bài khó thía! Có code được rồi thì cũng phải có 1 cái máy in 2 mặt để test chứ nhỉ!
 
Đề bài khó thía! Có code được rồi thì cũng phải có 1 cái máy in 2 mặt để test chứ nhỉ!
cũng hơi khó ạ !!!
em có hỏi các anh/chị trong công ty, nhưng mọi người đều chịu thua rồi ạ...
tra trên bác google thì cũng không có kết quả, phần lờn cũng có vài người hỏi nhưng chưa có ai trả lời.
hiện tại em cũng phải in từng sheet, mà nhiều sheet quá cũng hơi "gian nản bắt đầu nản" hiiihii ^_^
 
Gửi các Anh/Chị !

em có 1 file excel, trong đó có khoảng 300 sheet.... Trong mỗi sheet lại có khoảng 1 ~ 5 trang.

mong muốn của em là in đồng loạt 300 sheet này với các yêu cầu sau:
1. Sheet nào có 2 trang trở lên thì in 2 mặt.
2. Qua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. (kiểu như là không được in sheet 1 mặt trước, sheet 2 mặt sau của cùng 1 tờ giấy)

em có làm rồi nhưng hay gặp lỗi sau:
1. Chỉ in được 2 mặt ở sheet mà mình đang có con trỏ chuột, các sheet sau mặc dù có 2 trang nhưng cũng chỉ in ra 1 mặt.
2. Có trường hợp em lại in sheet 1 được 1 mặt, sheet 2 lại là trang sau của sheet 1...

Anh/Chị nào có cách xin chỉ giúp.
em cảm ơn
Đề bài của bạn cũng là vấn đề của 1 số người.
Khó nhưng chắc chắn giải quyết được bằng VBA đó bạn. Tuy nhiên, bạn phải gửi file thực tế lên để mọi người có giúp bạn thì thao tác ngay trên file đó luôn á. file 300 sheet có nặng quá ko ta?!
Mình chưa giúp được bạn. Để mình nhờ anh này xem.

@giaiphap : anh xem giúp bạn này với ! Em nghĩ là anh giải quyết được bài toán này, hi.
 
cũng hơi khó ạ !!!
em có hỏi các anh/chị trong công ty, nhưng mọi người đều chịu thua rồi ạ...
tra trên bác google thì cũng không có kết quả, phần lờn cũng có vài người hỏi nhưng chưa có ai trả lời.
hiện tại em cũng phải in từng sheet, mà nhiều sheet quá cũng hơi "gian nản bắt đầu nản" hiiihii ^_^
In được 1 sheet, thì bạn ghi lại macro quá trình in 1 sheet
Rồi code VBA vòng lặp để in cho tất cả các sheet - nếu không làm được bước này thì upload nội dung macro đó lên diễn đàn (cùng file kèm thì tốt hơn), mọi người giúp

Cách đó mới giúp được thôi, vì in phụ thuộc vào đặc điểm máy in, máy in 2 mặt nữa, không phải ai cũng có điều kiện thử và viết cho bạn.
 
cũng hơi khó ạ !!!
em có hỏi các anh/chị trong công ty, nhưng mọi người đều chịu thua rồi ạ...
tra trên bác google thì cũng không có kết quả, phần lờn cũng có vài người hỏi nhưng chưa có ai trả lời.
hiện tại em cũng phải in từng sheet, mà nhiều sheet quá cũng hơi "gian nản bắt đầu nản" hiiihii ^_^
Đây là toàn bộ code khai báo, hàm và thủ tục để in 2 mặt. Tôi chú thích sơ lược cho Sub Print_Options() để in. Bạn phải chịu khó "cày" thêm để code hoạt động tốt trên hệ thống của bạn (Tôi đã test setting thành công trên hệ thống của tôi: Win10, Office2013, Máy in dùng cổng TCP/IP)
Thủ tục SetDuplex có 2 tham số:
1/ Hàm GetPrinterFullName sẽ lấy ra tên đầy đủ gồm cả cổng in, như của tôi là "Brother MFC-L2700DW series on Ne04:"
(*) Brother MFC-L2700DW series là tên máy in có chức năng in 2 mặt của tôi, hiển thị trong hộp thoại Print. (bạn lấy tên trong hệ thống của bạn để đưa vào đây)
2/ Số 2 là in 2 mặt, 1 là in 1 mặt
Với câu lệnh in .PrintOut From:=1, To:=2, bạn cần dùng code macro 4 để lấy ra tổng số trang in để gán vào To:=x (vụ macro4 này nói sau)
Trên từng sheet cứ có lệnh ActiveWindow.SelectedSheets.PrintOut From:=1, To:=x là máy sẽ in đúng yêu cầu của bạn, cứ có trang số lẻ là nó chuyển sang tờ khác.
Dùng 1 vòng lặp duyệt tất cả các sheet cần in, máy sẽ in từng sheet từ From:=1 đến To:=x đúng yêu cầu của bạn, cứ có trang số lẻ trong 1 sheet là nó chuyển sang tờ khác và chuyển sang sheet khác là bắt đầu in từ mặt 1 của tờ mới.
PHP:
Public Type PRINTER_INFO_9
    pDevmode As Long '''' POINTER TO DEVMODE
End Type

Public Type DEVMODE
    dmDeviceName As String * 32
    dmSpecVersion As Integer: dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * 32
    dmUnusedPadding As Integer
    dmBitsPerPel As Integer
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
    dmICMMethod As Long
    dmICMIntent As Long
    dmMediaType As Long
    dmDitherType As Long
    dmReserved1 As Long
    dmReserved2 As Long
End Type

Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, pDefault As Any) As Long
Public Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, buffer As Long, ByVal pbSize As Long, pbSizeNeeded As Long) As Long
Public Declare Function SetPrinter Lib "winspool.drv" Alias "SetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, pPrinter As Any, ByVal Command As Long) As Long
Public Declare Function DocumentProperties Lib "winspool.drv" Alias "DocumentPropertiesA" (ByVal hWnd As Long, ByVal hPrinter As Long, ByVal pDeviceName As String, _
                                                                                            ByVal pDevModeOutput As Long, ByVal pDevModeInput As Long, _
                                                                                            ByVal fMode As Long) As Long
Public Declare Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal cbLength As Long)
Public Const DM_IN_BUFFER = 8
Public Const DM_OUT_BUFFER = 2

Public Sub SetPrinterProperty(ByVal sPrinterName As String, ByVal iPropertyType As Long)
Dim PrinterName, sPrinter, sDefaultPrinter As String
Dim Pinfo9 As PRINTER_INFO_9
Dim hPrinter, nRet As Long
Dim yDevModeData() As Byte
Dim dm As DEVMODE

'sPrinterName = "Brother MFC-L2700DW series"

'''' STROE THE CURRENT DEFAULT PRINTER
sDefaultPrinter = sPrinterName

'''' USE THE FULL PRINTER ADDRESS TO GET THE ADDRESS AND NAME MINUS THE PORT NAME
PrinterName = Left(sDefaultPrinter, InStr(sDefaultPrinter, " on ") - 1)

'''' OPEN THE PRINTER
nRet = OpenPrinter(PrinterName, hPrinter, ByVal 0&)

'''' GET THE SIZE OF THE CURRENT DEVMODE STRUCTURE
nRet = DocumentProperties(0, hPrinter, PrinterName, 0, 0, 0)
If (nRet < 0) Then MsgBox "Cannot get the size of the DEVMODE structure.": Exit Sub

'''' GET THE CURRENT DEVMODE STRUCTURE
ReDim yDevModeData(nRet + 100) As Byte
nRet = DocumentProperties(0, hPrinter, PrinterName, VarPtr(yDevModeData(0)), 0, DM_OUT_BUFFER)
If (nRet < 0) Then MsgBox "Cannot get the DEVMODE structure.": Exit Sub

'''' COPY THE CURRENT DEVMODE STRUCTURE
Call CopyMemory(dm, yDevModeData(0), Len(dm))

'''' CHANGE THE DEVMODE STRUCTURE TO REQUIRED
dm.dmDuplex = iPropertyType ' 1 = simplex, 2 = duplex

'''' REPLACE THE CURRENT DEVMODE STRUCTURE WITH THE NEWLEY EDITED
Call CopyMemory(yDevModeData(0), dm, Len(dm))

'''' VERIFY THE NEW DEVMODE STRUCTURE
nRet = DocumentProperties(0, hPrinter, PrinterName, VarPtr(yDevModeData(0)), VarPtr(yDevModeData(0)), DM_IN_BUFFER Or DM_OUT_BUFFER)

Pinfo9.pDevmode = VarPtr(yDevModeData(0))

'''' SET THE DEMODE STRUCTURE WITH ANY CHANGES MADE
nRet = SetPrinter(hPrinter, 9, Pinfo9, 0)
If (nRet <= 0) Then MsgBox "Cannot set the DEVMODE structure.": Exit Sub

'''' CLOSE THE PRINTER
nRet = ClosePrinter(hPrinter)

'''' GET THE FULL PRINTER NAME FOR THE CUTE PDF WRITER
sPrinter = GetPrinterFullName("Microsoft Print to PDF")

'''' CHECK TO MAKE SURE THE CUTEPDF WAS FOUND
If sPrinter <> vbNullString Then
'''' THIS FORCES EXCEL TO ACCEPT THE NEW CHANGES THAT HAVE BEEN MADE TO THE PRINTER SETTINGS
    '''' SET THE ACTIVE PRINTER TEMPERARILLY TO THE CUTE PDF WRITER
    Application.ActivePrinter = sPrinter
    '''' SET THE PRINTER BACK TO THE DEFAULY FOLLOW ME.
    Application.ActivePrinter = sDefaultPrinter
End If
End Sub

Public Sub SetDuplex(ByVal sPrinterName As String, iDuplex As Long)
   SetPrinterProperty sPrinterName, iDuplex
End Sub
Public Sub SetSimplex(ByVal sPrinterName As String, iDuplex As Long)
   SetPrinterProperty sPrinterName, iDuplex
End Sub

Public Function GetPrinterFullName(Printer As String) As String

' This function returns the full name of the first printerdevice that matches Printer.
' Full name is like "PDFCreator on Ne01:" for a English Windows and like
' "PDFCreator sur Ne01:" for French.
' Created: Frans Bus, 2015. See http://pixcels.nl/set-activeprinter-excel
' see http://blogs.msdn.com/b/alejacma/archive/2008/04/11/how-to-read-a-registry-key-and-its-values.aspx
' see http://www.experts-exchange.com/Software/Microsoft_Applications/Q_27566782.html

Const HKEY_CURRENT_USER = &H80000001
Dim regobj As Object
Dim aTypes As Variant
Dim aDevices As Variant
Dim vDevice As Variant
Dim sValue As String
Dim v As Variant
Dim sLocaleOn As String

' get locale "on" from current activeprinter
v = Split(Application.ActivePrinter, Space(1))
sLocaleOn = Space(1) & CStr(v(UBound(v) - 1)) & Space(1)

' connect to WMI registry provider on current machine with current user
Set regobj = GetObject("WINMGMTS:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")

' get the Devices from the registry
regobj.EnumValues HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", aDevices, aTypes

' find Printer and create full name
For Each vDevice In aDevices
    ' get port of device
    regobj.GetStringValue HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", vDevice, sValue
    ' select device
    If Left(vDevice, Len(Printer)) = Printer Then ' match!
        ' create localized printername
        GetPrinterFullName = vDevice & sLocaleOn & Split(sValue, ",")(1)
        Exit Function
    End If
Next

' at this point no match found
GetPrinterFullName = vbNullString

End Function

Sub Print_Options()

    SetDuplex GetPrinterFullName("Brother MFC-L2700DW series"), 2
    ActiveWindow.SelectedSheets.PrintOut From:=1, To:=2, Copies:=1
End Sub
 
Lần chỉnh sửa cuối:
Gửi các Anh/Chị !

em có 1 file excel, trong đó có khoảng 300 sheet.... Trong mỗi sheet lại có khoảng 1 ~ 5 trang.

mong muốn của em là in đồng loạt 300 sheet này với các yêu cầu sau:
1. Sheet nào có 2 trang trở lên thì in 2 mặt.
2. Qua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. (kiểu như là không được in sheet 1 mặt trước, sheet 2 mặt sau của cùng 1 tờ giấy)

em có làm rồi nhưng hay gặp lỗi sau:
1. Chỉ in được 2 mặt ở sheet mà mình đang có con trỏ chuột, các sheet sau mặc dù có 2 trang nhưng cũng chỉ in ra 1 mặt.
2. Có trường hợp em lại in sheet 1 được 1 mặt, sheet 2 lại là trang sau của sheet 1...

Anh/Chị nào có cách xin chỉ giúp.
em cảm ơn
Theo tôi thì sẽ có cách như sau:
1. Xuất tất cả các sheet sang PDF, sau đó in file PDF này. Dĩ nhiên là trước khi xuất sang PDF thì mình sẽ duyệt qua tất cả các sheet và kiểm tra sheet nào có số lượng trang in là lẻ thì thêm vào một trang trắng (trang không có dữ liệu). Cách này sẽ nhanh nhưng có cái hạn chế là các trang in không đánh số thứ tự được (số trang).
2. Duyệt qua tất cả các sheet và in từng sheet 1, cách này sẽ in rất chậm (Do phải ra lệnh in từng sheet) nhưng có thể đánh số trang được.
Hai cách trên thì phải dùng code hết, và dĩ nhiên thấy file mới biết thế nào mà viết code.
 
Theo tôi thì sẽ có cách như sau:
1. Xuất tất cả các sheet sang PDF, sau đó in file PDF này. Dĩ nhiên là trước khi xuất sang PDF thì mình sẽ duyệt qua tất cả các sheet và kiểm tra sheet nào có số lượng trang in là lẻ thì thêm vào một trang trắng (trang không có dữ liệu). Cách này sẽ nhanh nhưng có cái hạn chế là các trang in không đánh số thứ tự được (số trang).
2. Duyệt qua tất cả các sheet và in từng sheet 1, cách này sẽ in rất chậm (Do phải ra lệnh in từng sheet) nhưng có thể đánh số trang được.
Hai cách trên thì phải dùng code hết, và dĩ nhiên thấy file mới biết thế nào mà viết code.
Vấn đề chỗ in 2 mặt bạn à. Bạn có xuất ra pdf thì cũng phải tìm cách ra lệnh in 2 mặt.
 
Bằng cách nào bạn? In thủ công bằng cách mở properties chọn in 2 mặt thì không nói đến. Ở trong Excel vẫn in thế được mà
Đúng là Excel in được, nhưng vẫn đề của chủ thớt là Sheet nào có 2 trang trở lên thì in 2 mặtQua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. Chính vì vậy chọn tất cả các sheet và ra lệnh in thì không thể.
 
Đúng là Excel in được, nhưng vẫn đề của chủ thớt là Sheet nào có 2 trang trở lên thì in 2 mặtQua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. Chính vì vậy chọn tất cả các sheet và ra lệnh in thì không thể.
Tôi chưa thử in trực tiếp nhưng tôi nghĩ bài 6 giải quyết được vấn đề. Để ngày mai thực nghiệm nữa là thông báo kết quả cuối cùng
 
Đúng là Excel in được, nhưng vẫn đề của chủ thớt là Sheet nào có 2 trang trở lên thì in 2 mặtQua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. Chính vì vậy chọn tất cả các sheet và ra lệnh in thì không thể.
Nếu tất cả các sheet đều là số chẵn trang thì in tất cả các sheet được mà. Chỉ cần chọn Print entire workbook trong phần setting của in. Có cái nữa là nó nhận setting theo từng sheet. Phải vào từng sheet để cài đặt là in 2 mặt. Cách này là cách củ chuối rồi
 
Tôi chưa thử in trực tiếp nhưng tôi nghĩ bài 6 giải quyết được vấn đề. Để ngày mai thực nghiệm nữa là thông báo kết quả cuối cùng
Thông báo: đã test thành công, in tự động hết tất cả các sheet, đáp ứng đúng yêu cầu của chủ thớt.
 
Đây là toàn bộ code khai báo, hàm và thủ tục để in 2 mặt. Tôi chú thích sơ lược cho Sub Print_Options() để in. Bạn phải chịu khó "cày" thêm để code hoạt động tốt trên hệ thống của bạn (Tôi đã test setting thành công trên hệ thống của tôi: Win10, Office2013, Máy in dùng cổng TCP/IP)
Thủ tục SetDuplex có 2 tham số:
1/ Hàm GetPrinterFullName sẽ lấy ra tên đầy đủ gồm cả cổng in, như của tôi là "Brother MFC-L2700DW series on Ne04:"
(*) Brother MFC-L2700DW series là tên máy in có chức năng in 2 mặt của tôi, hiển thị trong hộp thoại Print. (bạn lấy tên trong hệ thống của bạn để đưa vào đây)
2/ Số 2 là in 2 mặt, 1 là in 1 mặt
Với câu lệnh in .PrintOut From:=1, To:=2, bạn cần dùng code macro 4 để lấy ra tổng số trang in để gán vào To:=x (vụ macro4 này nói sau)
Trên từng sheet cứ có lệnh ActiveWindow.SelectedSheets.PrintOut From:=1, To:=x là máy sẽ in đúng yêu cầu của bạn, cứ có trang số lẻ là nó chuyển sang tờ khác.
Dùng 1 vòng lặp duyệt tất cả các sheet cần in, máy sẽ in từng sheet từ From:=1 đến To:=x đúng yêu cầu của bạn, cứ có trang số lẻ trong 1 sheet là nó chuyển sang tờ khác và chuyển sang sheet khác là bắt đầu in từ mặt 1 của tờ mới.
PHP:
Public Type PRINTER_INFO_9
    pDevmode As Long '''' POINTER TO DEVMODE
End Type

Public Type DEVMODE
    dmDeviceName As String * 32
    dmSpecVersion As Integer: dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * 32
    dmUnusedPadding As Integer
    dmBitsPerPel As Integer
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
    dmICMMethod As Long
    dmICMIntent As Long
    dmMediaType As Long
    dmDitherType As Long
    dmReserved1 As Long
    dmReserved2 As Long
End Type

Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, pDefault As Any) As Long
Public Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, buffer As Long, ByVal pbSize As Long, pbSizeNeeded As Long) As Long
Public Declare Function SetPrinter Lib "winspool.drv" Alias "SetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, pPrinter As Any, ByVal Command As Long) As Long
Public Declare Function DocumentProperties Lib "winspool.drv" Alias "DocumentPropertiesA" (ByVal hWnd As Long, ByVal hPrinter As Long, ByVal pDeviceName As String, _
                                                                                            ByVal pDevModeOutput As Long, ByVal pDevModeInput As Long, _
                                                                                            ByVal fMode As Long) As Long
Public Declare Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal cbLength As Long)
Public Const DM_IN_BUFFER = 8
Public Const DM_OUT_BUFFER = 2

Public Sub SetPrinterProperty(ByVal sPrinterName As String, ByVal iPropertyType As Long)
Dim PrinterName, sPrinter, sDefaultPrinter As String
Dim Pinfo9 As PRINTER_INFO_9
Dim hPrinter, nRet As Long
Dim yDevModeData() As Byte
Dim dm As DEVMODE

'sPrinterName = "Brother MFC-L2700DW series"

'''' STROE THE CURRENT DEFAULT PRINTER
sDefaultPrinter = sPrinterName

'''' USE THE FULL PRINTER ADDRESS TO GET THE ADDRESS AND NAME MINUS THE PORT NAME
PrinterName = Left(sDefaultPrinter, InStr(sDefaultPrinter, " on ") - 1)

'''' OPEN THE PRINTER
nRet = OpenPrinter(PrinterName, hPrinter, ByVal 0&)

'''' GET THE SIZE OF THE CURRENT DEVMODE STRUCTURE
nRet = DocumentProperties(0, hPrinter, PrinterName, 0, 0, 0)
If (nRet < 0) Then MsgBox "Cannot get the size of the DEVMODE structure.": Exit Sub

'''' GET THE CURRENT DEVMODE STRUCTURE
ReDim yDevModeData(nRet + 100) As Byte
nRet = DocumentProperties(0, hPrinter, PrinterName, VarPtr(yDevModeData(0)), 0, DM_OUT_BUFFER)
If (nRet < 0) Then MsgBox "Cannot get the DEVMODE structure.": Exit Sub

'''' COPY THE CURRENT DEVMODE STRUCTURE
Call CopyMemory(dm, yDevModeData(0), Len(dm))

'''' CHANGE THE DEVMODE STRUCTURE TO REQUIRED
dm.dmDuplex = iPropertyType ' 1 = simplex, 2 = duplex

'''' REPLACE THE CURRENT DEVMODE STRUCTURE WITH THE NEWLEY EDITED
Call CopyMemory(yDevModeData(0), dm, Len(dm))

'''' VERIFY THE NEW DEVMODE STRUCTURE
nRet = DocumentProperties(0, hPrinter, PrinterName, VarPtr(yDevModeData(0)), VarPtr(yDevModeData(0)), DM_IN_BUFFER Or DM_OUT_BUFFER)

Pinfo9.pDevmode = VarPtr(yDevModeData(0))

'''' SET THE DEMODE STRUCTURE WITH ANY CHANGES MADE
nRet = SetPrinter(hPrinter, 9, Pinfo9, 0)
If (nRet <= 0) Then MsgBox "Cannot set the DEVMODE structure.": Exit Sub

'''' CLOSE THE PRINTER
nRet = ClosePrinter(hPrinter)

'''' GET THE FULL PRINTER NAME FOR THE CUTE PDF WRITER
sPrinter = GetPrinterFullName("Microsoft Print to PDF")

'''' CHECK TO MAKE SURE THE CUTEPDF WAS FOUND
If sPrinter <> vbNullString Then
'''' THIS FORCES EXCEL TO ACCEPT THE NEW CHANGES THAT HAVE BEEN MADE TO THE PRINTER SETTINGS
    '''' SET THE ACTIVE PRINTER TEMPERARILLY TO THE CUTE PDF WRITER
    Application.ActivePrinter = sPrinter
    '''' SET THE PRINTER BACK TO THE DEFAULY FOLLOW ME.
    Application.ActivePrinter = sDefaultPrinter
End If
End Sub

Public Sub SetDuplex(ByVal sPrinterName As String, iDuplex As Long)
   SetPrinterProperty sPrinterName, iDuplex
End Sub
Public Sub SetSimplex(ByVal sPrinterName As String, iDuplex As Long)
   SetPrinterProperty sPrinterName, iDuplex
End Sub

Public Function GetPrinterFullName(Printer As String) As String

' This function returns the full name of the first printerdevice that matches Printer.
' Full name is like "PDFCreator on Ne01:" for a English Windows and like
' "PDFCreator sur Ne01:" for French.
' Created: Frans Bus, 2015. See http://pixcels.nl/set-activeprinter-excel
' see http://blogs.msdn.com/b/alejacma/archive/2008/04/11/how-to-read-a-registry-key-and-its-values.aspx
' see http://www.experts-exchange.com/Software/Microsoft_Applications/Q_27566782.html

Const HKEY_CURRENT_USER = &H80000001
Dim regobj As Object
Dim aTypes As Variant
Dim aDevices As Variant
Dim vDevice As Variant
Dim sValue As String
Dim v As Variant
Dim sLocaleOn As String

' get locale "on" from current activeprinter
v = Split(Application.ActivePrinter, Space(1))
sLocaleOn = Space(1) & CStr(v(UBound(v) - 1)) & Space(1)

' connect to WMI registry provider on current machine with current user
Set regobj = GetObject("WINMGMTS:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")

' get the Devices from the registry
regobj.EnumValues HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", aDevices, aTypes

' find Printer and create full name
For Each vDevice In aDevices
    ' get port of device
    regobj.GetStringValue HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", vDevice, sValue
    ' select device
    If Left(vDevice, Len(Printer)) = Printer Then ' match!
        ' create localized printername
        GetPrinterFullName = vDevice & sLocaleOn & Split(sValue, ",")(1)
        Exit Function
    End If
Next

' at this point no match found
GetPrinterFullName = vbNullString

End Function

Sub Print_Options()

    SetDuplex GetPrinterFullName("Brother MFC-L2700DW series"), 2
    ActiveWindow.SelectedSheets.PrintOut From:=1, To:=2, Copies:=1
End Sub
Gửi Anh !
phần số 1 thì em hiểu và đã đổi sang tên máy in trong mục
'" sPrinterName = "FX DocuCentr-IV C2263 PCL 6 ""
phần số 2 thì chưa hiểu lắm. cần chỉnh thêm gì nữa ạ.
 
Gửi Anh !
phần số 1 thì em hiểu và đã đổi sang tên máy in trong mục
'" sPrinterName = "FX DocuCentr-IV C2263 PCL 6 ""
phần số 2 thì chưa hiểu lắm. cần chỉnh thêm gì nữa ạ.

(Cái mục số 2 là nói tham số thứ 2 của hàm SetDuplex)

Còn đoạn này không phải là phần số 2 như bạn nói:
Với câu lệnh in .PrintOut From:=1, To:=2, bạn cần dùng code macro 4 để lấy ra tổng số trang in để gán vào To:=x (vụ macro4 này nói sau)
Trên từng sheet cứ có lệnh ActiveWindow.SelectedSheets.PrintOut From:=1, To:=x là máy sẽ in đúng yêu cầu của bạn, cứ có trang số lẻ là nó chuyển sang tờ khác.
Dùng 1 vòng lặp duyệt tất cả các sheet cần in, máy sẽ in từng sheet từ From:=1 đến To:=x đúng yêu cầu của bạn, cứ có trang số lẻ trong 1 sheet là nó chuyển sang tờ khác và chuyển sang sheet khác là bắt đầu in từ mặt 1 của tờ mới.
 
Đây là toàn bộ code khai báo, hàm và thủ tục để in 2 mặt. Tôi chú thích sơ lược cho Sub Print_Options() để in. Bạn phải chịu khó "cày" thêm để code hoạt động tốt trên hệ thống của bạn (Tôi đã test setting thành công trên hệ thống của tôi: Win10, Office2013, Máy in dùng cổng TCP/IP)
Thủ tục SetDuplex có 2 tham số:
1/ Hàm GetPrinterFullName sẽ lấy ra tên đầy đủ gồm cả cổng in, như của tôi là "Brother MFC-L2700DW series on Ne04:"
(*) Brother MFC-L2700DW series là tên máy in có chức năng in 2 mặt của tôi, hiển thị trong hộp thoại Print. (bạn lấy tên trong hệ thống của bạn để đưa vào đây)
2/ Số 2 là in 2 mặt, 1 là in 1 mặt
Với câu lệnh in .PrintOut From:=1, To:=2, bạn cần dùng code macro 4 để lấy ra tổng số trang in để gán vào To:=x (vụ macro4 này nói sau)
Trên từng sheet cứ có lệnh ActiveWindow.SelectedSheets.PrintOut From:=1, To:=x là máy sẽ in đúng yêu cầu của bạn, cứ có trang số lẻ là nó chuyển sang tờ khác.
Dùng 1 vòng lặp duyệt tất cả các sheet cần in, máy sẽ in từng sheet từ From:=1 đến To:=x đúng yêu cầu của bạn, cứ có trang số lẻ trong 1 sheet là nó chuyển sang tờ khác và chuyển sang sheet khác là bắt đầu in từ mặt 1 của tờ mới.
PHP:
Public Type PRINTER_INFO_9
    pDevmode As Long '''' POINTER TO DEVMODE
End Type

Public Type DEVMODE
    dmDeviceName As String * 32
    dmSpecVersion As Integer: dmDriverVersion As Integer
    dmSize As Integer
    dmDriverExtra As Integer
    dmFields As Long
    dmOrientation As Integer
    dmPaperSize As Integer
    dmPaperLength As Integer
    dmPaperWidth As Integer
    dmScale As Integer
    dmCopies As Integer
    dmDefaultSource As Integer
    dmPrintQuality As Integer
    dmColor As Integer
    dmDuplex As Integer
    dmYResolution As Integer
    dmTTOption As Integer
    dmCollate As Integer
    dmFormName As String * 32
    dmUnusedPadding As Integer
    dmBitsPerPel As Integer
    dmPelsWidth As Long
    dmPelsHeight As Long
    dmDisplayFlags As Long
    dmDisplayFrequency As Long
    dmICMMethod As Long
    dmICMIntent As Long
    dmMediaType As Long
    dmDitherType As Long
    dmReserved1 As Long
    dmReserved2 As Long
End Type

Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, pDefault As Any) As Long
Public Declare Function GetPrinter Lib "winspool.drv" Alias "GetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, buffer As Long, ByVal pbSize As Long, pbSizeNeeded As Long) As Long
Public Declare Function SetPrinter Lib "winspool.drv" Alias "SetPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, pPrinter As Any, ByVal Command As Long) As Long
Public Declare Function DocumentProperties Lib "winspool.drv" Alias "DocumentPropertiesA" (ByVal hWnd As Long, ByVal hPrinter As Long, ByVal pDeviceName As String, _
                                                                                            ByVal pDevModeOutput As Long, ByVal pDevModeInput As Long, _
                                                                                            ByVal fMode As Long) As Long
Public Declare Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal cbLength As Long)
Public Const DM_IN_BUFFER = 8
Public Const DM_OUT_BUFFER = 2

Public Sub SetPrinterProperty(ByVal sPrinterName As String, ByVal iPropertyType As Long)
Dim PrinterName, sPrinter, sDefaultPrinter As String
Dim Pinfo9 As PRINTER_INFO_9
Dim hPrinter, nRet As Long
Dim yDevModeData() As Byte
Dim dm As DEVMODE

'sPrinterName = "Brother MFC-L2700DW series"

'''' STROE THE CURRENT DEFAULT PRINTER
sDefaultPrinter = sPrinterName

'''' USE THE FULL PRINTER ADDRESS TO GET THE ADDRESS AND NAME MINUS THE PORT NAME
PrinterName = Left(sDefaultPrinter, InStr(sDefaultPrinter, " on ") - 1)

'''' OPEN THE PRINTER
nRet = OpenPrinter(PrinterName, hPrinter, ByVal 0&)

'''' GET THE SIZE OF THE CURRENT DEVMODE STRUCTURE
nRet = DocumentProperties(0, hPrinter, PrinterName, 0, 0, 0)
If (nRet < 0) Then MsgBox "Cannot get the size of the DEVMODE structure.": Exit Sub

'''' GET THE CURRENT DEVMODE STRUCTURE
ReDim yDevModeData(nRet + 100) As Byte
nRet = DocumentProperties(0, hPrinter, PrinterName, VarPtr(yDevModeData(0)), 0, DM_OUT_BUFFER)
If (nRet < 0) Then MsgBox "Cannot get the DEVMODE structure.": Exit Sub

'''' COPY THE CURRENT DEVMODE STRUCTURE
Call CopyMemory(dm, yDevModeData(0), Len(dm))

'''' CHANGE THE DEVMODE STRUCTURE TO REQUIRED
dm.dmDuplex = iPropertyType ' 1 = simplex, 2 = duplex

'''' REPLACE THE CURRENT DEVMODE STRUCTURE WITH THE NEWLEY EDITED
Call CopyMemory(yDevModeData(0), dm, Len(dm))

'''' VERIFY THE NEW DEVMODE STRUCTURE
nRet = DocumentProperties(0, hPrinter, PrinterName, VarPtr(yDevModeData(0)), VarPtr(yDevModeData(0)), DM_IN_BUFFER Or DM_OUT_BUFFER)

Pinfo9.pDevmode = VarPtr(yDevModeData(0))

'''' SET THE DEMODE STRUCTURE WITH ANY CHANGES MADE
nRet = SetPrinter(hPrinter, 9, Pinfo9, 0)
If (nRet <= 0) Then MsgBox "Cannot set the DEVMODE structure.": Exit Sub

'''' CLOSE THE PRINTER
nRet = ClosePrinter(hPrinter)

'''' GET THE FULL PRINTER NAME FOR THE CUTE PDF WRITER
sPrinter = GetPrinterFullName("Microsoft Print to PDF")

'''' CHECK TO MAKE SURE THE CUTEPDF WAS FOUND
If sPrinter <> vbNullString Then
'''' THIS FORCES EXCEL TO ACCEPT THE NEW CHANGES THAT HAVE BEEN MADE TO THE PRINTER SETTINGS
    '''' SET THE ACTIVE PRINTER TEMPERARILLY TO THE CUTE PDF WRITER
    Application.ActivePrinter = sPrinter
    '''' SET THE PRINTER BACK TO THE DEFAULY FOLLOW ME.
    Application.ActivePrinter = sDefaultPrinter
End If
End Sub

Public Sub SetDuplex(ByVal sPrinterName As String, iDuplex As Long)
   SetPrinterProperty sPrinterName, iDuplex
End Sub
Public Sub SetSimplex(ByVal sPrinterName As String, iDuplex As Long)
   SetPrinterProperty sPrinterName, iDuplex
End Sub

Public Function GetPrinterFullName(Printer As String) As String

' This function returns the full name of the first printerdevice that matches Printer.
' Full name is like "PDFCreator on Ne01:" for a English Windows and like
' "PDFCreator sur Ne01:" for French.
' Created: Frans Bus, 2015. See http://pixcels.nl/set-activeprinter-excel
' see http://blogs.msdn.com/b/alejacma/archive/2008/04/11/how-to-read-a-registry-key-and-its-values.aspx
' see http://www.experts-exchange.com/Software/Microsoft_Applications/Q_27566782.html

Const HKEY_CURRENT_USER = &H80000001
Dim regobj As Object
Dim aTypes As Variant
Dim aDevices As Variant
Dim vDevice As Variant
Dim sValue As String
Dim v As Variant
Dim sLocaleOn As String

' get locale "on" from current activeprinter
v = Split(Application.ActivePrinter, Space(1))
sLocaleOn = Space(1) & CStr(v(UBound(v) - 1)) & Space(1)

' connect to WMI registry provider on current machine with current user
Set regobj = GetObject("WINMGMTS:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")

' get the Devices from the registry
regobj.EnumValues HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", aDevices, aTypes

' find Printer and create full name
For Each vDevice In aDevices
    ' get port of device
    regobj.GetStringValue HKEY_CURRENT_USER, "Software\Microsoft\Windows NT\CurrentVersion\Devices", vDevice, sValue
    ' select device
    If Left(vDevice, Len(Printer)) = Printer Then ' match!
        ' create localized printername
        GetPrinterFullName = vDevice & sLocaleOn & Split(sValue, ",")(1)
        Exit Function
    End If
Next

' at this point no match found
GetPrinterFullName = vbNullString

End Function

Sub Print_Options()

    SetDuplex GetPrinterFullName("Brother MFC-L2700DW series"), 2
    ActiveWindow.SelectedSheets.PrintOut From:=1, To:=2, Copies:=1
End Sub
Cái này hay nhỉ hihi
 
Gửi các Anh/Chị !

em có 1 file excel, trong đó có khoảng 300 sheet.... Trong mỗi sheet lại có khoảng 1 ~ 5 trang.

mong muốn của em là in đồng loạt 300 sheet này với các yêu cầu sau:
1. Sheet nào có 2 trang trở lên thì in 2 mặt.
2. Qua 1 sheet là sẽ bắt đầu in qua tờ giấy khác. (kiểu như là không được in sheet 1 mặt trước, sheet 2 mặt sau của cùng 1 tờ giấy)

em có làm rồi nhưng hay gặp lỗi sau:
1. Chỉ in được 2 mặt ở sheet mà mình đang có con trỏ chuột, các sheet sau mặc dù có 2 trang nhưng cũng chỉ in ra 1 mặt.
2. Có trường hợp em lại in sheet 1 được 1 mặt, sheet 2 lại là trang sau của sheet 1...

Anh/Chị nào có cách xin chỉ giúp.
em cảm ơn
Mình đang bị trường hợp: In 2 mặt ở các sheet thì chỉ được sheet đầu tiên in 2 mặt, các sheet tiếp theo chỉ in 1 mặt. Rất mong bạn nào biết chỉ giúp mình.
Bài đã được tự động gộp:

Mình đang bị trường hợp: In 2 mặt ở các sheet thì chỉ được sheet đầu tiên in 2 mặt, các sheet tiếp theo chỉ in 1 mặt. Rất mong bạn nào biết chỉ giúp mình.
 
Web KT
Back
Top Bottom