How to program a POS Printer using USB on Visual Basic 6
To access the printer settings, you can use the API function DocumentProperties. Here's the declaration for VB6:
Public Declare Function DocumentProperties Lib "winspool.drv" Alias "DocumentPropertiesA"_
(ByVal hwnd As Long, ByVal hPrinter As Long, ByVal pDeviceName As String, _
ByRef pDevModeOutput As DEVMODE, ByRef pDevModeInput As DEVMODE, _
ByVal fMode As Long) As Long
Here are some details:
http://support.microsoft.com/kb/167345
You can use the other API printing functions to print to any windows printer:
http://msdn.microsoft.com/en-us/library/dd162861(VS.85).aspx
Related
In a new Win32 project, I have the following Delphi function:
procedure SetValue(value1, value2 : Extended);
begin
end;
In the same project but from a C++ unit, I call this function:
SetValue(10, 40);
When I check value1 when compiling with BCC32C (CLang), I get 1.68132090507504896E-4932, that is not correct.
Compiling with BCC32 (classic), I get 10.
The second parameter is 40 in both cases.
It seems that there is a problem with Extended values and the parameter stack loading.
I use RAD Studio 10.1 Berlin.
How can I solve this?
UPDATE
I did not include the declaration because the hpp is created automatically when compiling. In any case, the declaration is:
extern DELPHI_PACKAGE void __fastcall SetValue(System::Extended value1, System::Extended value2);
To replicate the project:
1-Create a C++ project in Rad Studio
2-Add a Delphi unit with the above SetValue function
3-From C++ unit, add the hpp header with #include and call SetValue
It is all.
I need to use Extended type. I am using an external Delphi library, so I cannot change the types. The above code is a simplification of the problem. In reality, the problem is calling a function of this library that uses Extended in the parameters. Extended is a native type in Delphi but in C++ it is mapped as long double, 10 bytes (for Win32).
This seems to be an error in the BCC32C compiler. I guess it was not extended properly to handle Extended as Delphi needs it.
If you look at the CPU window, then the BCC32 compiler generates:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B8F 6802400000 push $00004002
00401B94 68000000A0 push $a0000000
00401B99 6A00 push $00
00401B9B 6804400000 push $00004004
00401BA0 68000000A0 push $a0000000
00401BA5 6A00 push $00
00401BA7 E80C000000 call Unit12::SetVal(long double,long double)
and that is correct. It first pushes 10, then 40 in Extended format. Note that each Extended occupies 12 byte on the stack.
But now look at the output for the BCC32C compiler:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B5D 89E0 mov eax,esp
00401B5F D90554F14E00 fld dword ptr [$004ef154]
00401B65 DB38 fstp tbyte ptr [eax]
00401B67 D90558F14E00 fld dword ptr [$004ef158]
00401B6D DB780A fstp tbyte ptr [eax+$0a]
00401B70 E81F000000 call Unit12::SetVal(long double,long double)
00401B75 83EC18 sub esp,$18
It first reads the 32 single precision float 40 and stores it as Extended at [ESP]. So far, so good. But then it reads in the next 32 bit single precision float 10 (still OK) but then stores it at [ESP+$0A], which is clearly wrong (for Delphi)! It should be stored at [ESP+$0C]! That is why the first value, which is read by the Pascal function at [ESP+$0C], but which is stored by BCC32C at [ESP+$0A], is wrong.
So this seems to be a bug. Reported as https://quality.embarcadero.com/browse/RSP-15737
Note that this is the normal way BCC32C pushes and expects such values. In a C++ function in the same module, i.e. compiled with BCC32C too, this works nicely:
void __fastcall Bla(long double a, long double b)
{
printf("%Lf %Lf\n", a, b);
}
But Delphi expects a 10 byte Extended to occupy 12 bytes on the stack, not 10 bytes as BCC32C does.
Weirdly enough, if the function to be called is not a Delphi __fastcall function, but a normal C++ (cdecl) function, the BCC32C compiler will store the Extendeds (long doubles) in [ESP+$0C] and [ESP] respectively.
Workarounds
As David Heffernan commented, you can pass multiple extendeds in a record. Alternatively, you could pass them as var parameters. In both cases, it is not as simple as calling SetVal(10.0, 40.0);.
I have written a VB6 dll with this code
Public Function RegGetStr(ByRef FullLocation_Name As String) As String
Dim oReg As Object
Set oReg = CreateObject("WScript.Shell")
RegGetStr = oReg.RegRead(FullLocation_Name, "REG_SZ")
Set oReg = Nothing
End Function
In Delphi I have library working
type
TRegGetStr = Function(Const FullLocation_Name: String): String; StdCall;
var
aRegGetStr: TRegGetStr;
and
#aRegGetStr := Windows.GetProcAddress(LibHandle, 'RegGetStr');
I have a crash
Is it because of the types of strings I'm using? or something else?
Your question asked about calling a VB DLL from Delphi.
VB6, out of the box, creates ActiveX dll's (created as an ActiveX DLL project)
How can I create a standard DLL in VB6? Look at the answer there describing ActiveX DLL.
While you can create standard DLL's in VB6 you have to go spelunking. It does not support that out of the box and is not recommended.
You would consume that VB ActiveX DLL just as you would any ActiveX dll in Delphi.
Deplhi Import Component - Type Library vs ActiveX
=====
IF, on the other hand, you are trying to call an external DLL (e.g. written in Delphi) FROM a VB application, you would create the Delphi function with pAnsiChar parameters for strings. Ex: in MyDelProcs.dll:
procedure MyDelProc(pStr1: pAnsiChar, MyInt: integer);stdcall;
.......................
exports
MyDelProc;
On the VB side you would then declare the procedure as
Declare Sub MyDelProc Lib "MyDelProcs.dll" (ByVal sStr1 as String, MyLong As Long)
Then call the procedure with
Call MyDelProc(sStr1, MyLong)
Some notes:
sStr1 is Unicode internal to VB, but on the call is converted to AnsiString. The ansistring has a chr(0) added to the end and moved to a buffer which is the length of the ansistring plus the chr(0).
Take care with other parameter types. Default Integer in VB is 2 bytes, default integer in Delphi is 4, which is equivalent to a Long type in VB.
If your Delphi function needs to know the length, pass it as another (long/integer) parameter.
The string buffer is fixed in size. If you are returning a string of longer length then either pad the original string with enough room, or pass a longer dummy string with enough length for the return.
Upon return the string is treated as ansistring. It will be converted back to Unicode to be placed into a VB internal string.
This question already has answers here:
How can I return a PChar from a DLL function to a VB6 application without risking crashes or memory leaks?
(7 answers)
Closed 8 years ago.
Vb6 code to run on the following error.
How can I fix the error?
How can I return a normal string?
vb6 code
Private Declare Function DllPchar Lib "C:\TEST\Project2.dll" (ByVal AStr As String) As String
Private Sub Command10_Click()
Dim tmp as String
tmp = DllPchar("123");
End Sub
Delphi7 code
function DllPchar( AStr: PChar) : PChar; stdcall;
Well, you haven't given much information about this problem. For instance, the error message. And the Delphi code for "DllPChar".
But never mind. The first thing I notice is that your Declare Function statement is returning a String. This will not work because VB is expecting a value of type BSTR to be returned. Delphi has a WideString type which is compatible with BSTR.
The reason why this is important is because VB strings are internally UTF-16, i.e. 2 byte per character Unicode Strings that are allocated by the COM memory manager. The Delphi PAnsiChar type is a pointer to an 8-bit ANSI character, and Delphi strings are allocated by Delphi's own memory manager. They are incompatible. However, there is a special case in VB6 where you can use Declare Function with a parameter ByVal ... As String, and VB handles and automatic conversion between a VB string and PAnsiChar before the call, and then between PAnsiChar and VB string after the call.
If you can't use BSTR in Delphi, your best bet is to rewrite DllPchar() so that it modifies the AStr parameter. Or alternatively, create a new parameter to return the value in.
If you can use BSTR, then you can modify AStr to pass it ByRef rather than ByVal. This will allow you to pass in a Unicode string from VB. You then return your result via the return value.
I have a program, written in VB6, and I'd like it to be able to know how much memory it itself is using.
Googling around a bit has only led me to the "GlobalMemoryStatusEx" Windows API function, but that doesn't seem to be what I'm looking for: It can be used to give information about the computer's memory overall, whereas I want information about the current process itself.
Any ideas? Thanks.
You can use the GetProcessMemoryInfo() function:
Declarations:
Public Type PROCESS_MEMORY_COUNTERS
cb As Long
PageFaultCount As Long
PeakWorkingSetSize As Long
WorkingSetSize As Long
QuotaPeakPagedPoolUsage As Long
QuotaPagedPoolUsage As Long
QuotaPeakNonPagedPoolUsage As Long
QuotaNonPagedPoolUsage As Long
PagefileUsage As Long
PeakPagefileUsage As Long
End Type
Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcessID As Long) As Long
Public Declare Function GetCurrentProcess Lib "kernel32" () As Long
Public Declare Function GetProcessMemoryInfo Lib "PSAPI.DLL" (ByVal hProcess As Long, ppsmemCounters As PROCESS_MEMORY_COUNTERS, ByVal cb As Long) As Long
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Calling code:
Dim tPMC As PROCESS_MEMORY_COUNTERS
'ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION Or PROCESS_VM_READ, 0, ProcessID)
ProcessHandle = GetCurrentProcess()
If (GetProcessMemoryInfo(ProcessHandle, tPMC, Len(tPMC)) <> 0) Then
PageFaultCount = Format(tPMC.PageFaultCount, "#,###")
WorkingSetSize = FormatFileSize(tPMC.WorkingSetSize)
PageFileUsage = FormatFileSize(tPMC.PagefileUsage)
End If
CloseHandle ProcessHandle
In this forum thread there is a suggestion involving the tasklist.exe shell command, which prints out all running processes along with memory usage. I'm not sure about the performance..
I have to create a DLL which is used by a VB6 application. This DLL has to provide several functions, some of them must return strings.
This is the VB6 declaration:
Declare Function MyProc Lib "mylib.dll" (ByVal Param As String) As String
And this the Delphi implementation stub in mylib.dll:
function MyProc(AParam: PChar): PChar; stdcall;
var
ReturnValue: string;
begin
ReturnValue := GetReturnValue(AParam);
Result := ???;
end;
What do I have to return here? Who will free the memory of the returnd PChar string?
EDIT: I'm asking about Delphi 2005 (PChar = PAnsiChar)
You need to craft a BSTR instead. VB6 strings are actually BSTRs. Call SysAllocString() on the Delphi side and return the BSTR to the VB6 side. The VB6 side will have to call SysFreeString() to free the string - it will do it automatically.
If PChar corresponds to an ANSI string (your case) you have to manually convert it to Unicode - use MultiByteToWideChar() for that. See this answer for how to better use SysAllocStringLen() and MultiByteToWideChar() together.
If you don't want to risk crashes or memory leaks, then craft your API using the Windows API as a model. There, the API functions generally don't allocate their own memory. Instead, the caller passes a buffer and tells the API how big the buffer is. The API fills the buffer up to that limit. See the GetWindowText function, for example. Functions don't return pointers, unless they're pointers to things the caller already provided. Instead, the caller provides everything itself, and the function just uses whatever it's given. You almost never see an output buffer parameter that isn't accompanied by another parameter telling the buffer's size.
A further enhancement you can make to that technique is to allow the function to tell the caller how big the buffer needs to be. When the input pointer is a null pointer, then the function can return how many bytes the caller needs to provide. The caller will call the function twice.
You don't need to derive your API from scratch. Use already-working APIs as examples for how to expose your own.
Combining Sharptooth and Lars D's answer; aren't widestrings already allocated via windows and BSTR?
I'm not familiar with Dephi, but here are the two main options when using strings with a non-COM DLL and VB6.
Option 1. Use "ANSI" strings.
'DLL routine expecting to be passed pointers to ANSI strings '
'VB6 will allocate and deallocate the strings '
'Its vital that VB6 allocates sufficient space for the return string '
Declare Sub MyProc Lib "mylib.dll" (ByVal Param As String, _
ByVal OutVal As String)
Function DoMyProc(ByVal Param As String) As String
Dim sResult As String
sResult = Space$(255) ' create 255 bytes of space for the return string '
Call MyProc(Param, sResult)
DoMyProc = sResult
End Function
Option two. Use BSTRs.
'DLL routine expecting to be passed two BSTRs. It will modify the second one. '
'VB6 "owns" both BSTRs and will deallocate them when it has finished with them. '
Declare Sub MyProc(ByVal lpParam As Long, ByVal lpOutVal As Long)
Function DoMyProc(ByVal Param As String) As String
Dim sResult As String
Call MyProc(StrPtr(Param), StrPtr(sResult))
DoMyProc = sResult
End Function
I'd also suggest looking at the Microsoft advice on writing C DLLs to be called from VB. Originally released with VB5 but still relevant to VB6.
Use the Windows API to allocate the memory that the PChar pointer points into. Then, the VB app can deallocate the memory after use, using the Windows API, too.
I would say that whoever allocates the memory must also free it in this case. You will run into problems with other scenarios. So the most safe and clean way would be:
The DLL allocates memory (because it knows how much) and returns the PChar to caller
After the caller is done with it, it calls FreePointer back to the DLL
DLL frees the memory in the FreePointer exported function
The setup would be like this:
unit DLL;
interface
uses
SysUtils;
function Execute(const Params: PChar): PChar; stdcall;
procedure FreePointer(const P: PChar); stdcall;
exports Execute;
exports FreePointer;
implementation
function Execute(const Params: PChar): PChar; stdcall;
var
Size: Cardinal;
begin
Size := Calculate the size;
GetMem(Result, Size);
...do something to fill the buffer
end;
procedure FreePointer(const P: PChar); stdcall;
begin
FreeMem(P);
end;
end.
You cannot return a PChar as a function result, but you can pass an additional PChar parameter and copy the string you want to return to this PChar. Note, that VB must allocate that string to the required size before passing it to the dll. Also in VB that parameter must be declared as byval param as string AND it must be passed with byval:
param = "aaaaaaaaaaaaaaaaaaaa" ' reserve 20 characters
call myproc(byval param)
The additional byval in the call will do the compiler magic of converting a VB string to a PChar and back.
(I hope I remember this is correctly, it has been quite a while since I was forced to use VB.)