I need help on global variable usage in an ActiveX(ATL) project. Basically the ActiveX component function is to navigae to a specified URL in composite control(webbrowser embedded). The URL string is initialize in the beginning and saved in a global variable. here is my source code file of ActiveX project.
(Do not be concerned about the grammar, I just want to show the code flow).
MyComponentInit.cpp
// MyComponentInit.cpp : Implementation of MyComponentInit.cpp
#include "stdafx.h"
#include <ios>
char szURL[1024] = "\0"; // global variable holding URL string
STDMETHODIMP CMyComponentInit::InitPlugin(BSTR url)
{
// convert BSTR to string
......
memcpy(szURL, szUrl, len);
}
MyComponentCtrl.cpp
// MyComponentCtrl.cpp : Implementation of MyComponentCtrl.cpp
#include "stdafx.h"
extern char szURL[1024]
LRESULT CMyComponentCtrl::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
......
m_spWebbrowser->Navigate(szURL, &v, &v, &v, &v);
}
The use of mycomponent ActiveX is in a MFC SDI(Single Doc/View) test application. The flow is like:
In App Initinstance, I called activeX InitPlugin to initialize the URL string.
In a Information dialog window, insert MyComponentCtrl ActiveX, the Information dialog is brought up by clicking Mainframe Information menu item. The goal is when Information dialog window pop up, it can always go to the specified url.
After my MFC test app starts, I click Information dialog menu, the Information dialog window can be up and navigate to the url correctly.
But the problem is if I close the Information dialog, wait fro a while (around 2~3 minutes), I tried to bring up Information dialog again, it could not navigate to the URL. I debug it and found the global variable szURL in MyComponenet project lost its value.
Can you please tell me what should I do to resolve this problem?
Thanks a lot in advance!
Bionicoder
It looks like the application and COM might be releasing and reloading your DLL between the initialization call and the control being displayed. This could happen if the main app has no active instances of any of the DLL's COM components and subsequently calls CoFreeUnusedLibraries().
You can test that by putting a breakpoint or trace from DllMain for fdwReason == DLL_PROCESS_DETACH. If this is the problem, the breakpoint will trigger after the initialization call but before OnInitDialog().
If this is the problem, you can solve it by initializing the control instance with the URL directly, using instance state instead of using a global variable.
You can alternately keep a reference to the initialization interface for the lifetime of the application.
Related
I created edge browser window using CreateCoreWebView2Host() method. This method is takes parent window handle and creates child window in which we can navigate the web page. After I am done with navigation I need to return my window handle, which I believe I am failed to return.
On Spy++ I see "Chrome_WidgetWin_0", "Chrome_WidgetWin_1", "Intermediate D3D Window" as child windows to my parent window. which one is the child window handle?I thought I am creating one child window.
I tried by fetching window handles using FindWindowEx() passing above mentioned class names. But still not getting expected results in my project. So I doubt if I am passing correct handle.
Now the question is , How to get the window handle(HWND) for the window created by CreateCoreWebView2Host?
You can get the edge window handle by using GetWindow, passing Handle of WebView2 control to it as the first argument and GW_CHILD as second argument. For exaample:
public const uint GW_CHILD = 5;
[DllImport("user32.dll")]
public static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
[DllImport("user32.dll")]
public static extern IntPtr SetFocus(IntPtr hWnd);
WebView2 webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
private async void Form1_Load(object sender, EventArgs e)
{
webView21.Dock = DockStyle.Fill;
this.Controls.Add(webView21);
await webView21.EnsureCoreWebView2Async();
webView21.Source = new Uri("https://bing.com");
webView21.NavigationCompleted += WebView21_NavigationCompleted;
}
private void WebView21_NavigationCompleted(
object sender, CoreWebView2NavigationCompletedEventArgs e)
{
var child = GetWindow(webView21.Handle, GW_CHILD);
SetFocus(child);
}
Note: You may find this GitHub thread useful. This is the trick that I've used to focus the browser in Windows Forms as well.
The WebView2 SDK doesn't provide this HWND because exactly how the WebView2 connects up to the provided parent HWND is intended to be an implementation detail that could change as the underlying Edge browser or WebView2 Runtime is updated even when you stay on the same version of the WebView2 SDK. We're relying in large part on the browser's logic for rendering and so this may change in the future.
Instead of providing an HWND, the CoreWebView2Host (its been renamed to CoreWebView2Controller in more recent SDK releases) provides various methods for you to focus, zoom, and so on. What are you trying to do with the HWND?
I'm working on a BHO written a long time ago in C++, without the use of any of the VS wizards. As a result, this project deviates from the COM conventions and the boilerplate for a COM product. I worked with COM long ago, but never really did any Windows GUI/dialog stuff...
I'm trying to add a dialog box to allow the user to set the values of some new settings:
// serverDialog will be NULL
HWND serverDialog = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PROPPAGE_SETTINGS), NULL, DialogProc);
id (!serverDialog)
{
int error = GetLastError(); //1813
...
}
....
1813 means that the resource cannot be found. The IDD used there is in resource.h, which I manually included where needed.
DialogProc is defined as:
INT_PTR CALLBACK DialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE;
}
Which I know I will have to change later if I want the dialog to actually process messages, but I haven't gotten that far yet. The 1813 error suggests failure before the dialog is even created as does the NULL dialog handle returned.
To add the dialog I used the Add Resource wizard and added a small property page.
I've tried to follow advice here, but to no avail.
Thanks!
You are passing GetModuleHandle(NULL) as the instance of the module that contains the resource. But GetModuleHandle(NULL) defines the executable file module. You need to pass the instance of the module containing your code. This question covers that topic: How do I get the HMODULE for the currently executing code?
You probably ought to pass a window handle to the hWndParent parameter so that the dialog is owned.
Is there a way I can get a HWND by it's property "name"? I know that every IDE has its own properties for HWND elements but those properties are applied to the HWND.
I'm not working in Visual Studio, this is just a case. I want to get HWNDs by Name in C++ without VS Libraries.
For example:
HWND button = GetHwndByName("button1"); //Example
Property "name" is button1
I'm going to assume you're either trying to access your GUI controls in code or some other program's GUI controls.
As some people have mentioned, the (name) property in the properties editor is just the variable name used for that control. Your screenshot shows Visual Studio editing a .net program. In the case of .net, the (name) field is the name of the class member of the window class that represents the control. So if (name) is button1 then Visual Studio might generate code like
// pseudo-C++/C#-like
class Form1 : public System.Windows.Forms.Form {
private:
System.Windows.Forms.Button *button1;
...
};
The idea here is that you would have event handlers as part of your Form1 class:
void Form1::onButton1Clicked(void)
{
this->button1->SetText("You clicked me!");
}
As such, the (name) is not an intrinsic property of the window from Windows's point of view.
I don't know what CA Plex's GUI editor looks like, but I would assume, given you said you were using C++, that it either
a) produces a class like the one I pasted above, in which case you would just use the (name) directly as members, or
b) produces a header file with each of those control names as global HWND variables
Either way, you can just use them directly from within your code. Perhaps have something like
void doToAllButtons(void (*f)(HWND, LPARAM), LPARAM lParam)
{
(*f)(button1, lParam);
(*f)(button2, lParam);
(*f)(button3, lParam);
}
and simply write an appropriate function to call via this one.
If you need to interface with another program and want to use its variable names, then you're out of luck. You'll need to find the windows you want some other way, such as with FindWindow().
I have followed this link to get the window handle of a ActiveX control
Sample Code from microsoft's site
// The following code should return the actual parent window of the ActiveX control.
HWND CMyOleControl::GetActualParent()
{
HWND hwndParent = 0;
// Get the window associated with the in-place site object,
// which is connected to this ActiveX control.
if (m_pInPlaceSite != NULL)
m_pInPlaceSite->GetWindow(&hwndParent);
return hwndParent; // Return the in-place site window handle.
}
But in my case I keep finding that "m_pInPlaceSite" is always NULL. I'm trying to run this code in my controls FinalConstruct. Is there something else I need to implement for the m_pInPlaceSite to be given a value? Or do I need to Query to get the value.
Thanks
FinalConstruct is way too early. In FinalConstruct your class is just being created and is not yet initialized. There is no "in place" site, there is no yet site at all.
Your control will be called by its owner, it will be given its site, then activated - only then you will possibly have m_pInPlaceSite available.
I want to quickly test an ocx. How do I drop that ocx in a console application. I have found some tutorials in CodeProject and but are incomplete.
Isn't an OCX an ActiveX User Control? (something that you put onto a form for the user to interact with)?
The easiest way I know of to test COM/ActiveX stuff is to use excel. (Yes I know it sounds dumb, bear with me)
Run Excel, create a new file if it hasn't done this for you
Press Alt+F11 to launch the Visual Basic Editor (if you have excel 2007 it's on the 'Developer' ribbon tab thing
Now that you're in happy visual basic land...
From the Tools menu, select References
Select your OCX/COM object from the list, or click Browse... to find the file if it's not registered with COM - You may be able to skip this step if your OCX is already registered.
From the Insert menu, select UserForm
In the floating Toolbox window, right click and select Additional Controls
Find your OCX in the list and tick it
You can then drag your OCX from the toolbox onto the userform
From the Run menu, run it.
Test your OCX and play around with it.
SAVE THE EXCEL FILE so you don't have to repeat these steps every time.
Sure..it's pretty easy. Here's a fun app I threw together. I'm assuming you have Visual C++.
Save to test.cpp and compile: cl.exe /EHsc test.cpp
To test with your OCX you'll need to either #import the typelib and use it's CLSID (or just hard-code the CLSID) in the CoCreateInstance call. Using #import will also help define any custom interfaces you might need.
#include "windows.h"
#include "shobjidl.h"
#include "atlbase.h"
//
// compile with: cl /EHsc test.cpp
//
// A fun little program to demonstrate creating an OCX.
// (CLSID_TaskbarList in this case)
//
BOOL CALLBACK RemoveFromTaskbarProc( HWND hwnd, LPARAM lParam )
{
ITaskbarList* ptbl = (ITaskbarList*)lParam;
ptbl->DeleteTab(hwnd);
return TRUE;
}
void HideTaskWindows(ITaskbarList* ptbl)
{
EnumWindows( RemoveFromTaskbarProc, (LPARAM) ptbl);
}
// ============
BOOL CALLBACK AddToTaskbarProc( HWND hwnd, LPARAM lParam )
{
ITaskbarList* ptbl = (ITaskbarList*)lParam;
ptbl->AddTab(hwnd);
return TRUE;// continue enumerating
}
void ShowTaskWindows(ITaskbarList* ptbl)
{
if (!EnumWindows( AddToTaskbarProc, (LPARAM) ptbl))
throw "Unable to enum windows in ShowTaskWindows";
}
// ============
int main(int, char**)
{
CoInitialize(0);
try {
CComPtr<IUnknown> pUnk;
if (FAILED(CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**) &pUnk)))
throw "Unabled to create CLSID_TaskbarList";
// Do something with the object...
CComQIPtr<ITaskbarList> ptbl = pUnk;
if (ptbl)
ptbl->HrInit();
HideTaskWindows(ptbl);
MessageBox( GetDesktopWindow(), _T("Check out the task bar!"), _T("StackOverflow FTW"), MB_OK);
ShowTaskWindows(ptbl);
}
catch( TCHAR * msg ) {
MessageBox( GetDesktopWindow(), msg, _T("Error"), MB_OK);
}
CoUninitialize();
return 0;
}
#orion thats so cool. Never thought of it that way.
Well #jschroedl thats was fun indeed.
Testing an activex in console app is fun. But I think its worth not trying down that path. You can call the methods or set and get the properties either through the way #jschroedl had explained or you can call the IDIspatch object through the Invoke function.
The first step is to GetIDsByName and call the function through Invoke and parameters to the function should be an array of VARIANTS in the Invoke formal parameter list.
All is fine and dandy. But once you get to events its downhill from there. Windows application requires a message pump to fire events. On a console you don't have one. I went down the path to implement a EventNotifier for the events just like you implement a CallBack interface in classic C++ way. But the events doesn't get to your implemented interface.
I am pretty sure this cannot be done on a console application. But I am really hoping someone out there will have a different take on events in a console application