We are hosting a 3rd party web page in a WPF WebBrowser control. The 3rd party web site uses a VBScript GetObject(, "Prog.Id") call to get access to an already instantiated object.
I am aware this is a silly idea so please don't just answer telling me that. We need to integrate with this site and they are not in a position to change their VBScript approach.
Using PInvoke I can call RegisterActiveObject and GetActiveObject from within my WPF app code.
[DllImport("oleaut32.dll")]
public static extern int RegisterActiveObject([MarshalAs(UnmanagedType.IUnknown)] object punk, ref Guid rclsid, uint dwFlags, out int pdwRegister);
[DllImport("oleaut32.dll", CharSet = CharSet.None, ExactSpelling = false)]
public static extern void GetActiveObject(ref Guid rclsid, IntPtr pvReserved, [MarshalAs(UnmanagedType.IUnknown)] out Object ppunk);
If I use GetObject in the WPF code after RegisterActiveObject I get the previously instantiated version but the VBScript running in the WebBrowser control cannot see the running instance
This is the code to Register an instance with the ROT
int regResult = RegisterActiveObject(gatewayObject, ref gatewayClsIdGuid, 0, out this.objectRegisterValue);
The VBScript GetObject call that is not seeing the registered instance is
If (existing = True) Then
MsgBox("Getting not creating")
Set objTest = GetObject(, progId)
Else
Set objTest = CreateObject(progId)
End if
On Error Goto 0
If (objTest Is Nothing) Then
MsgBox("Unable to get object")
Else
MsgBox("Got the object successfully")
End If
Why is the GetObject(, progId) call returning Nothing?
Related
I am developing a WPF application in which I am working with twitter API. To show twitter authentication page I am using WPF web-browser control. I am able to login and use twitter API successfully. My problem is that I need to clear web browser's cookies to implement logout functionality. Is there any way to clear session cookies in WPF web browser?
I ran into this issue yesterday and finally came up with a full solution today. The answer is mentioned here which is put into more detail here and here.
The primary issue here is that the WebBrowser (in WPF and WinForms) does not permit you to modify (delete) existing session cookies. These session cookies are what prevent a multi-user single device experience from being successful.
The StackOverflow response in the link above omits an important part, it requires the use of an unsafe code block, instead of using the Marshal service. Below is a full solution that can be placed into your project to suppress the session cookie persistence.
public static partial class NativeMethods
{
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InternetSetOption(IntPtr hInternet, int dwOption, IntPtr lpBuffer, int dwBufferLength);
private const int INTERNET_OPTION_SUPPRESS_BEHAVIOR = 81;
private const int INTERNET_SUPPRESS_COOKIE_PERSIST = 3;
public static void SuppressCookiePersistence()
{
var lpBuffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)));
Marshal.StructureToPtr(INTERNET_SUPPRESS_COOKIE_PERSIST, lpBuffer, true);
InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SUPPRESS_BEHAVIOR, lpBuffer, sizeof(int));
Marshal.FreeCoTaskMem(lpBuffer);
}
}
Check the following,
http://social.msdn.microsoft.com/Forums/en/wpf/thread/860d1b66-23c2-4a64-875b-1cac869a5e5d
private static void _DeleteSingleCookie(string name, Uri url)
{
try
{
// Calculate "one day ago"
DateTime expiration = DateTime.UtcNow - TimeSpan.FromDays(1);
// Format the cookie as seen on FB.com. Path and domain name are important factors here.
string cookie = String.Format("{0}=; expires={1}; path=/; domain=.facebook.com", name, expiration.ToString("R"));
// Set a single value from this cookie (doesnt work if you try to do all at once, for some reason)
Application.SetCookie(url, cookie);
}
catch (Exception exc)
{
Assert.Fail(exc + " seen deleting a cookie. If this is reasonable, add it to the list.");
}
}
I have not tested this, but I think the best way would be to define a Javascript method on the page (if you're able to) that clears the cookie.
document.cookie='c_user=;expires=Thu, 01 Jan 1970 00:00:00 GMT;domain=.facebook.com';
(or whatever the cookie name is). Then you can use the InvokeScript method on the WebBrowser control.
How can a WPF app know if its getting remotely operated (via VNC or remote desktop)?
In winforms there is System.Windows.Forms.SystemInformation.TerminalServerSession as per Detecting remote desktop connection but is there a strightforward way for this in WPF?
I guess the hack for now could be to have an invisible Winforms host on WPF and use its own capacity to host dummy win form that can identify the same... but that looks lame to me!
Any inputs would be appreciated!
Thx
I guess the hack for now could be to have an invisible Winforms host on WPF and use its own capacity to host dummy win form that can identify the same... but that looks lame to me!
You don't need an invisible WinForms host... you can just add a reference to the System.Windows.Forms assembly, and use the SystemInformation.TerminalServerSession static property.
If you don't want a dependency on WinForms, you can use the GetSystemMetrics Win32 API:
const int SM_REMOTESESSION = 0x1000;
[DllImport("user32")]
static extern int GetSystemMetrics(int nIndex);
public static bool IsTerminalServerSession()
{
return (GetSystemMetrics(SM_REMOTESESSION) & 1) != 0;
}
This method does not require a Windows Forms Window, only a reference to the DLL.
If you don't want to reference this, you can call the method to check this yourself, the implementation is as follows (I've wrapped it in a class):
static class SystemInformation
{
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
private static extern int GetSystemMetrics(int nIndex);
public static bool IsTerminalServerSession
{
get
{
//copied the Windows Forms implementation
return (GetSystemMetrics(0x1000) & 1) != 0;
}
}
}
I have a task, that includes grabbing some text that lie inside a third party ActiveX component that's embedded inside Internet Explorer webpage using "OBJECT id=S_DetectCom codeBase=xxxx.cab ..."
i have the .cab file having the activeX dlls(4 dlls)
i'm not familiar with OLE and activeX, but doing some research and trials , i could get an IDispatch to ActiveX object by the following steps :-
1- get handle to IE tab having class "Internet Explorer_Server"
2- get IHTMLDocument2 using ObjectFromLresult
3- get IHTMLElementCollection from IHTMLDocument2
4- get IDispatch from IHTMLElementCollection with element of name "S_DetectCom"
what lies in mind, is getting the typelib from DLLs to know the methods,... and their parameters
by try and error find which dll in the cab is embedded into that web page
right now i'm stuck, as i don't know how to reach Invoke to call methods from IDispatch i got
i'd appreciate if somebody could help me with ideas and implementation below is the code part that gets IDispatch from hwnd
int msg;
DWORD lRes = NULL ;
IHTMLDocument2 *pDoc= NULL;
IHTMLElementCollection *pElement = NULL ;
IHTMLInputTextElement *ppvInput ;
IDispatch *ppvDisp;
ITypeInfo *TypeInfo = NULL ;
VARIANT ObjName ;
_variant_t index = NULL;
HRESULT hr;
LRESULT lr ;
UINT cntTypeInf= NULL ;
long pItems = NULL;
BSTR pszOptText[200];
OleInitialize(NULL);
msg = RegisterWindowMessage(L"WM_HTML_GETOBJECT");
lr = SendMessageTimeout(hwnd, msg, 0, 0, SMTO_ABORTIFHUNG, 1000, &lRes);
hr = ObjectFromLresult((LRESULT)lRes, IID_IHTMLDocument2, 0, (void**)&pDoc);
hr = pDoc->get_all( &pElement );
BSTR BStrObjName = _com_util::ConvertStringToBSTR((const char *)"S_DetectCom");
ObjName.vt = VT_BSTR ;
ObjName.bstrVal = BStrObjName ;
hr = pElement->item( ObjName , index , &ppvDisp );
if (hr == S_OK && ppvDisp)
{
hr = ppvDisp->GetTypeInfoCount(&cntTypeInf);
hr = ppvDisp->GetTypeInfo(NULL , NULL , &TypeInfo);
// to do here
}
If you are using WM_HTML_GETOBJECT to get the HTML document you are definitely not an expected caller, since you are out of the current thread. The ActiveX is probably not thread-safe.
Anyway, if you can get a proxy plugin to execute in the IE's Tab thread (probably by installing a BHO), here are the steps to access the ActiveX's properties and methods. It is for accessing an Adobe Flash ActiveX but you can change file name in the import statement to import interfaces from the ocx file.
If I were you instead of trying to use COM directly I'd write a FireBreath plugin, which would then work on firefox, etc as well as IE (it implements an ActiveX Control as well as a NPAPI plugin).
That will do all of the IDispatch stuff for you, and then you can tie into the lower level classes (look at IDispatchAPI) to get the direct COM handle for the element after you grab it and do a queryinterface for the interface you need.
If nothing else you could use the IDispatchAPI class as an example of how to access IDispatch methods.
I'm using the WPF WebBrowser to display online help inside an app (just a few small web pages). Some of those pages use cookies to display items only for the first few times the pages are viewed (it's a "Why not try X" type of thing).
However, for some reason the cookies don't seem to be working inside the WebBrowser control. They work fine in full IE as well as Firefox and Chrome (so the items correctly hide), but they never hide when viewed through the WPF WebBrowser control.
Is there something special about using cookies in the WPF WebBrowser control? It seems to be behaving as if all the cookies are only stored in memory, rather than being persisted on disk.
Here's one of those pages inside a browser (where the cookies work):
And here's the exact same page inside the app:
That additional content should only be visible for the first few times of using the software (i.e. it should be hidden after N views of that web page), but because I can't get cookies to work it's always visible.
Cookies handling in Internet Explorer (or hosted versions) is tied to the IE's own notion of "URL Security Zones", doc here: About URL security Zones
So, IE determines an url zone using various alogorithms applied to the url. Depending on the zone, your hosted browser may or may not support session or persistent cookies.
Strangely, when I create a small WPF sample, add the web browser to it and have navigate to this persistent cookie tester utiliy page: http://www.rbaworld.com/Security/Computers/Cookies/givecook.shtml, it works fine. Each time I launch the sample app, the counter is incremented fine, so not everyone can reproduce your problem. Well, that's the whole purpose of URL Security zones: it can vary by machine, by user, by Windows policy, etc...
The next question is: Can I change the zone you're running in? The short and easy answer is ... no because it's heavily tied to the security.
If you were hosting IE yourself, you could implement your own security zone handle as described here: Implementing a Custom Security Manager and a sample here: SAMPLE: Secumgr.exe Overrides Security Manager for WebBrowser Host but you're relying on WPF's webbrowser that does not allow any override... You can get to Reflector and copy all WPF private/internal code but that's a log of risky work!
The last thing you can try is to manipulate the standard Internet Security Manager. Here is some sample code that gives some hints. At least you should be able to determine the zone you're running in (MapUrltoZone) and change the cookie (TryAllowCookie). The problem with the standard manager is most of the times, it pops up dialog to the end-user allowing authorization... (security again!):
[ComImport, Guid("7b8a2d94-0ac9-11d1-896c-00c04Fb6bfc4")]
private class InternetSecurityManager
{
}
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("79eac9ee-baf9-11ce-8c82-00aa004ba90b")]
private interface IInternetSecurityManager
{
void Unused1();
void Unused2();
[PreserveSig]
int MapUrlToZone([In, MarshalAs(UnmanagedType.BStr)] string pwszUrl, out int pdwZone, [In] int dwFlags);
void Unused3();
[PreserveSig]
int ProcessUrlAction(string pwszUrl, int dwAction, ref int pPolicy, int cbPolicy, ref Guid pContext, int cbContext, int dwFlags, int dwReserved);
// left undefined
}
public static SecurityZone MapUrlToZone(Uri uri)
{
IInternetSecurityManager securityManager = (IInternetSecurityManager)new InternetSecurityManager();
int zoneId;
if (securityManager.MapUrlToZone(uri.ToString(), out zoneId, 0) < 0)
return SecurityZone.NoZone;
return (SecurityZone)zoneId;
}
private const int URLACTION_COOKIES = 0x00001A02;
private const int URLACTION_COOKIES_ENABLED = 0x00001A10;
private const int URLPOLICY_ALLOW = 0x00;
private const int URLPOLICY_DISALLOW = 0x03;
private const int PUAF_DEFAULT = 0x00000000;
public static bool TryAllowCookies(Uri uri)
{
IInternetSecurityManager securityManager = (IInternetSecurityManager)new InternetSecurityManager();
int policy = 0;
Guid context = Guid.Empty;
int hr = securityManager.ProcessUrlAction(uri.ToString(), URLACTION_COOKIES_ENABLED, ref policy, Marshal.SizeOf(policy), ref context, Marshal.SizeOf(context), PUAF_DEFAULT, 0);
return (hr == 0) && policy == URLPOLICY_ALLOW;
}
Good luck :)
The WebBrowser control won't allow this by default. For security reasons, you probably wouldn't want different applications from different developers/companies being able to access cookie info that another app created.
However, check out this answer How to delete Cookies from windows.form?
That pertains to deleting cookies through javascript, but you may be able to use a similar method in order to persist and create the site cookie each time the application is loaded.
Is there a way to read/write the cookies that a WebBrowser control uses?
I am doing something like this...
string resultHtml;
HttpWebRequest request = CreateMyHttpWebRequest(); // fills http headers and stuff
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
resultHtml = sr.ReadToEnd();
}
WebBrowser browser = new WebBrowser();
browser.CookieContainer = request.CookieContainer; // i wish i could do this :(
browser.NavigateToString(resultHtml);
One of the potentially confusing things about the WebBrowser control and cookies is that at a first glance, it often looks like your app gets a separate cookie store. For example, if you log into a site that stores a persistent cookie to identify you, then whether you appear to be logged in for that site from inside an app hosting the control will be independent of whether you seem to be logged in via Internet Explorer.
In fact, you can even be logged in with different identities.
However, although it might be natural to draw the conclusion that each app hosting the WebBrowser therefore gets its own cookies, in fact that's not true. There are merely two sets of cookies: the ones used in 'low integrity' mode (which is what IE runs in by default), and the other set, which is what you'll get in a normal app that hosts the WebBrowser and also what you'll get if you run IE elevated.
the webbrowser control uses WinInet for networking, specifically use the InternetSetCookie(Ex) and InternetGetCookie(Ex) functions for Cookie management. There isn't a WinInet wrapper in .Net, but you can p-invoke.
Yes you are right, InternetGetCookieEx is the only way to retrieve HttpOnly cookies and it is the preferred way to grab cookie from WebBrowser control.
I posted a complete example here
You can use Application.GetCookie and Application.SetCookie methods.
Although Application is more or less related to WPF, you can use these methods in any desktop .NET code. In fact, they are wrappers on InternetGetCookieEx and InternetSetCookieEx Windows APIs.
I faced the same issue few days ago.
Besides the examples of the previous answers, here is a Win32 wrapper for the WebBrowser control. The advantage of this implementation is that it exposes more options that the default WebBrowser control.
Unfortunately if It's not WPF native, so you will have to create a wrapper if you're planning to use it in WPF.
http://code.google.com/p/csexwb2/
Here is sample from [link][1]
> public static class WinInetHelper
{
public static bool SupressCookiePersist()
{
// 3 = INTERNET_SUPPRESS_COOKIE_PERSIST
// 81 = INTERNET_OPTION_SUPPRESS_BEHAVIOR
return SetOption(81, 3);
}
public static bool EndBrowserSession()
{
// 42 = INTERNET_OPTION_END_BROWSER_SESSION
return SetOption(42, null);
}
static bool SetOption(int settingCode, int? option)
{
IntPtr optionPtr = IntPtr.Zero;
int size = 0;
if (option.HasValue)
{
size = sizeof(int);
optionPtr = Marshal.AllocCoTaskMem(size);
Marshal.WriteInt32(optionPtr, option.Value);
}
bool success = InternetSetOption(0, settingCode, optionPtr, size);
if (optionPtr != IntPtr.Zero) Marshal.Release(optionPtr);
return success;
}
[System.Runtime.InteropServices.DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InternetSetOption(
int hInternet,
int dwOption,
IntPtr lpBuffer,
int dwBufferLength
);
}