代码如下://这段代码在ontest中可以正常接收到发过来的信息
procedure TForm1.btn1Click(Sender: TObject);
  var pdata :array[0..4] of Byte  ;
      i : Integer;
begin  
  FillChar(pdata,5,0);
  for i := 0 to 4 do
  begin
    pdata[i] := i * 10;
  end;
  PostMessage(Handle,WM_TEST,5,LongWord(@pdata[0]));
end;procedure TForm1.OnTest(var Msg: TMessage);
  var pd : array[0..4] of Byte;
      i : Integer;
begin
  CopyMemory(@pd[0],Pointer(Msg.LParam),5);
  for i := 0 to Msg.WParam - 1 do
  begin
    ShowMessage(IntToStr(pd[i]));
  end;
end;//下面代码也可以正常接收,只是showmessage后会报错,不知什么原因
procedure TForm1.btn1Click(Sender: TObject);
  var pdata :pByte ;
      i : Integer;
begin
  GetMem(pdata,5);
  FillChar(pdata^,5,0);
  for i := 0 to 4 do
  begin
    pdata^ := i * 10;
    inc(LongWord(pdata),1);
  end;
  inc(LongWord(pdata),-5);
  SendMessage(Handle,WM_TEST,5,LongWord(@pdata));
  FreeMem(pdata,5);
end;procedure TForm1.OnTest(var Msg: TMessage);
  var pd : pByte;
      i : Integer;
      aa : TBitmap;
begin
  CopyMemory(@pd,Pointer(Msg.LParam),5);//拷贝正常,执行完毕报错
  //Move(Pointer(Msg.LParam)^,(@pd)^,5);//正常
  for i := 0 to Msg.WParam - 1 do
  begin
    ShowMessage(IntToStr(pd^));
    if i < Msg.WParam - 1then
     Inc(LongWord(pd),1);
  end;
end;
哪位达人解释一下
另外 能不能通过 InterlockedExchange 这个PAI直接为pd指向一个地址?

解决方案 »

  1.   

    补充一下
    procedure TForm1.OnTest(var Msg: TMessage);
      var pd : array[0..4] of Byte ;
    这样定义时,又该怎么拷贝?
      

  2.   

    CopyMemory(@pd,Pointer(Msg.LParam),5);//拷贝正常,执行完毕报错  pd没分配空间pd = AllocMem(SizeOf(Byte)*5);
      

  3.   

    原因是pd没有初始化,如果pd是全局变量的话,不初始化,那么它的值是0,拷贝肯定也会出错。但现在它是局部变量,它的值是不确定的,有时候可能是0,有时候可能会是其它任何值,主要看堆栈的使用情况。如果碰巧了,pd的值不是0,而是一块可读写的内存的地址值,那么你可以完成拷贝,但这是不确定的,不同的程序,不同的运行时间,它的值都可能是不同的。具体到楼主的程序中,接收时,必须和发送时一样,先初始化pd,使它指向一块你分配的内存。加一行:getmem(pd,5); 就行了。
      

  4.   

    看来楼上两位没手动试一下啊,我当然有试过为PD手动分配内存,可结果还是一样,所以没把那两句代码写上
    不管PD有没有分配内存,执行完后都一样报错!既然是未分配内存报错,为何move又可以呢?
      

  5.   

    都使用move的,没看到过CopyMemory
      

  6.   

    修改如下SendMessage(Handle,WM_TEST,5,LongWord(pdata));CopyMemory(pd,Pointer(Msg.LParam),5)之前你用的都是指针的地址@pd,这个CopyMemory中就有问题了
      

  7.   

    前面没仔细看楼主的代码,现在看了看发现,除了分配内存的问题外,楼主的copymemory那一行是错误的。pd本身就是一个指针,它记录了一块内存的地址,在32位程序中,它是一个占用4个字节的32位整数,楼主又加了@号,等于取指针变量的地址,就是取pd这个变量的地址,而不是楼主真正要取的目标内存的地址。将@号去掉就行了。另外,分配内存是必须的,即使不分配,碰巧了执行不报错,但说不定什么时候就又报错了,原因见我在3楼的回复。
      

  8.   

    看来是我的失误
    SendMessage(Handle,WM_TEST,5,LongWord(pdata));
    以前发送结构体时,会加上@,发送一个地址,但现在发送的是指针,本身就是地址。
      

  9.   

    一个小小的失误很可怕
    另外也可以这样
    InterlockedExchange(Integer(pdata),Integer(Pointer(msg.LParam)));

    在同一程序里,不为pdata分配内存,直接把pdata指向Pointer(msg.LParam)的地址。
    发送方式是以sendmessage以送的。
      

  10.   

    var pd : pByte;
          i : Integer;
          aa : TBitmap;
    begin
      CopyMemory(@pd,Pointer(Msg.LParam),5);//拷贝正常,执行完毕报错
    楼主怎么想的呢?
    32位系统下
    pd : pByte pd只占4个字节,你要复制5个直接过去,溢出了,程序运行完出错,你应该感到很幸运了,如果偶尔出哈错,就不好找到这个地方了
    直接
      pd := Ptr(Msg.LParam);
    即可,也不需要分配内存,
    procedure ...
    var
      pdata :array[0..4] of Byte;
    begin
      ...
      PostMessage(Handle,WM_TEST,5,LongWord(@pdata[0]));
    end;这种使用PostMessage是肯定不行的,数据一次没出错是运气好,并不代表一直都对
      

  11.   

    对这个函数InterlockedExchange的理解,不知道怎么说好,这个在关键的那一条指令执行时,是要锁总线的,因此非临界区多线程操作时,完全没必要.
    虽然确实可以实现交换,不过有点浪费总线资源了.
      

  12.   


    作了少少修改, 没有报错procedure TForm1.Button1Click(Sender: TObject);
     var
     pdata :pByte ;
          i : Integer;
    begin
      GetMem(pdata,5);
      FillChar(pdata^,5,0);
      for i := 0 to 4 do
      begin
        pdata^ := i * 10;
        inc(LongWord(pdata),1);
      end;
      inc(LongWord(pdata),-5);
      SendMessage(Handle,WM_TEST,5,LongWord(@pdata));
      FreeMem(pdata,5);
    end;procedure TForm1.OnTest(var Msg: TMessage);
      var pd : pByte;
          i : Integer;
    begin
      CopyMemory(@pd,Pointer(LongWord(Msg.LParam)),Sizeof(Pointer(LongWord(Msg.LParam))));
      for i := 0 to Msg.WParam - 1 do
      begin
        ShowMessage(IntToStr(pd^));
        if i < Msg.WParam - 1then
         Inc(LongWord(pd),1);
      end;
    end;
      

  13.   

    写这段代码,就是为了证实消息发送和接收指针的作法,作一下整理
    解释在这里

    pd : pByte pd只占4个字节,你要复制5个直接过去,溢出了
      

  14.   


    procedure TfrmMain.OnTest(var msg: TMessage);
      var i , size: Integer;
          pdata : PByte;
          hmem : Cardinal;
          path : PChar;
    begin
      Size := msg.WParam;
    //  hmem := GlobalAlloc(GHND,5);
    //  pdata := GlobalLock(hmem);
      InterlockedExchange(Integer(pdata),Integer(Pointer(msg.LParam))); 
    //发送方发送指针地址,不需分配内存
    //  Move(Pointer(msg.LParam)^,pdata^,5);         //发送方发送指针地址  ,需分配内存
    //  CopyMemory(pdata,PByte(msg.LParam),5);       //发送方发送指针地址  ,需分配内存
    //  pdata := ptr(msg.LParam);                    //发送方发送指针地址 ,不需分配内存
    //  CopyMemory(@pdata,Pointer(Msg.LParam),4);    //发送方发送指针变量地址
    //  Move(Pointer(Msg.LParam)^,(@pdata)^,4);      //发送方发送指针变量地址
      for i := 0 to size - 1 do
      begin
        ShowMessage(IntToStr(pdata^  ));
        Inc(LongWord(pdata),SizeOf(byte));
      end;
    //  GlobalUnlock(hmem);
    //  GlobalFree(hmem);
    end;procedure TfrmMain.btn1Click(Sender: TObject);
      var pdata : pByte;
          i : Integer;
    begin
      GetMem(pdata,5);
      for i := 0 to 4 do
      begin
        pdata^ := i * 10;
        Inc(LongWord(pdata),1);
      end;
      Inc(LongWord(pdata),-5);
      SendMessage(Handle,WM_test,5,LongWord(pdata));
      FreeMem(pdata,5);
    end;
      

  15.   

    新人不要学22楼的代码,好多都是往麻烦了写非常多余的
    像 Inc(LongWord(pdata),1) 应该直接写 Inc(pdata);Inc(LongWord(pdata),-5) 应该写 Dec(pdata, 5);OnTest 里和多线程操作无关,InterlockedExchange 纯属多余,直接 pdata:=PByte(msg.LParam) 或者 pdata:=Pointer(msg.LParam) 就可以另外,如果要动态分配 pdata 内存的话,将 pdata 声明成 array of Byte 然后 SetLength 的话可以不用写释放的代码(我的 delphi 代码几乎不用 GetMem/FreeMem),按偏移量访问会更加轻松,也不用担心内存泄露。如果非要用 GetMem/FreeMem 的话,写在 try...finally 块里代码会更健壮一些。
    同样,OnTest 里将 pdata 声明成 PByteArray 的话,按照偏移量访问会更容易一些。
      

  16.   

    TO :dinoalex
    CSDN居然没提示
    我一开始发送消息是这么写的
    SendMessage(Handle,WM_TEST,5,LongWord(@pdata));
      

  17.   

    其实那个问题不大啦, 都是为了传消息时, 把把转成Integer而已, 而消息接收的也是 Integer完全可以 SendMessage(Handle,WM_TEST,0,Integer(pdata));现在问题是传的对象问题, 你现在传的是一个Byte指针, 不是一个数组, 不是一个Stream所以Size只能是一个指针的大小.
      

  18.   


    //还在纠结哦
    procedure TForm1.btn1Click(Sender: TObject);
    var 
      //这里是私有变量,是栈上分配的,所以必须用SendMessage
      pdata :array [0..4] of Byte;  
      i : Integer;
    begin  
      //  FillChar(pdata,5,0); 这句完全多余
      for i := 0 to 4 do pdata[i] := i * 10;
      SendMessage(Handle,WM_TEST,5,LongWord(@pdata));  //这样即可,这里是把pdata的指针传过去
    end;  //函数执行完后,就会释放栈上的数据,因此必须用SendMessage,SendMessage要等消息处理后才返回的,保证了传递过去的指针的数据有效procedure TForm1.OnTest(var Msg: TMessage);
    var 
      pd : PByte;
      i : Integer;
    begin
      pd := Pointer(Msg.LParam);          //直接取传过来的指针即可,何必用CopyMemory?
      for i := 0 to Msg.WParam - 1 do begin
        ShowMessage(IntToStr(pd[i]));
        inc(integer(pd));
      end;
    end;
      

  19.   

    代码要修改一下
         ShowMessage(IntToStr(pd^));
        inc(integer(pd));
    不好意思,Sorry
      

  20.   

    ding &&&&***************************
      

  21.   

    再整理一下!
    SendMessage(Handle,WM_TEST,5,LongWord(pdata));
    //1.CopyMemory(pd,Pointer(msg.LParam),msg.WParam);需要分配内存
    //2.pd := Pointer(Msg.LParam); //直接使用,不需分配内存
    //3.InterlockedExchange(Integer(pd),Integer(Pointer(msg.LParam)));//不需分配内存SendMessage(Handle,WM_TEST,5,LongWord(@pdata));
    //1.CopyMemory(@pd,Pointer(LongWord(Msg.LParam)),Sizeof(Pointer(LongWord(Msg.LParam))));//不需分配内存
    //2.Move(Pointer(Msg.LParam)^,(@pd)^,Sizeof(Pointer(LongWord(Msg.LParam))));
      //不需分配内存
    最主要的就是这两者的区别吧。怎么老觉得越整越糊涂了下面代码是想通过接收方来改变指针指向的内容procedure TForm1.onmessage(var msg: TMessage);
     var pd : pByte;
          i : Integer;
    begin
      pd := Ptr(Msg.LParam);
      for i := 0 to Msg.WParam - 1 do
      begin
        ShowMessage(IntToStr(pd^));
        pd^ := pd^ + 100;
        inc(LongWord(pd));
      end;
    end;procedure TForm1.btn1Click(Sender: TObject);
      var pdata :pByte ;
          i : Integer;
    begin
      GetMem(pdata,5);
      FillChar(pdata^,5,0);
      for i := 0 to 4 do
      begin
        pdata^ := i * 10;
        inc(LongWord(pdata));
      end;
      Dec(LongWord(pdata),5);
      SendMessage(Handle,WM_TEST,5,LongWord(pdata));
      for i := 0 to 4 do
      begin
        ShowMessage(IntToStr(pdata^));
        inc(LongWord(pdata));
      end;
      // 这里值已经改变,但运行完后报错!不加这个循环就没问题,这是什么原因呢?
      
      FreeMem(pdata,5);
    end;
      

  22.   

    for i := 0 to 4 do
      begin
        pdata^ := i * 10;
        inc(LongWord(pdata));
      end;
      Dec(LongWord(pdata),5); //这里知道减5
      ...
      for i := 0 to 4 do
      begin
        ShowMessage(IntToStr(pdata^));
        inc(LongWord(pdata));
      end;
      //这里为什么不减5 ???????????????申请内存时,假设你申请到的内存地址是$10000000
      //通过上面的循环,你的pdata的值是$10000005,你调用FreeMem对$10000005进行释放,其结果可想而知
      FreeMem(pdata,5);  
      

  23.   


    都说了操作对像, 一个PBYTE, 你怎么可能申请内存为5啊????都说了, 一个PBYTE只是一个指针, 不是一个指针数组!!!!
      

  24.   

    31楼的代码
      Dec(LongWord(pdata),5);   // FreeMem前加这一句
      FreeMem(pdata,5);
      

  25.   

    因为你循环时, 地址已经变了, 所以FREEMEM时, 就要回到变量的原地址.  Dec(LongWord(pdata),5);  // 回原地址
      FreeMem(pdata,5);
      

  26.   


    //你在31楼没有用COPYMEMORY了, 所以不聊COPYMEMORY的对像问题.
    //31楼的代码, 加下面红色的语句, 跳回原来的地址, 就不会报错了.procedure TForm1.btn1Click(Sender: TObject);
      var pdata :pByte ;
          i : Integer;
    begin
      GetMem(pdata,5);
      FillChar(pdata^,5,0);
      for i := 0 to 4 do
      begin
        pdata^ := i * 10;
        inc(LongWord(pdata));
      end;
      Dec(LongWord(pdata),5);
      SendMessage(Handle,WM_TEST,5,LongWord(pdata));
      for i := 0 to 4 do
      begin
        ShowMessage(IntToStr(pdata^));
        inc(LongWord(pdata));
      end;
      Dec(LongWord(pdata),5);         // FreeMem前加这一句
      FreeMem(pdata,5);
    end;
      

  27.   

    我真不明白,为什么delphi能够存活下来?
      

  28.   

    //这段代码在ontest中可以正常接收到发过来的信息
    procedure TForm1.btn1Click(Sender: TObject);
      var pdata :array[0..4] of Byte  ;
          i : Integer;
    begin  
      FillChar(pdata,5,0);
      for i := 0 to 4 do
      begin
        pdata[i] := i * 10;
      end;
      PostMessage(Handle,WM_TEST,5,LongWord(@pdata[0]));
    end;这段代码本身就有问题,pdate 是局部变量,从函数中退出后就被释放掉了,而PostMessage发送消息后立即返回,但是,返回并不代表该消息已经被处理,很有可能等消息被处理的时候,pdate已经被释放掉了,从而在消息处理函数中出现内存错误。
      

  29.   

    我看了楼主的帖子,然后自己动手写了下,不过总觉得用PByte没有PChar来的爽快。
    procedure TForm1.btn1Click(Sender: TObject);
    var
      pb: PChar;
      i: Integer;
      tmp: Pointer;
    begin
      pb := nil;
      pb := GetMemory(5 * SizeOf(Char));
      FillChar(pb[0], SizeOf(pb), #0);
      for i := 0 to 4 do
      begin                                    s
        Pb[i] := Char(i + Ord('A'));
      end;
      SendMessage(Handle, WM_CopyPointer, 5, Integer(pb));
      Dec(Integer(pb), 5 * SizeOf(Char));
      FreeMemory(pb);
    end;procedure TForm1.ResponseCopyPointer(var AMsg: TMessage);
    var
      pb: PChar;
      I: Integer;
    begin
      pb := GetMemory(5 * SizeOf(Char));
      FillChar(pb[0], SizeOf(pb), #0);
      CopyMemory(pb, Pointer(AMsg.LParam), AMsg.WParam);
      for i := 0 to AMsg.WParam - 1 do
      begin
        ShowMessage(pb[i]);
      end;
      Dec(pb, SizeOf(Char) * 5);
      FreeMemory(pb);
    end;
    这是我写的,用PChar简单多了吧。
      

  30.   

    或者,申请PByte类型,但是操作的时候,强制转换成PChar
    procedure TForm1.btn1Click(Sender: TObject);
    var
      pb: PByte;
      i: Integer;
      tmp: Pointer;
    begin
      pb := nil;
      pb := GetMemory(5 * SizeOf(Char));
      FillChar(PChar(pb)[0], SizeOf(pb), #0);
      for i := 0 to 4 do
      begin
        PChar(Pb)[i] := Char(i + Ord('A'));
      end;
      SendMessage(Handle, WM_CopyPointer, 5, Integer(pb));
      Dec(Integer(PChar(pb)), 5 * SizeOf(Char));
      FreeMemory(pb);
    end;procedure TForm1.ResponseCopyPointer(var AMsg: TMessage);
    var
      pb: PByte;
      I: Integer;
    begin
      pb := GetMemory(5 * SizeOf(Char));
      FillChar(PChar(pb)[0], SizeOf(pb), #0);
      CopyMemory(pb, Pointer(AMsg.LParam), AMsg.WParam);
      for i := 0 to AMsg.WParam - 1 do
      begin
        ShowMessage(Format('%d', [Ord(PChar(pb)[i])]));
      end;
      Dec(PChar(pb), SizeOf(Char) * 5);
      FreeMemory(pb);
    end;
    //输出65,66,67,68,69