1) GetAsyncKeyState() 함수와 2) GetKeyState()는 사용방법이 다른데


1)은 비동기(Asynchronism)으로 처리 한다.

  : 호출된 시점에서 키 상태를 조사하여, 메시지 큐를 거치지 않고 바로 리턴을

    해주므로 키 입력을 바로 처리해 줄 수 가 있다.

2)는 호출된 시점에서 메시지 큐를 거치며, 메시지 발생 후의 상태를 리턴하게 되므로, 키보드 메시지

   처리 루틴내에서 사용해야 한다.


바로바로 키 입력을 처리해야하는 경우는 GetAsyncKeyState()를 사용하는 것이 바람직하다.


1) GetAsyncKeyState() 사용법


GetAsyncKeyState(해당키) 는 키가 눌려진 시점에 0x8000 값을 리턴해주고

키가 눌려 있었다면 0x0001 값을 리턴한다.


0x8000 은 현재 키가 눌려진 상태를 말하고

0x0001 은 키가 눌려있었음을 말해준다.


따라서 GetAsyncKeyState() & 0x8000 을 하면 키가 방금 눌렸을 경우만 1이 된다.

          GetAsyncKeyState() & 0x0001 을 하면 키가 눌려있었는지를 판단할 수 있다.


간단 예제


 while(1) {
  while(GetAsyncKeyState(VK_UP) & 0x0001) {
   au.vol_up();
  }
  while(GetAsyncKeyState(VK_DOWN) & 0x0001) {
   au.vol_down();
  }
  if(GetAsyncKeyState(VK_LEFT) & 0x0001) break;
 }


윗키, 아래키를 눌렀을때는 볼륨을 올리고 내리고

왼쪽키는 while문을 중단 시키는 역할을 한다.


 2) GetKeyState() 사용법

   : GetKeyState()는 해당키가 눌린 상태일때는 음수값을 리턴, 아닐 경우는

      해당키가 눌리지 않았음을 나타낸다.


if(GetKeyState(VK_CONTROL) < 0) // 눌려있는 상태이면... 이란 뜻으로 쓰인다

출처 : http://blog.naver.com/mklife/150020920925

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

Tag API

출처: http://www.javalinux.co.kr/study/windows/

1. Non-MFC DLL

1.1 Basis

1.1.1 Features

n         내부적으로 C++ 클래스를 사용할 수 있고, C 함수 Wrapper만을 Export 할 수 있다. 따라서 내부적인 C++ Class에 대한 변경은 DLL의 호출에 영향을 주지 않는다.

n         MFC를 사용할 수 없으며, 별도의 MFC Library가 필요없다.

n         DLL을 사용하는 Client는 DLL 호출을 지원하는 어떠한 Language로 작성될 수 있다.

n         AppWizard를 이용하여 자동으로 Project를 생성할 수 있다.

1.1.2 Function Export

DLL 내에서 정의된 Function을 export하기 위해서는 __declspec(dllexport) 를 사용한다. __declspec MS만의 C, C++의 확장된 syntax로서, 확장된 storage-class 정보를 정의한다. dllexport storage-class의 한 속성으로, DLL의 Function, Data, Object를 export할 수 있도록 하여준다. 반대로 DLL내의 Function을 import하기 위해서는 dllimport 속성을 사용한다. Win32 환경에서는 Function Export/Import를 위하여 이것을 이용하며, Win32 이전의 Windows 환경에서 사용되던 module-definition file (.DEF)을 사용하지 않는다. 단, VB와 호환가능한 DLL을 제작하는 경우, module-definition file을 사용하도록 한다.

· export / import

함수 export/import를 위하여 아래와 같이 함수를 선언한다. Coding의 편의를 위하여 export선언을 #define으로 간략화시킨다.

#define DLLImport  __declspec(dllimport)

#define DLLExport  __declspec(dllexport)

DLLExport void somefunc();

· export/import Tips

위 방법으로 export/import 함수를 정의하면, DLL 내에서의 함수 정의와 DLL을 사용하는 Client에서의 함수정의를 다르게 해야 하는 불편이 생긴다. DLL과 Client에서 동일한 Header File을 사용할 수 있도록 하기 위하여 아래와 같이 export/import 함수를 정의한다.

#ifdef DLLTEST_EXPORTS

#define DLLFunction  __declspec(dllexport)

#elseif

#define DLLFunction  __declspec(dllimport)

#endif

DLLFunction void somefunc();

DLLTEST3_EXPORTS 은 DLL의 Project Settings에 Preprocessor definitions에 프로젝트명_EXPORTS의 형식으로 정의 되어 있다. 따라서 DLL Project에서는 export로, Client Project에서는 import로 동작한다.

· Adjusting Naming Convention

C++은 C와 다른 Naming Convention을 사용한다. 따라서 export되는 함수명은 Compile시에 기존의 정의한 이름과 다르게 해석된다. 따라서 Naming Convention에 대한 조정과정이 없으면, export된 함수는 C++ 이외의 Language로 작성되는 프로그램에서는 호출될 수 없다. extern C는 함수가 C naming convention을 사용하도록 만들어주며, 이를 통하여 C++로 작성되어 export되는 함수를 다른 Language에서도 사용가능하도록 하여 준다. VC는 기본적으로 프로젝트생성시에 C++을 사용하도록 구성되므로 모든 export함수는 Naming Convention의 조정이 필요하다.

#ifdef _USRDLL

#define DLLFunction  __declspec(dllexport)

#elseif

#define DLLFunction  __declspec(dllimport)

#endif

#ifdef __cplusplus

extern C {

#endif

DLLFunction void somefunc();

#ifdef __cplusplus

}

#endif

· Adjusting Calling Convention

Visual Basic은 C와 다른 Calling Convention을 사용하므로 Visual Basic에서 사용될 DLL을 작성하는 경우, 이를 조정해주어야 한다. VC는 기본적으로 __cdecl 방식을 사용하며, VB는 __stdcall 방식을 사용한다. 따라서 Project Settings에서 C/C++ Tab의 Code Generation Category를 선택하여 Calling Convention을 __stdcall로 설정한다. 또한 module-definition file(.Def)을 작성하여 프로젝트에 포함시킨다. VC로 작성한 DLL을 VB에서 사용하는 경우, Calling Convention이 틀린다는 에러가 발생하거나 Run 시에 다운되는 현상을 자주 볼 수 있는데, 이는 모두 Calling Convention을 조정과정을 거치지 않아서 발생하는 문제이다.

testdll.def

LIBRARY              "TESTDLL.DLL"

DESCRIPTION 'SIMPLEDLL Windows Dynamic Link Library'

EXPORTS

   somefunc   @1

1.2 Making DLL

1.2.1 Object

간단한 DLL을 만들기 위하여 아래와 같은 두가지의 기능만을 가지는 DLL을 만들기로 한다.

n         int 형의 두 숫자를 parameter로 받아 그 합을 return하는 함수

n         두개의 string을 받아 연결된 string을 넘겨주고, 그 총 길이를 return하는 함수

1.2.2 Implementation

n       아래와 같이 Win32 Dynamic-Link Library를 선택하여 Project를 생성한다.

n       App-Wizard에서 아래와 같이 A simple DLL Project를 선택한다. 이는 간단한 DLLMain 함수를 자동으로 생성하여 준다.



n       export할 함수의 정의를 위하여 Header File을 생성한다. 이는 후에 import 측에서도 공용으로 사용될 것이다. SimpleDll.h의 이름으로 Header를 생성하고, 두개의 함수를 위한 함수정의를 만든다.

SimpleDll.h

#ifndef __SIMPLEDLL_H__

#define __SIMPLEDLL_H__

 

#ifdef SIMPLEDLL_EXPORTS

   #define DLLFunction  __declspec(dllexport)

#else

   #define DLLFunction  __declspec(dllimport)

#endif

 

extern "C" {

 

DLLFunction int addint(int n1, int n2);

DLLFunction int addchar(char* s1, char* s2, char* added);

 

}

 

#endif //__SIMPLEDLL_H__

 

n       선언한 함수에 대하여 기능을 작성한다. 두 함수는 단지 int형과 char형의 더하기 만을 지원하므로 아래와 같이 함수를 작성한다.

SimpleDll.cpp

#include "stdafx.h"

#include "stdio.h"

#include "SimpleDll.h"

 

BOOL APIENTRY DllMain( HANDLE hModule,

                       DWORD  ul_reason_for_call,

                       LPVOID lpReserved

                                       )

{

    return TRUE;

}

 

DLLFunction int addint(int n1, int n2)

{

   return n1 + n2;

}

 

DLLFunction int addchar(char* s1, char* s2, char* added)

{

   sprintf(added, "%s%s", s1, s2);

   return strlen(added);

}

 

1.3 Using DLL

위에서 작성한 Code를 Compile하여 SimpleDll.dll 파일을 얻는다. 이것을 Test하기 위하여 VC++ 및 VB에서 DLL내의 함수를 호출하는 기능을 작성한다.

 

1.3.1 Using DLL with VC++ (Link Implicitly)

Dialog-Based Project를 생성하여 아래와 같은 순서로 DLL의 함수를 호출하는 기능을 작성한다.

 

n       SimpleDll.h를 복사하여 프로젝트에 추가한다.

n       Project-Settings 메뉴를 선택하여 Project Settings 화면을 열어, Link Tab의 library modules에 SimpleDll.lib를 추가한다. SimpleDll.lib 파일은 VC++의 환경설정의 Directories에 존재하는 폴더 또는 Project 폴더에 복사하도록 한다.

 

n       Dialog를 다음과 같이 Design하고 각 컨트롤에 멤버변수를 추가한다.

 

 

 

 

 

 

n       DLL 함수를 호출할 Button에 대한 Handler를 작성한다.

SimpleDllTestDlg.cpp

#include "SimpleDll.h"

 

void CSimpleDllTestDlg::OnAddint()

{

   UpdateData(TRUE);

   m_nSumInt = addint(m_nInt1, m_nInt2);

   UpdateData(FALSE);

}

 

void CSimpleDllTestDlg::OnAddchar()

{

   char s1[9];

   char s2[9];

   char sSum[17];

 

   UpdateData(TRUE);

 

   lstrcpy(s1, (LPCTSTR)m_sChar1);

   lstrcpy(s2, (LPCTSTR)m_sChar2);

 

   addchar(s1, s2, sSum);

 

   m_sSumChar = sSum;

   UpdateData(FALSE);

}

 

위의 방법으로 DLL 함수를 호출하는 프로그램을 작성하여 실행하면 각 함수들이 정확하게 호출되고 있음을 확인할 수 있다.

 

 

1.3.2 Using DLL with VC++ (Link Explicitly)

Explicit Link를 사용하여 DLL 함수를 호출하는 경우, Header 파일과 Library 파일은 필요하지 않다. 단지 그 함수의 원형만을 알고 있으면 된다. Explicit Link를 사용하기 위하여 아래와 같은 순서로 DLL 함수를 호출하는 기능을 작성한다.

 

n       1.3.1에서와 같이 Dialog-Based Project를 생성한 후, 같은 모양으로 Dialog를 Design하고 멤버변수를 연결한다.

 

n       DLL 함수호출을 위한 Button의 Handler를 만들고 아래와 같이 코드를 작성한다.

SimpleDllTest2Dlg.cpp

void CSimpleDllTest2Dlg::OnAddint()

{

   int (*lpfnAddInt)(int, int);

   HINSTANCE hLib = LoadLibrary("SimpleDll.dll");

   if(hLib == NULL)

            return;

 

   lpfnAddInt = (int(*)(int, int))GetProcAddress(hLib, "addint");

   if(lpfnAddInt == NULL)

   {

            FreeLibrary(hLib);

            return;

   }

 

   UpdateData(TRUE);

   m_nSumInt = lpfnAddInt(m_nInt1, m_nInt2);

   UpdateData(FALSE);

 

   FreeLibrary(hLib);

}

 

위와 같이 LoadLibrary를 이용하여 Explicit Link로 DLL 함수를 호출하도록 하면, 별도의 Library와 Header가 필요하지 않으며, 실행 시간에 DLL의 Load와 Unload의 시점을 결정할 수 있는 장점이 있다.

 

 

1.3.3 Using DLL with VB

VB에서는 DLL의 함수 호출이 매우 간단하다. 단지 어떤 Library의 어떤 함수를 사용할 것인지에 대한 선언만 정확이 명시하면 된다. 아래와 같이 addintaddchar 함수를 선언한다.

test.mod

Public Declare Function addint

Lib "SimpleDll.dll" (ByVal n1 As Long, ByVal n2 As Long) As Long

Public Declare Function addchar

Lib "SimpleDll.dll" (ByVal s1 As String, ByVal s2 As String, ByVal sum As String) As Long

 

함수선언 후, 실행을 위한 Handler에서 아래와 같이 호출한다.

Private Sub cmdAddStr_Click()

    Dim sum As String * 32

    addchar txtInt1, txtInt2, sum

    txtSum = sum

End Sub

 

Private Sub cmdCall_Click()

    Dim n1, n2, sum As Long

    n1 = CLng(txtInt1)

    n2 = CLng(txtInt2)

   

    sum = addint(n1, n2)

    txtSum = CStr(sum)

End Sub

VB로 작업시 char* Type에 대한 Parameter를 넘길 때, 반드시 그 크기를 지정하여야 한다. VB는 그 값이 지정될 때, 메모리가 할당되므로 함수 호출 이전에 값을 지정하거나 위 코드의 sum 변수에서처럼 그 크기를 미리 지정하여야 한다.

www.codein.co.kr  프로그래머의 놀이터 Code人

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

Tag API, MFC, 소스, 후킹

이건 Windows API정복 -가남사 : 김상형저- 에 있던 소스 입니다..


대략 API의 정복이라 불리우는 책이죠.. 좋은책입니다.

게임을 만들기 위해.. DirectX를 해야 하지만.. 이 DirectX를 받쳐주는게 API 방식의 프로그래밍과
MFC의 프로그래밍...


물론 각각 C언어와 C++ 언어가 되야 하지만.. 이것만 어떻게든 하면 DirectX 는 상당히 쉬운편이라

생각되네요..


뭐 대략 요령을 깨우치지 못하면.. 무척 어렵지만....


----------------- 소스.. ------------------

// 밑에 부분 복사후 Alt + F8 인가로 소스 정렬후 봅시다.. 그게 편합니다..

// 나중에 자기가 짠 소스에 허우적 거리지 않기를..


#include <windows.h>

LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
HWND hWndMain;
LPCTSTR lpszClass=TEXT("Class");

int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance
   ,LPSTR lpszCmdParam,int nCmdShow)
{
 HWND hWnd;
 MSG Message;
 WNDCLASS WndClass;
 g_hInst=hInstance;
 
 WndClass.cbClsExtra=0;
 WndClass.cbWndExtra=0;
 WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
 WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
 WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
 WndClass.hInstance=hInstance;
 WndClass.lpfnWndProc=(WNDPROC)WndProc;
 WndClass.lpszClassName=lpszClass;
 WndClass.lpszMenuName=NULL;
 WndClass.style=CS_HREDRAW | CS_VREDRAW;
 RegisterClass(&WndClass);

 hWnd=CreateWindow(lpszClass,lpszClass,WS_OVERLAPPEDWINDOW,
  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
  NULL,(HMENU)NULL,hInstance,NULL);
 ShowWindow(hWnd,nCmdShow);
 hWndMain=hWnd;
 
 while(GetMessage(&Message,0,0,0)) {
  TranslateMessage(&Message);
  DispatchMessage(&Message);
 }
 return Message.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
 HDC hdc;
 PAINTSTRUCT ps;

 switch(iMessage) {
 case WM_CREATE:
  return 0;
 case WM_PAINT:
  hdc=BeginPaint(hWnd, &ps);
  EndPaint(hWnd, &ps);
  return 0;
 case WM_DESTROY:
  PostQuitMessage(0);
  return 0;
 }
 return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}


//---------------- 이하 소스 끝 -------------------


실행하면 화면에 박스(프로그램 창)가 하나 뜹니다. 이 박스를 생성할때 박스 생성 조절은...

WinMain 함수의 CreateWindow 함수에서 조정하면 되는것이고요..


이 박스는 WndProc 함수안의 switch 문 안의 여러 이벤트(환경 변화..

대략 프로그램 실행시 입력하는.. 마우스 움직임 이라던지.. 키보드 눌림 변화..)

에 동작 하게 만듭니다.

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

Tag API, 강좌

소스파일입니다.


우선..
"stdafx.h" 파일을 만들고
==================================================================
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once


#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#ifndef DWORD
#define DWORD unsigned long
#endif
// TODO: reference additional headers your program requires here
==================================================================
를 써줍니다.

다음.. 소스가 들어갈 파일..
파일명은 "LaunchBrowser.cpp" 로 해줍니다.
==================================================================

#include "stdafx.h"
#include <comutil.h>
#include <exdisp.h>
#include <process.h>    /* _beginthread, _endthread */
#include <stddef.h>

#pragma comment(lib, "comsupp.lib")

// Get a string from the registry
BOOL GetRegistryString(HKEY rootKey, const char *pzPath, const char *pzKey, char *zBuf, int nBufSize)
{
  HKEY hKey = NULL;
  LONG lReturnValue = RegOpenKeyEx (rootKey, pzPath, 0L, KEY_QUERY_VALUE, &hKey);
  if(lReturnValue != ERROR_SUCCESS)
    return FALSE;
  DWORD dwType = 0;
  DWORD dwSize = nBufSize;
  lReturnValue = RegQueryValueEx (hKey, pzKey, NULL, &dwType, (BYTE *) zBuf, &dwSize);
  RegCloseKey (hKey);
  if (lReturnValue != ERROR_SUCCESS)
    return FALSE;
  return TRUE;
}

// Determine if the version of MSIE is version 7
BOOL IsIE7()
{
  char szVersion[128];
  BOOL bRet = GetRegistryString(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Internet Explorer", "Version", szVersion, sizeof(szVersion));
  if (bRet == TRUE && szVersion[0] == '7')
    return TRUE;
  return FALSE;
}

// simple data structure for our browser settings
typedef struct
{
  char zUrl[1024];
  int nWidth;
  int nHeight;
 HICON hIcon;
}BROWSER_INFO;

BROWSER_INFO g_BrowserInfo;

// This thread demonstrates how to initialize OLE such that we have an exclusive COM apartment for this
// thread separate from the main thread.
//
// It also shows how to manipulate the IWebBrowser interface to do some interesting things such as:
//   1.  Make the IE window resizable or static (non-resizable) based on IE version number.
//  2.  Turn off the address bar, menu bar, status bar, and tool bar buttons.
//  3.  Set the width and height of the resulting window.
//  4.  Change the icon of the MSIE window to one of your own.
//
void BrowserThread( void* pParams )
{
  OleInitialize(NULL);
  BROWSER_INFO *pBrowserInfo = (BROWSER_INFO *)pParams;
   IWebBrowser2* m_pInetExplorer;

    HRESULT hr;
  HICON hIcon;
    CLSID clsid;
    LPUNKNOWN punk=NULL;
    CLSIDFromProgID (OLESTR("InternetExplorer.Application"), &clsid);
    hr = CoCreateInstance (clsid, NULL, CLSCTX_SERVER, IID_IUnknown, (LPVOID *) &punk);
    if (SUCCEEDED(hr))
   {
    punk->QueryInterface (IID_IWebBrowser2, (LPVOID *) &m_pInetExplorer);
    punk->Release();
    VARIANT vars[4];
        memset(vars,0,sizeof(vars));
        BSTR BStrURL = _com_util::ConvertStringToBSTR((const char *)(pBrowserInfo->zUrl));
    if (IsIE7())
      m_pInetExplorer->put_Resizable(VARIANT_TRUE);
    else
      m_pInetExplorer->put_Resizable(VARIANT_FALSE);
        m_pInetExplorer->put_ToolBar(FALSE);
        m_pInetExplorer->put_AddressBar(VARIANT_FALSE);
        m_pInetExplorer->put_MenuBar(VARIANT_FALSE);
        m_pInetExplorer->put_StatusBar(VARIANT_FALSE);
        m_pInetExplorer->put_Width(pBrowserInfo->nWidth);
        m_pInetExplorer->put_Height(pBrowserInfo->nHeight);

        m_pInetExplorer->put_Visible(VARIANT_TRUE);
        HRESULT hrie = m_pInetExplorer->Navigate(BStrURL,vars,vars+1,vars+2,vars+3);
        SysFreeString(BStrURL);
    if (SUCCEEDED(hrie))
    {
      VARIANT_BOOL bBusy = VARIANT_TRUE;
      while(bBusy == VARIANT_TRUE)
      {
        Sleep(500);
        m_pInetExplorer->get_Busy(&bBusy);
      }
       HWND hWnd = NULL;
      m_pInetExplorer->get_HWND ((long*)(&hWnd));
      if (IsWindow(hWnd) && pBrowserInfo->hIcon != NULL)
      {
        hIcon = pBrowserInfo->hIcon;
        SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)hIcon);
      }
  // Do other interesting IE stuff here while the window is valid.
  //   while(IsWindow(hWnd))
  //   {
  //     Sleep(500);
  //     m_pInetExplorer->get_HWND ((long*)(&hWnd));
  //   }
    }
    m_pInetExplorer->Release();
   }
    OleUninitialize();
  // thread exiting.
}

/*
 * Spawn the M browser
 */
void spawn_browser(
  const char *uri, // URL
  int nWidth,    // Window Width
  int nHeight,   // Window Height
 HICON hIcon)   // Handle to the icon
{
  g_BrowserInfo.nWidth = nWidth;
  g_BrowserInfo.nHeight = nHeight;
  memset(g_BrowserInfo.zUrl,0,(sizeof(g_BrowserInfo.zUrl)*sizeof(char)));
  strncpy(g_BrowserInfo.zUrl,uri,min(strlen(uri),(sizeof(g_BrowserInfo.zUrl)*sizeof(char))));
  g_BrowserInfo.hIcon = hIcon;
  HANDLE hThread = (HANDLE) _beginthread( BrowserThread, 0, &g_BrowserInfo );

  // Wait until IE is done loading before we return.
    WaitForSingleObject( hThread, INFINITE );
}

==================================================================

해줍니다..

구조상...
spawn_browser으로 구동시키며,
이거 쓰레드를 사용합니다... 허...
어쨋든..

spawn_browser("http://www.codeproject.com", 800, 600, hIcon);
식으로 쓰면, codeproject.com 웹을  800,600 크기, hIcon 이미지를 가진 대화상자를 만들어
거기에 띄워줍니다.

음...
이거.. 소스찾는데 애먹었습니다..허.. 조금만 수정하면 다용도로 사용가능할꺼 같네요

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소

/* CWebPage.c
 
This is a Win32 C application (ie, no MFC, WTL, nor even any C++ -- just plain C) that demonstrates
how to embed a browser "control" (actually, an OLE object) in your own window (in order to display a
web page, or an HTML file on disk).

This is very loosely based upon a C++ example written by Chris Becke. I used that to learn the minimum
of what I needed to know about hosting the browser object. Then I wrote this example from the ground up
in C.
*/



#include <windows.h>
#include <exdisp.h>  // Defines of stuff like IWebBrowser2. This is an include file with Visual C 6 and above
#include <mshtml.h>  // Defines of stuff like IHTMLDocument2. This is an include file with Visual C 6 and above
#include <crtdbg.h>  // for _ASSERT()




// A running count of how many windows we have open that contain a browser object
unsigned char WindowCount = 0;

// The class name of our Window to host the browser. It can be anything of your choosing.
static const TCHAR ClassName[] = "Browser Example";

// This is used by DisplayHTMLStr(). It can be global because we never change it.
static const SAFEARRAYBOUND ArrayBound = {1, 0};



// Our IStorage functions that the browser may call
HRESULT STDMETHODCALLTYPE Storage_QueryInterface(IStorage FAR* This, REFIID riid, LPVOID FAR* ppvObj);
HRESULT STDMETHODCALLTYPE Storage_AddRef(IStorage FAR* This);
HRESULT STDMETHODCALLTYPE Storage_Release(IStorage FAR* This);
HRESULT STDMETHODCALLTYPE Storage_CreateStream(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm);
HRESULT STDMETHODCALLTYPE Storage_OpenStream(IStorage FAR* This, const WCHAR * pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm);
HRESULT STDMETHODCALLTYPE Storage_CreateStorage(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg);
HRESULT STDMETHODCALLTYPE Storage_OpenStorage(IStorage FAR* This, const WCHAR * pwcsName, IStorage * pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg);
HRESULT STDMETHODCALLTYPE Storage_CopyTo(IStorage FAR* This, DWORD ciidExclude, IID const *rgiidExclude, SNB snbExclude,IStorage *pstgDest);
HRESULT STDMETHODCALLTYPE Storage_MoveElementTo(IStorage FAR* This, const OLECHAR *pwcsName,IStorage * pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags);
HRESULT STDMETHODCALLTYPE Storage_Commit(IStorage FAR* This, DWORD grfCommitFlags);
HRESULT STDMETHODCALLTYPE Storage_Revert(IStorage FAR* This);
HRESULT STDMETHODCALLTYPE Storage_EnumElements(IStorage FAR* This, DWORD reserved1, void * reserved2, DWORD reserved3, IEnumSTATSTG ** ppenum);
HRESULT STDMETHODCALLTYPE Storage_DestroyElement(IStorage FAR* This, const OLECHAR *pwcsName);
HRESULT STDMETHODCALLTYPE Storage_RenameElement(IStorage FAR* This, const WCHAR *pwcsOldName, const WCHAR *pwcsNewName);
HRESULT STDMETHODCALLTYPE Storage_SetElementTimes(IStorage FAR* This, const WCHAR *pwcsName, FILETIME const *pctime, FILETIME const *patime, FILETIME const *pmtime);
HRESULT STDMETHODCALLTYPE Storage_SetClass(IStorage FAR* This, REFCLSID clsid);
HRESULT STDMETHODCALLTYPE Storage_SetStateBits(IStorage FAR* This, DWORD grfStateBits, DWORD grfMask);
HRESULT STDMETHODCALLTYPE Storage_Stat(IStorage FAR* This, STATSTG * pstatstg, DWORD grfStatFlag);

// Our IStorage VTable. This is the array of pointers to the above functions in our C
// program that someone may call in order to store some data to disk. We must define a
// particular set of functions that comprise the IStorage set of functions (see above),
// and then stuff pointers to those functions in their respective 'slots' in this table.
// We want the browser to use this VTable with our IStorage structure (object).
IStorageVtbl MyIStorageTable = {Storage_QueryInterface,
Storage_AddRef,
Storage_Release,
Storage_CreateStream,
Storage_OpenStream,
Storage_CreateStorage,
Storage_OpenStorage,
Storage_CopyTo,
Storage_MoveElementTo,
Storage_Commit,
Storage_Revert,
Storage_EnumElements,
Storage_DestroyElement,
Storage_RenameElement,
Storage_SetElementTimes,
Storage_SetClass,
Storage_SetStateBits,
Storage_Stat};

// Our IStorage structure. NOTE: All it contains is a pointer to our IStorageVtbl, so we can easily initialize it
// here instead of doing that programmably.
IStorage   MyIStorage = { &MyIStorageTable };



// Our IOleInPlaceFrame functions that the browser may call
HRESULT STDMETHODCALLTYPE Frame_QueryInterface(IOleInPlaceFrame FAR* This, REFIID riid, LPVOID FAR* ppvObj);
HRESULT STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR* This);
HRESULT STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR* This);
HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR* This, HWND FAR* lphwnd);
HRESULT STDMETHODCALLTYPE Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR* This, BOOL fEnterMode);
HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR* This, LPRECT lprectBorder);
HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths);
HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths);
HRESULT STDMETHODCALLTYPE Frame_SetActiveObject(IOleInPlaceFrame FAR* This, IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName);
HRESULT STDMETHODCALLTYPE Frame_InsertMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths);
HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR* This, HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject);
HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared);
HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR* This, LPCOLESTR pszStatusText);
HRESULT STDMETHODCALLTYPE Frame_EnableModeless(IOleInPlaceFrame FAR* This, BOOL fEnable);
HRESULT STDMETHODCALLTYPE Frame_TranslateAccelerator(IOleInPlaceFrame FAR* This, LPMSG lpmsg, WORD wID);

// Our IOleInPlaceFrame VTable. This is the array of pointers to the above functions in our C
// program that the browser may call in order to interact with our frame window that contains
// the browser object. We must define a particular set of functions that comprise the
// IOleInPlaceFrame set of functions (see above), and then stuff pointers to those functions
// in their respective 'slots' in this table. We want the browser to use this VTable with our
// IOleInPlaceFrame structure.
IOleInPlaceFrameVtbl MyIOleInPlaceFrameTable = {Frame_QueryInterface,
Frame_AddRef,
Frame_Release,
Frame_GetWindow,
Frame_ContextSensitiveHelp,
Frame_GetBorder,
Frame_RequestBorderSpace,
Frame_SetBorderSpace,
Frame_SetActiveObject,
Frame_InsertMenus,
Frame_SetMenu,
Frame_RemoveMenus,
Frame_SetStatusText,
Frame_EnableModeless,
Frame_TranslateAccelerator};

// We need to pass an IOleInPlaceFrame struct to the browser object. And one of our IOleInPlaceFrame
// functions (Frame_GetWindow) is going to need to access our window handle. So let's create our own
// struct that starts off with an IOleInPlaceFrame struct (and that's important -- the IOleInPlaceFrame
// struct *must* be first), and then has an extra data field where we can store our own window's HWND.
//
// And because we may want to create multiple windows, each hosting its own browser object (to
// display its own web page), then we need to create a IOleInPlaceFrame struct for each window. So,
// we're not going to declare our IOleInPlaceFrame struct globally. We'll allocate it later using
// GlobalAlloc, and then stuff the appropriate HWND in it then, and also stuff a pointer to
// MyIOleInPlaceFrameTable in it. But let's just define it here.
typedef struct _IOleInPlaceFrameEx {
 IOleInPlaceFrame frame;  // The IOleInPlaceFrame must be first!

 ///////////////////////////////////////////////////
 // Here you add any variables that you need
 // to access in your IOleInPlaceFrame functions.
 // You don't want those functions to access global
 // variables, because then you couldn't use more
 // than one browser object. (ie, You couldn't have
 // multiple windows, each with its own embedded
 // browser object to display a different web page).
 //
 // So here is where I added my extra HWND that my
 // IOleInPlaceFrame function Frame_GetWindow() needs
 // to access.
 ///////////////////////////////////////////////////
 HWND    window;
} IOleInPlaceFrameEx;




// Our IOleClientSite functions that the browser may call
HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR* This, REFIID riid, void ** ppvObject);
HRESULT STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_Release(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR* This, DWORD dwAssign, DWORD dwWhichMoniker, IMoniker ** ppmk);
HRESULT STDMETHODCALLTYPE Site_GetContainer(IOleClientSite FAR* This, LPOLECONTAINER FAR* ppContainer);
HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR* This, BOOL fShow);
HRESULT STDMETHODCALLTYPE Site_RequestNewObjectLayout(IOleClientSite FAR* This);

// Our IOleClientSite VTable. This is the array of pointers to the above functions in our C
// program that the browser may call in order to interact with our frame window that contains
// the browser object. We must define a particular set of functions that comprise the
// IOleClientSite set of functions (see above), and then stuff pointers to those functions
// in their respective 'slots' in this table. We want the browser to use this VTable with our
// IOleClientSite structure.
IOleClientSiteVtbl MyIOleClientSiteTable = {Site_QueryInterface,
Site_AddRef,
Site_Release,
Site_SaveObject,
Site_GetMoniker,
Site_GetContainer,
Site_ShowObject,
Site_OnShowWindow,
Site_RequestNewObjectLayout};




// Our IOleInPlaceSite functions that the browser may call
HRESULT STDMETHODCALLTYPE Site_GetWindow(IOleInPlaceSite FAR* This, HWND FAR* lphwnd);
HRESULT STDMETHODCALLTYPE Site_ContextSensitiveHelp(IOleInPlaceSite FAR* This, BOOL fEnterMode);
HRESULT STDMETHODCALLTYPE Site_CanInPlaceActivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnInPlaceActivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnUIActivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_GetWindowContext(IOleInPlaceSite FAR* This, LPOLEINPLACEFRAME FAR* lplpFrame,LPOLEINPLACEUIWINDOW FAR* lplpDoc,LPRECT lprcPosRect,LPRECT lprcClipRect,LPOLEINPLACEFRAMEINFO lpFrameInfo);
HRESULT STDMETHODCALLTYPE Site_Scroll(IOleInPlaceSite FAR* This, SIZE scrollExtent);
HRESULT STDMETHODCALLTYPE Site_OnUIDeactivate(IOleInPlaceSite FAR* This, BOOL fUndoable);
HRESULT STDMETHODCALLTYPE Site_OnInPlaceDeactivate(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_DiscardUndoState(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_DeactivateAndUndo(IOleInPlaceSite FAR* This);
HRESULT STDMETHODCALLTYPE Site_OnPosRectChange(IOleInPlaceSite FAR* This, LPCRECT lprcPosRect);

// Our IOleInPlaceSite VTable. This is the array of pointers to the above functions in our C
// program that the browser may call in order to interact with our frame window that contains
// the browser object. We must define a particular set of functions that comprise the
// IOleInPlaceSite set of functions (see above), and then stuff pointers to those functions
// in their respective 'slots' in this table. We want the browser to use this VTable with our
// IOleInPlaceSite structure.
IOleInPlaceSiteVtbl MyIOleInPlaceSiteTable =  {Site_QueryInterface, // This gives a compiler warning because we're using
               // the same function as the MyIOleClientSiteTable uses.
               // And the first arg to that Site_QueryInterface() is
               // a pointer to a IOleClientSite. What we really should
               // have here is a separate function with its first arg
               // as a pointer to a IOleInPlaceSite (even though what
               // will really get passed to it is a _IOleClientSiteEx *).
               // But it's easier to use one function for QueryInterface()
               // in order to support "inheritance". ie, Sometimes, the
               // browser may call your IOleClientSite's QueryInterface
               // in order to get a pointer to your IOleInPlaceSite, or
               // vice versa. This is because these two structs will be
               // passed to the browser inside of a single struct. And
               // that makes them "interconnected" as far as
               // QueryInterface is concerned.
Site_AddRef,    // Ditto as above.
Site_Release,    // Ditto as above.
Site_GetWindow,
Site_ContextSensitiveHelp,
Site_CanInPlaceActivate,
Site_OnInPlaceActivate,
Site_OnUIActivate,
Site_GetWindowContext,
Site_Scroll,
Site_OnUIDeactivate,
Site_OnInPlaceDeactivate,
Site_DiscardUndoState,
Site_DeactivateAndUndo,
Site_OnPosRectChange};

// The structure we need to pass to the browser object must contain an IOleClientSite structure. The
// IOleClientSite struct *must* be first. Our IOleClientSite's QueryInterface() may also be asked to
// return a pointer to our IOleInPlaceSite struct. So we'll need to have that object handy. Plus,
// some of our IOleClientSite and IOleInPlaceSite functions will need to have the HWND to our window,
// and also a pointer to our IOleInPlaceFrame struct. So let's create a single struct that has both
// the IOleClientSite and IOleInPlaceSite structs inside it, and also has an extra data field to
// store a pointer to our IOleInPlaceFrame struct. (The HWND is stored in the IOleInPlaceFrame
// struct, so we can get at it there). As long as the IOleClientSite struct is the first thing, it's
// all ok. We'll call this new struct a _IOleClientSiteEx.
//
// And because we may want to create multiple windows, each hosting its own browser object (to
// display its own web page), then we need to create IOleClientSite and IOleInPlaceSite structs for
// each window. (ie, Each window needs its own _IOleClientSiteEx struct). So, we're not going to
// declare this struct globally. We'll allocate it later using GlobalAlloc, and then store the
// appropriate IOleInPlaceFrame struct pointer then.

typedef struct __IOleInPlaceSiteEx {
 IOleInPlaceSite  inplace;  // My IOleInPlaceSite object. Must be first.

 ///////////////////////////////////////////////////
 // Here you add any variables that you need
 // to access in your IOleInPlaceSite functions.
 // You don't want those functions to access global
 // variables, because then you couldn't use more
 // than one browser object. (ie, You couldn't have
 // multiple windows, each with its own embedded
 // browser object to display a different web page.
 //
 // So here is where I added my extra pointer to my
 // IOleInPlaceFrame struct.
 ///////////////////////////////////////////////////
 IOleInPlaceFrameEx *frame;
} _IOleInPlaceSiteEx;

typedef struct __IOleClientSiteEx {
 IOleClientSite  client;   // My IOleClientSite object. Must be first.
 _IOleInPlaceSiteEx inplace;  // My IOleInPlaceSite object.

 ///////////////////////////////////////////////////
 // Here you add any variables that you need
 // to access in your IOleClientSite functions.
 ///////////////////////////////////////////////////

} _IOleClientSiteEx;



// This is a simple C example. There are lots more things you can control about the browser object, but
// we don't do it in this example. _Many_ of the functions we provide below for the browser to call, will
// never actually be called by the browser in our example. Why? Because we don't do certain things
// with the browser that would require it to call those functions (even though we need to provide
// at least some stub for all of the functions).
//
// So, for these "dummy functions" that we don't expect the browser to call, we'll just stick in some
// assembly code that causes a debugger breakpoint and tells the browser object that we don't support
// the functionality. That way, if you try to do more things with the browser object, and it starts
// calling these "dummy functions", you'll know which ones you should add more meaningful code to.
#define NOTIMPLEMENTED _ASSERT(0); return(E_NOTIMPL)



////////////////////////////////////// My IStorage functions  /////////////////////////////////////////
// NOTE: The browser object doesn't use the IStorage functions, so most of these are us just returning
// E_NOTIMPL so that anyone who *does* call these functions knows nothing is being done here.

HRESULT STDMETHODCALLTYPE Storage_QueryInterface(IStorage FAR* This, REFIID riid, LPVOID FAR* ppvObj)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_AddRef(IStorage FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Storage_Release(IStorage FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Storage_CreateStream(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStream **ppstm)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_OpenStream(IStorage FAR* This, const WCHAR * pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_CreateStorage(IStorage FAR* This, const WCHAR *pwcsName, DWORD grfMode, DWORD reserved1, DWORD reserved2, IStorage **ppstg)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_OpenStorage(IStorage FAR* This, const WCHAR * pwcsName, IStorage * pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_CopyTo(IStorage FAR* This, DWORD ciidExclude, IID const *rgiidExclude, SNB snbExclude,IStorage *pstgDest)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_MoveElementTo(IStorage FAR* This, const OLECHAR *pwcsName,IStorage * pstgDest, const OLECHAR *pwcsNewName, DWORD grfFlags)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_Commit(IStorage FAR* This, DWORD grfCommitFlags)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_Revert(IStorage FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_EnumElements(IStorage FAR* This, DWORD reserved1, void * reserved2, DWORD reserved3, IEnumSTATSTG ** ppenum)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_DestroyElement(IStorage FAR* This, const OLECHAR *pwcsName)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_RenameElement(IStorage FAR* This, const WCHAR *pwcsOldName, const WCHAR *pwcsNewName)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_SetElementTimes(IStorage FAR* This, const WCHAR *pwcsName, FILETIME const *pctime, FILETIME const *patime, FILETIME const *pmtime)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_SetClass(IStorage FAR* This, REFCLSID clsid)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Storage_SetStateBits(IStorage FAR* This, DWORD grfStateBits, DWORD grfMask)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Storage_Stat(IStorage FAR* This, STATSTG * pstatstg, DWORD grfStatFlag)
{
 NOTIMPLEMENTED;
}





///////////////////////////////// My IOleClientSite/IOleInPlaceSite functions  /////////////////////////////////

/************************* Site_QueryInterface() *************************
 * The browser object calls this when it wants a pointer to one of our
 * IOleClientSite or IOleInPlaceSite structures. They are both in the
 * _IOleClientSiteEx struct we allocated in EmbedBrowserObject() and
 * passed to DoVerb() and OleCreate().
 *
 * This =  A pointer to whatever _IOleClientSiteEx struct we passed to
 *    OleCreate() or DoVerb().
 * riid =  A GUID struct that the browser passes us to clue us as to
 *    which type of struct (object) it would like a pointer
 *    returned for.
 * ppvObject = Where the browser wants us to return a pointer to the
 *    appropriate struct. (ie, It passes us a handle to fill in).
 *
 * RETURNS: S_OK if we return the struct, or E_NOINTERFACE if we don't have
 * the requested struct.
 */

HRESULT STDMETHODCALLTYPE Site_QueryInterface(IOleClientSite FAR* This, REFIID riid, void ** ppvObject)
{
 // It just so happens that the first arg passed to us is our _IOleClientSiteEx struct we allocated
 // and passed to DoVerb() and OleCreate(). Nevermind that 'This' is declared is an IOleClientSite *.
 // Remember that in EmbedBrowserObject(), we allocated our own _IOleClientSiteEx struct, and lied
 // to OleCreate() and DoVerb() -- passing our _IOleClientSiteEx struct and saying it was an
 // IOleClientSite struct. It's ok. An _IOleClientSiteEx starts with an embedded IOleClientSite, so
 // the browser didn't mind. So that's what the browser object is passing us now. The browser doesn't
 // know that it's really an _IOleClientSiteEx struct. But we do. So we can recast it and use it as
 // so here.

 // If the browser is asking us to match IID_IOleClientSite, then it wants us to return a pointer to
 // our IOleClientSite struct. Then the browser will use the VTable in that struct to call our
 // IOleClientSite functions. It will also pass this same pointer to all of our IOleClientSite
 // functions.
 //
 // Actually, we're going to lie to the browser again. We're going to return our own _IOleClientSiteEx
 // struct, and tell the browser that it's a IOleClientSite struct. It's ok. The first thing in our
 // _IOleClientSiteEx is an embedded IOleClientSite, so the browser doesn't mind. We want the browser
 // to continue passing our _IOleClientSiteEx pointer wherever it would normally pass a IOleClientSite
 // pointer.
 //
 // The IUnknown interface uses the same VTable as the first object in our _IOleClientSiteEx
 // struct (which happens to be an IOleClientSite). So if the browser is asking us to match
 // IID_IUnknown, then we'll also return a pointer to our _IOleClientSiteEx.

 if (!memcmp(riid, &IID_IUnknown, sizeof(GUID)) || !memcmp(riid, &IID_IOleClientSite, sizeof(GUID)))
  *ppvObject = &((_IOleClientSiteEx *)This)->client;

 // If the browser is asking us to match IID_IOleInPlaceSite, then it wants us to return a pointer to
 // our IOleInPlaceSite struct. Then the browser will use the VTable in that struct to call our
 // IOleInPlaceSite functions.  It will also pass this same pointer to all of our IOleInPlaceSite
 // functions (except for Site_QueryInterface, Site_AddRef, and Site_Release. Those will always get
 // the pointer to our _IOleClientSiteEx.
 //
 // Actually, we're going to lie to the browser. We're going to return our own IOleInPlaceSiteEx
 // struct, and tell the browser that it's a IOleInPlaceSite struct. It's ok. The first thing in
 // our IOleInPlaceSiteEx is an embedded IOleInPlaceSite, so the browser doesn't mind. We want the
 // browser to continue passing our IOleInPlaceSiteEx pointer wherever it would normally pass a
 // IOleInPlaceSite pointer. My, we're really playing dirty tricks on the browser here. heheh.
 else if (!memcmp(riid, &IID_IOleInPlaceSite, sizeof(GUID)))
  *ppvObject = &((_IOleClientSiteEx *)This)->inplace;

 // For other types of objects the browser wants, just report that we don't have any such objects.
 // NOTE: If you want to add additional functionality to your browser hosting, you may need to
 // provide some more objects here. You'll have to investigate what the browser is asking for
 // (ie, what REFIID it is passing).
 else
 {
  *ppvObject = 0;
  return(E_NOINTERFACE);
 }

 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_AddRef(IOleClientSite FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Site_Release(IOleClientSite FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Site_SaveObject(IOleClientSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_GetMoniker(IOleClientSite FAR* This, DWORD dwAssign, DWORD dwWhichMoniker, IMoniker ** ppmk)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_GetContainer(IOleClientSite FAR* This, LPOLECONTAINER FAR* ppContainer)
{
 // Tell the browser that we are a simple object and don't support a container
 *ppContainer = 0;

 return(E_NOINTERFACE);
}

HRESULT STDMETHODCALLTYPE Site_ShowObject(IOleClientSite FAR* This)
{
 return(NOERROR);
}

HRESULT STDMETHODCALLTYPE Site_OnShowWindow(IOleClientSite FAR* This, BOOL fShow)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_RequestNewObjectLayout(IOleClientSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_GetWindow(IOleInPlaceSite FAR* This, HWND FAR* lphwnd)
{
 // Return the HWND of the window that contains this browser object. We stored that
 // HWND in our _IOleInPlaceSiteEx struct. Nevermind that the function declaration for
 // Site_GetWindow says that 'This' is an IOleInPlaceSite *. Remember that in
 // EmbedBrowserObject(), we allocated our own _IOleInPlaceSiteEx struct which
 // contained an embedded IOleInPlaceSite struct within it. And when the browser
 // called Site_QueryInterface() to get a pointer to our IOleInPlaceSite object, we
 // returned a pointer to our _IOleClientSiteEx. The browser doesn't know this. But
 // we do. That's what we're really being passed, so we can recast it and use it as
 // so here.
 *lphwnd = ((_IOleInPlaceSiteEx FAR*)This)->frame->window;

 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_ContextSensitiveHelp(IOleInPlaceSite FAR* This, BOOL fEnterMode)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_CanInPlaceActivate(IOleInPlaceSite FAR* This)
{
 // Tell the browser we can in place activate
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_OnInPlaceActivate(IOleInPlaceSite FAR* This)
{
 // Tell the browser we did it ok
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_OnUIActivate(IOleInPlaceSite FAR* This)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_GetWindowContext(IOleInPlaceSite FAR* This, LPOLEINPLACEFRAME FAR* lplpFrame, LPOLEINPLACEUIWINDOW FAR* lplpDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
 // Give the browser the pointer to our IOleInPlaceFrame struct. We stored that pointer
 // in our _IOleInPlaceSiteEx struct. Nevermind that the function declaration for
 // Site_GetWindowContext says that 'This' is an IOleInPlaceSite *. Remember that in
 // EmbedBrowserObject(), we allocated our own _IOleInPlaceSiteEx struct which
 // contained an embedded IOleInPlaceSite struct within it. And when the browser
 // called Site_QueryInterface() to get a pointer to our IOleInPlaceSite object, we
 // returned a pointer to our _IOleClientSiteEx. The browser doesn't know this. But
 // we do. That's what we're really being passed, so we can recast it and use it as
 // so here.
 //
 // Actually, we're giving the browser a pointer to our own IOleInPlaceSiteEx struct,
 // but telling the browser that it's a IOleInPlaceSite struct. No problem. Our
 // IOleInPlaceSiteEx starts with an embedded IOleInPlaceSite, so the browser is
 // cool with it. And we want the browser to pass a pointer to this IOleInPlaceSiteEx
 // wherever it would pass a IOleInPlaceSite struct to our IOleInPlaceSite functions.
 *lplpFrame = (LPOLEINPLACEFRAME)((_IOleInPlaceSiteEx FAR*)This)->frame;

 // We have no OLEINPLACEUIWINDOW
 *lplpDoc = 0;

 // Fill in some other info for the browser
 lpFrameInfo->fMDIApp = FALSE;
 lpFrameInfo->hwndFrame = ((IOleInPlaceFrameEx FAR*)*lplpFrame)->window;
 lpFrameInfo->haccel = 0;
 lpFrameInfo->cAccelEntries = 0;

 // Give the browser the dimensions of where it can draw. We give it our entire window to fill
 GetClientRect(lpFrameInfo->hwndFrame, lprcPosRect);
 GetClientRect(lpFrameInfo->hwndFrame, lprcClipRect);

 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_Scroll(IOleInPlaceSite FAR* This, SIZE scrollExtent)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_OnUIDeactivate(IOleInPlaceSite FAR* This, BOOL fUndoable)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_OnInPlaceDeactivate(IOleInPlaceSite FAR* This)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Site_DiscardUndoState(IOleInPlaceSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_DeactivateAndUndo(IOleInPlaceSite FAR* This)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Site_OnPosRectChange(IOleInPlaceSite FAR* This, LPCRECT lprcPosRect)
{
 return(S_OK);
}




////////////////////////////////////// My IOleInPlaceFrame functions  /////////////////////////////////////////

HRESULT STDMETHODCALLTYPE Frame_QueryInterface(IOleInPlaceFrame FAR* This, REFIID riid, LPVOID FAR* ppvObj)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_AddRef(IOleInPlaceFrame FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Frame_Release(IOleInPlaceFrame FAR* This)
{
 return(1);
}

HRESULT STDMETHODCALLTYPE Frame_GetWindow(IOleInPlaceFrame FAR* This, HWND FAR* lphwnd)
{
 // Give the browser the HWND to our window that contains the browser object. We
 // stored that HWND in our IOleInPlaceFrame struct. Nevermind that the function
 // declaration for Frame_GetWindow says that 'This' is an IOleInPlaceFrame *. Remember
 // that in EmbedBrowserObject(), we allocated our own IOleInPlaceFrameEx struct which
 // contained an embedded IOleInPlaceFrame struct within it. And then we lied when
 // Site_GetWindowContext() returned that IOleInPlaceFrameEx. So that's what the
 // browser is passing us. It doesn't know that. But we do. So we can recast it and
 // use it as so here.
 *lphwnd = ((IOleInPlaceFrameEx FAR*)This)->window;
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_ContextSensitiveHelp(IOleInPlaceFrame FAR* This, BOOL fEnterMode)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_GetBorder(IOleInPlaceFrame FAR* This, LPRECT lprectBorder)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_RequestBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetBorderSpace(IOleInPlaceFrame FAR* This, LPCBORDERWIDTHS pborderwidths)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetActiveObject(IOleInPlaceFrame FAR* This, IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_InsertMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetMenu(IOleInPlaceFrame FAR* This, HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_RemoveMenus(IOleInPlaceFrame FAR* This, HMENU hmenuShared)
{
 NOTIMPLEMENTED;
}

HRESULT STDMETHODCALLTYPE Frame_SetStatusText(IOleInPlaceFrame FAR* This, LPCOLESTR pszStatusText)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_EnableModeless(IOleInPlaceFrame FAR* This, BOOL fEnable)
{
 return(S_OK);
}

HRESULT STDMETHODCALLTYPE Frame_TranslateAccelerator(IOleInPlaceFrame FAR* This, LPMSG lpmsg, WORD wID)
{
 NOTIMPLEMENTED;
}





/*************************** UnEmbedBrowserObject() ************************
 * Called to detach the browser object from our host window, and free its
 * resources, right before we destroy our window.
 *
 * hwnd =  Handle to the window hosting the browser object.
 *
 * NOTE: The pointer to the browser object must have been stored in the
 * window's USERDATA field. In other words, don't call UnEmbedBrowserObject().
 * with a HWND that wasn't successfully passed to EmbedBrowserObject().
 */

void UnEmbedBrowserObject(HWND hwnd)
{
 IOleObject **browserHandle;
 IOleObject *browserObject;

 // Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
 // we initially attached the browser object to this window.
 if ((browserHandle = (IOleObject **)GetWindowLong(hwnd, GWL_USERDATA)))
 {
  // Unembed the browser object, and release its resources.
  browserObject = *browserHandle;
  browserObject->lpVtbl->Close(browserObject, OLECLOSE_NOSAVE);
  browserObject->lpVtbl->Release(browserObject);

  GlobalFree(browserHandle);

  return;
 }

 // You must have called this for a window that wasn't successfully passed to EmbedBrowserObject().
 // Bad boy!
 _ASSERT(0);
}




/******************************* DisplayHTMLStr() ****************************
 * Takes a string containing some HTML BODY, and displays it in the specified
 * window. For example, perhaps you want to display the HTML text of...
 *
 * <P>This is a picture.<P><IMG src="mypic.jpg">
 *
 * hwnd =  Handle to the window hosting the browser object.
 * string =  Pointer to nul-terminated string containing the HTML BODY.
 *    (NOTE: No <BODY></BODY> tags are required in the string).
 *
 * RETURNS: 0 if success, or non-zero if an error.
 *
 * NOTE: EmbedBrowserObject() must have been successfully called once with the
 * specified window, prior to calling this function. You need call
 * EmbedBrowserObject() once only, and then you can make multiple calls to
 * this function to display numerous pages in the specified window.
 */

long DisplayHTMLStr(HWND hwnd, LPCTSTR string)
{
 IWebBrowser2 *webBrowser2;
 LPDISPATCH  lpDispatch;
 IHTMLDocument2 *htmlDoc2;
 IOleObject  *browserObject;
 SAFEARRAY  *sfArray;
 VARIANT   myURL;
 VARIANT   *pVar;
 BSTR   bstr;

 // Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
 // we initially attached the browser object to this window.
 browserObject = *((IOleObject **)GetWindowLong(hwnd, GWL_USERDATA));

 // Assume an error.
 bstr = 0;

 // We want to get the base address (ie, a pointer) to the IWebBrowser2 object embedded within the browser
 // object, so we can call some of the functions in the former's table.
 if (!browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2))
 {
  // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
  // webBrowser2->lpVtbl.

  // Before we can get_Document(), we actually need to have some HTML page loaded in the browser. So,
  // let's load an empty HTML page. Then, once we have that empty page, we can get_Document() and
  // write() to stuff our HTML string into it.
  VariantInit(&myURL);
  myURL.vt = VT_BSTR;
  myURL.bstrVal = SysAllocString(L"about:blank");

  // Call the Navigate2() function to actually display the page.
  webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0);

  // Free any resources (including the BSTR).
  VariantClear(&myURL);

  // Call the IWebBrowser2 object's get_Document so we can get its DISPATCH object. I don't know why you
  // don't get the DISPATCH object via the browser object's QueryInterface(), but you don't.
  if (!webBrowser2->lpVtbl->get_Document(webBrowser2, &lpDispatch))
  {
   // We want to get a pointer to the IHTMLDocument2 object embedded within the DISPATCH
   // object, so we can call some of the functions in the former's table.
   if (!lpDispatch->lpVtbl->QueryInterface(lpDispatch, &IID_IHTMLDocument2, (void**)&htmlDoc2))
   {
    // Ok, now the pointer to our IHTMLDocument2 object is in 'htmlDoc2', and so its VTable is
    // htmlDoc2->lpVtbl.

    // Our HTML must be in the form of a BSTR. And it must be passed to write() in an
    // array of "VARIENT" structs. So let's create all that.
    if ((sfArray = SafeArrayCreate(VT_VARIANT, 1, (SAFEARRAYBOUND *)&ArrayBound)))
    {
     if (!SafeArrayAccessData(sfArray, (void**)&pVar))
     {
      pVar->vt = VT_BSTR;
#ifndef UNICODE
      {
      wchar_t  *buffer;
      DWORD  size;

      size = MultiByteToWideChar(CP_ACP, 0, string, -1, 0, 0);
      if (!(buffer = (wchar_t *)GlobalAlloc(GMEM_FIXED, sizeof(wchar_t) * size))) goto bad;
      MultiByteToWideChar(CP_ACP, 0, string, -1, buffer, size);
      bstr = SysAllocString(buffer);
      GlobalFree(buffer);
      }
#else
      bstr = SysAllocString(string);
#endif
      // Store our BSTR pointer in the VARIENT.
      if ((pVar->bstrVal = bstr))
      {
       // Pass the VARIENT with its BSTR to write() in order to shove our desired HTML string
       // into the body of that empty page we created above.
       htmlDoc2->lpVtbl->write(htmlDoc2, sfArray);

       // Normally, we'd need to free our BSTR, but SafeArrayDestroy() does it for us
//       SysFreeString(bstr);
      }
     }

     // Free the array. This also frees the VARIENT that SafeArrayAccessData created for us,
     // and even frees the BSTR we allocated with SysAllocString
     SafeArrayDestroy(sfArray);
    }

    // Release the IHTMLDocument2 object.
bad:   htmlDoc2->lpVtbl->Release(htmlDoc2);
   }

   // Release the DISPATCH object.
   lpDispatch->lpVtbl->Release(lpDispatch);
  }

  // Release the IWebBrowser2 object.
  webBrowser2->lpVtbl->Release(webBrowser2);
 }

 // No error?
 if (bstr) return(0);

 // An error
 return(-1);
}




/******************************* DisplayHTMLPage() ****************************
 * Displays a URL, or HTML file on disk.
 *
 * hwnd =  Handle to the window hosting the browser object.
 * webPageName = Pointer to nul-terminated name of the URL/file.
 *
 * RETURNS: 0 if success, or non-zero if an error.
 *
 * NOTE: EmbedBrowserObject() must have been successfully called once with the
 * specified window, prior to calling this function. You need call
 * EmbedBrowserObject() once only, and then you can make multiple calls to
 * this function to display numerous pages in the specified window.
 */

long DisplayHTMLPage(HWND hwnd, LPTSTR webPageName)
{
 IWebBrowser2 *webBrowser2;
 VARIANT   myURL;
 IOleObject  *browserObject;

 // Retrieve the browser object's pointer we stored in our window's GWL_USERDATA when
 // we initially attached the browser object to this window.
 browserObject = *((IOleObject **)GetWindowLong(hwnd, GWL_USERDATA));

 // We want to get the base address (ie, a pointer) to the IWebBrowser2 object embedded within the browser
 // object, so we can call some of the functions in the former's table.
 if (!browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2))
 {
  // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
  // webBrowser2->lpVtbl.

  // Our URL (ie, web address, such as "http://www.microsoft.com" or an HTM filename on disk
  // such as "c:\myfile.htm") must be passed to the IWebBrowser2's Navigate2() function as a BSTR.
  // A BSTR is like a pascal version of a double-byte character string. In other words, the
  // first unsigned short is a count of how many characters are in the string, and then this
  // is followed by those characters, each expressed as an unsigned short (rather than a
  // char). The string is not nul-terminated. The OS function SysAllocString can allocate and
  // copy a UNICODE C string to a BSTR. Of course, we'll need to free that BSTR after we're done
  // with it. If we're not using UNICODE, we first have to convert to a UNICODE string.
  //
  // What's more, our BSTR needs to be stuffed into a VARIENT struct, and that VARIENT struct is
  // then passed to Navigate2(). Why? The VARIENT struct makes it possible to define generic
  // 'datatypes' that can be used with all languages. Not all languages support things like
  // nul-terminated C strings. So, by using a VARIENT, whose first field tells what sort of
  // data (ie, string, float, etc) is in the VARIENT, COM interfaces can be used by just about
  // any language.
  VariantInit(&myURL);
  myURL.vt = VT_BSTR;

#ifndef UNICODE
  {
  wchar_t  *buffer;
  DWORD  size;

  size = MultiByteToWideChar(CP_ACP, 0, webPageName, -1, 0, 0);
  if (!(buffer = (wchar_t *)GlobalAlloc(GMEM_FIXED, sizeof(wchar_t) * size))) goto badalloc;
  MultiByteToWideChar(CP_ACP, 0, webPageName, -1, buffer, size);
  myURL.bstrVal = SysAllocString(buffer);
  GlobalFree(buffer);
  }
#else
  myURL.bstrVal = SysAllocString(webPageName);
#endif
  if (!myURL.bstrVal)
  {
badalloc: webBrowser2->lpVtbl->Release(webBrowser2);
   return(-6);
  }

  // Call the Navigate2() function to actually display the page.
  webBrowser2->lpVtbl->Navigate2(webBrowser2, &myURL, 0, 0, 0, 0);

  // Free any resources (including the BSTR we allocated above).
  VariantClear(&myURL);

  // We no longer need the IWebBrowser2 object (ie, we don't plan to call any more functions in it,
  // so we can release our hold on it). Note that we'll still maintain our hold on the browser
  // object.
  webBrowser2->lpVtbl->Release(webBrowser2);

  // Success
  return(0);
 }

 return(-5);
}



/***************************** EmbedBrowserObject() **************************
 * Puts the browser object inside our host window, and save a pointer to this
 * window's browser object in the window's GWL_USERDATA field.
 *
 * hwnd =  Handle of our window into which we embed the browser object.
 *
 * RETURNS: 0 if success, or non-zero if an error.
 *
 * NOTE: We tell the browser object to occupy the entire client area of the
 * window.
 *
 * NOTE: No HTML page will be displayed here. We can do that with a subsequent
 * call to either DisplayHTMLPage() or DisplayHTMLStr(). This is merely once-only
 * initialization for using the browser object. In a nutshell, what we do
 * here is get a pointer to the browser object in our window's GWL_USERDATA
 * so we can access that object's functions whenever we want, and we also pass
 * pointers to our IOleClientSite, IOleInPlaceFrame, and IStorage structs so that
 * the browser can call our functions in our struct's VTables.
 */

long EmbedBrowserObject(HWND hwnd)
{
 IOleObject   *browserObject;
 IWebBrowser2  *webBrowser2;
 RECT    rect;
 char    *ptr;
 IOleInPlaceFrameEx *iOleInPlaceFrameEx;
 _IOleClientSiteEx *_iOleClientSiteEx;

 // Our IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame functions need to get our window handle. We
 // could store that in some global. But then, that would mean that our functions would work with only that
 // one window. If we want to create multiple windows, each hosting its own browser object (to display its
 // own web page), then we need to create a unique IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame
 // structs for each window. (Actually, the IOleClientSite and IOleInPlaceSite must be allocated as a
 // single struct, with the IOleClientSite embedded first). And we'll put an extra field at the end of those
 // structs to store our extra data such as a window handle. Remember that a pointer to our IOleClientSite we
 // create here will be passed as the first arg to every one of our IOleClientSite functions. Ditto with the
 // IOleInPlaceFrame object we create here, and the IOleInPlaceFrame functions. So, our functions are able to
 // retrieve the window handle we'll store here, and then, they'll work with all such windows containing a
 // browser control. Of course, that means we need to GlobalAlloc the structs now. We'll just get all 3 with
 // a single call to GlobalAlloc, but you could do them separately if desired.
 //
 // Um, we're not actually allocating a IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame structs. Because
 // we're appending our own fields to them, we're getting an IOleInPlaceFrameEx and an _IOleClientSiteEx (which
 // contains both the IOleClientSite and IOleInPlaceSite. But as far as the browser is concerned, it thinks that
 // we're giving it the plain old IOleClientSite, IOleInPlaceSite, and IOleInPlaceFrame.
 //
 // One final thing. We're going to allocate extra room to store the pointer to the browser object.
 if (!(ptr = (char *)GlobalAlloc(GMEM_FIXED, sizeof(IOleInPlaceFrameEx) + sizeof(_IOleClientSiteEx) + sizeof(IOleObject *))))
  return(-1);
 
 // Initialize our IOleInPlaceFrame object with a pointer to our IOleInPlaceFrame VTable.
 iOleInPlaceFrameEx = (IOleInPlaceFrameEx *)(ptr + sizeof(IOleObject *));
 iOleInPlaceFrameEx->frame.lpVtbl = &MyIOleInPlaceFrameTable;

 // Save our HWND (in the IOleInPlaceFrame object) so our IOleInPlaceFrame functions can retrieve it.
 iOleInPlaceFrameEx->window = hwnd;

 // Initialize our IOleClientSite object with a pointer to our IOleClientSite VTable.
 _iOleClientSiteEx = (_IOleClientSiteEx *)(ptr + sizeof(IOleInPlaceFrameEx) + sizeof(IOleObject *));
 _iOleClientSiteEx->client.lpVtbl = &MyIOleClientSiteTable;

 // Initialize our IOleInPlaceSite object with a pointer to our IOleInPlaceSite VTable.
 _iOleClientSiteEx->inplace.inplace.lpVtbl = &MyIOleInPlaceSiteTable;

 // Save a pointer to our IOleInPlaceFrameEx object so that our IOleInPlaceSite functions can retrieve it.
 _iOleClientSiteEx->inplace.frame = iOleInPlaceFrameEx;

 // Get a pointer to the browser object and lock it down (so it doesn't "disappear" while we're using
 // it in this program). We do this by calling the OS function OleCreate().
 //
 // NOTE: We need this pointer to interact with and control the browser. With normal WIN32 controls such as a
 // Static, Edit, Combobox, etc, you obtain an HWND and send messages to it with SendMessage(). Not so with
 // the browser object. You need to get a pointer to its "base structure" (as returned by OleCreate()). This
 // structure contains an array of pointers to functions you can call within the browser object. Actually, the
 // base structure contains a 'lpVtbl' field that is a pointer to that array. We'll call the array a 'VTable'.
 //
 // For example, the browser object happens to have a SetHostNames() function we want to call. So, after we
 // retrieve the pointer to the browser object (in a local we'll name 'browserObject'), then we can call that
 // function, and pass it args, as so:
 //
 // browserObject->lpVtbl->SetHostNames(browserObject, SomeString, SomeString);
 //
 // There's our pointer to the browser object in 'browserObject'. And there's the pointer to the browser object's
 // VTable in 'browserObject->lpVtbl'. And the pointer to the SetHostNames function happens to be stored in an
 // field named 'SetHostNames' within the VTable. So we are actually indirectly calling SetHostNames by using
 // a pointer to it. That's how you use a VTable.
 //
 // NOTE: We pass our _IOleClientSiteEx struct and lie -- saying that it's a IOleClientSite. It's ok. A
 // _IOleClientSiteEx struct starts with an embedded IOleClientSite. So the browser won't care, and we want
 // this extended struct passed to our IOleClientSite functions.

 if (!OleCreate(&CLSID_WebBrowser, &IID_IOleObject, OLERENDER_DRAW, 0, (IOleClientSite *)_iOleClientSiteEx, &MyIStorage, (void**)&browserObject))
 {
  // Ok, we now have the pointer to the browser object in 'browserObject'. Let's save this in the
  // memory block we allocated above, and then save the pointer to that whole thing in our window's
  // USERDATA field. That way, if we need multiple windows each hosting its own browser object, we can
  // call EmbedBrowserObject() for each one, and easily associate the appropriate browser object with
  // its matching window and its own objects containing per-window data.
  *((IOleObject **)ptr) = browserObject;
  SetWindowLong(hwnd, GWL_USERDATA, (LONG)ptr);

  // We can now call the browser object's SetHostNames function. SetHostNames lets the browser object know our
  // application's name and the name of the document in which we're embedding the browser. (Since we have no
  // document name, we'll pass a 0 for the latter). When the browser object is opened for editing, it displays
  // these names in its titlebar.
  //
  // We are passing 3 args to SetHostNames. You'll note that the first arg to SetHostNames is the base
  // address of our browser control. This is something that you always have to remember when working in C
  // (as opposed to C++). When calling a VTable function, the first arg to that function must always be the
  // structure which contains the VTable. (In this case, that's the browser control itself). Why? That's
  // because that function is always assumed to be written in C++. And the first argument to any C++ function
  // must be its 'this' pointer (ie, the base address of its class, which in this case is our browser object
  // pointer). In C++, you don't have to pass this first arg, because the C++ compiler is smart enough to
  // produce an executable that always adds this first arg. In fact, the C++ compiler is smart enough to
  // know to fetch the function pointer from the VTable, so you don't even need to reference that. In other
  // words, the C++ equivalent code would be:
  //
  // browserObject->SetHostNames(L"My Host Name", 0);
  //
  // So, when you're trying to convert C++ code to C, always remember to add this first arg whenever you're
  // dealing with a VTable (ie, the field is usually named 'lpVtbl') in the standard objects, and also add
  // the reference to the VTable itself.
  //
  // Oh yeah, the L is because we need UNICODE strings. And BTW, the host and document names can be anything
  // you want.

  browserObject->lpVtbl->SetHostNames(browserObject, L"My Host Name", 0);

  GetClientRect(hwnd, &rect);

  // Let browser object know that it is embedded in an OLE container.
  if (!OleSetContainedObject((struct IUnknown *)browserObject, TRUE) &&

   // Set the display area of our browser control the same as our window's size
   // and actually put the browser object into our window.
   !browserObject->lpVtbl->DoVerb(browserObject, OLEIVERB_SHOW, NULL, (IOleClientSite *)_iOleClientSiteEx, -1, hwnd, &rect) &&

   // Ok, now things may seem to get even trickier, One of those function pointers in the browser's VTable is
   // to the QueryInterface() function. What does this function do? It lets us grab the base address of any
   // other object that may be embedded within the browser object. And this other object has its own VTable
   // containing pointers to more functions we can call for that object.
   //
   // We want to get the base address (ie, a pointer) to the IWebBrowser2 object embedded within the browser
   // object, so we can call some of the functions in the former's table. For example, one IWebBrowser2 function
   // we intend to call below will be Navigate2(). So we call the browser object's QueryInterface to get our
   // pointer to the IWebBrowser2 object.
   !browserObject->lpVtbl->QueryInterface(browserObject, &IID_IWebBrowser2, (void**)&webBrowser2))
  {
   // Ok, now the pointer to our IWebBrowser2 object is in 'webBrowser2', and so its VTable is
   // webBrowser2->lpVtbl.

   // Let's call several functions in the IWebBrowser2 object to position the browser display area
   // in our window. The functions we call are put_Left(), put_Top(), put_Width(), and put_Height().
   // Note that we reference the IWebBrowser2 object's VTable to get pointers to those functions. And
   // also note that the first arg we pass to each is the pointer to the IWebBrowser2 object.
   webBrowser2->lpVtbl->put_Left(webBrowser2, 0);
   webBrowser2->lpVtbl->put_Top(webBrowser2, 0);
   webBrowser2->lpVtbl->put_Width(webBrowser2, rect.right);
   webBrowser2->lpVtbl->put_Height(webBrowser2, rect.bottom);

   // We no longer need the IWebBrowser2 object (ie, we don't plan to call any more functions in it
   // right now, so we can release our hold on it). Note that we'll still maintain our hold on the
   // browser object until we're done with that object.
   webBrowser2->lpVtbl->Release(webBrowser2);

   // Success
   return(0);
  }

  // Something went wrong!
  UnEmbedBrowserObject(hwnd);
  return(-3);
 }

 GlobalFree(ptr);
 return(-2);
}




/****************************** WindowProc() ***************************
 * Our message handler for our window to host the browser.
 */

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
 if (uMsg == WM_CREATE)
 {
  // Embed the browser object into our host window. We need do this only
  // once. Note that the browser object will start calling some of our
  // IOleInPlaceFrame and IOleClientSite functions as soon as we start
  // calling browser object functions in EmbedBrowserObject().
  if (EmbedBrowserObject(hwnd)) return(-1);

  // Another window created with an embedded browser object
  ++WindowCount;

  // Success
  return(0);
 }

 if (uMsg == WM_DESTROY)
 {
  // Detach the browser object from this window, and free resources.
  UnEmbedBrowserObject(hwnd);

  // One less window
  --WindowCount;

  // If all the windows are now closed, quit this app
  if (!WindowCount) PostQuitMessage(0);

  return(TRUE);
 }

 // NOTE: If you want to resize the area that the browser object occupies when you
 // resize the window, then handle WM_SIZE and use the IWebBrowser2's put_Width()
 // and put_Height() to give it the new dimensions.

 return(DefWindowProc(hwnd, uMsg, wParam, lParam));
}



/****************************** WinMain() ***************************
 * C program entry point.
 *
 * This creates a window to host the web browser, and displays a web
 * page.
 */

int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hInstNULL, LPSTR lpszCmdLine, int nCmdShow)
{
 MSG   msg;

 // Initialize the OLE interface. We do this once-only.
 if (OleInitialize(NULL) == S_OK)
 {
  WNDCLASSEX  wc;

  // Register the class of our window to host the browser. 'WindowProc' is our message handler
  // and 'ClassName' is the class name. You can choose any class name you want.
  ZeroMemory(&wc, sizeof(WNDCLASSEX));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hInstance = hInstance;
  wc.lpfnWndProc = WindowProc;
  wc.lpszClassName = &ClassName[0];
  RegisterClassEx(&wc);

  // Create a window. NOTE: We embed the browser object duing our WM_CREATE handling for
  // this window.
/*  if ((msg.hwnd = CreateWindowEx(0, &ClassName[0], "An HTML string", WS_OVERLAPPEDWINDOW,
       CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
       HWND_DESKTOP, NULL, hInstance, 0)))
  {
   // For this window, display a string in the BODY of a web page.
   DisplayHTMLStr(msg.hwnd, "<H2><CENTER>HTML string test</CENTER></H2><P><FONT COLOR=RED>This is a <U>HTML string</U> in memory.</FONT>");

   // Show the window.
   ShowWindow(msg.hwnd, nCmdShow);
   UpdateWindow(msg.hwnd);
  }
*/
  // Create another window with another browser object embedded in it.
  if ((msg.hwnd = CreateWindowEx(0, &ClassName[0], "Microsoft's web site", WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
      HWND_DESKTOP, NULL, hInstance, 0)))
  {
   // For this window, display a URL. This could also be a HTML file on disk such as "c:\\myfile.htm".
   DisplayHTMLPage(msg.hwnd, "http://www.naver.com/");

   // Show the window.
   ShowWindow(msg.hwnd, nCmdShow);
   UpdateWindow(msg.hwnd);
  }

  // Do a message loop until WM_QUIT.
  while (GetMessage(&msg, 0, 0, 0))
  {
   TranslateMessage(&msg);
   DispatchMessage(&msg);
  }

  // Free the OLE library.
  OleUninitialize();

  return(0);
 }

 MessageBox(0, "Can't open OLE!", "ERROR", MB_OK);
 return(-1);
}


코드 구루였나.. 거기서 가져온겁니다...
문제는 C언어라.. C++로 돌리면 컴파일 에러가 난다는거...
음... C++로 돌려야 할텐데..

신고
블로그 이미지

프로그래머 지향자 RosaGigantea

바쁜 일상 생활중의 기억 장소