Direct Show Video capture performance - c++

I originally started out looking for an example of how I can use FFMPEG in c++ builder to create an application to record from usb capture device and playback video because of apparrent poor performance
I tried Mitov components, Datastead, FFMPEGVCL and winsoft camera which use directshow but their capture performance seemed poor.
I need to capture 1920x1080 at up to 60fps into a compressed format and play this back later at both normal speed and slow speed.
What I found was that DirectShow itself has a number of limitations which can be improved by adding things like FFMPEG, but ultimately PC hardware, in particular the HDD and processor limit capture capability.
1920x1080 60fps is basically the upper end for DirectShow so you need to have best performing hardware in order to achieve this sort of performance #Spektre kindly gave me examples of DirectShow using the API direct which were good for comparison with the purchased components.
Using this and comparing to the components I found that MITOV has a major issue with regards the larger video sizes and frame rates. Using this 1920x108 30fps and 60fps can be previewed but they have a massive delay between video feed and preview (5 or 6 seconds). The other components performed similar to the API direct method with only minor variations in performance. None were able to capture and record 1920x108 60fps with any sort of compression filter without large frame drops and very jerky preview.

I do not think your problem is in video capture component itself nor FFMPEG. The main problem is that Directshow and VFW API to obtain image from camera is relatively slow. The speed can be improved by setting proper format of the image like:
smaller resolution
different color encoding (RGB 24bpp is not a good idea)
using camera JPEG format (not all cameras support it)
Without JPEG output format I never got pass 15 fps even on small resolutions. Also my experience shows that DirectShow is slightly slower then VFW (at least for my cameras). However not all cameras provide a VFW driver :( anymore.
Also be sure to ensure USB bandwidth by using proper version of USB port and do not diminish its bandwidth with other devices on the same HUB !!!
This is what I use for camera capture (VFW I coded years ago) in Borland/Embarcadero BDS 2006 C++:
VideoCaptureVFW.h:
//---------------------------------------------------------------------------
//--- VFW Video Capture ver: 2.0 --------------------------------------------
//---------------------------------------------------------------------------
#ifndef _VideoCaptureVFW_h
#define _VideoCaptureVFW_h
//---------------------------------------------------------------------------
#include <vfw.h>
#include <jpeg.hpp>
#include <Clipbrd.hpp>
//---------------------------------------------------------------------------
const int _vfw_callbach_onframe=1; // bit mask for each callback
//---------------------------------------------------------------------------
#ifndef _TDirectMemoryStream
#define _TDirectMemoryStream
class TDirectMemoryStream:TMemoryStream // just for accessing protected SetPointer
{
public:
void SetMemory(BYTE *ptr,DWORD siz) { SetPointer(ptr,siz); Position=0; };
};
#endif
//---------------------------------------------------------------------------
#ifndef _avgfps
#define _avgfps
class avgfps
{
public:
int N,N2;
int frame,frame0;
double fps,t0,t1,dt,Ts;
avgfps()
{
N=40; N2=N<<1;
fps=0.0; t0=0.0; frame=0; frame0=0;
LARGE_INTEGER i;
QueryPerformanceFrequency(&i); Ts=1.0/double(i.QuadPart);
}
~avgfps() {}
void update()
{
double t;
LARGE_INTEGER i;
QueryPerformanceCounter(&i); t=double(i.QuadPart)*Ts; dt=t-t0;
if (frame<=0)
{
t0=t; t1=t;
dt=0.0;
frame=0;
frame0=0;
}
if (dt>1e-6) fps=double(frame0)/dt; else fps=0.0;
frame++; frame0++;
if (frame0==N ) t1=t;
if (frame0==N2) { t0=t1; t1=t; frame0=N; }
}
};
#endif
//---------------------------------------------------------------------------
class VideoCaptureVFW
{
private:
HWND hcap,hown; // video capture window
public:
int ins_ix,ins_use; // instance index and usage for callbacks class reference
CAPDRIVERCAPS driver_cp; // driver capabilities
CAPTUREPARMS capture; // capture setup
CAPSTATUS state;
BITMAPINFO format;
// on frame callback
avgfps fps; // average fps
TMemoryStream *mem; // just for loading jpg from memory without copy
Graphics::TBitmap *bmp; // grabbed frame
VideoCaptureVFW();
~VideoCaptureVFW();
void ins_rst();
void ins_inc();
void ins_dec();
void set_owner(HWND _hown);
AnsiString get_video_drivers();
void set_video_driver(int ix);
void dlg_source() { if(driver_cp.fHasDlgVideoSource) capDlgVideoSource(hcap); }
void dlg_format() { if(driver_cp.fHasDlgVideoFormat) capDlgVideoFormat(hcap); get_state(); get_format(); }
void dlg_display(){ if(driver_cp.fHasDlgVideoDisplay)capDlgVideoDisplay(hcap);}
void dlg_compress() { capDlgVideoCompression(hcap); }
void get_capabil(){ capDriverGetCaps (hcap,&driver_cp,sizeof(CAPDRIVERCAPS)); }
void get_setup() { capCaptureGetSetup(hcap,&capture,sizeof(CAPTUREPARMS)); }
void set_setup() { capCaptureSetSetup(hcap,&capture,sizeof(CAPTUREPARMS)); }
void get_state() { capGetStatus (hcap,&state,sizeof(CAPSTATUS)); }
void get_format() { capGetVideoFormat (hcap,&format,sizeof(BITMAPINFO)); }
void preview_start(){ capPreview(hcap,TRUE ); }
void preview_stop() { capPreview(hcap,FALSE); }
void grab_start() { set_callback_on_frame(); capGrabFrameNoStop(hcap); }
void grab_stop() { res_callback_on_frame(); }
void copy_to_clipboard() { capEditCopy(hcap); }
void set_callback_on_frame();
void res_callback_on_frame();
LRESULT _on_frame(HWND hwnd,LPVIDEOHDR hdr);
void (*on_frame)(VideoCaptureVFW &cap);
};
//---------------------------------------------------------------------------
// on frame
const int _VideoCaptureVFW_ins=32;
void* VideoCaptureVFW_ins[_VideoCaptureVFW_ins]=
{
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
};
int VideoCaptureVFW_ins_get()
{
for (int i=0;i<_VideoCaptureVFW_ins;i++)
if (VideoCaptureVFW_ins[i]==NULL) return i;
return -1;
}
LRESULT PASCAL VideoCaptureVFW00_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 0]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW01_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 1]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW02_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 2]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW03_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 3]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW04_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 4]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW05_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 5]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW06_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 6]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW07_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 7]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW08_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 8]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW09_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[ 9]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW10_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[10]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW11_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[11]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW12_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[12]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW13_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[13]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW14_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[14]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW15_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[15]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW16_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[16]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW17_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[17]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW18_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[18]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW19_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[19]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW20_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[20]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW21_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[21]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW22_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[22]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW23_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[23]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW24_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[24]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW25_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[25]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW26_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[26]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW27_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[27]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW28_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[28]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW29_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[29]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW30_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[30]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL VideoCaptureVFW31_on_frame(HWND hwnd,LPVIDEOHDR hdr) { return ((VideoCaptureVFW*)(VideoCaptureVFW_ins[31]))->_on_frame(hwnd,hdr); }
LRESULT PASCAL(*VideoCaptureVFW_on_frame[_VideoCaptureVFW_ins])(HWND hwnd,LPVIDEOHDR hdr)=
{
VideoCaptureVFW00_on_frame,
VideoCaptureVFW01_on_frame,
VideoCaptureVFW02_on_frame,
VideoCaptureVFW03_on_frame,
VideoCaptureVFW04_on_frame,
VideoCaptureVFW05_on_frame,
VideoCaptureVFW06_on_frame,
VideoCaptureVFW07_on_frame,
VideoCaptureVFW08_on_frame,
VideoCaptureVFW09_on_frame,
VideoCaptureVFW10_on_frame,
VideoCaptureVFW11_on_frame,
VideoCaptureVFW12_on_frame,
VideoCaptureVFW13_on_frame,
VideoCaptureVFW14_on_frame,
VideoCaptureVFW15_on_frame,
VideoCaptureVFW16_on_frame,
VideoCaptureVFW17_on_frame,
VideoCaptureVFW18_on_frame,
VideoCaptureVFW19_on_frame,
VideoCaptureVFW20_on_frame,
VideoCaptureVFW21_on_frame,
VideoCaptureVFW22_on_frame,
VideoCaptureVFW23_on_frame,
VideoCaptureVFW24_on_frame,
VideoCaptureVFW25_on_frame,
VideoCaptureVFW26_on_frame,
VideoCaptureVFW27_on_frame,
VideoCaptureVFW28_on_frame,
VideoCaptureVFW29_on_frame,
VideoCaptureVFW30_on_frame,
VideoCaptureVFW31_on_frame,
};
//---------------------------------------------------------------------------
VideoCaptureVFW::VideoCaptureVFW()
{
hcap=NULL;
hown=NULL;
ins_ix=-1; ins_use=0;
on_frame=NULL;
mem=new TMemoryStream();
bmp=new Graphics::TBitmap;
}
//---------------------------------------------------------------------------
VideoCaptureVFW::~VideoCaptureVFW()
{
capDriverDisconnect(hcap);
res_callback_on_frame();
if (mem) delete mem;
if (bmp) delete bmp;
}
//---------------------------------------------------------------------------
void VideoCaptureVFW::set_owner(HWND _hown)
{
hown=_hown;
hcap=capCreateCaptureWindow("",WS_CHILD|WS_VISIBLE,0,0,1,1,hown,1);
}
//---------------------------------------------------------------------------
AnsiString VideoCaptureVFW::get_video_drivers()
{
const int _size=256;
char drv_name[_size];
char drv_ver[_size];
char dev_name[_size];
AnsiString s0,s1,list;
int i;
list="";
for (i=0;;i++)
{
if (!capGetDriverDescription(i,drv_name,_size,drv_ver,_size)) break;
s0=drv_name;
s1=drv_ver;
list+=s0+" "+s1+"\n";
}
return list;
}
//---------------------------------------------------------------------------
void VideoCaptureVFW::set_video_driver(int ix)
{
if (hcap==NULL) return;
capDriverConnect(hcap,ix);
capDriverGetCaps(hcap,&driver_cp,sizeof(CAPDRIVERCAPS));
capCaptureGetSetup(hcap,&capture,sizeof(CAPTUREPARMS));
// capture.dwRequestMicroSecPerFrame=10; // 1/fps [us]
capCaptureSetSetup(hcap,&capture,sizeof(CAPTUREPARMS));
capPreviewRate(hcap,1); // set preview [ms]
capPreviewScale(hcap,FALSE); // stretching off
//preview_start();
}
//---------------------------------------------------------------------------
void VideoCaptureVFW::set_callback_on_frame()
{
if (ins_ix<0) ins_ix=VideoCaptureVFW_ins_get();
if (ins_ix<0) return;
VideoCaptureVFW_ins[ins_ix]=this;
ins_use|=_vfw_callbach_onframe;
capSetCallbackOnFrame(hcap,(void*)(VideoCaptureVFW_on_frame[ins_ix]));
}
//---------------------------------------------------------------------------
void VideoCaptureVFW::res_callback_on_frame()
{
if (ins_ix<0) return;
if (int(ins_use&_vfw_callbach_onframe))
{
ins_use^=_vfw_callbach_onframe;
capSetCallbackOnFrame(hcap,NULL);
}
if (ins_use) return;
VideoCaptureVFW_ins[ins_ix]=NULL;
ins_ix=-1;
}
//---------------------------------------------------------------------------
LRESULT VideoCaptureVFW::_on_frame(HWND hwnd,LPVIDEOHDR hdr)
{
fps.update();
int e=0;
if (hdr->dwBytesUsed<16) return 0; // ignore too small images
((TDirectMemoryStream*)(mem))->SetMemory(hdr->lpData,hdr->dwBytesUsed);
if ((hdr->lpData[6]=='J') // JPEG signature
&&(hdr->lpData[7]=='F')
&&(hdr->lpData[8]=='I')
&&(hdr->lpData[9]=='F'))
{
e=1;
TJPEGImage *jpg=new TJPEGImage;
jpg->LoadFromStream(mem);
bmp->Assign(jpg);
delete jpg;
} else
if ((hdr->lpData[0]=='B') // BMP signature
&&(hdr->lpData[1]=='M'))
{
e=1;
bmp->LoadFromStream(mem);
}
else{ // others
e=1;
copy_to_clipboard();
try {
bmp->LoadFromClipboardFormat(CF_BITMAP,Clipboard()->GetAsHandle(CF_BITMAP),NULL);
}
catch(char *str)
{
e=0;
int hnd=FileCreate("unsuproted_format.dat");
FileWrite(hnd,hdr->lpData,hdr->dwBytesUsed);
FileClose(hnd);
}
}
if (e)
{
if (on_frame) on_frame(*this);
}
return 0;
}
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
I have a small test app with this source code:
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
// select API:
#define _capture_VFW
// #define _capture_DirectShow
//---------------------------------------------------------------------------
#ifdef _capture_VFW
#include "VideoCaptureVFW.h"
#endif
#ifdef _capture_DirectShow
#include "DirectX92\\VideoCaptureDirectShow.cpp"
#endif
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
Graphics::TBitmap *bmp=new Graphics::TBitmap;
int _callback=0;
int _frame=0;
int _update=false;
//---------------------------------------------------------------------------
#ifdef _capture_VFW
VideoCaptureVFW vfw;
void on_frame_VFW(VideoCaptureVFW &cap)
{
if (_callback) Form1->Canvas->Draw(0,26,cap.bmp);
else if (!_frame) { bmp->Assign(cap.bmp); _frame=1; }
}
#endif
//---------------------------------------------------------------------------
#ifdef _capture_DirectShow
VideoCaptureDirectShow dsh;
void on_frame_DirectShow(VideoCaptureDirectShow &cap)
{
if (_callback) Form1->Canvas->Draw(0,26,cap.bmp);
else if (!_frame) { bmp->Assign(cap.bmp); _frame=1; }
}
#endif
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
#ifdef _capture_VFW
pan_VFW->Visible=true;
vfw.set_owner(this->Handle);
cb_driver->Items->Clear();
cb_driver->Items->Text=vfw.get_video_drivers();
cb_driver->ItemIndex=0;
vfw.on_frame=on_frame_VFW;
vfw.set_video_driver(cb_driver->ItemIndex);
vfw.grab_start();
#endif
#ifdef _capture_DirectShow
pan_DirectShow->Visible=true;
cb_device->Items->Clear();
cb_device->Items->Text=dsh.get_devices();
dsh.on_frame=on_frame_DirectShow;
_update=1;
cb_device->ItemIndex=0;
_update=0;
cb_device->OnChange(this);
/*
dsh.Select(0);
dsh.Start();
dsh.Stop();
*/
#endif
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
#ifdef _capture_VFW
vfw.grab_stop();
#endif
#ifdef _capture_DirectShow
dsh.Stop();
#endif
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
if ((!_callback)&&(_frame)) { Canvas->Draw(0,26,bmp); _frame=0; }
#ifdef _capture_VFW
Caption=AnsiString().sprintf("frame: %2i fps: %2.1lf",vfw.fps.frame,vfw.fps.fps);
#endif
#ifdef _capture_DirectShow
Caption=AnsiString().sprintf("frame: %2i fps: %2.1lf",dsh.fps.frame,dsh.fps.fps);
#endif
}
//---------------------------------------------------------------------------
//--- VFW -------------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TForm1::bt_dialog_sourceClick(TObject *Sender)
{
#ifdef _capture_VFW
vfw.dlg_source();
#endif
}
void __fastcall TForm1::bt_dialog_formatClick(TObject *Sender)
{
#ifdef _capture_VFW
vfw.dlg_format();
#endif
}
void __fastcall TForm1::bt_dialog_displayClick(TObject *Sender)
{
#ifdef _capture_VFW
vfw.dlg_display();
#endif
}
void __fastcall TForm1::cb_driverChange(TObject *Sender)
{
#ifdef _capture_VFW
vfw.set_video_driver(cb_driver->ItemIndex);
vfw.grab_start();
#endif
}
//---------------------------------------------------------------------------
//--- DirectShow ------------------------------------------------------------
//---------------------------------------------------------------------------
void __fastcall TForm1::cb_deviceChange(TObject *Sender)
{
#ifdef _capture_DirectShow
if (_update) return;
_update=1;
dsh.Select(cb_device->ItemIndex);
cb_format->Items->Clear();
cb_format->Items->Text=dsh.get_formats();
if (cb_format->Items->Count)
cb_format->ItemIndex=0;
_update=0;
cb_format->OnChange(this);
#endif
}
//---------------------------------------------------------------------------
void __fastcall TForm1::cb_formatChange(TObject *Sender)
{
#ifdef _capture_DirectShow
if (_update) return;
_update=1;
dsh.set_format(cb_format->Text);
_update=0;
#endif
}
//---------------------------------------------------------------------------
Its single form app with few buttons and combo box lists for dialog boxes and configuration (you can mimic them or ignore them). Did not share the DirectShow as its too big for 30K limit and slower anyway (but ist just a header + lib file no 3th party components). I have these VCL components on the form:
TTimer *Timer1; // 10ms info text update
TPanel *pan_VFW; // just to hold the components for VFW
TSpeedButton *bt_dialog_source; // these 3 buttons configure VFW ...
TSpeedButton *bt_dialog_format;
TSpeedButton *bt_dialog_display;
TComboBox *cb_driver; // this selects VFW device
TPanel *pan_DirectShow; // just to hold DirectShow components
TComboBox *cb_device; // this selects DirectShow device
TComboBox *cb_format; // this selects DirectShow format
I encapsulated the VFW and DirectShow stuff into configuration #define so you can ignore the DirectShow stuff completely.
Now when you use this you can play with resolution and formats to compare the fps to your DirectShow component grabber.
As you can see I do not use any 3th party components to grab image data from camera the VideoCaptureVFW.h is the only stuff you need.
[Edit1]
Here is the link to the Demo containing both VFW and DirectShow examples (source and win32 binaries) in Embarcadero BDS2006 C++.
[Edit2] your unsupported format
The file size is exactly 1920*1080*3 Bytes which hints raw 24bpp RGB feed. When I tried to visualize it it works (and yes Y is flipped) see tyhe code (no components on form this time):
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
Graphics::TBitmap *bmp=NULL;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
int xs=1920,ys=1080,x,y,a,hnd,siz;
BYTE *p,*dat=NULL;
// load frame
hnd=FileOpen("maybe_RGB24.dat",fmOpenRead);
siz=FileSeek(hnd,0,2); dat=new BYTE[siz];
FileSeek(hnd,0,0);
FileRead(hnd,dat,siz);
FileClose(hnd);
// convert RGB24 to 32bpp bitmap
bmp=new Graphics::TBitmap;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf32bit;
bmp->SetSize(xs,ys);
for (a=0,y=ys-1;y>=0;y--) // flip y
{
p=(BYTE*)bmp->ScanLine[y];
for (x=0;x<xs;x++)
{
p[0]=dat[a]; a++;
p[1]=dat[a]; a++;
p[2]=dat[a]; a++;
p[3]=0;
p+=4;
}
}
delete dat;
// resize form
ClientWidth=xs;
ClientHeight=ys;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
if (bmp) delete bmp; bmp=NULL;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
Canvas->Draw(0,0,bmp);
}
//---------------------------------------------------------------------------
And resulting screen:
beware I renamed the filename to maybe_RGB24.dat. You could decode this file-format by the frame size alone but its info should be somewhere like in the
AM_MEDIA_TYPE* mt;
structure but do not ask me where exactly I have no clue as I did code this a long time ago and do not use DirectX since (as all my attempts in the past reveals its inferiority to other apis does not matter if its sound or gfx or grabbing...)

Related

Have Direct2D, DXGI SwapChain, HWND transparency, all working at once in C++, without XAML or similar

Context
I have 2 big projects I'm working on: a game engine, and a file explorer replacement. My focus is Windows only. I know they're 2 huge things I'll likely never complete, I just enjoy coding during free time.
Across my many projects I started building up a collection of utilities and essentially made my own library. I'm actually enjoying writing generalized stuff.
That's the tale of how I started trying to make a generic-ish windowing framework for Windows.
A big requirement that libs like glfw and SFML are missing is modularity; my windows are made up of multiple modules, which avoids having a ginormous procedure function that handles everything, and lets me extend the window with future specific modules (I was working on a vulkan module, now i'm working on a direct2d module). Hence all the code you'll be seeing in my snippets related to HWNDs and window procecdures only handles drawing-related messages, which I hope will make navigation easier. Don't worry about the absence of all the WM_CREATE and other kind of messages unrelated to drawing in these snippets.
For the game engine all I need is "updated" direct2d drawing, so with the newer DeviceContext that goes through DXGISwapChain etcc instead of the older RenderTarget.
For the file explorer I "need" (well, ok, want) window transparency, which with the older RenderTarget "just works" (you pass it the ALPHA_PREMULTIPLIED flag and everything is nice).
I can also get a DeviceContext from the older RenderTarget as answered by Paint.NET's creator here; that gives me access to newer functions even though the context was cast from the older RenderTarget. Important: this approach gives me no control over which device that context is associated with.
For the file explorer I also wish to share resources across multiple windows, and the older RenderTarget doesn't seem to allow for that. Resources will be bound to the individual RenderTarget, while with DeviceContexts the resources should be bound to a Device and can be accessed by all the contexts associated with the same Device (at least for my understanding so far).
Q: correct me if I'm wrong about last sentence^
Where the pain begun
Here comes the dreaded issue: creating a swapchain with CreateSwapChainForHWND does not support any ALPHA_* option besides ALPHA_NONE. So if I try to draw on a window the DeviceContext and retain control of which Device creates them I need to find another way.
In short:
One window + DeviceContext newer functions + transparency -> use RenderTarget and cast to DeviceContext
Resources shared across multiple windows + DeviceContext newer functions -> use DXGISwapChain + DeviceContext
Resources shared across multiple windows + DeviceContext newer functions + transparency -> ¯\_(ツ)_/¯ + ???
The debug layer told me CreateSwapChainForComposition does support ALPHA_PREMULTIPLIED, but at that point a whole separate topic of Windows APIs seems to open up, and I've already a huge chunk of DirectX to learn, so I'd rather avoid further months-long-derailings.
But it seems there's no avoiding this issue.
Anyway, I created my CreateSwapChainForComposition swapchain and... That function takes no HWND parameter! So I assume I did create a swapchain somewhere, and direct2d calls are drawing on that swapchain, but the content of that swapchain never reaches the actual window, unless I do some magic with the compoisition APIs perhaps?
Then I started giving a look at those composition APIs and... pretty much everything assumes you're working on an higher level framework, mostly XAML, which I'm not.
The closest thing I found in the docs is this
However that example retreives a direct2d device context from the composition APIs through BeginDraw... there's no swapchain in sight.
And even worse this brings me back to the same issue I had with RenderTarget: If I've no guarantee the DeviceContexts are created from the same Device, so I cannot share resources across multiple windows.
Plus having to learn a new whole topic just to have 2 things working together that I already managed to make work individually really discouraged me.
My code, notes
Notes
d2d::*, dxgi::*, d3d::* classes are just wrappers around MS's ComPtr where I gave various classes a throwing constructor that wraps the "return HRESULT, reference actual return object" functions from MS.
the procedure_result return value of my window modules procedures exist because multiple modules can work on the same message, the behaviour is as follows:
procedure_result.next() this module didn't return any value, let next module evaluate the message.
procedure_result.stop(value) this module returned value, no other module will evaluate the message.
procedure_result.next(value) this module returned value, let next module evaluate the message.
After all modules have processed the message, or after one module called stop(), return the value returned by the last module that evaluated the current message, or pass to the default window procedure if no module returned any value.
I use whitesmiths indentation. I know it's not really widespread but please don't kill me for it :)
If you want to try my code, the project expects you have my CPP_Utils repository in the same directory as follows:
root
> CPP_Utilities_MS //the project we're discussing
> .git
> ...
> CPP_Utilities
> .git
> ...
The CPP_Utilities repository is entirely header-only because screw compile times, so no need to link .libs or add .dlls
For record-keeping reasons I'll leave links to the last commit to the date this question was asked:
CPP_Utilities_MS
CPP_Utilities
...now to the actual directly meaningful code:
How I make my window transparent
inline bool make_glass_CompositionAttribute(HWND hwnd)
{
if (HMODULE hUser = GetModuleHandleA("user32.dll"))
{
//Windows >= 10
pfnSetWindowCompositionAttribute SetWindowCompositionAttribute = (pfnSetWindowCompositionAttribute)GetProcAddress(hUser, "SetWindowCompositionAttribute");
if (SetWindowCompositionAttribute)
{
ACCENT_POLICY accent = {ACCENT_ENABLE_BLURBEHIND, 0, 0, 0};
WINDOWCOMPOSITIONATTRIBDATA data;
data.Attrib = WCA_ACCENT_POLICY;
data.pvData = &accent;
data.cbData = sizeof(accent);
SetWindowCompositionAttribute(hwnd, &data);
return true;
}
}
return false;
}
How I create my RenderTarget and get a DeviceContext out of it
used for (1): One window + DeviceContext newer functions + transparency
Create d2d factory
namespace d2d
{
struct factory : details::com_ptr<ID2D1Factory6>
{
using com_ptr::com_ptr;
factory() : com_ptr{[]
{
D2D1_FACTORY_OPTIONS options
{
.debugLevel{details::enable_debug_layer ? D2D1_DEBUG_LEVEL_INFORMATION : D2D1_DEBUG_LEVEL_NONE}
};
self_t ret{nullptr};
details::throw_if_failed(D2D1CreateFactory
(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
options,
ret.address_of()
));
return ret;
}()} {}
};
}
Create RenderTarget from d2d factory and HWND
namespace d2d
{
struct hwnd_render_target : details::com_ptr<ID2D1HwndRenderTarget>
{
using com_ptr::com_ptr;
hwnd_render_target(const factory& factory, const HWND& hwnd) : com_ptr{[&factory, &hwnd]
{
self_t ret{nullptr};
D2D1_RENDER_TARGET_PROPERTIES properties
{
.type{D2D1_RENDER_TARGET_TYPE_DEFAULT},
.pixelFormat
{
.format{DXGI_FORMAT_UNKNOWN},
.alphaMode{D2D1_ALPHA_MODE_PREMULTIPLIED}
}
};
details::throw_if_failed(factory->CreateHwndRenderTarget(properties, D2D1::HwndRenderTargetProperties(hwnd), ret.address_of()));
return ret;
}()} {}
};
}
Create DeviceContext from RenderTarget
namespace d2d
{
class device_context : public details::com_ptr<ID2D1DeviceContext5>
{
public:
using com_ptr::com_ptr;
device_context(const d2d::hwnd_render_target& hwnd_rt) : com_ptr{create(hwnd_rt )} {}
private:
inline static self_t create(const d2d::hwnd_render_target& hwnd_rt)
{
return hwnd_rt.as<interface_type>();
}
};
}
Window module that makes use of all that
//window modules file/namespace
class render_target : public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = render_target;
const d2d::factory& d2d_factory;
std::function<on_draw_signature> on_render;
//adds the WS_EX_NOREDIRECTIONBITMAP flag to the flags used to create the base window
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
render_target(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
d2d_hwnd_rt{create_info.d2d_factory, get_base().get_handle()},
d2d_device_context{d2d_hwnd_rt}
{
}
std::function<on_draw_signature> on_render;
void present() noexcept
{
}
private:
d2d::hwnd_render_target d2d_hwnd_rt;
d2d::device_context d2d_device_context;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
on_render(get_base(), d2d_device_context);
ValidateRect(get_base().get_handle(), NULL);
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
d2d_hwnd_rt->Resize({size.x, size.y});
}
};
How I create my DeviceContext from a DXGISwapChain
used for (2): Resources shared across multiple windows + DeviceContext newer functions, does NOT support transparency
Create d3d device
namespace d3d
{
class device : public details::com_ptr<ID3D11Device2>
{
public:
using com_ptr::com_ptr;
device() : com_ptr{create()} {}
private:
inline static self_t create()
{
details::com_ptr<ID3D11Device> base_device;
UINT creation_flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
if constexpr (details::enable_debug_layer)
{
// If the project is in a debug build, enable debugging via SDK Layers with this flag.
creation_flags |= D3D11_CREATE_DEVICE_DEBUG;
}
D3D_FEATURE_LEVEL feature_level_created;
std::array<D3D_DRIVER_TYPE, 3> attempts{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP, D3D_DRIVER_TYPE_SOFTWARE};
HRESULT last{S_FALSE};
for (const auto& attempt : attempts)
{
last = D3D11CreateDevice
(
nullptr, // specify null to use the default adapter
attempt,
0,
creation_flags, // optionally set debug and Direct2D compatibility flags
nullptr, // use the lastest feature level
0, // use the lastest feature level
D3D11_SDK_VERSION,
base_device.address_of(), // returns the Direct3D device created
&feature_level_created, // returns feature level of device created
nullptr
);
if (details::succeeded(last)) { break; }
}
details::throw_if_failed(last);
return base_device.as<self_t>();
}
};
}
Get dxgi device from d3d device
namespace dxgi
{
struct device : details::com_ptr<IDXGIDevice3>
{
using com_ptr::com_ptr;
device(const d3d::device& d3d_device) : com_ptr{[&d3d_device]
{
return d3d_device.as<interface_type>();
}()} {}
};
}
Create dxgi swapchain from dxgi device for HWND
namespace dxgi
{
class swap_chain : public details::com_ptr<IDXGISwapChain1>
{
public:
swap_chain(const dxgi::device& dxgi_device, HWND hwnd) : com_ptr{create(dxgi_device, hwnd)} {}
void resize(utils::math::vec2u size)
{
HRESULT hresult{get()->ResizeBuffers(2, size.x, size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
void present()
{
HRESULT hresult{get()->Present(1, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
private:
inline static self_t create(const dxgi::device& dxgi_device, HWND hwnd)
{
RECT client_rect{0, 0, 0, 0};
GetClientRect(hwnd, &client_rect);
utils::math::rect<long> rectl{.ll{client_rect.left}, .up{client_rect.top}, .rr{client_rect.right}, .dw{client_rect.bottom}};
details::com_ptr<IDXGIAdapter> dxgi_adapter;
details::throw_if_failed(dxgi_device->GetAdapter(dxgi_adapter.address_of()));
details::com_ptr<IDXGIFactory2> dxgi_factory;
details::throw_if_failed(dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.address_of())));
DXGI_SWAP_CHAIN_DESC1 desc
{
.Width {static_cast<UINT>(rectl.w())},
.Height {static_cast<UINT>(rectl.h())},
.Format {DXGI_FORMAT_B8G8R8A8_UNORM},
.Stereo {false},
.SampleDesc
{
.Count {1},
.Quality{0}
},
.BufferUsage {DXGI_USAGE_RENDER_TARGET_OUTPUT},
.BufferCount {2},
.Scaling {DXGI_SCALING_NONE},
.SwapEffect {DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL},
.AlphaMode {DXGI_ALPHA_MODE_IGNORE},
//.AlphaMode {DXGI_ALPHA_MODE_PREMULTIPLIED}, // I wish it was this easy!
.Flags {0},
};
DXGI_SWAP_CHAIN_FULLSCREEN_DESC desc_fullscreen
{
.RefreshRate{.Numerator{1}, .Denominator{0}},
.Scaling {DXGI_MODE_SCALING_CENTERED},
};
self_t ret{nullptr};
details::throw_if_failed(dxgi_factory->CreateSwapChainForHwnd(dxgi_device.get(), hwnd, &desc, &desc_fullscreen, nullptr, ret.address_of()));
dxgi_device->SetMaximumFrameLatency(1);
return ret;
}
}
}
Create d2d device from dxgi device, and create d2d device context from d2d device
namespace d2d
{
class device : public details::com_ptr<ID2D1Device5>
{
public:
using com_ptr::com_ptr;
device(const d2d::factory& d2d_factory, const dxgi::device& dxgi_device) : com_ptr{create(d2d_factory, dxgi_device)} {}
dxgi::device get_dxgi_device() const noexcept
{
details::com_ptr<IDXGIDevice> ret{nullptr};
details::throw_if_failed(get()->GetDxgiDevice(ret.address_of()));
return ret.as<dxgi::device>();
}
private:
inline static self_t create(const d2d::factory& d2d_factory, const dxgi::device& dxgi_device)
{
self_t ret{nullptr};
details::throw_if_failed(d2d_factory->CreateDevice(dxgi_device.get(), ret.address_of()));
return ret;
}
};
class device_context : public details::com_ptr<ID2D1DeviceContext5>
{
public:
using com_ptr::com_ptr;
device_context(const d2d::device& d2d_device) : com_ptr{create(d2d_device)} {}
private:
inline static self_t create(const d2d::device& d2d_device)
{
self_t ret{nullptr};
details::throw_if_failed(d2d_device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, ret.address_of()));
return ret;
}
};
}
Create d2d bitmap from d2d device context and dxgi swapchain
namespace d2d
{
class bitmap : public details::com_ptr<ID2D1Bitmap1>
{
public:
using com_ptr::com_ptr;
bitmap(const d2d::device_context& d2d_device_context, const dxgi::swap_chain& dxgi_swapchain) : com_ptr{create(d2d_device_context, dxgi_swapchain)} {}
private:
inline static self_t create(const d2d::device_context& d2d_device_context, const dxgi::swap_chain& dxgi_swapchain)
{
//details::com_ptr<ID3D11Texture2D> d3d_texture_back_buffer;
//details::throw_if_failed(dxgi_swapchain->GetBuffer(0, IID_PPV_ARGS(d3d_texture_back_buffer.address_of())));
details::com_ptr<IDXGISurface2> dxgi_back_buffer;
details::throw_if_failed(dxgi_swapchain->GetBuffer(0, IID_PPV_ARGS(dxgi_back_buffer.address_of())));
D2D1_BITMAP_PROPERTIES1 properties
{
.pixelFormat{DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED},
.dpiX{1},//TODO dpi stuff
.dpiY{1},
.bitmapOptions{D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW},
};
self_t ret{nullptr};
details::throw_if_failed(d2d_device_context->CreateBitmapFromDxgiSurface(dxgi_back_buffer.get(), &properties, ret.address_of()));
return ret;
}
};
}
Window module that makes use of all that
//window modules file/namespace
class swap_chain: public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = swap_chain;
const d2d ::device& d2d_device;
std ::function<on_draw_signature> on_render;
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
swap_chain(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
d2d_device_context{create_info.d2d_device},
dxgi_swapchain{create_info.d2d_device.get_dxgi_device(), get_base().get_handle()},
d2d_bitmap_target{d2d_device_context, dxgi_swapchain}
{
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
std::function<on_draw_signature> on_render;
private:
d2d::device_context d2d_device_context;
dxgi::swap_chain dxgi_swapchain;
d2d::bitmap d2d_bitmap_target;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
on_render(get_base(), d2d_device_context);
dxgi_swapchain.present();
ValidateRect(get_base().get_handle(), NULL);
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
//Release things that reference the swapchain before resizing
d2d_device_context->SetTarget(nullptr);
d2d_bitmap_target.reset();
dxgi_swapchain.resize(size);
//re-get back buffer
d2d_bitmap_target = d2d::bitmap{d2d_device_context, dxgi_swapchain};
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
};
Attempt at creating a DeviceContext from a DXGISwapChain WITH transparency and somhow composition (questions here)
would be used for (3): Resources shared across multiple windows + DeviceContext newer functions + transparency
This just doesn't work. I followed the guide linked before, but as mentioned it bypasses/does not consider the manual creation of a swapchain.
I didn't even get it working since what the example is doing doesn't seem to lead me in the right direction. The following code is relevant more for my commented questions than for actual code
Create dxgi swapchain from dxgi device using the CreateSwapChainForComposition method
namespace dxgi
{
class swap_chain : public details::com_ptr<IDXGISwapChain1>
{
public:
//that nullptr_t is just a temporary flag for testing purposes, to distinguish from the other constructor used in the previous snippets
swap_chain(const dxgi::device& dxgi_device, HWND hwnd, nullptr_t) : com_ptr{create_composition(dxgi_device, hwnd)} {}
void resize(utils::math::vec2u size)
{
HRESULT hresult{get()->ResizeBuffers(2, size.x, size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
void present()
{
HRESULT hresult{get()->Present(1, 0)};
if (hresult == DXGI_ERROR_DEVICE_REMOVED || hresult == DXGI_ERROR_DEVICE_RESET)
{
throw std::runtime_error("Device removed or reset");
}
else { details::throw_if_failed(hresult); }
}
private:
inline static self_t create_composition(const dxgi::device& dxgi_device, HWND hwnd)
{
RECT client_rect{0, 0, 0, 0};
GetClientRect(hwnd, &client_rect);
utils::math::rect<long> rectl{.ll{client_rect.left}, .up{client_rect.top}, .rr{client_rect.right}, .dw{client_rect.bottom}};
details::com_ptr<IDXGIAdapter> dxgi_adapter;
details::throw_if_failed(dxgi_device->GetAdapter(dxgi_adapter.address_of()));
details::com_ptr<IDXGIFactory2> dxgi_factory;
details::throw_if_failed(dxgi_adapter->GetParent(IID_PPV_ARGS(dxgi_factory.address_of())));
DXGI_SWAP_CHAIN_DESC1 desc
{
.Width {static_cast<UINT>(rectl.w())},
.Height {static_cast<UINT>(rectl.h())},
.Format {DXGI_FORMAT_B8G8R8A8_UNORM},
.Stereo {false},
.SampleDesc
{
.Count {1},
.Quality{0}
},
.BufferUsage {DXGI_USAGE_RENDER_TARGET_OUTPUT},
.BufferCount {2},
.Scaling {DXGI_SCALING_STRETCH},
.SwapEffect {DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL},
.AlphaMode {DXGI_ALPHA_MODE_PREMULTIPLIED},
.Flags {DXGI_SWAP_CHAIN_FLAG_FOREGROUND_LAYER},
};
self_t ret{nullptr};
details::throw_if_failed(dxgi_factory->CreateSwapChainForComposition(dxgi_device.get(), &desc, nullptr, ret.address_of()));
dxgi_device->SetMaximumFrameLatency(1);
return ret;
}
};
}
Composition wrappers, pretty straightforward
namespace composition
{
struct device : details::com_ptr<IDCompositionDevice>
{
using com_ptr::com_ptr;
device(const dxgi::device& dxgi_device) : com_ptr{[&dxgi_device]
{
self_t ret{nullptr};
details::throw_if_failed(DCompositionCreateDevice(dxgi_device.get(), __uuidof(interface_type), reinterpret_cast<void**>(ret.address_of())));
return ret;
}()} {}
};
struct target : details::com_ptr<IDCompositionTarget>
{
using com_ptr::com_ptr;
target(const composition::device& composition_device, HWND hwnd) : com_ptr{[&composition_device, &hwnd]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateTargetForHwnd(hwnd, TRUE, ret.address_of()));
return ret;
}()} {}
};
struct visual : details::com_ptr<IDCompositionVisual>
{
using com_ptr::com_ptr;
visual(const composition::device& composition_device) : com_ptr{[&composition_device]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateVisual(ret.address_of()));
return ret;
}()} {}
};
struct surface : details::com_ptr<IDCompositionSurface>
{
using com_ptr::com_ptr;
surface(const composition::device& composition_device, HWND hwnd) : com_ptr{[&composition_device, &hwnd]
{
self_t ret{nullptr};
details::throw_if_failed(composition_device->CreateSurface(128, 128, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_ALPHA_MODE_PREMULTIPLIED, ret.address_of()));
return ret;
}()} {}
};
}
Final window for the composition swapchain, questions and doubts are in the comments of this snippet
//window modules file/namespace
class composition_swap_chain : public utils::MS::window::module
{
public:
using on_draw_signature = void(utils::MS::window::base&, const d2d::device_context&);
struct create_info
{
using module_type = composition_swap_chain;
const d2d ::device& d2d_device;
std ::function<on_draw_signature> on_render;
inline void adjust_base_create_info(utils::MS::window::base::create_info& base_create_info) const noexcept
{
base_create_info.style_ex |= WS_EX_NOREDIRECTIONBITMAP;
}
};
composition_swap_chain(utils::MS::window::base& base, create_info create_info) :
module{base},
on_render{create_info.on_render},
// from here...
d2d_device_context{create_info.d2d_device},
dxgi_swapchain{create_info.d2d_device.get_dxgi_device(), get_base().get_handle(), nullptr/*flag to create swapchain with the "ForComposition" function*/},
d2d_bitmap_target{d2d_device_context, dxgi_swapchain}
// ...to here, I get the swapchain I wish to use
// from here...
composition_device{create_info.d2d_device.get_dxgi_device()},
composition_surface{composition_device, get_base().get_handle()},
// ...to here I started initializing some composition stuff
// that I can't seem to be able to "connect" to the previously
// created swapchain in any way
{
// I wish to target the bitmap from the swapchain backbuffer,
// but again this isn't connected in any way to the composition stuff
d2d_device_context->SetTarget(d2d_bitmap_target.get());
}
std::function<on_draw_signature> on_render;
private:
d2d::device_context d2d_device_context;
composition::device composition_device;
composition::surface composition_surface;
dxgi::swap_chain dxgi_swapchain;
d2d::bitmap d2d_bitmap_target;
virtual utils::MS::window::procedure_result procedure(UINT msg, WPARAM wparam, LPARAM lparam) override
{
switch (msg)
{
case WM_SIZE:
on_resize({LOWORD(lparam), HIWORD(lparam)});
return utils::MS::window::procedure_result::next(0);
case WM_DISPLAYCHANGE:
//InvalidateRect(get_handle(), NULL, FALSE);
break;
case WM_ERASEBKGND: return utils::MS::window::procedure_result::stop(1);
case WM_PAINT:
if (on_render)
{
// If BeginDraw gives me a d2dDevice context...
POINT offset{}; //?
auto comp_surf_interop{composition_surface.as<ABI::Windows::UI::Composition::ICompositionDrawingSurfaceInterop>()};
auto HR{comp_surf_interop->BeginDraw(nullptr, __uuidof(ID2D1DeviceContext5), reinterpret_cast<void**>(d2d_device_context.address_of()), &offset)};
if (SUCCEEDED(HR))
{
on_render(get_base(), d2d_device_context);
// ...what am I supposed to do with the swapchain?
//dxgi_swapchain.present();
details::throw_if_failed(composition_surface->EndDraw());
ValidateRect(get_base().get_handle(), NULL);
}
else
{
std::cout << details::hr_to_string(HR) << std::endl;
}
return utils::MS::window::procedure_result::next(0);
}
break;
}
return utils::MS::window::procedure_result::next();
}
void on_resize(utils::math::vec2u size)
{
// So do I resize a swapchain or not?
}
};

How to solve intellisense identifier undefined in MFC uArt?

I'm having trouble in fixing the errors in this MFC uArt code. Three intellisense is undefined even though I included some header files to the program.
This is the error code when I build and run the program.
ERROR
PROGRAM
// MFCApplication2Dlg.cpp : implementation file
//
#include "stdafx.h"
#include "MFCApplication2.h"
#include "MFCApplication2Dlg.h"
#include "afxdialogex.h"
#include "afxwin.h"
#include "CyAPI.h"
#include "Periph.h"
#define UART_H
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
bool IsConnect = false;
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// Dialog Data
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CMFCApplication2Dlg dialog
CMFCApplication2Dlg::CMFCApplication2Dlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CMFCApplication2Dlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CMFCApplication2Dlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CMFCApplication2Dlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CMFCApplication2Dlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &CMFCApplication2Dlg::OnBnClickedButton2)
ON_BN_CLICKED(IDC_BUTTON3, &CMFCApplication2Dlg::OnBnClickedButton3)
END_MESSAGE_MAP()
// CMFCApplication2Dlg message handlers
BOOL CMFCApplication2Dlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
void CMFCApplication2Dlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CMFCApplication2Dlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CMFCApplication2Dlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CMFCApplication2Dlg::OnBnClickedButton1()
{
USBDevice->Open(0);
if (USBDevice->IsOpen() != TRUE)
{
AfxMessageBox(_T("Failed to Open Device"));
}
else
{
IsConnect = true;
}
}
void CMFCApplication2Dlg::OnBnClickedButton3()
{
USBDevice->Close();
IsConnect = false;
}
void CMFCApplication2Dlg::OnBnClickedButton2()
{
char tmpUart[60];
long OutPacketSize;
OutPacketSize = sizeof(sUart);
LPTSTR pBuffer;
CString sBuffer;
int i;
if (IsConnect == false)
{
AfxMessageBox(_T("USB Connect Fail"));
return;
}
CEdit *OutValue = (CEdit*)GetDlgItem(IDC_OUT_VALUE);
pBuffer = sBuffer.GetBuffer(60);
OutValue->GetWindowText(pBuffer, 60);
strcpy(tmpUart, pBuffer);
OutPacketSize = strlen(tmpUart);
for (i = 0; i<OutPacketSize; i++) sUart[i] = tmpUart[i];
sUart[OutPacketSize + 1] = 0;
OutPacketSize = OutPacketSize + 1;
//Perform the BULK OUT
if (USBDevice->BulkOutEndPt)
{
USBDevice->BulkOutEndPt->XferData(sUart, OutPacketSize);
}
}
Does anyone have any idea what header files I've been missing to include with? cause when i declare char sUart[60] in the code there's an error and also on the strcpy method there seems to be an error in the pBuffer LPTSTR. Please help.
This is usually a sign that you're doing a Unicode build, where LPTSTR expands out to wchar_t *, and you're trying to mix it with non-Unicode strings (in your case a char array.
Instead define tmpUart as:
TCHAR tmpUart[60];
and use _tcscpy to copy the string. This way your code will compile in Unicode and non-Unicode builds.

Mouse and Keyboard system Global Hook

I want to set a global system hook for keyboard and mouse in C++ using a DLL. My program is working but not exactly as I need. For example the program will not hook mouse events on other windows or popup menu, or modal dialogs. I want to hook the mouse and keyboard events anywhere in other windows, programs, ect.., all system mouse and keyboard events. What is wrong with my code what to change to get it to work as I need, I cannot find myself what is wrong, I'm doing as is described in documentation, I need help.
I'm coding in C++ Builder using VCL
DLL:
#include <vcl.h>
#include <windows.h>
#include "main.h"
#pragma hdrstop
#pragma argsused
typedef struct _HOOKSTRUCT
{
int nType;
HOOKPROC hkprc;
HHOOK hhook;
bool bInstalled;
} HOOKSTRUCT;
static HOOKSTRUCT hook[2];
HINSTANCE hDLL;
int nThreadCode;
bool bInit;
TForm1 *form;
extern "C" __stdcall __declspec(dllexport) LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
extern "C" __stdcall __declspec(dllexport) LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam);
//---------------------------------------------------------------------------
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
hDLL = hinst;
bInit = false;
return 1;
}
//---------------------------------------------------------------------------
extern "C" __stdcall __declspec(dllexport) void Init()
{
hook[0].nType = WH_KEYBOARD;
hook[0].hkprc = (HOOKPROC)KeyboardProc;
hook[0].bInstalled = false;
hook[1].nType = WH_MOUSE;
hook[1].hkprc = (HOOKPROC)MouseProc;
hook[1].bInstalled = false;
bInit = true;
}
//---------------------------------------------------------------------------
extern "C" __stdcall __declspec(dllexport) bool SetHook(int nHook)
{
if(!bInit)
{
Init();
}
hook[nHook].hhook = NULL;
hook[nHook].hhook = SetWindowsHookEx(hook[nHook].nType,
(HOOKPROC)hook[nHook].hkprc, hDLL, 0);
if(hook[nHook].hhook != NULL)
{
MessageBox(NULL, "Setup hook successful", "Information", MB_OK);
}
return true;
}
//---------------------------------------------------------------------------
extern "C" __stdcall __declspec(dllexport) void UnsetHook(int nHook)
{
UnhookWindowsHookEx(hook[nHook].hhook);
}
//---------------------------------------------------------------------------
extern "C" __stdcall __declspec(dllexport) LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(((DWORD)lParam&0x40000000) && (HC_ACTION==nCode))
{
if(form != NULL)
{
form->Memo1->Lines->Add("Key Pressed");
}
}
return CallNextHookEx(hook[0].hhook, nCode, wParam, lParam);
}
//---------------------------------------------------------------------------
extern "C" __stdcall __declspec(dllexport) LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char str[128];
PMOUSEHOOKSTRUCT Info = (PMOUSEHOOKSTRUCT)lParam;
wsprintf(str, "The mouse message at X: %d,Y: %d", Info->pt.x, Info->pt.y);
form->Memo2->Lines->Add(str);
return CallNextHookEx(hook[1].hhook, nCode, wParam, lParam);
}
//---------------------------------------------------------------------------
extern "C" __stdcall __declspec(dllexport) void SetControl(TForm1 *Object, int nApp)
{
form = Object;
nThreadCode = nApp;
}
Program:
#include <vcl.h>
#pragma hdrstop
#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
HINSTANCE hInst;
typedef bool (*install)(int);
typedef void (*uninstall)(int);
typedef void (*passself)(TForm1 *, int);
install InstallHook;
uninstall UninstallHook;
passself PassThis;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
N2->Enabled = true;
N3->Enabled = true;
N4->Enabled = false;
N5->Enabled = false;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
Memo1->Clear();
Memo2->Clear();
hInst = LoadLibrary("ch49dll.dll");
if(hInst == NULL)
{
ShowMessage("Load DLL error " + AnsiString(GetLastError()));
return;
}
InstallHook = (install)GetProcAddress(hInst, "SetHook");
if(InstallHook == NULL)
{
ShowMessage("Get SetHook ProcAddress Error");
return;
}
UninstallHook = (uninstall)GetProcAddress(hInst, "UnsetHook");
if(UninstallHook == NULL)
{
ShowMessage("Get UnsetHook ProcAddress Error");
return;
}
PassThis = (passself)GetProcAddress(hInst, "SetControl");
if(PassThis == NULL)
{
ShowMessage("Get SetControl ProcAddress Error");
return;
}
PassThis(this, 0);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
FreeLibrary(hInst);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::N2Click(TObject *Sender)
{
InstallHook(0);
N2->Enabled = false;
N4->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::N3Click(TObject *Sender)
{
InstallHook(1);
N3->Enabled = false;
N5->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::N4Click(TObject *Sender)
{
UninstallHook(0);
N4->Enabled = false;
N2->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::N5Click(TObject *Sender)
{
UninstallHook(1);
N5->Enabled = false;
N3->Enabled = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::N7Click(TObject *Sender)
{
Close();
}
//---------------------------------------------------------------------------
Program header:
#ifndef mainH
#define mainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <Menus.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TMemo *Memo1;
TMemo *Memo2;
TMainMenu *MainMenu1;
TMenuItem *N1;
TMenuItem *N2;
TMenuItem *N3;
TMenuItem *N4;
TMenuItem *N5;
TLabel *Label1;
TLabel *Label2;
TMenuItem *N6;
TMenuItem *N7;
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormDestroy(TObject *Sender);
void __fastcall N2Click(TObject *Sender);
void __fastcall N3Click(TObject *Sender);
void __fastcall N4Click(TObject *Sender);
void __fastcall N5Click(TObject *Sender);
void __fastcall N7Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
So the program work and can hook external events, but for some dialog windows , my program will not hook the mouse events. for example when I right click the icon from taskbar Notification Area , a popup will display and if I enter mouse cursor in the popup window area then my program will not record those mouse move events. Other case is when I click About program menu in other programs, or About menu, then a window will display and if I move my cursor inside the area of that window then my program will not hook events of mouse and keyboard, why ? how to make it to hook anywhere for any window
Two very obvious problems:
The DLL will be injected into all other processes. You cannot expect to call VCL methods from a DLL that has been hooked into another process. You'll have to find another way to report diagnostics.
Because you are using a global hook, you will need to deal with 32/64 bit issues. You would need to produce both 32 and 64 bit versions of the DLL, and set hooks to them from 32 and 64 bit processes.
If you can use a low-level hook you should consider taking that option. The advantage is that there is no injection with a low-level hook. You don't need to create a DLL at all.
A few comments, hopefully will solve your problems: Use the "Low Level" hooks (WH_MOUSE_LL, WH_KEYBOARD_LL) because they are guaranteed to be only global. It feels to me that your application got only a "local" (thread) hook.
I have bad experiences with CBuilder created DLLs when using together with hooks. ... ie: the same problem exactly as you describe. I'd recommend to recompile your DLL with a Microsoft compiler (and provide a .DEF file to use).
There are chances that you will need to place the DLL in a place identified as system directory.
You don't need to place the SetHook in the DLL, that can be done from the "main" application too.
On Win 7 you can only hook messages from programs with the same or lower privileges. I.e. if your own program runs with standard privileges it will never see any Mouse or Keyboard messages targeted at a process with e.g. admin privileges. – iamjoosy

C++ using SetWindowsHookEx only works with strange vcl code added to it. in BCB2009

I have a strange situation using SetWindowsHookEx
I have a bcb 2009 project with a form and a Memo on it.
in the create we load the Dll and attach the function handler's to both sides.
The idea is that when the key board is hit a message appear in the memo box and when a mouse event happen an other text appears in the memo box.
The strange this is that when I cleaned the code from debug information it stops working. That means the hook got triggered one time and than it was over.
In the debug I was using some VCL TStringList to log key stokes data to disk. Playing with that code I finally detected that by adding
[code]
TList* lList = new TList();
delete lList;
To every one of the hook functions (keyboard, mouse) the code is working again.
What is wrong in my code that I have to do this?
This is the first time in 15 years I make a dll. so it can be something real basic in creating a dll or exporting the functions.
Every suggestion is welcome.
regards
JVDN
Some new additional information:
[solved]My target is win XP embedded. my application creates a error that closes the explorer by windows. And the hook is not working global in xp but only local. But it is working on my develop platform win 7 x64 global typing and mousing in notepad result in messages in the application.
[solution] Modified the WH_KEYBOARD to WH_KEYBOARD_LL and the mouse from WH_MOUSE to WH_MOUSE_LL solves the receiving key and mouse on Windows XP embedded.
Both the dll and the application have no runtime lib or packages.
DLL Code
[code]
//---------------------------------------------------------------------------
#include <vcl.h>
#include <windows.h>
#pragma hdrstop
//---------------------------------------------------------------------------
// Important note about DLL memory management when your DLL uses the
// static version of the RunTime Library:
//
// If your DLL exports any functions that pass String objects (or structs/
// classes containing nested Strings) as parameter or function results,
// you will need to add the library MEMMGR.LIB to both the DLL project and
// any other projects that use the DLL. You will also need to use MEMMGR.LIB
// if any other projects which use the DLL will be performing new or delete
// operations on any non-TObject-derived classes which are exported from the
// DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling
// EXE's to use the BORLNDMM.DLL as their memory manager. In these cases,
// the file BORLNDMM.DLL should be deployed along with your DLL.
//
// To avoid using BORLNDMM.DLL, pass string information using "char *" or
// ShortString parameters.
//
// If your DLL uses the dynamic version of the RTL, you do not need to
// explicitly add MEMMGR.LIB as this will be done implicitly for you
//---------------------------------------------------------------------------
typedef void __stdcall ( *typFn)(WPARAM,LPARAM);
static typFn gGUIProcessingKeyboard = NULL;
static HHOOK gGUIProcessingKeyboardHook = NULL;
static typFn gGUIProcessingMouse = NULL;;
static HHOOK gGUIProcessingMouseHook = NULL;
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
return 1;
}
//---------------------------------------------------------------------------
extern "C"
{ __declspec(dllexport) void SetGUIProcessingKeyboard(typFn aHandle);
__declspec(dllexport) void ReleaseGUIProcessingKeyboard(typFn aHandle);
__declspec(dllexport) LRESULT CALLBACK wireKeyboardProc(int code, WPARAM wParam,LPARAM lParam);
__declspec(dllexport) void SetKeyboardHookHandle(HHOOK aHook );
__declspec(dllexport) void SetGUIProcessingMouse(typFn aHandle);
__declspec(dllexport) void ReleaseGUIProcessingMouse(typFn aHandle);
__declspec(dllexport) void SetMouseHookHandle(HHOOK aHook );
__declspec(dllexport) LRESULT CALLBACK wireMouseProc(int code, WPARAM wParam,LPARAM lParam);
/**
* Set the keyboard loop back handle
*/
void SetGUIProcessingKeyboard(typFn aHandle)
{
if (aHandle != gGUIProcessingKeyboard)
{
gGUIProcessingKeyboard = aHandle;
}
}
/**
* Release the keyboard loop back handle
*/
void ReleaseGUIProcessingKeyboard(typFn aHandle)
{
gGUIProcessingKeyboard = NULL;
}
/**
* Set the handle used for tapping the Keyboard
*/
void SetKeyboardHookHandle(HHOOK aHook )
{
gGUIProcessingKeyboardHook = aHook;
}
/**
* Tapping the keyboard from the other applications
*/
LRESULT CALLBACK wireKeyboardProc(int code, WPARAM wParam,LPARAM lParam)
{
TList* lList = new TList();
delete lList;
if (code < 0) {
return CallNextHookEx(gGUIProcessingKeyboardHook, code, wParam, lParam);
}
if (NULL != gGUIProcessingKeyboard)
{
gGUIProcessingKeyboard( wParam,lParam);
}
return CallNextHookEx(gGUIProcessingKeyboardHook, code, wParam, lParam);
}
/**
* Set the mouse loop back handle
*/
void SetGUIProcessingMouse(typFn aHandle)
{
if (aHandle != gGUIProcessingMouse)
{
gGUIProcessingMouse = aHandle;
}
}
/**
* Release the mouse loop back handle
*/
void ReleaseGUIProcessingMouse(typFn aHandle)
{
gGUIProcessingMouse = NULL;
}
/**
* Set the handle used for tapping the mouse
*/
void SetMouseHookHandle(HHOOK aHook )
{
gGUIProcessingMouseHook = aHook;
}
/**
* Tapping the mouse from the other applications
*/
LRESULT CALLBACK wireMouseProc(int code, WPARAM wParam,LPARAM lParam)
{
TList* lList = new TList();
delete lList;
// if (gGUIProcessingMouseHook != NULL)
// {
if (code < 0) {
return CallNextHookEx(gGUIProcessingMouseHook, code, wParam, lParam);
}
if (NULL != gGUIProcessingMouse)
{
gGUIProcessingMouse( wParam,lParam);
}
return CallNextHookEx(gGUIProcessingMouseHook, code, wParam, lParam);
// }
// return 0;
}
} // extern C
And here is the application.
[code cpp]
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MonitoringToolMain.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
typedef void __stdcall ( __closure *typFn)(WPARAM,LPARAM);
TForm1 *Form1;
HHOOK TForm1::mHook = NULL;
typedef void __stdcall (*typSetHook)(HHOOK);
typedef LRESULT CALLBACK ( *typHookFunc)(int,WPARAM,LPARAM);
static HHOOK gMyGUIProcessingKeyboardHook = NULL;
static HHOOK gMyGUIProcessingMouseHook = NULL;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
void __stdcall TForm1::MyKeyboardProc(
WPARAM wParam,
LPARAM lParam
)
{
if (Form1 != NULL)
{
Form1->Memo1->Lines->Add(L"GotA keyboard");
}
}
void __stdcall TForm1::MyMouseProc(
WPARAM wParam,
LPARAM lParam
)
{
if (Form1 != NULL)
{
Form1->Memo1->Lines->Add(L"Pip pip");
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
if (NULL == mHinst)
{
mHinst = LoadLibrary("KeyboardMouseHookDLL.dll");
}
if (mHinst)
{
typedef void (*Install)(typFn);
// the keyboard
typSetHook SetHook = (typSetHook) GetProcAddress( mHinst, "_SetKeyboardHookHandle" );
typHookFunc wireKeyboardProc = (typHookFunc)GetProcAddress(mHinst, "wireKeyboardProc" );
Install install = (Install) GetProcAddress(mHinst, "_SetGUIProcessingKeyboard");
if (install)
{
install(&MyKeyboardProc);
}
if ((NULL != wireKeyboardProc) &&
(NULL != SetHook) )
{
gMyGUIProcessingKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)wireKeyboardProc,mHinst,NULL);
SetHook(gMyGUIProcessingKeyboardHook);
}
// The mouse
typSetHook SetMouseHook = (typSetHook) GetProcAddress(mHinst, "_SetMouseHookHandle");
typHookFunc wireMouseProc = (typHookFunc)GetProcAddress(mHinst, "wireMouseProc");
Install installMouse = (Install) GetProcAddress(mHinst, "_SetGUIProcessingMouse");
if (installMouse)
{
installMouse(&MyMouseProc);
}
if ((NULL != wireMouseProc) &&
(NULL != SetMouseHook) )
{
gMyGUIProcessingMouseHook = SetWindowsHookEx(WH_MOUSE,(HOOKPROC)wireMouseProc,mHinst,NULL);
SetMouseHook(gMyGUIProcessingMouseHook);
}
}
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
if (NULL == mHinst)
{
mHinst = LoadLibrary("KeyboardMouseHookDLL.dll");
}
if (mHinst)
{
if (NULL != gMyGUIProcessingKeyboardHook )
{
UnhookWindowsHookEx(gMyGUIProcessingKeyboardHook);
gMyGUIProcessingKeyboardHook = NULL;
}
typedef void (*Uninstall)(typFn);
Uninstall uninstall = (Uninstall) GetProcAddress(mHinst, "_ReleaseGUIProcessingKeyboard");
if (uninstall)
{
uninstall(&MyKeyboardProc);
}
if (NULL != gMyGUIProcessingMouseHook )
{
UnhookWindowsHookEx(gMyGUIProcessingMouseHook);
gMyGUIProcessingMouseHook = NULL;
}
Uninstall uninstallMouse = (Uninstall) GetProcAddress(mHinst, "_ReleaseGUIProcessingMouse");
if (uninstallMouse)
{
uninstallMouse(&MyMouseProc);
}
FreeLibrary(mHinst);
mHinst = NULL;
}
}
//---------------------------------------------------------------------------
And the form header
[code]
//---------------------------------------------------------------------------
#ifndef MonitoringToolMainH
#define MonitoringToolMainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TMemo *Memo1;
void __fastcall FormCreate(TObject *Sender);
void __fastcall FormDestroy(TObject *Sender);
private: // User declarations
int __stdcall lKeyBoard();
void __stdcall MyKeyboardProc( WPARAM wParam, LPARAM lParam );
void __stdcall MyMouseProc( WPARAM wParam, LPARAM lParam );
HINSTANCE mHinst;
static HHOOK mHook;
public: // User declarations
__fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
If you're installing a global system hook, the hook DLL will be injected in each running process. As each process has its own memory space, you'll need to define a shared data section to place variables like the hook handles, otherwise they will be different for each process.
#pragma data_seg(".SHARDAT")
static HHOOK gGUIProcessingKeyboardHook = NULL;
static HHOOK gGUIProcessingMouseHook = NULL;
#pragma data_seg()
Also don't register function pointers to the hook DLL, as you will ask other processes to call the registered functions in your application. It's better to register the HWND of your application and a window message.
Create a exported function in your DLL that sets the hook and stores HWND and custom message number, f.e.:
#pragma data_seg(".SHARDAT")
static HHOOK g_keybHook = NULL;
static HHOOK g_mouseHook = NULL;
HWND g_registeredWnd = NULL;
UINT g_registeredKeybMsg = 0;
UINT g_registeredMouseMsg = 0;
#pragma data_seg()
HINSTANCE g_hInstance = NULL;
BOOL InstallHook(HWND registeredWnd, UINT registeredKeybMsg, UINT registeredMouseMsg)
{
if (g_hHook != NULL) return FALSE; // Hook already installed
g_keybHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)KeybProc, g_hInstance, 0);
if (g_keybHook == NULL) return FALSE; // Failed to install hook
g_registeredWnd = registeredWnd;
g_registeredKeybMsg = registeredKeybMsg;
g_registeredMouseMsg = registeredMouseMsg;
return TRUE;
}
In the DllEntryPoint you save hinst in g_hInstance in case reason == DLL_PROCESS_ATTACH:
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
if ((reason == DLL_PROCESS_ATTACH) && (g_hInstance == NULL))
g_hInstance = hinst;
return 1;
}
In your application you register 2 window messages with the RegisterWindowMessage function and pass these values to the InstallHook function from the hook DLL. Then your application needs to handle those messages in its message loop.
registeredKeybMsg = RegisterWindowMessage("MyOwnKeybHookMsg");
registeredMouseMsg = RegisterWindowMessage("MyOwnMouseHookMsg");
InstallHook(hwnd, registeredKeybMsg, registeredMouseMsg);

Linker issue with Visual Studio 2010 Pro

I'm having a very frustrating issue setting up a DirectX 9 (irrelevant to the issue.. I think) framework in VS 2010. Here is my DirectX framework:
#ifndef _DX9_H_
#define _DX9_H_
// window includes
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// required for ush typedef and window properties to setup backbuffer
#include "sys_params.h"
// directx9 includes
#define DIRECTINPUT_VERSION 0x0800
#include <d3dx9.h>
#include <dinput.h>
#include <DxErr.h>
#include <vector>
#include <iterator>
// directx9 libraries
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")
#pragma comment(lib, "dinput8.lib")
#pragma comment(lib, "dxguid.lib")
#pragma comment(lib, "dxerr.lib")
namespace nsdx9
{
using nssysprms::ush;
#define CheckHR(hr) CheckForDXError(__FILE__, __LINE__, hr)
class DX9
{
public:
DX9(HINSTANCE& inst, int cmdShow, const std::string& title, ush wndwidth, ush wndheight, short wndx, short wndy);
~DX9();
// windows message processor
UINT ProcessMessages();
// --- DIRECTX GAME FUNCTIONS --- //
// input functions
void InputUpdate();
BYTE KeyHeld(ush key);
bool KeyPressed(ush key);
// sprite functions
const LPD3DXSPRITE& GetSpriteInterface();
void SpriteBeginDraw(DWORD flags = D3DXSPRITE_ALPHABLEND);
void SpriteEndDraw();
// font functions
void MakeFont(int height, int width, UINT weight = FW_DONTCARE, LPCSTR face = "Calibri", bool italic = false);
const LPD3DXFONT& GetFontAtIndex(ush index);
const std::vector<LPD3DXFONT>& GetFontVector();
// --- END DIRECTX GAME FUNCTIONS --- //
private:
// --- WINDOW FUNCTIONS/VARIABLES --- //
WNDCLASSEX _wc;
HWND _hwnd;
MSG _msg;
HINSTANCE _inst;
std::string _title;
static LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wprm, LPARAM lprm);
// --- END WINDOW FUNCTIONS/VARIABLES --- //
// --- DIRECTX FUNCTIONS/VARIABLES --- //
// D3D interfaces
LPDIRECT3D9 _d3d;
LPDIRECT3DDEVICE9 _d3ddev;
D3DPRESENT_PARAMETERS _d3dpp;
// directinput interfaces
LPDIRECTINPUT8 _dInput;
LPDIRECTINPUTDEVICE8 _diMouse;
LPDIRECTINPUTDEVICE8 _diKeyboard;
DIMOUSESTATE _mouseState;
BYTE _keys[256];
bool _keyStates[256];
bool _keyboardStateChanged;
void AcquireInputDevice(const LPDIRECTINPUTDEVICE8& dev);
// sprite interfaces
LPD3DXSPRITE _spriteBatch;
// font vector
std::vector<LPD3DXFONT> _fonts;
// hresult checker, for debugging only
void CheckForDXError(const char *file, int line, HRESULT hr);
// --- END DIRECTX FUNCTIONS/VARIABLES --- //
};
/*=================================================*/
/*--------------DIRECTX CONSTRUCTOR----------------*/
/*=================================================*/
DX9::DX9(HINSTANCE& inst, int cmdShow, const std::string& title, ush wndwidth, ush wndheight, short wndx, short wndy)
{
/*=================================================*/
/*--------------WINDOW INITIALIZATION--------------*/
/*=================================================*/
_title = title;
_inst = inst;
// init window class struct
_wc.cbClsExtra = NULL;
_wc.cbSize = sizeof(WNDCLASSEX);
_wc.cbWndExtra = NULL;
_wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
_wc.hCursor = LoadCursor(NULL, IDC_ARROW);
_wc.hIcon = NULL;
_wc.hIconSm = NULL;
_wc.hInstance = inst;
_wc.lpfnWndProc = (WNDPROC)WinProc;
_wc.lpszClassName = title.c_str();
_wc.lpszMenuName = NULL;
_wc.style = CS_HREDRAW | CS_VREDRAW;
RegisterClassEx(&_wc);
// create handle to the window
_hwnd = CreateWindow(title.c_str(),
title.c_str(),
WS_OVERLAPPEDWINDOW,
wndx,
wndy,
wndwidth,
wndheight,
NULL,
NULL,
inst,
NULL);
// required to make the window show up
ShowWindow(_hwnd, cmdShow);
UpdateWindow(_hwnd);
/*=================================================*/
/*--------------END WINDOW INITIALIZATION----------*/
/*=================================================*/
/*=================================================*/
/*--------------DIRECTX INITIALIZATION-------------*/
/*=================================================*/
// --- INITIALIZE DIRECTX9 VARIABLES --- //
SecureZeroMemory(&_d3dpp, sizeof(_d3dpp));
SecureZeroMemory(&_mouseState, sizeof(_mouseState));
SecureZeroMemory(&_keys, sizeof(_keys));
for (int i = 0; i < 256; i++)
{
_keyStates[i] = true;
}
_d3d = NULL;
_d3ddev = NULL;
_dInput = NULL;
_diMouse = NULL;
_diKeyboard = NULL;
_keyboardStateChanged = false;
_spriteBatch = NULL;
// --- END INITIALIZE DIRECTX9 VARIABLES --- //
// --- DIRECTX9 INITIALIZATION --- //
_d3d = Direct3DCreate9(D3D_SDK_VERSION);
if (!_d3d)
{
OutputDebugString("Error: Failed to create Direct3D.\n");
}
// set d3d present parameters
_d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
_d3dpp.BackBufferCount = 1;
_d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
_d3dpp.BackBufferHeight = nssysprms::WND_HEIGHT;
_d3dpp.BackBufferWidth = nssysprms::WND_WIDTH;
_d3dpp.EnableAutoDepthStencil = 1;
_d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
//_d3dpp.FullScreen_RefreshRateInHz
_d3dpp.hDeviceWindow = _hwnd;
//_d3dpp.MultiSampleQuality
//_d3dpp.MultiSampleType
_d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
_d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
_d3dpp.Windowed = nssysprms::isWindowed;
// create d3d device
CheckHR(_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, _hwnd, D3DCREATE_MIXED_VERTEXPROCESSING, &_d3dpp, &_d3ddev));
// --- END DIRECTX9 INITIALIZATION --- //
// --- INITIALIZE DIRECTINPUT --- //
CheckHR(DirectInput8Create(inst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&_dInput, NULL));
// create mouse and keyboard
CheckHR(_dInput->CreateDevice(GUID_SysKeyboard, &_diKeyboard, NULL));
CheckHR(_dInput->CreateDevice(GUID_SysMouse, &_diMouse, NULL));
// initialize keyboard
CheckHR(_diKeyboard->SetDataFormat(&c_dfDIKeyboard));
CheckHR(_diKeyboard->SetCooperativeLevel(_hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND));
AcquireInputDevice(_diKeyboard);
// initialize mouse
CheckHR(_diMouse->SetDataFormat(&c_dfDIMouse));
CheckHR(_diMouse->SetCooperativeLevel(_hwnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND));
AcquireInputDevice(_diMouse);
// create sprite object
CheckHR(D3DXCreateSprite(_d3ddev, &_spriteBatch));
// --- END INITIALIZE DIRECTINPUT --- //
/*=================================================*/
/*--------------END DIRECTX INITIALIZATION---------*/
/*=================================================*/
}
/*=================================================*/
/*--------------END DIRECTX CONSTRUCTOR------------*/
/*=================================================*/
/*=================================================*/
/*--------------DIRECTX DESTRUCTOR-----------------*/
/*=================================================*/
DX9::~DX9()
{
// set all stack variables to NULL
SecureZeroMemory(&_d3dpp, sizeof(_d3dpp));
SecureZeroMemory(&_mouseState, sizeof(_mouseState));
SecureZeroMemory(&_keys, sizeof(_keys));
SecureZeroMemory(&_keyStates, sizeof(_keyStates));
// free all the D3D interfaces from memory
if (!_fonts.empty())
{
for (std::vector<LPD3DXFONT>::iterator it = _fonts.begin(); it != _fonts.end(); it++)
{
// SOLVEPROBLEM figure out why this doesn't work
//*it->OnLostDevice();
}
_fonts.erase(_fonts.begin(), _fonts.end() - 1);
}
if (_spriteBatch != NULL)
{
_spriteBatch->Release();
_spriteBatch = NULL;
}
if (_diKeyboard != NULL)
{
_diKeyboard->Release();
_diKeyboard = NULL;
}
if (_diMouse != NULL)
{
_diMouse->Release();
_diMouse = NULL;
}
if (_d3ddev != NULL)
{
_d3ddev->Release();
_d3ddev = NULL;
}
if (_d3d != NULL)
{
_d3d->Release();
_d3d = NULL;
}
// free the window class from memory
UnregisterClass(_title.c_str(), _inst);
}
/*=================================================*/
/*--------------END DIRECTX DESTRUCTOR-------------*/
/*=================================================*/
/*=================================================*/
/*--------------HRESULT ERROR CHECK----------------*/
/*=================================================*/
void DX9::CheckForDXError(const char *file, int line, HRESULT hr)
{
if (SUCCEEDED(hr))
{
return;
}
// Get the direct X error and description
char desc[1024];
sprintf_s(desc,"(DX) %s - %s", DXGetErrorString(hr), DXGetErrorDescription(hr));
// Output the file and line number in the correct format + the above DX error
char buf[2048];
sprintf_s(buf,"%s(%d) : Error: %s\n", file, line, desc);
OutputDebugString(buf);
}
/*=================================================*/
/*--------------END HRESULT ERROR CHECK------------*/
/*=================================================*/
/*=================================================*/
/*--------------MESSAGE PROCESSOR------------------*/
/*=================================================*/
UINT DX9::ProcessMessages()
{
if (PeekMessage(&_msg, NULL, NULL, NULL, PM_REMOVE))
{
TranslateMessage(&_msg);
DispatchMessage(&_msg);
}
return _msg.message;
}
/*=================================================*/
/*--------------END MESSAGE PROCESSOR--------------*/
/*=================================================*/
/*=================================================*/
/*--------------MESSAGE HANDLER--------------------*/
/*=================================================*/
LRESULT CALLBACK DX9::WinProc(HWND hwnd, UINT msg, WPARAM wprm, LPARAM lprm)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hwnd, msg, wprm, lprm);
}
/*=================================================*/
/*--------------END MESSAGE HANDLER----------------*/
/*=================================================*/
/*=================================================*/
/*--------------DIRECTINPUT FUNCTIONS--------------*/
/*=================================================*/
// for init only, helper function to initially acquire the
// mouse and keyboard
void DX9::AcquireInputDevice(const LPDIRECTINPUTDEVICE8& dev)
{
// loop and attempt to acquire the device until success
while (FAILED(dev->Acquire()))
{
dev->Acquire();
}
}
// update the state of the mouse and keyboard
void DX9::InputUpdate()
{
_diKeyboard->GetDeviceState(sizeof(_keys), (LPVOID)_keys);
_diMouse->GetDeviceState(sizeof(_mouseState), (LPVOID)&_mouseState);
// after directinput has been updated, check to see if any keys are no,
// longer pressed, and reset the keystate array at that key's index if,
// this is the case
// keystates true = key is available for key press
if (_keyboardStateChanged)
{
for (int i = 0; i < 256; i++)
{
if (!KeyHeld(i))
{
_keyStates[i] = true;
}
}
}
_keyboardStateChanged = false;
}
// captures a key being held down
BYTE DX9::KeyHeld(ush key)
{
return _keys[key] & 0x80;
}
// captures a single key press
bool DX9::KeyPressed(ush key)
{
if (KeyHeld(key) && _keyStates[key])
{
_keyStates[key] = false;
return true;
}
else
{
if (!KeyHeld(key) && !_keyStates[key])
{
_keyboardStateChanged = true;
}
return false;
}
}
/*=================================================*/
/*--------------END DIRECTINPUT FUNCTIONS----------*/
/*=================================================*/
/*=================================================*/
/*--------------SPRITE FUNCTIONS-------------------*/
/*=================================================*/
// returns the sprite interface
const LPD3DXSPRITE& DX9::GetSpriteInterface()
{
return _spriteBatch;
}
// begin drawing with the sprite batch
void DX9::SpriteBeginDraw(DWORD flags)
{
_spriteBatch->Begin(flags);
}
// end sprite batch drawing
void DX9::SpriteEndDraw()
{
_spriteBatch->End();
}
/*=================================================*/
/*--------------END SPRITE FUNCTIONS---------------*/
/*=================================================*/
/*=================================================*/
/*--------------FONT FUNCTIONS---------------------*/
/*=================================================*/
// create a font
void DX9::MakeFont(int height, int width, UINT weight, LPCSTR face, bool italic)
{
LPD3DXFONT newfont;
CheckHR(D3DXCreateFont(_d3ddev,
height,
width,
weight,
0,
italic,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLEARTYPE_QUALITY,
DEFAULT_PITCH | FF_DONTCARE,
face,
&newfont));
_fonts.push_back(newfont);
}
// gets a font at the specified index
const LPD3DXFONT& DX9::GetFontAtIndex(ush index)
{
return _fonts[index];
}
const std::vector<LPD3DXFONT>& DX9::GetFontVector()
{
return _fonts;
}
/*=================================================*/
/*--------------END FONT FUNCTIONS-----------------*/
/*=================================================*/
}
#endif
This really doesn't do anything but create and initialize a window, DirectX and some basic DirectX functions.
The actual error is this:
Error 1 error LNK2005: "public: __thiscall nsdx9::DX9::DX9(struct HINSTANCE__ * &,int,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,unsigned short,unsigned short,short,short)" (??0DX9#nsdx9##QAE#AAPAUHINSTANCE__##HABV?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##GGFF#Z) already defined in game_manager.obj C:\Users\JAREK\documents\visual studio 2010\Projects\example\example\main.obj
This error is repeated for every single function in dx9.h. If I ONLY include dx9.h in main.cpp, I do not get this error. It is only if I include dx9.h in any other cpp file, or use any of the parameters in dx9.h in cpp files that have access to dx9.h by being included after dx9.h in main.cpp that I get this error. This sounds confusing, so here are some samples from other parts of the program:
main.cpp:
#include "sys_params.h"
#include "dx9.h"
#include "game_manager.h"
int WINAPI WinMain (HINSTANCE inst, HINSTANCE pinst, LPSTR cmdLine, int cmdShow)
{
// state of the program
bool appEnd = false;
// create and initialize the window and directx9
nsdx9::DX9* _dx9 = new nsdx9::DX9(inst, cmdShow, nssysprms::GAME_TITLE,
nssysprms::WND_WIDTH, nssysprms::WND_HEIGHT,
nssysprms::WND_POS_X, nssysprms::WND_POS_Y);
// create and initialize the game manager
Game_Manager* _mngr = new Game_Manager(_dx9);
// Windows message handler
// also the entry point for the main game loop
while (_dx9->ProcessMessages() != WM_QUIT && !appEnd)
{
if (!_mngr->Game_Run())
{
appEnd = true;
}
}
// clean up everything
delete _mngr;
delete _dx9;
return 0;
}
game_manager.h:
#ifndef _GAME_MANAGER_H_
#define _GAME_MANAGER_H_
#include <stack>
#include <vector>
#include "dx9.h"
#include "screen.h"
#include "message_handler.h"
class Game_Manager
{
public:
Game_Manager(nsdx9::DX9* dx9);
~Game_Manager();
bool Game_Run();
private:
nsdx9::DX9* _dx9;
std::stack<Screen*> _screens;
Message_Handler* _msg_hnd;
// *** DECLARE SCREENS HERE *** //
void InitFonts();
void InitScreens();
};
#endif
This should be what is actually causing the issue. The issue is originating from main.cpp through game_manager.h. Nothing I've tried has solved the problem for me. I've included header guards in dx9.h, so I have no idea what could be causing this. if you guys need more information, please let me know. Thanks!
Your definitions of the DX9 methods (as opposed to just the class definition) seem to be in the dx9.h header file - so you violate C++'s one-definition rule when you include dx9.h in multiple .cpp files. To solve this, move the method implementations into a .cpp file (e.g. dx9.cpp).
Don't include the implementation in the header file. This causes the implementation to be compiled again everywhere it is included.
for example:
/*=================================================*/
/*--------------DIRECTX CONSTRUCTOR----------------*/
/*=================================================*/
DX9::DX9(HINSTANCE& inst, int cmdShow, const std::string& title, ush wndwidth, ush wndheight, short wndx, short wndy)
{
.... et al
put that in it's own cpp