I am attempting to use the FindWindow API using Visual Studio 2017 (.NET Framework 4.6.1) and VB.NET to retrieve the window handle for a currently running instance of Microsoft Word. I am finding that, although it has worked in the past (and is working in another area of the code) in one particular instance, although the FindWindow call is returning a value, I am not able to assign it to a variable. I have verified this in debug mode (screenshots available). I am trying to figure why the API call is not working in this particular instance.
Screenshots link: https://imgur.com/a/NuwpUyz
I have executed this call in some areas of the .NET code I am working with, so I know that it does work. I've changed the type in the definition of the "assignee" variable (from Object, to Integer, to IntPtr, etc., etc.) and rerun the application, with the same results (the "assignee" variable ends up with a value of zero, but the FindWindow call itself returns a integer value which appears to be the correct window handle.
The FindWindow API definition:
<DllImport("user32.dll")>
Public Shared Function FindWindow(ByVal strclassName As String, ByVal strWindowName As String) As Integer
End Function
The FindWindow API call:
.
.
.
Public hndMDIWord As Integer
.
.
.
.
If Word_Previously_Running Then
Try
_mdiWordApp = GetObject(, "Word.Application")
Catch ex As Exception
_mdiWordApp = New Word.Application
End Try
Else
_mdiWordApp = New Word.Application
End If
hndMDIWord = FindWindow("Opusapp", "")
If hndMDIWord <> 0 Then
SetParent(hndMDIWord, Me.Handle.ToInt32())
End If
I am expecting FindWindow to return an integer representing the window handle of the currently running instance of Word and than have that result assigned to the hndMDIWord variable. FindWindow does return the expected result, but the assignment statement for the hndMDIWord variable does not execute properly; hndMDIWord ends up with a value of zero. There is no error and no exception is thrown.
Any suggestions and/or insights will, of course, be greatly appreciated.
Regards,
Chris Fleetwood
I think the problem is: IntPtr is not compatible with Integer.
You need to declare return type as IntPtr:
<DllImport("user32.dll")>
Public Shared Function FindWindow(ByVal strclassName As String, ByVal strWindowName As String) As IntPtr
End Function
Because:
FindWindow has HWND return type
From MSDN handles are marshaled as IntPtr
Also there is a pinvoke.net website with examples of .net interop with wast majority of WinAPI functions.
Also hndMDIWord need to be declared as IntPtr and used accordingly, and other WinAPI functions need to be declared to use IntPtr for handlers too:
Public hndMDIWord As IntPtr
. . . .
If hndMDIWord <> IntPtr.Zero Then
Related
I use VB .Net to call the Kernel32.dll WriteFile API:
Public Declare Function WriteFile Lib "kernel32" _
( _
ByVal hFile As IntPtr, _
ByVal lpBuffer As Byte(), _
ByVal nNumberOfBytesToWrite As Int32, _
ByRef lpNumberOfBytesWritten As Int32, _
ByVal lpOverlapped As IntPtr _
) _
As Boolean
Can anybody tell me how to create an Overlapped structure (lpOverlapped) for this function, and how to correctly pass it (the API expects a pointer?) Please show a working code snippet, if possible...
All info I found either didn't show usable examples or were too complicated to understand for me, or just weren't for VB .Net ...
Probably resolved... I guess it's done like this:
Dim structPtr As IntPtr
'Create an empty pointer
structPtr = Marshal.AllocHGlobal(Marshal.SizeOf(my_struct))
'Copy the structure and data to the pointer in memory
Marshal.StructureToPtr(my_struct, structPtr, True)
You don't need to pass a pointer to an overlapped structure and if you are finding the examples too complicated don't use it. WinAPI can be complicated in general and it is often easier to accomplish what you need directly in .Net and avoid WinAPI altogehter.
To answer your question the structure would be defined as:
Public Structure OVERLAPPED
Public Internal As Long
Public InternalHigh As Long
Public offset As Long
Public OffsetHigh As Long
Public hEvent As Long
End Structure
However, System.Threading has two classes Overlapped and NativeOverlapped meant to make life easier. Overlapped is a .Net class which you can pack into a NativeOverlapped. The structure allows you to set a call back to be fired:
In C# you would define it like this:
Overlapped overlapped = new Overlapped();
NativeOverlapped* nativeOverlapped = overlapped.Pack(
DeviceWriteControlIOCompletionCallback,
null);
Note I said in C#, that's because Net. doesn't support the return type in VB because it is unsafe code:
Visual Basic does not support APIs that consume or return unsafe types.
So if you are really looking to do Overlapped IO, it might be a lot easier for you code your overlapped methods in a C# class library, which you can then reference in and call in your VB application.
From All API:
ยท lpOverlapped Points to an OVERLAPPED structure. This structure is
required if hFile was opened with FILE_FLAG_OVERLAPPED. If hFile was
opened with FILE_FLAG_OVERLAPPED, the lpOverlapped parameter must not
be NULL. It must point to a valid OVERLAPPED structure. If hFile was
opened with FILE_FLAG_OVERLAPPED and lpOverlapped is NULL, the
function can incorrectly report that the write operation is complete.
If hFile was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not
NULL, the write operation starts at the offset specified in the
OVERLAPPED structure and WriteFile may return before the write
operation has been completed. In this case, WriteFile returns FALSE
and the GetLastError function returns ERROR_IO_PENDING. This allows
the calling process to continue processing while the write operation
is being completed. The event specified in the OVERLAPPED structure is
set to the signaled state upon completion of the write operation. If
hFile was not opened with FILE_FLAG_OVERLAPPED and lpOverlapped is
NULL, the write operation starts at the current file position and
WriteFile does not return until the operation has been completed. If
hFile was not opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not
NULL, the write operation starts at the offset specified in the
OVERLAPPED structure and WriteFile does not return until the write
operation has been completed.
In a VB6 application I am checking if a certain VB.NET WinForms window exists:
Public Declare Function IsWindow Lib "user32" (ByVal hwnd As Long) As Long
If Not IsWindow(102937) Then
MessageBox("Window not found!")
End If
The messagebox is shown, but the window DOES exist.
I inspect it by
Debug.Print(Me.Handle.ToInt32)'it prints 102937
What goes wrong here?
Am I perhaps handling the return value of "IsWindow" incorrectly?
Thank you.
I found the solution:
I was indeed using the WinAPI function incorrectly.
I should have used
If IsWindow(102937) <> 1 Then
i am new to data macro in ms access 2013 and need some help with it.
so lets assume that i have a simple database with only one table of Users.
when i change the "Age" Field in the table, i want to run an external exe file (the reason why dosent matter).
so i learn the subject during the last few days and end up with this:
1. i build a module in ms access called RunMiniFix (MiniFix is the name of the exe file i want to run). the module uses ShellExecute function and the whole module looks like that:
Option Compare Database
Const SW_SHOW = 1
Const SW_SHOWMAXIMIZED = 3
Public Declare Function ShellExecute Lib "Shell32.dll" Alias "ShellExecuteA"
(ByVal hwnd As Long, _
ByVal lpOperation As String, _
ByVal lpFile As String, _
ByVal lpParameters As String, _
ByVal lpDirectory As String, _
Optional ByVal nShowCmd As Long) As Long
Public Function RunMiniFix()
Dim RetVal As Long
On Error Resume Next
RetVal = ShellExecute(0, "open", "C:\Program Files\MiniFix\MiniFix.exe", "<arguments>", _
"<run in folder>", SW_SHOWMAXIMIZED)
End Function
Now, when activating the module, everything works just fine and the exe file is running.
in ms access, i build a data macro based on an 'If Then' statement, asking
if [Users]![Age] > x and then calls the build-in RunCode event from the action catalog which calls the RunMiniFix function.
Now, when saving the data macro, the ms access pops up a message box saying Microsoft access dosen't have the ability to find the name "Users" i mentioned in the phrase and recommend me to look for the right control in the form. "the form"? yes, the form!
this database is not form based. i have no gui designed what so ever. i have no buttons or click event to handle.
what i am asking is how can i trigger the RunMiniFix module when the Age field is modify. please, this is very important!
thanks a head,
oron.
Please use SetLocalVar from the action list in data macro and set the expression to name of your function. Sample:
name: "test"
Expression: RunMiniFix()
I've got a project in Access 2010 that runs without issues. That is, until I add a breakpoint and I try to debug code. As soon as it reaches the first breakpoint, the VBA project opens up and about 1 second later Access crashes and restarts.
I can add a Debug.Print and all works fine. I just can't step through code.
Repair and compact did not work, nor to create a new project and import everything.
Looking at the event viewer I get:
Faulting application name: MSACCESS.EXE, version: 14.0.4750.1000, time stamp: 0x4b8bae0f
Faulting module name: ntdll.dll, version: 6.2.9200.16579, time stamp: 0x51637f77
Exception code: 0xc0150010
Fault offset: 0x00000000001041c0
Faulting process id: 0x2ef4
Faulting application start time: 0x01cf180a44c35b8a
Faulting application path: C:\Program Files\Microsoft Office 2010\Office14\MSACCESS.EXE
I can't unregister and reregister the DLL (entry point not found).
I've tried everything on http://pcsupport.about.com/od/fixtheproblem/a/ntdlldll.htm short of reisntalling Windows and still nothing.
Running Access in Safe mode does help, but does not fix it permanently.
Any other ideas?
UPDATE: I now have a new laptop and upgraded to Access 365. And it still happens. But only on one specific project. Other projects work fine.
Seems this is not the first time this type of error happens https://learn.microsoft.com/en-us/answers/questions/269052/vba-access-stop-command.html
The above link is very detailed (in terms of text + screenshots) and may be similar to what you experience.
Anyhow... please try to open your access software using the run as administrator option (right click access icon and pick this option). There is a chance that vba is trying to use elevated permissions and without it - it results in a spectacular crash
I got this error and discovered it was because of not declaring a variable as the correct type when using the GetUserName function, declared from the advapi32.dll library. I had to change "Dim buffer As String" to "Dim buffer As String * 256" before I could call "GetUserName(buffer, Size)". This is the code:
#If VBA7 Then
Private Declare PtrSafe Function GetUserName Lib "advapi32.dll" _
Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#Else
Private Declare Function GetUserName Lib "advapi32.dll" _
Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
#End If
Function getNetworkName() As String
Dim ret As Long, Size As Long
Dim buffer As String * 256
Size = 256
ret = GetUserName(buffer, Size)
If ret <> 0 Then
getNetworkName = Mid(buffer, 1, InStr(1, buffer, vbNullChar) - 1)
Else
getNetworkName = "API ERROR"
End If
End Function
I don't know if your issues would be the exact same thing, but maybe it's due to a data type issue like this.
What I'm trying to do is tracing Ctrl+Tab keypress on my main form using following code
<DllImport("user32.dll", PreserveSig:=False)>
Private Shared Function GetAsyncKeyState(ByVal vKey As System.Windows.Forms.Keys) As Short
End Function
Private ReadOnly Property CtrlPressed As Boolean
Get
Dim keyval As Integer
keyval = GetAsyncKeyState(Keys.ControlKey)
If keyval = 0 Then
CtrlPressed = False
Else
CtrlPressed = True
End If
End Get
End Property
But while calling the property CtrlPressed I'm getting PInvokeStackImbalance error. I'm sure with declaration of GetAsyncKeyState and also have imported InteropServices but the CtrlPressed property has something syntactically wrong. How can I solve this issue?
Thanks
A stack imbalance error almost always means that your P/Invoke definition signature is wrong. The first thing to do is check carefully all of your types and make sure that they match the documented signature for the native function. In this case, it looks good.
Except that you've set PreserveSig to False, and I'm not really sure why. As the documentation for that field indicates, it is designed for use with unmanaged methods that return an error code (for example, an HRESULT code). It instructs the runtime to automatically convert those error codes into exceptions. But the GetAsyncKeyState method doesn't return an error code, so enabling this option doesn't make a lot of sense.
The following code works fine for me:
<DllImport("user32.dll")> _
Private Shared Function GetAsyncKeyState(ByVal vKey As Keys) As Short
End Function
Private ReadOnly Property IsCtrlPressed As Boolean
Get
Dim isPressed As Short = GetAsyncKeyState(Keys.ControlKey)
Return (isPressed & &H8000) != 0
End Get
End Property
Make sure that you've paid close attention to the documentation for the GetAsyncKeyState function. Specifically, the section on return values:
If the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down, and if the least significant bit is set, the key was pressed after the previous call to GetAsyncKeyState. However, you should not rely on this last behavior; for more information, see the Remarks.
The return value is zero for the following cases:
The current desktop is not the active desktop
The foreground thread belongs to another process and the desktop does not allow the hook or the journal record.