Unknownguy
Unknownguy

Reputation: 191

undeclared identifer of a wndproc function

I have a class, which I tried in another MFC project which compiled fine, but for some reason in this project it doesn't compile and complains about my wndproc function that is declared in a header file.

Below is the header file of the class I used in another MFC project. I had to comment out #include <windows.h> because it would say it can't be used in MFC, which wasn't the case in the other MFC project (what must be noted is that the other MFC project had precompiled headers).

skin.h

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
// This is the source-code that shows what is discussed in the tutorial.
// The code is simplified for the sake of clarity, but all the needed
// features for handling skinned windows is present. Please read
// the article for more information.
//
// skin.h     : CSkin class declaration
// 28/02/2002 : initial release.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-





#ifndef _SKIN_H_

#define _SKIN_H_
//#include <windows.h>
#include <afxwin.h>


  // --------------------------------------------------------------------------
  // The CSkin class will load the skin from a resource
  // and subclass the associated window, so that the
  // WM_PAINT message will be redirected to the provided
  // window procedure. All the skin handling will be automatized.
  // --------------------------------------------------------------------------

  class CSkin
  {

    // --------------------------------------------------------------------------
    // the skin window procedure, where the class
    // will handle WM_PAINT and WM_LBUTTONDOWN automatically.
    // --------------------------------------------------------------------------
    friend LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);

    private:

      // the associated window handle
      HWND      m_hWnd;
      
      // the associated CWnd window handle
      CWnd *     m_cwnd;

      // the old window procedure
      WNDPROC   m_OldWndProc;

      // skin region
      HRGN      m_rgnSkin;

      // the internal skin device context handle
      HDC       m_dcSkin;

      // bitmap and old bitmap from the device context
      HBITMAP   m_hBmp, m_hOldBmp;

      // skin dimensions
      int       m_iWidth, m_iHeight;

      // on|off toggle
      bool      m_bEnabled;

      // tell the class if it has a window subclassed.
      bool      m_bHooked;
      
      // skin retrieval helper
      bool      GetSkinData(int iSkinRegion, int iSkinBitmap);

    public:

      // ----------------------------------------------------------------------------
      // constructor 1 - use it when you have not already created the app window.
      // this one will not subclass automatically, you must call Hook() to subclass.
      // will throw an exception if unable to initialize skin from resource.
      // ----------------------------------------------------------------------------

      CSkin(int iSkinRegion, int iSkinBitmap);

      // ----------------------------------------------------------------------------
      // constructor 2 - use it when you have already created the app window.
      // this one will subclass the window automatically.
      // will throw an exception if unable to initialize skin from resource.
      // ----------------------------------------------------------------------------

      CSkin(HWND hWnd, int iSkinRegion, int iSkinBitmap);

      // ----------------------------------------------------------------------------
      // destructor - just call the destroyer
      // ----------------------------------------------------------------------------

      virtual ~CSkin();

      // ----------------------------------------------------------------------------
      // destroy skin resources and free allocated resources
      // ----------------------------------------------------------------------------

      void Destroy();

      // ----------------------------------------------------------------------------
      // subclass a window.
      // ----------------------------------------------------------------------------

      bool    Hook(HWND hWnd);

      // ----------------------------------------------------------------------------
      // subclass a window. The CWnd version
      // ----------------------------------------------------------------------------

      bool    Hook(CWnd *cwnd);


      // ----------------------------------------------------------------------------
      // unsubclass the subclassed window.
      // ----------------------------------------------------------------------------

      bool    UnHook();

      // ----------------------------------------------------------------------------
      // unsubclass the subclassed window. The CWnd version
      // ----------------------------------------------------------------------------

      bool    UnHookCWnd();



      // ----------------------------------------------------------------------------
      // tell us if we have a window subclassed.
      // ----------------------------------------------------------------------------

      bool    Hooked();

      // ----------------------------------------------------------------------------
      // toggle skin on/off.
      // ----------------------------------------------------------------------------

      bool    Enable(bool bEnable);

      // ----------------------------------------------------------------------------
      // tell if the skinning is enabled
      // ----------------------------------------------------------------------------

      bool    Enabled();

      // ----------------------------------------------------------------------------
      // return the skin bitmap width.
      // ----------------------------------------------------------------------------

      int     Width();

      // ----------------------------------------------------------------------------
      // return the skin bitmap height.
      // ----------------------------------------------------------------------------

      int     Height();

      // ----------------------------------------------------------------------------
      // return the skin device context.
      // ----------------------------------------------------------------------------

      HDC     HDC();
      
      
      // ----------------------------------------------------------------------------
      // return the handle region.
      // ----------------------------------------------------------------------------

      HRGN     HRGN();

      // toggle skin on/off for Cwnd
      bool EnableCWnd(bool bEnable);
};

#endif

This is the cpp file:

skin.cpp

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// WINDOWS SKINNING TUTORIAL - by Vander Nunes - virtware.net
// This is the source-code that shows what is discussed in the tutorial.
// The code is simplified for the sake of clarity, but all the needed
// features for handling skinned windows is present. Please read
// the article for more information.
//
// skin.cpp   : CSkin class implementation
// 28/02/2002 : initial release.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


#include "skin.h"



// ----------------------------------------------------------------------------
// constructor 1 - use it when you have not already created the app window.
// this one will not subclass automatically, you must call Hook() and Enable()
// to subclass the app window and enable the skin respectively.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------

CSkin::CSkin(int iSkinRegion, int iSkinBitmap)
{
  // default starting values
  m_bHooked = false;
  m_OldWndProc = NULL;

  // try to retrieve the skin data from resource.
  if ( !GetSkinData(iSkinRegion, iSkinBitmap) )
    throw ("Unable to retrieve the skin.");
}



// ----------------------------------------------------------------------------
// constructor 2 - use it when you have already created the app window.
// this one will subclass the window and enable the skin automatically.
// will throw an exception if unable to initialize skin from resource.
// ----------------------------------------------------------------------------
CSkin::CSkin(HWND hWnd, int iSkinRegion, int iSkinBitmap)
{
  // default starting values
  m_bHooked = false;
  m_OldWndProc = NULL;

  // initialize
  CSkin(iSkinRegion, iSkinBitmap);

  // subclass
  Hook(hWnd);

  // enable
  Enable(true);
}



// ----------------------------------------------------------------------------
// destructor - just call the destroyer
// ----------------------------------------------------------------------------
CSkin::~CSkin()
{
  Destroy();
}



// ----------------------------------------------------------------------------
// destroy skin resources and free allocated resources
// ----------------------------------------------------------------------------
void CSkin::Destroy()
{
  // unhook the window
  UnHook();

  // free bitmaps and device context
  if (m_dcSkin) { SelectObject(m_dcSkin, m_hOldBmp); DeleteDC(m_dcSkin); m_dcSkin = NULL; }
  if (m_hBmp) { DeleteObject(m_hBmp); m_hBmp = NULL; }

  // free skin region
  if (m_rgnSkin) { DeleteObject(m_rgnSkin); m_rgnSkin = NULL; }
}



// ----------------------------------------------------------------------------
// toggle skin on/off - must be Hooked() before attempting to enable skin.
// ----------------------------------------------------------------------------
bool CSkin::Enable(bool bEnable)
{
  // refuse to enable if there is no window subclassed yet.
  if (!Hooked()) return false;

  // toggle
  m_bEnabled = bEnable;

  // force window repainting
  InvalidateRect(m_hWnd, NULL, TRUE);

  return true;
}



// ----------------------------------------------------------------------------
// tell if the skinning is enabled
// ----------------------------------------------------------------------------
bool CSkin::Enabled()
{
  return m_bEnabled;
}



// ----------------------------------------------------------------------------
// hook a window
// ----------------------------------------------------------------------------
bool CSkin::Hook(HWND hWnd)
{
  // unsubclass any other window
  if (Hooked()) UnHook();

  // this will be our new subclassed window
  m_hWnd = hWnd;

  // set the skin region to the window
  SetWindowRgn(m_hWnd, m_rgnSkin, true);

  // subclass the window procedure
  m_OldWndProc = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)SkinWndProc);

  // store a pointer to our class instance inside the window procedure.
  if (!SetProp(m_hWnd, "skin", (void*)this))
  {
    // if we fail to do so, we just can't activate the skin.
    UnHook();
    return false;
  }

  // update flag
  m_bHooked = ( m_OldWndProc ? true : false );

  // force window repainting
  InvalidateRect(m_hWnd, NULL, TRUE);

  // successful return if we're hooked.
  return m_bHooked;
}

bool CSkin::Hook(CWnd *cwnd)
{   
    if (Hooked() )UnHookCWnd();

    m_cwnd = cwnd;
    m_cwnd->SetWindowRgn(m_rgnSkin, TRUE);
    m_bHooked = true;
    m_cwnd->InvalidateRect(NULL, TRUE);
    //m_cwnd->Invalidate(true);
    //m_cwnd->UpdateWindow();
    return m_bHooked;
}



// ----------------------------------------------------------------------------
// unhook the window
// ----------------------------------------------------------------------------
bool CSkin::UnHook()
{
  // just to be safe we'll check this
  WNDPROC OurWnd;

  // cannot unsubclass if there is no window subclassed
  // returns true anyways.
  if (!Hooked()) return true;

  // remove the skin region from the window
  SetWindowRgn(m_hWnd, NULL, true);

  // unsubclass the window procedure
  OurWnd = (WNDPROC)SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_OldWndProc);

  // remove the pointer to our class instance, but if we fail we don't care.
  RemoveProp(m_hWnd, "skin");

  // update flag - if we can't get our window procedure address again,
  // we failed to unhook the window.
  m_bHooked = ( OurWnd ? false : true );

  // force window repainting
  InvalidateRect(m_hWnd, NULL, TRUE);

  // successful return if we're unhooked.
  return !m_bHooked;
}

bool CSkin::UnHookCWnd()
{   
    if (!Hooked()) return true;
    m_cwnd->SetWindowRgn(NULL, TRUE);
    m_bHooked = false;
    m_cwnd->InvalidateRect(NULL, TRUE);
    return !m_bHooked;
}



// ----------------------------------------------------------------------------
// tell us if there is a window subclassed
// ----------------------------------------------------------------------------
bool CSkin::Hooked()
{
  return m_bHooked;
}



// ----------------------------------------------------------------------------
// return the skin bitmap width
// ----------------------------------------------------------------------------
int CSkin::Width()
{
  return m_iWidth;
}



// ----------------------------------------------------------------------------
// return the skin bitmap height
// ----------------------------------------------------------------------------
int CSkin::Height()
{
  return m_iHeight;
}



// ----------------------------------------------------------------------------
// return the skin device context
// ----------------------------------------------------------------------------
HDC CSkin::HDC()
{
  return m_dcSkin;
}

HRGN CSkin::HRGN()
{
    return m_rgnSkin;
}



// ----------------------------------------------------------------------------
// skin retrieval helper
// ----------------------------------------------------------------------------
bool CSkin::GetSkinData(int iSkinRegion, int iSkinBitmap)
{
  // get app instance handle
  HINSTANCE hInstance = GetModuleHandle(NULL);

  // -------------------------------------------------
  // retrieve the skin bitmap from resource.
  // -------------------------------------------------

  m_hBmp = LoadBitmap(hInstance, MAKEINTRESOURCE(iSkinBitmap));
  if (!m_hBmp) return false;

  // get skin info
  BITMAP bmp;
  GetObject(m_hBmp, sizeof(bmp), &bmp);

  // get skin dimensions
  m_iWidth = bmp.bmWidth;
  m_iHeight = bmp.bmHeight;


  // -------------------------------------------------
  // then, we retrieve the skin region from resource.
  // -------------------------------------------------

  // ask resource for our skin.
  HRSRC hrSkin = FindResource(hInstance, MAKEINTRESOURCE(iSkinRegion),"BINARY");
  if (!hrSkin) return false;

  // this is standard "BINARY" retrieval.
  LPRGNDATA pSkinData = (LPRGNDATA)LoadResource(hInstance, hrSkin);
  if (!pSkinData) return false;

  // create the region using the binary data.
  m_rgnSkin = ExtCreateRegion(NULL, SizeofResource(NULL,hrSkin), pSkinData);

  // free the allocated resource
  FreeResource(pSkinData);

  // check if we have the skin at hand.
  if (!m_rgnSkin) return false;


  // -------------------------------------------------
  // well, things are looking good...
  // as a quick providence, just create and keep
  // a device context for our later blittings.
  // -------------------------------------------------

  // create a context compatible with the user desktop
  m_dcSkin = CreateCompatibleDC(0);
  if (!m_dcSkin) return false;

  // select our bitmap
  m_hOldBmp = (HBITMAP)SelectObject(m_dcSkin, m_hBmp);


  // -------------------------------------------------
  // done
  // -------------------------------------------------
  return true;
}



// ------------------------------------------------------------------------
// Default skin window procedure.
// Here the class will handle WM_PAINT and WM_LBUTTONDOWN, originally sent
// to the application window, but now subclassed. Any other messages will
// just pass through the procedure and reach the original app procedure.
// ------------------------------------------------------------------------
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
  // we will need a pointer to the associated class instance
  // (it was stored in the window before, remember?)
  CSkin *pSkin = (CSkin*)GetProp(hWnd, "skin");

  // to handle WM_PAINT
    PAINTSTRUCT ps;

  // if we fail to get our class instance, we can't handle anything.
  if (!pSkin) return DefWindowProc(hWnd,uMessage,wParam,lParam);

  switch(uMessage)
  {

    case WM_PAINT:
    {
      // ---------------------------------------------------------
      // here we just need to blit our skin
      // directly to the device context
      // passed by the painting message.
      // ---------------------------------------------------------
      BeginPaint(hWnd,&ps);

      // blit the skin
      BitBlt(ps.hdc,0,0,pSkin->Width(),pSkin->Height(),pSkin->HDC(),0,0,SRCCOPY);

      EndPaint(hWnd,&ps);
      break;
    }

    case WM_LBUTTONDOWN:
    {
      // ---------------------------------------------------------
      // this is a common trick for easy dragging of the window.
      // this message fools windows telling that the user is
      // actually dragging the application caption bar.
      // ---------------------------------------------------------
      SendMessage(hWnd, WM_NCLBUTTONDOWN, HTCAPTION,NULL);
      break;
    }

  }

  // ---------------------------------------------------------
  // call the default window procedure to keep things going.
  // ---------------------------------------------------------
  return CallWindowProc(pSkin->m_OldWndProc, hWnd, uMessage, wParam, lParam);
}


// toggle skin on/off for Cwnd
bool CSkin::EnableCWnd(bool bEnable)
{
    // TODO: Add your implementation code here.
    if (!Hooked()) return false;

    // toggle
    m_bEnabled = bEnable;

    m_cwnd->InvalidateRect(NULL, TRUE);
    //m_cwnd->Invalidate(TRUE);
    //m_cwnd->UpdateWindow();
    return true;
}

Here are my other files, if you think they will be relevant:

Hello.h

#pragma once
class CMyApp : public CWinApp
{
public:
    virtual BOOL InitInstance();
};
class CMainWindow : public CFrameWnd
{
public:
    CMainWindow();
protected:
    afx_msg void OnPaint();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
    // This is the height of the bitmap
    int iHeight;
    int iWidth;
    int BitmapId;
    // This sets the height variable of the bitmap
    void SetHeight(int height);
    // This sets the width variable of the bitmap
    void SetWidth(int width);
    // This is sets the bitmap id  for the image
    void SetBitmapId(int bitmapid);
};

Hello.cpp

#include <afxwin.h>
#include "Hello.h"
#include "SkinClass/skin.h"
#include "resource.h"
CMyApp myApp;
/////////////////////////////////////////////////////////////////////////
// CMyApp member functions
BOOL CMyApp::InitInstance()
{
    m_pMainWnd = new CMainWindow;
    
    CSkin ii(ID_bitmap, ID_REGION);
    CMainWindow* cmw = dynamic_cast<CMainWindow*>(m_pMainWnd);
    cmw->SetHeight(ii.Height());
    cmw->SetWidth(ii.Width());
    cmw->SetBitmapId(ID_bitmap);
    ii.Hook(m_pMainWnd);
    ii.Enable(true);
    m_pMainWnd->ShowWindow(m_nCmdShow);
    m_pMainWnd->UpdateWindow();
    return TRUE;
}
/////////////////////////////////////////////////////////////////////////
// CMainWindow message map and member functions
BEGIN_MESSAGE_MAP(CMainWindow, CFrameWnd)
    ON_WM_PAINT()
    ON_WM_KEYDOWN()
END_MESSAGE_MAP()
CMainWindow::CMainWindow()
{
    Create(NULL, _T("The Hello Application"));
}
void CMainWindow::OnPaint()
{
    //CPaintDC dc(this);
    //CRect rect;
    //GetClientRect(&rect);
    //dc.DrawText(_T("Hello, MFC"), -1, &rect,
    //  DT_SINGLELINE | DT_CENTER | DT_VCENTER);

    CPaintDC dc(this); // device context for painting
                       // TODO: Add your message handler code here
                       // Do not call CFrameWnd::OnPaint() for painting messages
    CDC memDC;
    memDC.CreateCompatibleDC(&dc);

    CBitmap bm;
    bm.LoadBitmap(BitmapId);

    memDC.SelectObject(bm);
    dc.BitBlt(0, 0, iWidth, iHeight, &memDC, 0, 0, SRCCOPY);

}

void CMainWindow::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: Add your message handler code here and/or call default
    // if (nChar == VK_ESCAPE) {
    //  MessageBox("we have entered here");
    //  //std::cout << "some stuff in here";
    //}

    if (nChar == VK_ESCAPE) {
        PostQuitMessage(0);
       }

    CFrameWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}


// This sets the height variable of the bitmap
void CMainWindow::SetHeight(int height)
{     

    // TODO: Add your implementation code here.

    iHeight = height;
}


// This sets the width variable of the bitmap
void CMainWindow::SetWidth(int width)
{     
    // TODO: Add your implementation code here.
    iWidth = width;
}


// This is sets the bitmap id  for the image
void CMainWindow::SetBitmapId(int bitmapid)
{
    // TODO: Add your implementation code here.
    BitmapId = bitmapid;
}

The error message I am getting is:

Error   C2065   'SkinWndProc': undeclared identifier    mfcexample2 C:\Users\samsu_kag5dbu\source\repos\mfcexample2\mfcexample2\SkinClass\skin.cpp  134 

Upvotes: 1

Views: 162

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 596592

Per https://en.cppreference.com/w/cpp/language/friend :

A name first declared in a friend declaration within a class or class template X becomes a member of the innermost enclosing namespace of X, but is not visible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at namespace scope is provided - see namespaces for details.

You are declaring SkinWndProc() for the first time in skin.h as a friend of CSkin, and then both declaring and defining the actual SkinWndProc() function only in skin.cpp after the definition of CSkin::Hook() tries to use it. As such, SkinWndProc() has not actually been declared yet in the namespace that owns CSkin, so it is not visible to Hook(), which is why you are getting an "undeclared identifier" error.

To fix this, you can either:

  • in skin.cpp, add a forward declaration for SkinWndProc() above the definition of CSkin::Hook(), eg:
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);

bool CSkin::Hook(HWND hWnd)
{
    // can now use SkinWndProc as needed...
}
 
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    ...
}
  • in skin.cpp, move the entire definition of SkinWndProc() above the definition of CSkin::Hook(), eg:
LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    ...
}

bool CSkin::Hook(HWND hWnd)
{
    // can now use SkinWndProc as needed...
}
  • in skin.h and skin.cpp, change SkinWndProc() to be a static class method instead of a standalone friend function, eg:
class CSkin
{
    static LRESULT CALLBACK SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
    ...
};
bool CSkin::Hook(HWND hWnd)
{
    // can use CSkin::SkinWndProc as needed...
}

LRESULT CALLBACK CSkin::SkinWndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    ...
}

Upvotes: 2

Related Questions