用拷贝钩子实现对文件夹的监控

本文原出处已不知。原作:webber84, 经ccrun(老妖)修改并在BCB在调试通过。
ICopyHook 是一个用于创建拷贝钩子处理程序COM接口,它决定一个文件夹或者打印机对象是否可以被移动,拷贝,重命名或删除。Shell在执行这些操作之前,会调用 ICopyHook接口的CopyCallback方法对它们进行验证。CopyCallback返回一个int值指示Shell是否应该继续执行这个操 作。返回值IDYES表示继续,而返回值IDNO和IDCANCEL则表示终止。

一个文件夹对象可以安装多个拷贝钩子处理程序。如果出现这种情况,Shell会依次调用每个处理程序。只有当每个处理程序都返回IDYES时,Shell才真正执行用户请求的操作。

拷 贝钩子处理程序的作用是在上述四种操作执行前对它们进行验证,但是Shell并不会把操作的结果通知给拷贝钩子处理程序。而windows提供的API函 数FindFirstChangeNotification和FindNextChangeNotification却可以实现这个功能。因此,只有把这 种两种方法结合起来,才能对一个文件夹的状态进行完全的监控。

拷贝钩子处理程序实现并不困难,首先创建一个作为进程内组件的COM对象, 它只需要暴露一个ICopyHook接口(当然还有IUnknown)。然后用regsrv32.exe注册这个COM组件。最后一步是向Shell注册 你的这个拷贝钩子处理程序,方法是在注册表HKEY_CLASSES_ROOTDirectoryShellexCopyHookHandlers 下创建一个名称任意的sub key,在此sub key中创建一个类型为REG_SZ的项并将你的COM对象的CLSID作为它的默认值就可以了。

下面就是一个拷贝钩子的实现程序(注:以下代码经老妖改动并添加了详细操作过程,在BCB6中成功编译并通过测试)

1. 从ICopyHook接口创建TCopyHook,从IClassFactory接口创建TClassFactory:

// TCopyHook.h
// TCopyHook类实现了ICopyHook接口,TClassFactory实现了IClassFactory接口
//—————————————————————————
#define NO_WIN32_LEAN_AND_MEAN
#include <shlobj.h>
//—————————————————————————
class TCopyHook: public ICopyHook
{
public:
     TCopyHook():m_refcnt(0) {}
     STDMETHODIMP QueryInterface(REFIID iid,void **ppvObject);
     STDMETHODIMP_(ULONG) AddRef();
     STDMETHODIMP_(ULONG) Release();
     STDMETHODIMP_(UINT) CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags,
             LPCTSTR pszSrcFile, DWORD dwSrcAttribs,
             LPCTSTR pszDestFile, DWORD dwDestAttribs);
private:
     int m_refcnt;
};
//—————————————————————————
class TClassFactory : public IClassFactory
{
public:
     TClassFactory():m_refcnt(0) {}
     STDMETHODIMP QueryInterface(REFIID iid, void **ppvObject);
     STDMETHODIMP_(ULONG) AddRef();
     STDMETHODIMP_(ULONG) Release();
     STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
     STDMETHODIMP LockServer(BOOL fLock);
private:
     int m_refcnt;
};

// TCopyHook.cpp
// TCopyHook对象和TClassFactory对象的实现文件
#include <stdio.h>
#include "TCopyHook.h"
//—————————————————————————
extern LONG nLocks;           // 对象计数,用于DllCanUnloadNow
ULONG __stdcall TCopyHook::AddRef()
{
     if(m_refcnt == 0)
         nLocks++;
     m_refcnt++;
     return m_refcnt;
}
//—————————————————————————
ULONG __stdcall TCopyHook::Release()
{
     int nNewCnt = –m_refcnt;
     if(nNewCnt <= 0)
     {
         nLocks–;
         delete this;
     }
     return nNewCnt;
}
//—————————————————————————
HRESULT __stdcall TCopyHook::QueryInterface(REFIID dwIID, void **ppvObject)
{
     if(dwIID == IID_IUnknown)
         *ppvObject = static_cast<IUnknown*>(this);
     else
         if(dwIID == IID_IShellCopyHook)
             *ppvObject = static_cast<ICopyHook*>(this);
         else
             return E_NOINTERFACE;
     reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
     return S_OK;
}
//—————————————————————————
// 这就是CopyCallback方法,拷贝钩子的所有功能由它实现。参数的具体值参看MSDN
UINT __stdcall TCopyHook::CopyCallback(HWND hwnd, UINT wFunc, UINT wFlags,
         LPCTSTR pszSrcFile, DWORD dwSrcAttribs,
         LPCTSTR pszDestFile, DWORD dwDestAttribs)
{
     char szMessage[MAX_PATH+14];
     sprintf(szMessage, "对%s进行的操作,是否继续?", pszSrcFile);
     return MessageBox(NULL, szMessage, "确认", MB_YESNO | MB_ICONEXCLAMATION);
}
//—————————————————————————
ULONG __stdcall TClassFactory::AddRef()
{
     if(m_refcnt==0)
         nLocks++;
     m_refcnt++;
     return m_refcnt;
}
//—————————————————————————
ULONG __stdcall TClassFactory::Release()
{

     int nNewCnt = –m_refcnt;

     if(nNewCnt <= 0)
     {
         nLocks–;
         delete this;
     }
     return nNewCnt;
}
//—————————————————————————
HRESULT __stdcall TClassFactory::QueryInterface(REFIID dwIID, void **ppvObject)
{
     if(dwIID == IID_IUnknown)
         *ppvObject = static_cast<IUnknown*>(this);
     else
         if(dwIID == IID_IClassFactory)
             *ppvObject = static_cast<IClassFactory*>(this);
         else
             return E_NOINTERFACE;
     reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
     return S_OK;
}
//—————————————————————————
HRESULT __stdcall TClassFactory::CreateInstance(IUnknown* pUnkownOuter,
         REFIID riid, void** ppvObj)
{
     if(pUnkownOuter != NULL)
         return CLASS_E_NOAGGREGATION;
     TCopyHook *pObj = new TCopyHook;
     pObj->AddRef();
     HRESULT hr = pObj->QueryInterface(riid, ppvObj);
     pObj 文章

Read: 727

钩子的应用: 程序运行监视

作者:Victor Chen
来自:C++ 爱好者
http://www.cppfans.com

——————————————————————————–

程序介绍:

利用这个程序:
1.可以监视在你的电脑运行的程序, 把在你的电脑运行过的程序的时间和名字记录下来;
2.可以阻止你规定的禁用程序的执行, 比如不让玩游戏。
3.这个程序需要加入注册表, 在系统启动时就运行, 达到监视的目的。注册表大概都不陌生,就是这里:
HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionRun

程序的记录格式:

2003-02-03 17:31:25 – [System Startup – Windows XP 5.01.2600]
2003-02-03 17:31:29 "CabinetWClass" -> "我的电脑"
2003-02-03 17:31:59 "Red Alert" -> "Red Alert" (关闭禁用程序)
2003-02-03 17:32:19 "扫雷" -> "扫雷" (关闭禁用程序)
2003-02-03 17:32:35 "OpusApp" -> "Microsoft Word"
2003-02-03 17:32:50 – [System Shutdown – 0 days, 0 hrs, 1 mins, 25 secs]

2003-02-03 17:35:37 – [System Startup – Windows 98 SE 4.10.2222]
2003-02-03 17:35:53 "扫雷" -> "扫雷" (关闭禁用程序)
2003-02-03 17:36:05 "CabinetWClass" -> ""
2003-02-03 17:36:31 "Red Alert" -> "Red Alert" (关闭禁用程序)
2003-02-03 17:36:56 "ExploreWClass" -> ""
2003-02-03 17:37:07 – [System Shutdown – 0 days, 0 hrs, 1 mins, 30 secs]

程序运行只需要3个文件:
hwhpapp.exe 可执行文件
hwhpdrv.dll 安装钩子的动态链接库
hwhpapp.cfg 禁用软件黑名单, 可用记事本修改
程序运行会自动产生记录文件:
hwhpapp.sys 可以用记事本打开看

程序原理:

一.钩子
利用 API 函数 SetWindowsHookEx() 安装一个全局钩子, 钩子类型为 WH_SHELL。
WH_SHELL 钩子可监视所有应用程序的主窗口创建或者关闭。
最典型的应用就是 Windows 的状态栏,当程序运行时,把主窗口的标题加入状态栏,程序退出时,从状态栏删除。
如果你截获这个钩子,可以做到禁止状态栏的显示,也可以自己作一个状态栏,或者作一个历史记录,把所有在你电脑曾经运行的程序记录下来。如果运行的程序不是你希望的,你可以直接关闭这个程序,达到禁止运行的目的。

二.动态链接库
由于钩子是全局的,必须把这个钩子定义到 .DLL 的动态链接库里,这就涉及到建立动态链接库。

三.共享内存
由于钩子是安装到系统里的,钩子的运行是在操作系统里面,因此这个钩子不能使用你的程序所定义的任何全局变量!
既 然如此,有什么办法解决呢?在本程序里利用共享内存技术,利用 API 函数 CreateFileMapping() 可创建共享内存,这个内存可以在任何运行的程序中使用,也就是说任何运行的 .EXE、.DLL 和其他程序都可以使用这块内存。在本程序中直接使用了 Victor 串口 VCL 控件里的 TSharedMemory 共享内存类。

四.记录文件和软件黑名单文件
把所有在你的电脑执行的程序记录保存在一个文本文件里, 因为扩展名为 .txt 很容易被发现,因此采用扩展名 .sys
软件黑名单文件保存在 .cfg 文件里,同样因为 .ini 文件很容易发现并且打开修改。
这两个文件都保存在与你的 .exe 文件的相同文件夹里,并且与 .exe 文件同名.

五.保证你的程序只能同时运行一个
如果你同时运行两个程序,记录文件就会乱套,所以必须保证只能运行一个。
当你的程序刚开始运行的时候,就是在 WinMain() 函数的最开始,就要判断是否已经运行了,如果已经运行,就直接退出。
判断的方法很简单,就是检查程序共享的内存是否存在,如果检查到共享的内存已经存在,那就是已经运行了。

六.程序隐身, 不能显示在任务栏和任务管理器里面
这个也很简单,只要在主程序的 Application->Run(); 前面加一句话:
SetWindowLong(Application->Handle, GWL_EXSTYLE, GetWindowLong(Application->Handle, GWL_EXSTYLE)|WS_EX_TOOLWINDOW);
就可以了。

——————————————————————————–

程序介绍
.DLL 文件:这是最关键的钩子的代码:

#include <vcl.h>
#include "yb_base.h" //Victor 串口控件里面的一个头文件

#define MYAPPMARK "VICTOR_APPMONI_20010612" //共享内存标志

class __export THookedProcs
{
    public:
        THookedProcs();
        ~THookedProcs();
        void WINAPI InitFuncs(void);
        void WINAPI UninitFuncs(void);
    private:
        HHOOK hThisHook; //保存钩子的句柄
        static LRESULT CALLBACK HookedShellProc(int nCode, WPARAM wParam, LPARAM lParam);
};

//定义共享的数据结构
typedef struct
{
   HHOOK hHook; //当前使用的 HOOK
//… 此处可增加其他共享的数据
} THookSharedData;

THookedProcs::THookedProcs()
{
   hThisHook = NULL;
}

THookedProcs::~THookedProcs()
{
    UninitFuncs();
}

void WINAPI THookedProcs::InitFuncs(void)
{
    UninitFuncs();
    hThisHook = SetWindowsHookEx(WH_SHELL, (HOOKPROC) HookedShellProc, HInstance, 0);
    TSharedMemory AppMem(MYAPPMARK,4096); //在 EXE 文件里共享的内存
    THookSharedData *HookSharedData = ((THookSharedData*)(AppMem.AppInfo->Data)); //共享的数据
    HookSharedData->hHook = hThisHook; //把 hThisHook 保存到共享内存里
}

void WINAPI THookedProcs::UninitFuncs(void)
{
    if(hThisHook)
    {
        UnhookWindowsHookEx(hThisHook);
        hThisHook = NULL;
    }
}

LRESULT CALLBACK THookedProcs::HookedShellProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    TSharedMemory AppMem(MYAPPMARK, 4096); //使用在 .EXE 文件里共享的内存
    if(AppMem.Valid)if(AppMem.Exists) //如果共享内存存在
    {
        HWND hMainWnd = AppMem.AppInfo->hMainForm;

        if(hMainWnd)
        {
            if(nCode==HSHELL_WINDOWCREATED)
            {
                PostMessage(hMainWnd, WM_USERCMD, UC_WINHOOK, wParam);
            }
        }
    }
    //在 Hook 里无法调用 hThisHook, 必须用共享内存里面的 hHook
    THookSharedData *HookSharedData = ((THookSharedData*)(AppMem.AppInfo->Data)); //共享的数据
    return CallNextHookEx(HookSharedData->hHook, nCode, wParam, lParam);
}

——————————————————————————–

EXE文件主程序的代码:

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR lpCmdLine, int)
{
    if(!AppMem.Valid)
    {
        return 1;
    }
    if(AppMem.Exists) //已经存在 (程序以前已经运行过了, 并且正在运行)
    {
        if(stricmp(lpCmdLine, "/SHOW")==0) //如果监测到命令行参数 /SHOW 就显示出已经运行的程序的主窗口
        {
            PostMessage(AppMem.AppInfo->hMainForm, WM_USERCMD, UC_SHOWWIN, 0);
        }
        return 0;
    }
    AppMem.ClearBuffer();

    try
    {
        Application->Initialize();

Read: 630

软件看门狗–别让你的程序没有响应

原作者姓名 陆其明
文章原始出处 http://hqtech.nease.net

正文
一.概述
一些重要的程 序,必须让它一直跑着;而且还要时时关心它的状态——不能让它出现死锁现象。当然,如果一个主程序会出现死锁,肯定是设计或者编程上的失误。我们首要做的 事是,把这个Bug揪出来。但如果时间紧迫,这个Bug又“飘忽不定”,那么,我们还是先写一个软件“看门狗”,暂时应一下急吧。

“看门狗”的需求描述:“看门狗”的运行不出现界面窗口,具有一定的隐蔽性;定时判断目标进程是否运行在当前系统中,如果没有则启动目标进程;判断目标进程是否“没有响应”,如果是则终止目标进程;如果目标进程“没有响应”的次数超过一定的数量,则将计算机系统重启。

二.预备知识
首 先要介绍两个主要的函数,能够判断目标进程是否“没有响应”。在User32.dll中(没有文档公开),Win2k/NT下的 IsHungAppWindow和Win9X下的IsHungThread;前者是以一个窗口句柄作为参数,后者是以线程ID作为参数。我们可以通过VC 开发工具的Depends查到这两个函数。
要使用这两个函数,我们必须先动态导入,如下:
if (m_hUser32 == NULL)
{
     m_hUser32 = GetModuleHandle("USER32.DLL");
}
if (m_hUser32)
{
     m_IsHungNT   = (HUNG_FUNNT) GetProcAddress(m_hUser32, "IsHungAppWindow");
     m_IsHung9X   = (HUNG_FUN9X) GetProcAddress(m_hUser32, "IsHungThread");
}
另外,还有如下知识点:
1.     如何让窗口隐藏(当然通过Windows任务管理器还是可以看到的)
在框架窗口类的PreCreateWindow中修改窗口风格,如下:
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
     if( !CFrameWnd::PreCreateWindow(cs) )
         return FALSE;
     // TODO: Modify the Window class or styles here by modifying
     //   the CREATESTRUCT cs

     cs.dwExStyle |= WS_EX_TOOLWINDOW;   // Make invisible in taskbar
     cs.style       = WS_POPUP;           // Hide the main window

     return TRUE;
}
2.     如何让“看门狗”只运行一个进程
使用互斥量。在CWatchDogApp::InitInstance()中,执行如下代码:
bool CWatchDogApp::IsUniqueCopyInProc()
{
     m_Mutex = CreateMutex(NULL, TRUE, "System Watch Dog");
     if (GetLastError() == ERROR_ALREADY_EXISTS)
     {
         return false;
     }
     return true;
}
该函数如果返回false,说明已经有一个WatchDog进程在运行了,当前进程就没有必要再执行下去了。在InitInstance如下处理:
if (!IsUniqueCopyInProc())
return FALSE;
3.     如何判断当前操作系统类型
bool CWatchDogApp::IsWinNT()
{
     OSVERSIONINFO OSVersionInfo;
     OSVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
     GetVersionEx(&OSVersionInfo);
     if (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
     {
         return true;
     }
     return false;
}
4.     如何自动重启计算机
在Win9x和Win2k/NT下,重启计算机的处理略有不同:
if (theApp.IsWinNT())
{
     // 在Win NT/2000下赋予关闭系统的权限
     static HANDLE hToken;
     static TOKEN_PRIVILEGES tp;
     static LUID luid;
::OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken ) ;
     ::LookupPrivilegeValue( NULL, SE_SHUTDOWN_NAME, &luid );
     tp.PrivilegeCount            = 1;
     tp.Privileges[0].Luid        = luid;
     tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
     ::AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL );
     return ::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
}
else
{
     return ::ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0);
}
5.     如何启动、结束其他进程
启动进程用CreateProcess,终止进程用TerminateProcess。参考代码如下:
bool CWatchDogView::RunTheSysProc()
{
     char     szPath[MAX_PATH];
     GetModuleFileName(NULL, szPath, MAX_PATH);
     CString strPath = szPath;
     strPath = strPath.Left(strPath.ReverseFind(‘\’)) + "\HungDemo.exe";

     STARTUPINFO             StartInfo;
     PROCESS_INFORMATION     procStruct;
     memset(&StartInfo,0,sizeof(STARTUPINFO));
     StartInfo.cb = sizeof(STARTUPINFO);

     if (!::CreateProcess(
         (LPCTSTR) strPath,
         NULL,
         NULL,
         NULL,
         FALSE,
         NORMAL_PRIORITY_CLASS,
         NULL,
         NULL,
         &StartInfo,
         &procStruct))
         return false;
     return true;
}
需要提醒的是,TerminateProcess是在万不得已的情况下使用的,它不会进入进程使用的DLL的入口点通知“脱离”(Detaching)状态。有时候,这样做是很危险的(DLL内部的全局数据可能受影响较大)。
6.     如何让Win2k/NT自动登录
修 改注册表。在HKEY_LOCAL_MACHINE目录下的SoftwareMicrosoftWindows NT CurrentVersionWinLogon下的AutoAdminLogon(字符串型)设置成1,并在DefaultUserName设置默认登 录用户,DefaultPassword设置默认用户的密码。
7.     如何让Win2k/NT登录成功后直接执行你的程序(而不是默认的文件浏览器)
修改注册表。在注册表HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindows NT CurrentVersionWinlogonShell的值从原先的explorer.exe修改为自己程序的绝对路径。

三.功能演示(Win2k/NT下)
友 情提醒:开始演示之前,请先将你目前的工作保存。运行“看门狗”WatchDog;同时使用Ctrl+Alt+Del打开“Windows任务管理器”。 稍候片刻,可以看到目标程序HungDemo会被启动(这个程序模拟了“没有响应”)。然后,WatchDog发现这个程序“没有响应”,则把它杀掉,然 后重新启动一个新的HungDemo进程。如此的处理重复六次以后,系统会自动重启。

正文完
源程序:http://www.vchelp.net/ASP/ibr_upload/357.zip

Read: 774

Windows下DLL编程技术及应用

摘 要: 本文介绍了DLL技术在Windows编程中的基本运用方法及应用,给出了直接内存
访问及端口I/O的两个实用DLL的全部源代码。
关键词: DLL Windows编程 内存访问 I/O

一 、引 言
由 于Windows为微机提供了前所未有的标准用户界面、图形处理能力和简单灵便的操作,绝大多数程序编制人员都已转向或正在转向Windows编程。在许 多用户设计的实际应用系统的编程任务中,常常要实现软件对硬件资源和内存资源的访问,例如端口I/O、DMA、中断、直接内存访问等等 。若是编制DOS程序,这是轻而易举的事情,但要是编制Windows程序,尤其是WindowsNT环境下的程序,就会显得较困难。
因为 Windows具有"与设备无关"的特性,不提倡与机器底层的东西打交道,如果直接用Windows的 API函数或I/O读写指令进行访问和操作,程序运行时往往就会产生保护模式错误甚至死机,更严重的情况会导致系统崩溃。那么在Windows下怎样方便 地解决上述问题呢?用DLL(Dynamic Link Libraries)技术就是良好途径之一。
DLL是Windows最重要的组成要素, Windows中的许多新功能、新特性都是通过DLL来实现的,因此掌握它、应用它是非常重要的。其实Windows本身就是由许多的DLL组成的,它最 基本的三大组成模块Kernel、GDI和User 都是DLL,它所有的库模块也都设计成DLL。凡是以.DLL、.DRV、.FON、.SYS和许多以.EXE为扩展名的系统文件都是DLL,要是打开 WindowsSystem目录,就可以看到许多的DLL模块。尽管DLL在Ring3优先级下运行,仍是实现硬件接口的简便途径。DLL可以有自己的 数据段,但没有自己的堆栈,使用与调用它的应用程序相同的堆栈模式,减少了编程设计上的不便;同时,一个DLL在内存中只有一个实例,使之能高效经济地使 用内存;DLL实现的代码封装性,使得程序简洁明晰;此外还有一个最大的特点,即DLL的编制与具体的编程语言及编译器无关,只要遵守DLL的开发规范和 编程策略,并安排正确的调用接口,不管用何种编程语言编制的DLL都具有通用性。例如在BC31中编制的DLL程序,可用于BC、VC、VB、 Delphi等多种语言环境中。笔者在BC31环境下编译了Windows下直接内存访问和端口I/O两个DLL,用在多个自制系统的应用软件中,
运行良好。

二、DLL的建立和调用
DLL的建立及调用方法在许多资料上有详细的介绍,为了节省篇幅,在这里仅作一些
主要的概括。
1.DLL的建立
关于DLL的建立,有如下几个方面的要素是不可缺少和必须掌握的:
?. 入口函数LibMain( )
就象C程序中的WinMain( )一样,Windows每次加载DLL时都要执行LibMain( )函数,主要用来进行一些初始化工作。通常的形式是:

int FAR PASCAL LibMain(HINSTANCE hInstance,WORD wDataSeg,WORD
wHeapSize,LPSTR lpszCmdLine)
{
if(wHeapSize!=0) //使局部堆、数据段可移动
UnlockData(0); //解锁数据段
/*此处可进行一些用户必要的初始化工作*/
return 1; //初始化成功
}
?出口函数WEP( )
Windows从内存中卸载DLL时,调用相应的出口函数WEP( ),主要做一些清理工作,如释放占用的内存资源;丢弃某些字串、位图等资源;关闭打开的文件等等。
?自定义的输出函数
为了让位于不同内存段的应用程序进行远程调用,自定义的输出函数必须定义为远程函数(使用FAR关键字),以防使用近程指针而得到意外的结果;同时,加上PASCAL关键字可加快程序的运行速度,使代码简单高效,提高程序的运行速度。
?输出函数的引出方法
? 在DLL的模块定义文件中(.DEF)由EXPORTS语句对输出函数逐一列出。例如:
EXPORTS WEP @1 residentname //residentname可提高DLL效率和处理速度
PortIn @2
PortOut @3 //通常对所有输出函数附加系列号
? 在每个输出函数定义的说明中使用_export关键字来对其引出。
以上两种方法任选其中的一种即可,不可重复。后面的两个实例分别使用了上述两种不同的引出方式,请留意。

2.DLL的调用
加载DLL时,Windows寻找相应DLL的次序如下:
?.当前工作盘。
?Windows目录;GetWindowsDirectory( )函数可提供该目录的路径名。
?Windows系统目录,即System子目录;调用GetSystemDiretory( )函数可获得这个目录的路径名。
?DOS的PATH命令中罗列的所有目录。
?网络中映象的目录列表中的全部目录。

DLL模块中输出函数的调用方法:
不 论使用何种语言对编译好的DLL进行调用时,基本上都有两种调用方式,即静态调用方式和动态调用方式。静态调用方式由编译系统完成对DLL的加载和应用程 序结束时DLL卸载的编码(如还有其它程序使用该DLL,则Windows对DLL的应用记录减1,直到所有相关程序都结束对该DLL的使用时才释放 它),简单实用,但不够灵活,只能满足一般要求。动态调用方式是由编程者用API函数加载和卸载DLL来达到调用DLL的目的,使用上较复杂,但能更加有 效地使用内存,是编制大型应用程序时的重要方式。具体来说,可用如下的方法调用:
?.在应用程序模块定义文件中,用IMPORTS语句列出所要调用DLL的函数名。如:
IMPORTS MEMORYDLL.MemoryRead
MEMORYDLL.MemoryWrite
?让应用程序运行时与DLL模块动态链接

先用LoadLibrary加载DLL,再用GetProcAddress函数检取其输出函数的地址,获得其指针来调用。如:
HANDLE hLibrary;
FARPROC lpFunc;
int PortValue;
M
hLibrary=LoadLibrary("PORTDLL.DLL"); //加载DLL
if(hLibrary>31) //加载成功
{
lpFunc=GetProcAddress(hLibrary,"PortIn"); //检取PortIn函数地址
if(lpFunc!=(FARPROC)NULL) //检取成功则调用
PortValue=(*lpFunc)(port); //读port端口的值
FreeLibrary(hLibrary); //释放占用的内存
}
M

三、DLL应用实例源程序
1.直接内存访问的DLL源代码
//.DEF文件
LIBRARY MEMORYDLL
DESCRIPTION ‘DLL FOR MEMORY_READ_WRITE ‘
EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
HEAPSIZE 1024 //DLL无自己的堆栈,故没有STACKSIZE语句
EXPORTS WEP @1 residentname
ReadMemory @2
WriteMemory @3

//.CPP文件
#include <windows.h>

int FAR PASCAL LibMain(HINSTANCE hInstance,WORD wDataSeg,WORD
wHeapSize,LPSTR lpszCmdLine)
{
if(wHeapSize!=0)
UnlockData(0);
return 1;
}

int FAR PASCAL MemoryRead(unsigned int DosSeg,unsigned int DosOffset)
{
WORD wDataSelector,wSelector;
char far *pData;
char value;
wDataSelector=HIWORD((DWORD)(WORD FAR *)&wDataSelector);
wSelector=AllocSelector(wDataSelector); //分配选择器
SetSelectorLimit(wSelector,0x2000); //置存取界限
SetSelectorBase(wSelector,(((DWORD)DosSeg)<<4)+(DWORD)DosOffset); //置基地址
pData=(char far *)((DWORD)wSelector<<16);
value=*pData;
FreeSelector(wSelector); //释放选择器
return (value);
}

void FAR PASCAL MemoryWrite(unsigned int DosSeg,unsigned int DosOffset,char Data)
{
WORD wDataSelector,wSelector;
char far *pData;
wDataSelector=HIWORD((DWORD)(WORD FAR *)&wDataSelector);
wSelector=AllocSelector(wDataSelector);
SetSelectorLimit(wSelector,0x2000);
SetSelectorBase(wSelector,(((DWORD)DosSeg)<<4)+(DWORD)DosOffset);
pData=(char far *)((DWORD)wSelector<<16);
*pData=Data;
FreeSelector(wSelector);
}

int FAR PASCAL WEP(int nParam)
{
return 1;
}

2.端口读写I/O的DLL源代码
//.DEF文件
LIBRARY PORTDLL
DESCRIPTION ‘DLL FOR PORT_IN_OUT ‘
EXETYPE WINDOWS
CODE PRELOAD MOVEABLE DISCARDABLE
DATA PRELOAD MOVEABLE SINGLE
HEAPSIZE 1024

//.CPP文件
#include <windows.h>
#include <dos.h>

int FAR PASCAL LibMain(HINSTANCE hInstance,WORD wDataSeg,WORD
wHeapSize,LPSTR lpszCmdLine)
{
if(wHeapSize!=0)
UnlockData(0);
return 1;
}

int FAR PASCAL _export PortOut(int port,unsigned char value)
{
outp(port,value);
return 1;
}

int FAR PASCAL _export PortIn(int port)
{
int result;
result=inp(port);
return (result);
}

int FAR PASCAL _export WEP(int nParam)
{
return 1;
}

分别将上面两个实例的.DEF文件和.CPP文件各自组成一个.PRJ文件,并进行编译链接成.EXE或.DLL文件就可以在应用程序中对其进行调用。

四、结 束 语
在 上面,我们利用DLL技术方便地实现了Windows环境下对内存的直接访问和端口I/O的访问,仿效这两个例子,还可以编制出更多的适合自己应用系统所 需的DLL,如用于数据采集卡的端口操作及扩展内存区访问、视频区缓冲区及BIOS数据区操作等许多实际应用的编程任务中。必要时只需直接更新DLL,而 用不着对应用程序本身作任何改动就可以对应用程序的功能和用户接口作较大的改善,实现版本升级。因此,掌握好DLL技术对Windows程序开发者很有裨 益。

Read: 611

在Windows桌面创建快捷方式

API提供了一个叫做IShellLink的COM接口允许我们创建快捷方式。为在桌面创建快捷方式,我们创建一个IShellLink对象,设置它的属性,然后把这个link保存到desktop目录。
下面的例子代码演示了怎样创建一个快捷方式。在这个例子里,这个快捷方式保存在C:根目录下。
//———————————————————————-
#define NO_WIN32_LEAN_AND_MEAN
#include <shlobj.h>
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//—————————————————————————
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//—————————————————————————
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//—————————————————————————
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    if(OpenDialog1->Execute())
        CreateShortCut(OpenDialog1->FileName);
}
//—————————————————————————
void TForm1::CreateShortCut(const AnsiString &file)
{
    IShellLink* pLink;
    IPersistFile* pPersistFile;
    if(SUCCEEDED(CoInitialize(NULL)))
    {
        if(SUCCEEDED(CoCreateInstance(CLSID_ShellLink, NULL,CLSCTX_INPROC_SERVER,
            IID_IShellLink, (void **) &pLink)))
        {
            pLink->SetPath(file.c_str());
            pLink->SetDescription("哈哈,你看到我了吧.");
            pLink->SetShowCmd(SW_SHOW);
            if(SUCCEEDED(pLink->QueryInterface(IID_IPersistFile,(void **)&pPersistFile)))
            {
                WideString strShortCutLocation("C:\bcbshortcut.lnk");
                pPersistFile->Save(strShortCutLocation.c_bstr(), TRUE);
                pPersistFile->Release();
            }
            pLink->Release();
        }
    }
}
//———————————————————————-
上 面的例子只是把快捷方式文件保存到了c:根目录下,但没保存到desktop目录下。要让快捷方式出现在桌面上,只须把快捷方式文件保存到 desktop目录下。首先我们要找到windows的desktop目录。一旦我们知道了desktop所在的目录,我们就能将快捷方式文件保存到 desktop目录下。然后windows就能将快捷方式图标显示到桌面上。下面是经过改进了的例子:
//———————————————————————-
void TForm1::CreateShortCut(const AnsiString &file)
{
    IShellLink *pLink;
    IPersistFile *pPersistFile;
    LPMALLOC ShellMalloc;
    LPITEMIDLIST DesktopPidl;
    char DesktopDir[MAX_PATH];
    if(FAILED(SHGetMalloc(&ShellMalloc)))
        return;
    if(FAILED(SHGetSpecialFolderLocation(NULL,CSIDL_DESKTOPDIRECTORY,&DesktopPidl)))
        return;
    if(!SHGetPathFromIDList(DesktopPidl, DesktopDir))
    {
        ShellMalloc->Free(DesktopPidl);
        ShellMalloc->Release();
        return;
    }

    ShellMalloc->Free(DesktopPidl);
    ShellMalloc->Release();

    if(SUCCEEDED(CoInitialize(NULL)))
    {
        if(SUCCEEDED(CoCreateInstance(CLSID_ShellLink,NULL,CLSCTX_INPROC_SERVER,IID_IShellLink,(void **)&pLink)))
        {
            pLink->SetPath(file.c_str());
            pLink->SetDescription("哈哈,你看到我了吧.");
            pLink->SetShowCmd(SW_SHOW);
            if(SUCCEEDED(pLink->QueryInterface(IID_IPersistFile,(void **)&pPersistFile)))
            {
                WideString strShortCutLocation(DesktopDir);
                strShortCutLocation += "\bcbshortcut.lnk";
                pPersistFile->Save(strShortCutLocation.c_bstr(), TRUE);
                pPersistFile->Release();
            }
            pLink->Release();
        }
        CoUninitialize();
    }
}
//———————————————————————-

不要陷于COM的泥沼之中
创建快捷方式包括一些对COM的使用。不要让你陷入到COM的复杂之中。COM只是创建和使用对象的一种方法。在这个例子里我们可以考虑不使用COM而是用等价的C++技术。

 

下面是COM的代码与等价C++代码对比,COM代码以浅色表示,等价C++以深色表示

IShellLink* pLink;

IPersistFile* pPersistFile;

TShellLink *Link;

TPersistFile *PersistFile;

CoInitialize();

CoCreateInstance(CLSID_ShellLink, NULL,CLSCTX_INPROC_SERVER,IID_IShellLink, (void **) &pLink)

Link = new TShellLink;
pLink->SetPath(file.c_str());
Link->SetPath(file.c_str());
pLink->SetShowCmd(SW_SHOW);
Link->SetShowCmd(SW_SHOW);
pLink->QueryInterface(IID_IPersistFile dynamic_cast<TPersistFile*>(Link);
PersistFile = (void **)&pPersistFile)))
pPersistFile->Save("C:\", TRUE);
PersistFile-&
文章录入:admin     责任编辑:admin

Read: 624