<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>my tech blog &#187; POST</title>
	<atom:link href="http://billauer.se/blog/tag/post/feed/" rel="self" type="application/rss+xml" />
	<link>https://billauer.se/blog</link>
	<description>Anything I found worthy to write down.</description>
	<lastBuildDate>Thu, 12 Mar 2026 11:36:00 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>Catching the transient cookies: Log in, then crawl</title>
		<link>https://billauer.se/blog/2009/07/transient-cookies-lwp-login-bot-crawling/</link>
		<comments>https://billauer.se/blog/2009/07/transient-cookies-lwp-login-bot-crawling/#comments</comments>
		<pubDate>Tue, 30 Jun 2009 22:23:15 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Internet]]></category>
		<category><![CDATA[perl]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[agent]]></category>
		<category><![CDATA[bot]]></category>
		<category><![CDATA[cookies]]></category>
		<category><![CDATA[crawling]]></category>
		<category><![CDATA[http]]></category>
		<category><![CDATA[login]]></category>
		<category><![CDATA[lwp]]></category>
		<category><![CDATA[POST]]></category>
		<category><![CDATA[transient]]></category>
		<category><![CDATA[web spider]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=267</guid>
		<description><![CDATA[The old way Sometimes all you need is a quick crawl within a site, which requires to log in first. There are two main techniques I can think about: One is to POST the login form with your script, and get the necessary cookie setting. The second is to login manually with a browser, and [...]]]></description>
			<content:encoded><![CDATA[<h3>The old way</h3>
<p>Sometimes all you need is a quick crawl within a site, which requires to log in first. There are two main techniques I can think about: One is to POST the login form with your script, and get the necessary cookie setting. The second is to login manually with a browser, and then hand over the web cookies to your script. Let&#8217;s start with the first (traditional?) method:</p>
<p>You could use <a href="http://search.cpan.org/dist/WWW-Mechanize/" target="_blank">WWW::Mechanize</a> for that (not that I&#8217;ve tried), or use the good old LWP. Something like:</p>
<pre>#!/usr/bin/perl

use warnings;
use HTTP::Request::Common qw(POST);
use HTTP::Cookies;
use LWP::UserAgent;

$basedir = 'http://www.somesite.com/';

# Create a cookie jar and log into the server

$ua = LWP::UserAgent-&gt;new;

$ua-&gt;agent("Mozilla/5.0"); # pretend we are very capable browser
$jar = HTTP::Cookies-&gt;new();
$ua-&gt;cookie_jar($jar);

my $req = POST $basedir.'login.php',
  [ username =&gt; 'dracula',
    password =&gt; 'bloodisgood'
  ];
print "Now logging in...\n";
$res = $ua-&gt;request($req);

# We're not really interested in the result.
# This was only a cookie thing.

die "Error: " . $res-&gt;status_line . "\n"
  unless ($res-&gt;is_success);

# And now we continue to whatever we wanted to do</pre>
<p>The problem is that sometimes the login form is complicated. At times it&#8217;s obfuscated intentionally, and uses several tricks to make it difficult to automate the login. Sniffing a successful login (your own, I hope) may be helpful, since the correct POST data is there. If the login is through https, just go through the web page, replace all &#8220;https&#8221; with &#8220;http&#8221; and make a fake login. It may not login for real (it usually does), but at least you have the dump info.</p>
<p>But the bottom line is that it may be difficult. In some cases, it&#8217;s easier to do the login manually, and continue with your script from there.</p>
<h3>Cookie stealing basics</h3>
<p>So the plan is to login manually, and then give away the web cookies to your script. The target server can&#8217;t tell the difference. In extreme cases, you may need to set up the HTTP headers, so that your script&#8217;s and the browser send the same ones exactly. I suggest making your script identify itself with exactly the same user agent header as your browser. Some sites check that, and reject your login if there&#8217;s no match. Believe that.</p>
<p>There are several examples for this trick. One is using wget and its <a href="http://davehall.com.au/tags/wget" target="_blank">&#8211;load-cookies</a> flag. It&#8217;s quick and dirty, and loads cookies from a cookie file in good old Netscape format. Some browsers can export their cookies to such a file (Firefox uses another format internally, for exampe). But there is still one major problem, and that&#8217;s the transient cookies.</p>
<h3>Who ate my (transient) cookie?</h3>
<p>Every cookie, which is sent from the server to the browser (or whatever you have there) has an expiration date. Some cookies are marked to be erased when the browser (ha!) quits. These are transient cookies.</p>
<p>The thing is, that the browser has no reason to write these cookies to the cookie file on the disk. Why write something that will be erased anyhow? So stealing cookies from the cookie file doesn&#8217;t help very much, if the crucial cookies are transient. If you can&#8217;t stay logged in to a site after shutting down your browser and getting it back on, that site may be using transient cookies for its session.</p>
<p>The only simple way I know to get a hand on those transient cookies, is to dump them into a file while the browser is alive and kicking. <a href="https://addons.mozilla.org/en-US/firefox/addon/8154" target="_blank">The Export Cookies</a> add-on for Firefox does exactly that.</p>
<p>I suppose that wget works properly with the add-on&#8217;s output. I haven&#8217;t tried. I wanted to do this with Perl.</p>
<h3>Importing Netscape cookies to LWP</h3>
<p>It was supposed to be simple. The <a href="http://search.cpan.org/~gaas/libwww-perl-5.828/lib/HTTP/Cookies/Netscape.pm" target="_blank">HTTP::Cookies::Netscape</a> should have slurped the cookie file with joy, and taken things from there. But it didn&#8217;t. The module, if I may say so, has a problematic programming interface, which is miles away from the common Perl spirit.</p>
<p>The worst thing about it, is that if no cookies are imported because it didn&#8217;t like the cookie file, or didn&#8217;t find it at all, there is no notification. An empty cookie jar is silently created. I think that any Perl programmer would expect a noisy die() on that event. I mean, if the cookie file wasn&#8217;t read, there&#8217;s no point going on in 99% of the cases.</p>
<p>A second problem is with transient cookies. Their expiration time in the cookie file is set to 0 (surprise, surprise, they&#8217;re not supposed to survive at all), and the module simply discards them. I don&#8217;t blame the module for that, since transient cookies aren&#8217;t supposed to be found in a cookie file.</p>
<p>I&#8217;ve made the necessary changes for making it to work with the Export Cookies add-on, and got a new module, <a href="https://billauer.se/download/Exported.pm" target="_blank">Exported.pm</a> (click to download). I suggest to copy it next to where you find Netscape.pm in your Perl distribution.</p>
<p>Bottom line, the script looks like this:</p>
<pre>#!/usr/bin/perl

use strict;
use warnings;
use LWP::UserAgent;
use HTTP::Cookies::Exported;

my $baseurl = 'http://www.somesite.com/juicydata.php';
my $ua = LWP::UserAgent-&gt;new;
# A user agent string matching your browser is a good idea.
$ua-&gt;agent('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.7) Gecko/2009021910 Firefox/3.0.7');

# Not loading file in new(), because we don't want writeback
my $cookie_jar = HTTP::Cookies::Exported-&gt;new();
$cookie_jar-&gt;load('cookies.txt');
$ua-&gt;cookie_jar($cookie_jar);

my $req = HTTP::Request-&gt;new(GET =&gt; $baseurl);

my $res = $ua-&gt;request($req);
if ($res-&gt;is_success) {
  my $data = $res-&gt;content;

  # Here we do something with the data
}
else {
  die "Fatal error: $res-&gt;status_line\n";
}</pre>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2009/07/transient-cookies-lwp-login-bot-crawling/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Making an IE toolbar button: Notes to self (from hell)</title>
		<link>https://billauer.se/blog/2009/05/making-ie-internet-explorer-toolbar-button-setup/</link>
		<comments>https://billauer.se/blog/2009/05/making-ie-internet-explorer-toolbar-button-setup/#comments</comments>
		<pubDate>Tue, 12 May 2009 19:02:50 +0000</pubDate>
		<dc:creator>eli</dc:creator>
				<category><![CDATA[Microsoft]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[button]]></category>
		<category><![CDATA[COM]]></category>
		<category><![CDATA[Extension]]></category>
		<category><![CDATA[Interface]]></category>
		<category><![CDATA[Internet Explorer]]></category>
		<category><![CDATA[Navigate2]]></category>
		<category><![CDATA[POST]]></category>
		<category><![CDATA[toolbar]]></category>

		<guid isPermaLink="false">https://billauer.se/blog/?p=158</guid>
		<description><![CDATA[Nothing to see here, folks&#8230; These are just some notes I wrote down for myself, in case I&#8217;ll ever want to repeat this mess.  Microsoft Visual Studio 2003 was used for developing the C++ class as well as the setup project. For testing, IE 6 was used. A button running a script or executable The [...]]]></description>
			<content:encoded><![CDATA[<h3>Nothing to see here, folks&#8230;</h3>
<p>These are just some notes I wrote down for myself, in case I&#8217;ll ever want to repeat this mess.  Microsoft Visual Studio 2003 was used for developing the C++ class as well as the setup project. For testing, IE 6 was used.</p>
<h3>A button running a script or executable</h3>
<p>The simple way is described <a href="http://msdn.microsoft.com/en-us/library/aa753588(VS.85).aspx" target="_blank">here</a>. The interesting thing is that it all boils down to setting up some registry keys and values, put a couple of files somewhere, which contain the icon and whatever you want to execute, and off you are. The execution target can be some EXE or a script, including Javascript (!) which is pretty cool. What is less cool, is that the script is pretty crippled. In particular, it can&#8217;t manipulate the browser (as of IE6) and I&#8217;m not sure about its capabilities in manipulating the current document. So it&#8217;s easy, but not very useful.</p>
<h3>Now, seriously</h3>
<p>I didn&#8217;t want to face the facts, but I had no choice: There is no easy way to write a Toolbar button that actually does something useful. A <a href="http://msdn.microsoft.com/en-us/library/bb735854(VS.85).aspx" target="_blank">terrible Microsoft document</a> (&#8220;the guide&#8221; henceforth) offers some clues about how to make a COM DLL for this purpose.</p>
<p>I&#8217;ve seen plenty of web sites offering extensions for Firefox, but not for Internet Explorer. I thought the reason was that people with brain prefer Firefox. After writing an extension for IE and Firefox, I realize that the huge difference in difficulty is the probable reason.</p>
<h3>Making a &#8220;Hello, world&#8221; toolbar button</h3>
<ul>
<li>In Visual Studio, create a &#8220;regular&#8221; ATL project. Keep it as DLL, uncheck &#8220;Attributed&#8221; and then check &#8220;<label id="MERGE_PROXY_STUB_LABEL" for="MERGE_PROXY_STUB">Allow merging of  <span style="text-decoration: underline;">p</span>roxy/stub code</label>&#8220;. Otherwise a separate stub/proxy DLL is created, and the IID/CLSID/LIBID symbols aren&#8217;t resolved in the h-file. I&#8217;m sure there&#8217;s a better way to solve this. I&#8217;m sure it would take me days to find out how.</li>
<li>Right-clicking the &#8220;Source Files&#8221;, add a class. Pick ATL Simple Object, and be sure to set the Options: Aggregation is &#8220;No&#8221; and IObjectWithSite checked.</li>
<li>(Build it and see that it is OK. Just so you know it&#8217;s possible)</li>
<li>Now open the .rc file in the solution explorer. Just walk through its properties and make sure that they make sense. The language may be set to something unnecessarily local. In particular, fix the Version-&gt;VS_VERSION_INFO so that Company Name and such say something more respectable than TODO-something.</li>
</ul>
<p>At this point, we sort-of follow <a href="http://msdn.microsoft.com/en-us/library/bb735854(VS.85).aspx" target="_blank">Microsoft&#8217;s disastrous guide</a>. The first changes are in the .h-file.</p>
<ul>
<li>The guide tells us to add the IOleCommandTarget interface. This boils down to adding only two lines (the public declaration and COM_INTERFACE_ENTRY), which are those mentioning IOleCommandTarget explicitly. All the rest is already there, courtesy of Visual Studio.</li>
<li>Add an #include &lt;atlctl.h&gt; in the beginning.</li>
<li>And immediately after END_COM_MAP:
<pre>public:
    STDMETHOD(Exec)(const GUID *pguidCmdGroup, DWORD nCmdID,
        DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut);
    STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds,
        OLECMD *prgCmds, OLECMDTEXT *pCmdText);</pre>
</li>
<li>These methods need to be implemented, of course. For an &#8220;Hello, world&#8221; application, this is enough (put in .cpp file):
<pre>STDMETHODIMP Cjunkie::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
    DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut)
{
     MessageBox(NULL, _T("Hello, world"), _T("It works!"), 0);

    return S_OK;
}

STDMETHODIMP Cjunkie::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds,
    OLECMD prgCmds[], OLECMDTEXT* pCmdText)
{
	int i;

	// Indicate that we can do everything!

	for (i=0; i&lt;((int) cCmds); i++)
		prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;

    return  S_OK;
}</pre>
</li>
<li>Just a word about the QueryStatus method implemented above: <a href="http://msdn.microsoft.com/en-us/library/ms688491(VS.85).aspx" target="_blank">Microsoft describes</a> what this function should do, but I found almost no sample implementation of it. Basically, the purpose of this function is to tell the world what the module is ready to do and what not. I went for an I-can-do-all approach, since any call to a toolbar button means &#8220;do your thing&#8221;. I&#8217;m not sure if this is the right thing to do, but given <a href="http://msdn.microsoft.com/en-us/library/aa753588(VS.85).aspx#details_com" target="_blank">the promises</a> regarding how narrowminded the calls are expected to be, I think this approach wins. I mean, ask a silly question, get a silly answer.</li>
<li>At this point, believe it or not, the project should build. <strong>The source code up to this stage is listed at the end of this post.</strong></li>
</ul>
<h3>Setup project</h3>
<ul>
<li>Create a new Setup project. Give it a nice name (it will be the MSI file&#8217;s name)</li>
<li>Put its configuration as Release (as opposed to Debug) and check its &#8220;build&#8221; checkbox in the Configuration Manager if necessary. So it gets compiled&#8230;</li>
<li>Create a special folder (Windows Folder) to put the files in (too little to open an application folder for)</li>
<li>Make a subfolder in the Windows folder.</li>
<li>Put all files there: The DLL (Add-&gt;Project Output&#8230;-&gt;Primary output) and the icon file (read its format <a href="http://msdn.microsoft.com/en-us/library/aa753621(VS.85).aspx" target="_blank">here</a>).</li>
<li>Set the &#8220;Register&#8221; property of &#8220;Primary Output&#8221; to vsdrpCOMSelfReg (explained below).</li>
<li>Open a properties window, and set up the Setup project&#8217;s properties.</li>
<li>Open the Setup project&#8217;s Registry Editor and set up the entries. A sample screenshot below.</li>
<li>Make sure that the  &#8216;DeleteAtUninstall&#8217; property of the extension&#8217;s GUID is  &#8216;True&#8217; (but none of the others&#8217;!)</li>
</ul>
<div id="attachment_177" class="wp-caption alignnone" style="width: 622px"><img class="size-full wp-image-177" title="The Registry Editor" src="https://billauer.se/blog/wp-content/uploads/2009/05/registry1.png" alt="Visual Studio's Setup project: The Registry Editor" width="612" height="208" /><p class="wp-caption-text">Visual Studio&#39;s Setup project: The Registry Editor</p></div>
<p>Note that the path to the Windows Folder is given as [WindowsFolder]. This makes the value point to where the file was actually installed. A list of such variables can be found <a href="http://makemsi-manual.dennisbareis.com/system_folder_properties.htm" target="_blank">here</a>.</p>
<p>And of course, the &#8216;{4B19&#8230;}&#8217; -thing is the button&#8217;s class ID (in GUID form). Put your own instead.</p>
<ul>
<li>Next, I went for the User Interface Editor. That&#8217;s a great opportunity to make the installation process neater. First I removed the &#8220;Installation Folder&#8221; and &#8220;Confirm Installation&#8221; steps. The only folder used is the Windows folder anyhow, and with nothing to choose there is nothing to confirm.</li>
<li>Then a 500x70 BMP file was added to the target directory. This is used as a banner on the installation dialogs by setting the BannerBitmap property for each installation dialog. Since the banner is overlaid with black text, it makes sense to put the logo at the bottom right corner and keep the banner bright.</li>
</ul>
<h3>A note about registration</h3>
<p>This was a really bad one. The DLL has to be registered as the owner of the GUID, so that when that GUID is mentioned in the Explorer&#8217;s extension list, Explorer knows what DLL to fetch and run &#8220;Exec&#8221; on. (I suppose the important part is an entry with the key HKEY_CLASSES_ROOT\CLSID\{here comes the GUID}. Or maybe HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{here comes the GUID}?)</p>
<p>I wasn&#8217;t ready to think about pinpointing the keys to be set up (what do I know about Windows?). Neither was I ready to run Regsvr32 at installation for that (a great opportunity to fail the installation on a hostile computer).</p>
<p>The solution was proposed <a href="http://support.microsoft.com/kb/307367" target="_blank">here</a>: You go to the setup project, pick the item which marks the placement of the DLL (appears as &#8220;Primary Output from &#8230;&#8221;), right-click it and open the Properties page. There you change the &#8220;Register&#8221; property from vsdrpDoNotRegister (the default) to vsdrpCOMSelfReg.</p>
<p>Now, the project has an RGS file, which it seems wasn&#8217;t respected at all, but since the DLL&#8217;s registration is now secured, I don&#8217;t mind setting up the rest in the Setup project (the &#8220;Registry Editor&#8221; within a Setup project comes handy for this).</p>
<p>Just a word of caution: In the Setup project&#8217;s Registry Editor, you need to line up some of the existing keys as if they should be added, so to bring you to the desired path in the Registry (that is, &#8216;Microsoft&#8217;, &#8216;Internet Explorer&#8217; and &#8216;Extensions&#8217;). Be sure that the &#8216;DeleteAtUninstall&#8217; property of these is &#8216;False&#8217;, or you will cause some serious damage to the registry during uninstallation. Also, it&#8217;s a good idea to back up the complete registry before starting to play with the Setup project.</p>
<p>To make things a bit more complicated, the property of your GUID key should have the &#8216;DeleteAtUninstall&#8217; property &#8216;True&#8217;, so that Explorer won&#8217;t look for your button after uninstalling.</p>
<h3>Interaction with the browser</h3>
<p>The &#8220;Hello world&#8221; application could have been written in Javascript. For some real action, just follow that horrible guide. At this stage, things actually get pretty easy.</p>
<ul>
<li>To get a hold of the browser, we need to implement the SetSite method. Copied it right off the guide to the .cpp file.</li>
<li>The private property declaration, as well as the prototype of Setsite were copied into the .h-file</li>
<li>At this point, the project built and run (and I could verify that Setsite had been executed once, before the first call to Exec)</li>
<li>Then I switched to the Exec() method they offered. Basically, I changed nothing except the class name, and put zero instead of  navOpenInNewTab (not supported in my environment, which hasn&#8217;t heard about IE7).</li>
</ul>
<h3>Making a POST request</h3>
<p>At some point, I decided that I needed to implement a POST request. This was more or the less the stage, at which I realized, that I was actually writing Visual Basic, only in C++. The lesson learned was that maybe I should have started with Visual Basic (YUCK!) to begin with.  And of course, I confirmed an old rule in Microsoft programming: &#8220;Prepare to spend a crazy amount of time to implement a trivial feature.&#8221;</p>
<p>Implementing POST forces the use of Navigate2, which is a quicksand of SAFEARRAYs and VARIANTs. The available examples show you how to get it done with code that makes you puke and looks like it depends on luck more than some solid API.</p>
<p>To my surprise and delight, I managed to narrow the whole thing down to this relatively-elegant code:</p>
<pre>char postdata[] = "postdata=yeah";
CComVariant RequestUrl(_T("http://my.site.com")); 

VARIANT noArg;
noArg.vt = VT_EMPTY;

VARIANT flags;
flags.vt = VT_I4;
flags.lVal = 0;

CComSafeArray&lt;byte&gt; pSar(strlen(postdata), 0);

for (int x=0; x&lt;strlen(postdata); x++)
  pSar.SetAt(x, postdata[x]);

CComVariant postdata(pSar); // Make this an array

m_spWebBrowser-&gt;Navigate2(&amp;RequestUrl, &amp;flags, &amp;noArg, &amp;postdata, &amp;noArg);</pre>
<h3>Sources of Hello World application</h3>
<p>Since the most difficult part was to get the application open a dialog box when the button was clicked, here&#8217;s the code for it. The main attempt here is to keep it simple.</p>
<p>And by the way, opening dialog boxes seems to be a bad idea. Explorer crashed a few times when the button was clicked before the dialog box was closed. It seems like the response to the Exec() call should be swift.</p>
<p>Header file:</p>
<pre>// junkie.h : Declaration of the Cjunkie

#pragma once
#include "resource.h"       // main symbols

#include "myproject.h"
#include
#include  // For handling BSTRs
#include  // For handling BSTRs

// Cjunkie

class ATL_NO_VTABLE Cjunkie :
public CComObjectRootEx,
  public CComCoClass,
  public IObjectWithSiteImpl,
  public IDispatchImpl,
  public IOleCommandTarget
{
 public:
  Cjunkie()
    {
    }

  DECLARE_REGISTRY_RESOURCEID(IDR_JUNKIE)

    DECLARE_NOT_AGGREGATABLE(Cjunkie)

    BEGIN_COM_MAP(Cjunkie)
    COM_INTERFACE_ENTRY(Ijunkie)
    COM_INTERFACE_ENTRY(IDispatch)
    COM_INTERFACE_ENTRY(IObjectWithSite)
    COM_INTERFACE_ENTRY(IOleCommandTarget)
    END_COM_MAP()

    public:
  STDMETHOD(Exec)(const GUID *pguidCmdGroup, DWORD nCmdID,
		  DWORD nCmdExecOpt, VARIANTARG *pvaIn, VARIANTARG *pvaOut);
  STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds,
			 OLECMD *prgCmds, OLECMDTEXT *pCmdText);

  DECLARE_PROTECT_FINAL_CONSTRUCT()

    HRESULT FinalConstruct()
    {
      return S_OK;
    }

  void FinalRelease()
    {
    }

};

OBJECT_ENTRY_AUTO(__uuidof(junkie), Cjunkie)</pre>
<p>And application file:</p>
<pre>// junkie.cpp : Implementation of Cjunkie

#include "stdafx.h"
#include "junkie.h"

STDMETHODIMP Cjunkie::Exec(const GUID *pguidCmdGroup, DWORD nCmdID,
			   DWORD nCmdExecOpt, VARIANTARG *pvaIn,
			   VARIANTARG *pvaOut)
{
  MessageBox(NULL, _T("Hello, world"), _T("It works!"), 0);

  return S_OK;
}

STDMETHODIMP Cjunkie::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds,
				  OLECMD prgCmds[], OLECMDTEXT* pCmdText)
{
  int i;

  // Indicate that we can do everything!

  for (i=0; i&lt;((int) cCmds); i++)
    prgCmds[i].cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;

  return  S_OK;
}</pre>
]]></content:encoded>
			<wfw:commentRss>https://billauer.se/blog/2009/05/making-ie-internet-explorer-toolbar-button-setup/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
