Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
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
End Function
to get a window Handle as IntPtr ( in vb.net). However the C program uses functions such as:
BOOL NEAR GoModal( HINSTANCE hInstance, LPCSTR lpszTemplate,HWND hWnd, DLGPROC lpDlgProc, LPARAM lParam )
By importing the user32.dll I can't get the hWnd handle but I can get the IntPtr ( If I understood well, is a point to a window as an integer right? Kinda similar to hWnd --> I could be TOTALLY wrong, but this is what I understood)
As I go to the definition of each type (i.e: NEAR , HINSTANCE, LPCSTR, etc...) I try to find an equivalent call in VB but ... its useless. For instance, I was looking at NEAR and I guess it was to decide if two pointers were close in memory ? ( again might be wrong, it's just what I gathered).
Ultimately, my question is, would such call exist in vb.net / even be useful with the more modern framework and such?
Let's break down the parameters in the function call:
BOOL NEAR GoModal(HINSTANCE hInstance,
LPCSTR lpszTemplate,
HWND hWnd,
DLGPROC lpDlgProc,
LPARAM lParam )
BOOL is a 32 bit integer. You can use the System.Boolean type in your code, but it needs the MarshalAsAttribute applied to it, specifying the type as UnmanagedType.Bool
NEAR specifies information to the compiler. When translating to .net, you can ignore it.
HINSTANCE is a Handle to an Instance. Translating to .net, handles are typically defined as IntPtr values. This doesn't mean that it's a pointer to memory. (ie- Marshal.Read can't be used.)
LPCSTR is a Long Pointer to a C style STRing. This is an ANSI string, so it means that the parameter needs the MarshalAsAttribute with UnmanagedType.LPStr. If this is was an out parameter, things would be more complicated, and you'd either need to pass a StringBuilder or use Marshal.AllocHGlobal/Marshal.AllocCoTaskMem to get unmanaged memory allocated.
HWND is a Handle to a Window. Like the HINSTANCE it is typically marshalled as an IntPtr
DLGPROC is a bit more complex, so I'll address it below.
LPARAM is a Long Parameter. The standard implementation that Microsoft used for this type when they created the Message class, is 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 HWND and LPARAM, so let's take a quick look at the other things in this signature.
INT_PTR is an Integer Pointer. If that sounds familiar, it should. It's just an IntPtr.
CALLBACK is 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.
WPARAM stands for Word Param. In this case, it's old terminology, and you will want to use IntPtr to 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.Zero is the same as NULL
New IntPtr(100) creates an IntPtr with the specified value. (Sometimes the LPARAM is 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.
Related
I have to import a few functions of a dll written in C into a VB6 project. I have an example written in C# but I don't really know how to do the same thing in VB6.
In C# it goes like this:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int someCallback(IntPtr ch, uint chL, IntPtr cbData);
[DllImport("someDLL.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int someFunction(IntPtr con, someCallback callback, IntPtr cbData);
Everything works fine in the example when calling someFunction.
The documentation of the dll just gives me this:
typedef int(SOMEAPI_CALL * someCallback)(const unsigned char *ch,
unsigned int chL,
void *cbData)
SOMEAPI_CALL someFunction(Con* con,
someCallback callback,
void* cbData)
There should be a way to do the same in VB6 but I don't have that much experience with this language. Searched the web for a good time but didn't find anything that could help me.
I know how to declare functions from that dll in my project but thats that. How to convert this UnmanagedFunctionPointer thingy into VB6 code, I just don't know.
VB6 has no attributes in the way VB.NET (and C#) does, and the annotation would be unnecessary anyway. You can just pass a function pointer to a C API function via the AddressOf operator:
Declare Function someFunction Lib "someDLL" ( _
ByVal con As Long, _
ByVal callback As Long, _
ByVal data As Long _
) As Long
…
Call someFunction(con, AddressOf SomeCallback, data)
But even that won’t work since VB6 does not support native interop using the cdecl calling convention, it only supports stdcall. You will either need to recompile your DLL using the stdcall calling convention, or create a wrapper in C or IDL (there are some hacks using inline assembly to wrap individual cdecl calls but I wouldn’t recommend using these).
I had wrote a CMyDailog with MFC based on CDialog. And there is a OnTimer method override the CDialog one.
CMyDailog : public CDialog
{
afx_msg void OnTimer(UINT_PTR nIDEvent)
}
Now, we are migrating our MFC code to ATL. I have to make CMyDailog inherited from CAxDialogImpl<CMyDailog>.
CMyDailog : public CAxDialogImpl<CMyDailog>
{}
I didn't find an equivalent of CDialog::OnTimer in the ATL side. Do I have to use the Windows API ::SetTimer(GolbalTimerFunction) to achive that? In that case, I have to move my original member variable values into the globe scope -- I don't like that.
I find this maybe help:
https://www.codeproject.com/Messages/2870588/what-is-the-event-i-should-use.aspx
I found the equivalent one.
LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
For more detail
This question already has answers here:
Finding the handle to a WPF window
(5 answers)
Closed 3 years ago.
I am trying to find the handle of a VB.Net WPF application. I have tried various hWnd IntPtr commands with no luck
Me.Handle gives the error 'Handle' is not a parameter of MainWindow
Dim itnrp As IntPtr = Process.GetCurrentProcess().MainWindowHandle
gives the error:
error BC30657: 'ToPointer' has a return type that is not supported or parameter types that are not supported.
If I try putting it into a function to try and get the type that the above error appears to refer to like this...:
Public Function GetHandleOfWindow()
winHandle = Process.GetCurrentProcess().MainWindowHandle.GetType()
'Return winHandle
End Function
I get the same error BC30657 as previous
If I try:
Public winHandle As IntPtr
winHandle = New WindowInteropHelper(Me).Handle
I get the same error BC30657 as previous
Any help would be much appreciated!!
It would appear this is a bug within Visual Studio 2019
[https://developercommunity.visualstudio.com/content/problem/491378/vb-intptr-error-bc30657-topointer-has-a-return-typ.html]]
I am using the windowsformshost and trying to get dragover and drop events.
I have set allowdrop on the WPF window, Windowsformshost and the ChromiumWebBrowser. I can understand that the WPF window will not get the event due to the windowsformshost airspace issues. But I dont understand why the windowsformshost or the ChromiumWebBrowser does not get any of the events. It appears they are swallowed and not passed on by CEF/CefSharp. How can I handle the events and/or what do I need to disable in CEF/CefSharp?
As I am moving from pure WPF CefSharp I have implemented a dragenter, dragmove and dragdrop with the WPF ChromiumWebBrowser (not using the IDragHandler).
What I would like to achieve is to show a different drag effect based on where the mouse is dragging and secondly I would like to be able to intercept the drop event to first check the file type where the user drops over a file upload type element.
What is the effect of disabling or calling RevokeDragDrop and which Hwnd (window) should this be called on from a Cefsharp point of view?
Answering my own question: it is possible to restore the drag and drop events which have been swallowed upstream.
First I used the IOleDropTarget Interface which looks like this;
<ComImport, Guid("00000122-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Interface IOleDropTarget
<PreserveSig>
Function OleDragEnter(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal pDataObj As Object,
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
<PreserveSig>
Function OleDragOver(
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
<PreserveSig>
Function OleDragLeave() As Integer
<PreserveSig>
Function OleDrop(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal pDataObj As Object,
<[In], MarshalAs(UnmanagedType.U4)> ByVal grfKeyState As Integer,
<[In], MarshalAs(UnmanagedType.U8)> ByVal pt As Long,
<[In], Out> ByRef pdwEffect As Integer) As Integer
End Interface
Next and while we are on the interfaces let use create the nice icons that windows explorer shows when a file is dragged with this interface.
Imports IDataObject_Com = System.Runtime.InteropServices.ComTypes.IDataObject
Imports System.Windows.Interop
Imports System.Runtime.InteropServices
Namespace Browser
<StructLayout(LayoutKind.Sequential)>
Public Structure Win32Point
Public x As Integer
Public y As Integer
End Structure
<ComImport>
<Guid("4657278A-411B-11d2-839A-00C04FD918D0")>
Public Class DragDropHelper
End Class
<ComVisible(True)>
<ComImport>
<Guid("4657278B-411B-11D2-839A-00C04FD918D0")>
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)>
Interface IDropTargetHelper
Sub DragEnter(
<[In]> ByVal hwndTarget As IntPtr,
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal dataObject As IDataObject_Com,
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub DragLeave()
Sub DragOver(
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub Drop(
<[In], MarshalAs(UnmanagedType.[Interface])> ByVal dataObject As IDataObject_Com,
<[In]> ByRef pt As Win32Point,
<[In]> ByVal effect As Integer)
Sub Show(
<[In]> ByVal show As Boolean)
End Interface
You need to implement the IOleDropTaget interface which will then supply the events of DragEnter, Over, Leave and Drop.
To hook up these events you don't need to know anything about the messageloop or wndproc messages as was hinted at above. What you do need to know is that one of the Chromium windows with class name "Chrome_WidgetWin_0" is registered for drag and drop and this has to be first revoked before you can get the events.
CefSharp examples show how to drill down into the Chromium windows but it is normally to get another window class. In this case I use the following (note I revoke all windows I find along the way in the call back function but it appears that only Chrome_WidgetWin_0 is registered.
Const Chrome_WidgetWin As String = "Chrome_WidgetWin_0"
Private Function TryFindHandl(ByVal browserHandle As IntPtr, <Out> ByRef chromeWidgetHostHandle As IntPtr) As Boolean
Dim cbXL As New NativeMethodsEx.EnumChildCallback(AddressOf EnumChildProc_Browser)
NativeMethodsEx.EnumChildWindows(browserHandle, cbXL, chromeWidgetHostHandle)
Return chromeWidgetHostHandle <> IntPtr.Zero
End Function
Private Shared Function EnumChildProc_Browser(ByVal hwndChild As Integer, ByRef lParam As Integer) As Boolean
Dim buf As New StringBuilder(128)
NativeMethodsEx.GetClassName(hwndChild, buf, 128)
Dim ret = NativeMethodsEx.RevokeDragDrop(hwndChild)
If ret = NativeMethodsEx.DRAGDROP_E_NOTREGISTERED Then
Debug.Print("")
End If
If buf.ToString = Chrome_WidgetWin Then
lParam = hwndChild
Return False
End If
Return True
End Function
Once you have this handle and you have revoked it as drop target then you can call RegisterDragDrop passing in the handle and your IOleDropTarget class.
A few of my WinAPI signature look like this
Friend Const DRAGDROP_E_NOTREGISTERED = &H80040100
Friend Const DRAGDROP_E_INVALIDHWND = &H80040102
Friend Const DRAGDROP_E_ALREADYREGISTERED = &H80040101
Friend Const E_OUTOFMEMORY = &H8007000E
Friend Const S_OK = 0
<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Friend Shared Function GetClassName(ByVal hWnd As System.IntPtr, ByVal lpClassname As System.Text.StringBuilder, ByVal nMaxCount As Integer) As Integer
End Function
Friend Delegate Function EnumChildCallback(ByVal hwnd As Integer, ByRef lParam As Integer) As Boolean
<DllImport("User32.dll")>
Friend Shared Function EnumChildWindows(ByVal hWndParent As Integer, ByVal lpEnumFunc As EnumChildCallback, ByRef lParam As Integer) As Boolean
End Function
<DllImport("ole32.dll")>
Friend Shared Function RegisterDragDrop(ByVal hwnd As IntPtr, DropTarget As Browser.IOleDropTarget) As IntPtr
End Function
<DllImport("ole32.dll")>
Friend Shared Function RevokeDragDrop(ByVal hwnd As IntPtr) As IntPtr
End Function
An example of the event and how to use the IDropTargetHelper is like this
Public Function OleDragEnter(<[In]> <MarshalAs(UnmanagedType.Interface)> pDataObj As Object, <[In]> <MarshalAs(UnmanagedType.U4)> grfKeyState As Integer, <[In]> <MarshalAs(UnmanagedType.U8)> pt As Long, <[In]> <Out> ByRef pdwEffect As Integer) As Integer Implements IOleDropTarget.OleDragEnter
Dim winPT As Win32Point
winPT.x = CInt(pt And &H7FFFFFFF)
winPT.y = CInt((pt >> 32) And &H7FFFFFFF)
Dim eff As DragDropEffects = DragDropEffects.None
'this is my event I am sending back to the browser class to deal with.
RaiseEvent DBDragEnter(eff, New Point(winPT.x, winPT.y))
'you need to pass in the effect
pdwEffect = CInt(eff)
'this is the helper which shows the nice icon you drag around.
ddHelper.DragEnter(targetHwnd, CType(pDataObj, IDataObject_Com), winPT, CInt(eff))
Return NativeMethodsEx.S_OK
End Function
Certainly something that would be good to see in CefSharp for WinForms especially as the control has a whole bunch or useless properties (AllowDrop) and events (Drag and Drop) at the moment which are not implemented.
I created a Winforms AutoComplete combobox control inheriting from ComboBox with the following method:
Protected Overrides Sub OnKeyPress(ByVal e As System.Windows.Forms.KeyPressEventArgs)
MyBase.OnKeyPress(e)
Dim text As String = Me.Text
If Char.IsLetter(e.KeyChar) Then
text = text + e.KeyChar
End If
If (text.Length = AutoCompleteSearchChars And Char.IsLetter(e.KeyChar)) Then
SetAutoCompleteMethod.Invoke(text)
Me.AutoCompleteSource = AutoCompleteSource.ListItems
Me.Text = text
Me.SelectionStart = text.Length
Me.SelectionLength = 0
If Me.Items.Count > 0 Then
Me.SelectedIndex = 0
End If
e.Handled = True
End If
End Sub
The "SetAutoCompleteMethod" is a delegate pointing to a method that populates the
combobox items based on using the current text in the combo box as the prefix for a
wildcard search. This provides similar functionality to the ajax autocomplete combobox.
This all works fine the first time it is called, but the second time it is called I get the "Attempted to read or write protected memory. This is often an indication that other memory has been corrupted" error. The error appears after all the code to update the combobox is completed. I get the following stack trace:
at
System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG&
msg) at
System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.
UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(Int32
dwComponentID, Int32 reason, Int32
pvLoopData) at
System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at
System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32
reason, ApplicationContext context)
at
System.Windows.Forms.Application.Run(ApplicationContext
context) at
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.OnRun()
at
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.DoApplicationModel()
at
Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.Run(String[]
commandLine) at
ApplicationShell.My.MyApplication.Main(String[]
Args) in
17d14f5c-a337-4978-8281-53493378c1071.vb:line
81 at
System.AppDomain._nExecuteAssembly(Assembly
assembly, String[] args) at
System.AppDomain.ExecuteAssembly(String
assemblyFile, Evidence
assemblySecurity, String[] args) at
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at
System.Threading.ThreadHelper.ThreadStart_Context(Object
state) at
System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback
callback, Object state) at
System.Threading.ThreadHelper.ThreadStart()
Is Anyone able to point me in the right direction to solve this?
When I've gotten that error, it turned out to really be an issue with dereferencing a null pointer, but it made the method call anyway and didn't crash until it tried to access one of the member variables. I haven't really worked with VB since before the days of .Net, but your stack trace suggests it died in a native call somewhere. If that's the case, you might be running into the same issue in which case I'd recommend you double check those pointers, not just where it crashes, but a couple levels up too.
Adding an object to the items collection whose ToString() method returns nothing is an easy way to get this. There are probably other ways to reach the same behavior.
Proceed to diagnosis it as if it were a NullReferenceException.
See What's the strangest corner case you've seen in C# or .NET? for another case.