Is there a COM accessible library to allow URL Encoding? - com

Using VB6. It's not hard to roll your own, but I wondered if was a prebuilt one out there?

Prompted by Bob's comment: Google found this wrapper for UrlEscape in a newsgroup post from Karl Peterson.
Private Declare Function UrlEscape Lib "Shlwapi.dll" Alias "UrlEscapeA" ( _
ByVal pszURL As String, ByVal pszEscaped As String, ByRef pcchEscaped As Long, _
ByVal dwFlags As Long) As Long
Private Const URL_DONT_ESCAPE_EXTRA_INFO As Long = &H2000000
Private Function EscapeURL(ByVal URL As String) As String
' Purpose: A thin wrapper for the URLEscape API function. '
Dim EscTxt As String
Dim nLen As Long
' Create a maximum sized buffer. '
nLen = Len(URL) * 3
EscTxt = Space$(nLen)
If UrlEscape(URL, EscTxt, nLen, URL_DONT_ESCAPE_EXTRA_INFO) = 0 Then
EscapeURL = Left$(EscTxt, nLen)
End If
End Function
Disclaimer: I haven't tried this code myself.

You should use CoInternetParseUrl(), with URL_ENCODE.
The sample from MSDN, modified for your purposes. Of course, you'll have to figure out how to call CoInternetParseUrl() from VB6, but you seem well on your way to that.
#include <wininet.h>
// ...
WCHAR encoded_url[INTERNET_MAX_URL_LENGTH];
DWORD encoded_url_len = ARRAYSIZE(encoded_url);
// Assumes |url| contains the value you want to encode.
HRESULT hr = CoInternetParseUrl(url, PARSE_CANONICALIZE, URL_ENCODE, encoded_url,
INTERNET_MAX_URL_LENGTH, & encoded_url_len, 0);
if (SUCCEEDED(hr)) {
// Do stuff...
}
You may want to use PARSE_ENCODE instead of PARSE_CANONICALIZE, depending on your needs.
Also, consider using google-url. May be difficult since it's C++ and not COM based.

Related

Assign a string to a byte array in a VBA user-defined type

I work with a relay module that I normaly connect via USB. That all works perfectly. Now I would like to connect it via the network. All manufacturer's VB.NET code works in vba except for accessing this module over the network.
Public Declare PtrSafe Function DapiOpenModuleEx Lib "DELIB64" (ByVal moduleID As Long, ByVal nr As Long, ByRef exbuffer As DAPI_OPENMODULEEX_STRUCT, ByVal open_options As Long) As Long
' Definitions for DapiOpenEx
Public Type DAPI_OPENMODULEEX_STRUCT
address(255) As Byte
timeout As Long
portno As Long
encryption_type As Long
encryption_password(31) As Byte
End Type
'Open ETH-Module with parameter
Dim handle as Ulong
Dim MyModuleID As UInt32
MyModuleID = 42
Dim open_buffer As New DELib64.DAPI_OPENMODULEEX_STRUCT
open_buffer.address = System.String.Copy(192.168.1.1 As String) As String
open_buffer.portno = 0
handle = DELib.DapiOpenModuleEx(MyModuleID, 0, open_buffer)
I am getting an error "open_buffer.address = System.String.Copy(192.168.1.1 As String) As String "
Can someone help me with what i need to change here?
Dim handle as LongLong
Dim MyModuleID As Long
MyModuleID = 42
Dim open_buffer As Delib64.DAPI_OPENMODULEEX_STRUCT
open_buffer.address = system.String.Copy("192.168.1.1" AS String) As String
open_buffer.portno = 0
handle1 = DapiOpenModuleEx(MyModuleID, 0, open_buffer, 0)
According to your comment, the original line of code is
strcpy((char*) open_buffer.address, "192.168.1.10");
So you need to copy the ASCII (single-byte) string "192.168.1.10" into a VBA byte array. This is surprisingly hard, since the obvious approach of open_buffer.address = StrConv("192.168.1.10", vbFromUnicode) won't work (you can't assign to a fixed-size array that's part of a type).
One obvious solution would be to make a Windows API call to CopyMemory, but if we want a VBA-only solution, a simple loop should suffice:
Dim i As Long
Dim b() As Byte
b = StrConv("192.168.1.10", vbFromUnicode)
For i = 0 To UBound(b)
open_buffer.address(i) = b(i)
Next
open_buffer.address(UBound(b) + 1) = 0 ' C-strings need to be 0-terminated
(I do have the feeling that this should be easier, so I'll gladly upvote competing, simpler answers.)

CH341DLL.DLL + I2C not works properly with VB.NET

I write VB.NET class for implement CH341DLL.DLL functionality. The method CH341StreamI2C() is used for stream write and read into device. This way I've imported the method CH341StreamI2C() from DLL:
<DllImport("CH341DLL.DLL", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)>
Private Shared Function CH341StreamI2C(ByVal iIndex As Integer, ByVal iWriteLength As Integer, ByRef iWriteBuffer As IntPtr, ByVal iReadLength As Integer, ByRef oReadBuffer As IntPtr) As Boolean
End Function
For check how this method works, I use I2C humidity and temperature sensor HTU21D. It's IIC address is 40h, and register where temperature is getting is E3h. So I invoke method CH341StreamI2C() like this:
Dim writeBuffer as Byte() = {&H40, &hE3} 'Address+Command
Dim s As String = Encoding.Unicode.GetString(writeBuffer)
Dim writeBufPtr As IntPtr = Marshal.StringToHGlobalAuto(s) 'Get pointer for write buffer
Dim wLen As Integer = writeBuffer.Length
Dim readBufPtr As IntPtr = IntPtr.Zero 'Init read pointer
Dim rLen as Integer = 3 'Sensor must return 3 bytes
Dim res As Boolean = CH341StreamI2C(0, wLen, writeBufPtr, rLen, readBufPtr)
I use logic analyzer to see what is on the SDA and SCL lines. And result is unpredictable. For example, if call previous code 4 times, that's the result:
It's seen, that physically CH341 device writes unpredictable values in the line. This is not DLL error, because other applications use this method and the result is correct. For note, other methods, e.g. CH341ReadI2C() and CH341WriteI2C(), that reads/writes only one byte per time, acts correct in my code.
What is the probably reason of the such behavior? May be, I've marshalled write buffer incorrect? How is the right way to do this?
If this is what you're using, the original declaration is:
BOOL WINAPI CH341StreamI2C(ULONG iIndex, ULONG iWriteLength, PVOID iWriteBuffer, ULONG iReadLength, PVOID oReadBuffer);
Since the buffer parameters are PVOIDs, you should be able to just marshal them directly to byte arrays:
<DllImport("CH341DLL.DLL", SetLastError:=True, CallingConvention:=CallingConvention.StdCall)>
Private Shared Function CH341StreamI2C(ByVal iIndex As Integer, ByVal iWriteLength As Integer, ByVal iWriteBuffer As Byte(), ByVal iReadLength As Integer, ByVal oReadBuffer As Byte()) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
Arrays are reference types (classes) which means you always refer to them via their memory pointer. Thus when you pass them to a function (P/Invoked or not) you're actually passing the array's pointer, rather than the array itself. This is really helpful when P/Invoking since it often lets you pass the arrays as-is.

StartDocPrinter WinAPI call fails in VBA

Objective: Print from string or rich text box to any printer desired, using the Win API functions specified.
Problem: Calling StartDocPrinter in VBA Access always returns 0.
Info: The code below runs through, without breaking. OpenPrinter appears to get a good handle. When StartDocPrinter is called, it returns 0.
Using the following code I have tried,
Saving different info to dDocInfo and per #David_Heffernan recommendation, declared DOCINFO properties as Long and set values to 0.
When .pDatatype = vbNullstring, GetLastError returns,
Error 124 (invalid level) when StartDocPrinter parameter Level = 1
Error 6 (invalid handle) when StarDocPrinter parameter Level = ByVal 1, though an apparent valid handle shows in hPrinter
When .pDatatype = "RAW", GetLastError returns 0 regardless.
When .pDatatype = 'vbNullString and either DOCINFO property is set to a string, GetLastError returns 0 regardless.
Changing the parameters of the WinAPI functions (ByRef DOCINFO)
Checking into access privilege issues. It appears, from other's code, that setting the last OpenPrinter parameter to 0 should set the requested access to the printer to be PRINTER_ACCESS_USE. Is it possible GetLastError is not returning access denial errors?
Converting multiple references' code from C++ to VBA, but converting or not including pointers is confusing. Am I not converting StartDocPrinter(printer, 1, (LPBYTE) &docInfo); correctly?
Code:
Declarations:
Type DOCINFO
pDocName As String
pOutputFile As String
pDatatype As String
End Type
Public Declare Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, hPrinter As Long, ByVal pDefault As Long) As Long
Public Declare Function StartDocPrinter Lib "winspool.drv" Alias "StartDocPrinterA" (hPrinter As Long, Level As Long, dDocInfo As DOCINFO) As Long
Function:
Public Function printRawData(sPrinterName As String, lData As String) As Boolean
Dim bStatus As Boolean, hPrinter As Long, dDocInfo As DOCINFO, lJob As Long, nWritten As Integer
' Open a handle to the printer.
bStatus = OpenPrinter(sPrinterName, hPrinter, 0)
If bStatus Then
' Fill in the structure with info about this "document."
dDocInfo.pDocName = vbNullString
dDocInfo.pOutputFile = vbNullString
dDocInfo.pDatatype = "RAW"
' Inform the spooler the document is beginning.
lJob = StartDocPrinter(hPrinter, 1, dDocInfo) 'Returns 0 :(
Debug.Print hPrinter, sPrinterName, lJob, GetLastError()
If lJob > 0 Then
' Start a page.
bStatus = StartPagePrinter(hPrinter)
If bStatus Then
' Send the data to the printer.
bStatus = WritePrinter(hPrinter, lData, Len(lData), nWritten)
EndPagePrinter (hPrinter)
End If
' Inform the spooler that the document is ending.
EndDocPrinter (hPrinter)
End If
' Close the printer handle.
ClosePrinter (hPrinter)
End If
' Check to see if correct number of bytes were written.
If Not bStatus Or (nWritten <> Len(lData)) Then
printRawData = False
Else
printRawData = True
End If
End Function
References/Relevant Questions:
- http://support.microsoft.com/kb/154078 Basis document for this code. Edit: Found where ByVal was missed on a few declarations here.
- Send Raw Data to ZPL Printer using Visual Basic (MS Access 2000) This person seems to use nearly identical code effectively, so why can't I? The answer to this question is written in C++.
- http://www.cplusplus.com/forum/general/26184/ The code here is also written in C++ and I am unsure how to convert.
- http://codingdomain.com/visualbasic/win32api/datatypes/ Guidance I'm using on converting datatypes and pointers, which I don't fully understand.
- StartDocPrinter(hPrinter, 1, di) returns false Some code was provided here but no answer. This is where I got idea to give errors provided.
- excel bva code to send command to usb printer I tried this and do not have the required access privileges. I would still like to know how to use the above code correctly, even if this is what I would end up doing.
With #HansPassant's link to the MS KB, I discovered the error in my code. The declaration of the StartDocPrinter function was missing ByVal for the hPrinter and Level parameters.
Public Declare Function StartDocPrinter Lib "winspool.drv" Alias "StartDocPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, dDocInfo As DOCINFO) As Long
Your declaration for the DOCINFO structure does not look correct. It should be declared as:
Type DOCINFO
cbSize As Integer
lpszDocName As String
lpszOutput As String
lpszDatatype As String
fwType As Integer
End Type
The cbSize should be initialized to the size of the structure in bytes, and fwType should be set to 0.

Conversion of VB Code to Delphi (It will extract image from EMF File)

While searching in the net i got few lines of code in VB for extracting an image from EMF File.
I tried to convert that into Delphi but doesnt work.
Help me in converting this code to delphi.
Public Function CallBack_ENumMetafile(ByVal hdc As Long, _
ByVal lpHtable As Long, _
ByVal lpMFR As Long, _
ByVal nObj As Long, _
ByVal lpClientData As Long) As Long
Dim PEnhEMR As EMR
Dim PEnhStrecthDiBits As EMRSTRETCHDIBITS
Dim tmpDc As Long
Dim hBitmap As Long
Dim lRet As Long
Dim BITMAPINFO As BITMAPINFO
Dim pBitsMem As Long
Dim pBitmapInfo As Long
Static RecordCount As Long
lRet = PlayEnhMetaFileRecord(hdc, ByVal lpHtable, ByVal lpMFR, ByVal nObj)
RecordCount = RecordCount + 1
CopyMemory PEnhEMR, ByVal lpMFR, Len(PEnhEMR)
Select Case PEnhEMR.iType
Case 1 'header
RecordCount = 1
Case EMR_STRETCHDIBITS
CopyMemory PEnhStrecthDiBits, ByVal lpMFR, Len(PEnhStrecthDiBits)
pBitmapInfo = lpMFR + PEnhStrecthDiBits.offBmiSrc
CopyMemory BITMAPINFO, ByVal pBitmapInfo, Len(BITMAPINFO)
pBitsMem = lpMFR + PEnhStrecthDiBits.offBitsSrc
tmpDc = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0&)
hBitmap = CreateDIBitmap(tmpDc, _
BITMAPINFO.bmiHeader, _
CBM_INIT, _
ByVal pBitsMem, _
BITMAPINFO, _
DIB_RGB_COLORS)
lRet = DeleteDC(tmpDc)
End Select
CallBack_ENumMetafile = True
End Function
What you've posted is an instance of an EnumMetaFileProc callback function, so we'll start with the signature:
function Callback_EnumMetafile(
hdc: HDC;
lpHTable: PHandleTable;
lpMFR: PMetaRecord;
nObj: Integer;
lpClientData: LParam
): Integer; stdcall;
It begins by declaring a bunch of variables, but I'll skip that for now since I don't know which ones we'll really need, and VB has a more limited type system than Delphi. I'm going to declare them as we need them; you can move them all to the top of the function yourself.
Next comes a call to PlayEnhMetaFileRecord using most of the same parameters that were passed into the callback function. The function returns a Bool, but then the code ignores it, so let's not bother with lRet.
PlayEnhMetaFileRecord(hdc, lpHtable, lpMFR, nObj);
Next we initialize RecordCount. It's declared static, which means it retains its value from one call to the next. That looks a little dubious; it should probably be passed in as a pointer in the lpClientData parameter, but let's not veer too far from the original code for now. Delphi does static variables with typed constants, and they need to be modifiable, so we'll use the $J directive:
{$J+}
const
RecordCount: Integer = 0;
{$J}
Inc(RecordCount);
Next we mcopy some of the meta record into another variable:
var
PEnhEMR: TEMR;
CopyMemory(#PEnhEMR, lpMFR, SizeOf(PEnhEMR));
It looks a little strange to copy the TMetaRecord structure onto a TEMR structure since they aren't really similar, but again, I don't want to veer from the original code too much.
Next is a case statement on the iType field. The first case is when it's 1:
case PEnhEMR.iType of
1: RecordCount := 1;
The next case is that it's emr_StretchDIBits. It copies more of the meta record, and then assigns some other pointers to refer to subsections of the main data structure.
var
PEnhStretchDIBits: TEMRStretchDIBits;
BitmapInfo: TBitmapInfo;
pBitmapInfo: Pointer;
pBitsMem: Pointer;
emr_StretchDIBits: begin
CopyMemory(#PEnhStrecthDIBits, lpMFR, SizeOf(PEnhStrecthDIBits));
pBitmapInfo := Pointer(Cardinal(lpMFR) + PEnhStrecthDiBits.offBmiSrc);
CopyMemory(#BitmapInfo, pBitmapInfo, SizeOf(BitmapInfo));
pBitsMem := Pointer(Cardinal(lpMFR) + PEnhStrecthDiBits.offBitsSrc);
Then comes what seems to be the real meat of the function, where we create a display context and a bitmap to go with it using the DIBits extracted using the previous code.
var
tmpDc: HDC;
hBitmap: HBitmap;
tmpDc := CreateDC('DISPLAY', nil, nil, nil);
hBitmap := CreateDIBitmap(tmpDc, #BitmapInfo.bmiHeader, cbm_Init,
pBitsMem, #BitmapInfo, dib_RGB_Colors);
DeleteDC(tmpDc);
end; // emr_StretchDIBits
end; // case
Finally, we assign a return value to the callback function:
Result := 1;
So, there's your translation. Wrap it in a begin-end block, remove my commentary, and move all the variable declarations to the top, and you should have Delphi code that's equivalent to your VB code. However, all this code ultimately does is generate memory leaks. The hBitmap variable is local to the function, so the bitmap handle it holds is leaked as soon as this function returns. I assume the VB code works for you, though, so I guess you have some other plans for what to do with it.
If you're working with metafiles, have you considered using the TMetafile class in the Graphics unit? It might make your life easier.

How do I import and call unmanaged C dll with ANSI C string "char *" pointer string from VB.NET?

I have written my own function, which in C would be declared like this, using standard Win32 calling conventions:
int Thing( char * command, char * buffer, int * BufSize);
I have the following amount of Visual Basic code figured out, which should import the DLL file and call this function, wrapping it up to make it easy to call Thing("CommandHere",GetDataBackHere).
UPDATE: This code is now a working solution, as shown here:
Imports Microsoft.VisualBasic
Imports System.Runtime.InteropServices
Imports System
Imports System.Text
Namespace dllInvocationSpace
Public Class dllInvoker
' I tried attributes, but I could not make it build:
' <DllImport("thing1.dll", False, CallingConvention.Cdecl, CharSet.Ansi, "Thing", True, True, False, True)>
Declare Ansi Function Thing Lib "thing1.dll" (ByVal Command As String, ByRef Buffer As StringBuilder, ByRef BufferLength As Integer) As Integer
' This part contributed by helpful user:
Shared Function dllCall(ByVal Command As String, ByRef Results As String) As Integer
Dim Buffer As StringBuilder = New StringBuilder(65536)
Dim Length As Integer = Buffer.Capacity
Dim retCode As Integer = Thing(Command, Buffer, Length)
Results = Buffer.ToString()
'Debug.Assert(Results.Length = Length) ' This assertion is not true for me
Return retCode
End Function
End Class
End Namespace
I got the code to build by following the help received here, and then I had forgot the As Return Type (which got me a MarshalDirectiveException PInvokeRestriction). Then I had an assertion failure inside my DLL, which lead to an SEHException. Once fixed, this works BEAUTIFULLY. Thank you folks. There are newsgroups where people are saying this can not be done, that Visual Basic only loads managed DLL assemblies (which I guess is the normal thing most Visual Basic users are used to).
It depends on how you use the buffer argument in your C code. If you only pass a string from your VB.NET code to your C code then declaring it ByVal String is good enough. If however you let the C code return a string in the buffer then you have to declare it ByVal StringBuilder and initialize it properly before the call. For example:
Public Class dllInvoker
Declare Ansi Function Thing Lib "Thing1.dll" (ByVal Command As String, ByVal Buffer As StringBuilder, ByRef BufferLength As Integer) As Integer
Shared Function dllCall(ByVal Command As String, ByRef Results As String) As Integer
Dim Buffer As StringBuilder = New StringBuilder(65536)
Dim Length As Integer = Buffer.Capacity
Dim retCode As Integer = Thing(Command, Buffer, Length)
Results = Buffer.ToString()
Debug.Assert(Results.Length = Length)
Return retCode
End Function
End Class
Note the ambiguity in the returned Length value.
You cannot convert a StringBuilder instance to a string instance, instead, use the 'ToString' method to convert it back to the string type...here's the portion of the code in the dllCall function...
retCode = Thing(Command, Buffer, bufsz)
Results = Buffer.ToString();