I have to "translate" / up-date a program that was written in C about 20 years ago to VB.net due to its old SQL connection style that isn't compatible anymore. However, I have minimal experience with C and even less with the winAPI(which the C application uses)... I was wondering, can the same functions from the API be use in VB.net?
I have been able to add a declaration like this:
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Public Shared Function SetWindowText(hWnd As IntPtr, lpString As String) As Boolean
BOOL NEAR GoModal( HINSTANCE hInstance, LPCSTR lpszTemplate,HWND hWnd, DLGPROC lpDlgProc, LPARAM lParam )
Let's break down the parameters in the function call:
BOOL NEAR GoModal(HINSTANCE hInstance, LPCSTR lpszTemplate, HWND hWnd, DLGPROC lpDlgProc, LPARAM lParam )
BOOLis a 32 bit integer. You can use the
System.Booleantype in your code, but it needs the
MarshalAsAttributeapplied to it, specifying the type as
NEARspecifies information to the compiler. When translating to .net, you can ignore it.
Instance. Translating to .net, handles are typically defined as
IntPtrvalues. This doesn't mean that it's a pointer to memory. (ie-
Marshal.Readcan't be used.)
STRing. This is an
ANSIstring, so it means that the parameter needs the
UnmanagedType.LPStr. If this is was an out parameter, things would be more complicated, and you'd either need to pass a
Marshal.AllocCoTaskMemto get unmanaged memory allocated.
Window. Like the
HINSTANCEit is typically marshalled as an
DLGPROCis a bit more complex, so I'll address it below.
Parameter. The standard implementation that Microsoft used for this type when they created the
IntPtr. You should follow suit.
As I said above
DLGPROC is more complex. It is actually a pointer (reference) to a function. Specifically one with the signature:
INT_PTR CALLBACK DialogProc( _In_ HWND hwndDlg, _In_ UINT uMsg, _In_ WPARAM wParam, _In_ LPARAM lParam );
We've already seen
LPARAM, so let's take a quick look at the other things in this signature.
Pointer. If that sounds familiar, it should. It's just an
CALLBACKis another bit of information that the compiler uses to determine how the function should be compiled, and how people using it should send their parameters to the function. By default the .net marshal handles this, so no additional configuration is needed.
Param. In this case, it's old terminology, and you will want to use
IntPtrto receive the value.
To marshal a pointer to a
DLGPROC, you will need to create a delegate with the appropriate signature:
Delegate Function DLGPROC(ByVal hWnd As IntPtr, ByVal uMsg As UInt32, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr
You then need to create a function somewhere in your code, with the same signature, to receive the call from the native code. Something like:
Private Function MyDialogProc(ByVal hWnd As IntPtr, ByVal uMsg As UInt32, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As IntPtr Return IntPtr.Zero ' Put the appropriate code here. End Function
You then can add a class member to hold that delegate:
Private DialogProc As DLGPROC = New DLGPROC(AddressOf Me.MyDialogProc)
After all of that, you're finally ready to import the
GoModal function prototype. (YAY!) That looks like:
<DllImport("DLL_NAME")> Private Shared Function GoModal(ByVal hInstance As IntPtr, <MarshalAs(UnmanagedType.LPStr)> ByVal lpszTemplate As String, ByVal hWnd As IntPtr, ByVal lpDlgProc As IntPtr, ' More on this below ByVal lParam As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean End Function
We can now call the function from our code:
Dim dialogProcedure As IntPtr = Marshal.GetFunctionPointerForDelegate(Me.DialogProc) Dim result As Boolean = GoModal(IntPtr.Zero, "Template", Me.Handle, dialogProcedure, New IntPtr(100))
Two notes about the function call:
IntPtr.Zerois the same as
New IntPtr(100)creates an
IntPtrwith the specified value. (Sometimes the
LPARAMis just a number)
And if you made it this far, Congratulations! There may be a couple bugs in the code, but this should get you going. For other Windows API types, you can find a lot of information at pinvoke.net.
If you have a lot of interop to do, it would be easier to just add a C++/CLI dll to your project and call these functions natively. It's much, much easier, and you can define the functions exactly how you want them. In addition, C++/CLI works with any of the .net languages.