tags:

views:

214

answers:

3

hi all I want to write an text editor and to assign the txt files to it. My problem is that I want to have only one instance running and when a new file is opened to send the filename to the first app that is already running... (I want to do this using mutex). Here is a small test

DPR looks like this

uses
  Windows, Messages, SysUtils,
  Forms,
  wndMain in 'wndMain.pas' {frmMain};

{$R *.res}

var
  PrevWindow : HWND;
  S : string;
  CData : TCopyDataStruct;
begin 
  PrevWindow := 0;
  if OpenMutex(MUTEX_ALL_ACCESS, False, 'MyMutex') <> 0 then
  begin
  repeat
     PrevWindow:=FindWindow('TfrmMain', nil);
  until PrevWindow<>Application.Handle;

     if IsWindow(PrevWindow) then
     begin
        SendMessage(PrevWindow, WM_SYSCOMMAND, SC_RESTORE, 0);
        BringWindowToTop(PrevWindow);
        SetForegroundWindow(PrevWindow);

        if FileExists(ParamStr(1)) then
        begin
           S:=ParamStr(1);
           CData.dwData:=0;
           CData.lpData:=PChar(S);
           CData.cbData:=1+Length(S);

           SendMessage(PrevWindow, WM_COPYDATA, 0, DWORD(@CData) );
           end;
        end;
     end
   else
      CreateMutex(nil, False, 'MyMutex');

  Application.Initialize;
  Application.CreateForm(TfrmMain, frmMain);
  Application.Run;
end.

PAS:

type TfrmMain = class(TForm)
   memo: TMemo;
   private
      procedure WMCopyData ( var msg : TWMCopyData ) ; message WM_COPYDATA;
  public
     procedure OpenFile(f : String);
end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.WMCopyData ( var msg : TWMCopyData ) ;
var
   f : String;
begin
   f:=PChar(msg.CopyDataStruct.lpData);
   //ShowMessage(f);
   OpenFile(f);
end;

procedure TfrmMain.OpenFile(f : String);
begin
   memo.Clear;
   memo.Lines.LoadFromFile(f);
   Caption:=f;
end;

this code should be ok, but if i want to open a text file (from the second app), the first app receives a message like this:

alt text

thanks

+1  A: 

This looks like a Unicode problem. You're probably in D2009 or D2010. You need to give the size in bytes, not in chaaracters. Try multiplying your length call by sizeof(char) and it should work.

Mason Wheeler
+1 : This is probably going to be the most common mistake of Delphi developers for the years to come. Instead of "SizeOf(Char)", I'd suggest using "StringElementSize(StringVariable)".
Ken Bourassa
A: 

I don't have Delphi with me at the moment. But my first impression is that your mutex is working right but the string is corrupted. From your call it looks like you are using a string and casting to PChar which I think is the problem.

String in Delphi 2009/2010 is WideString, while PChar is (IIRC) a pointer to a null terminated AnsiString. SO you probably want to use either AnsiStrings on both sides, or use a PWideChar.

I know that this is not a complete answer. Hope I have been of assistance.

Muhammad Alkarouri
This is not correct. String in Delphi 2009/2010 is a UnicodeString, which is not quite the same thing as a WideString, and PChar is a pointer to a null-terminated string of Chars, whatever Char may be defined as. So for D2009 and later, PChar is the same as PWideChar.
Mason Wheeler
Ahh. The difference in communities.@Mason of course you are right. I just had a hunch about the problem and I thought I would point in the general direction of the answer as the poster clearly knows enough about Delphi to verify. Which is a good idea in various forums I am used to (this is my first day here). I guess here one says wrong things, one gets penalised.
Muhammad Alkarouri
@Muhammad: Welcome to Stack Overflow. Don't worry about getting downvoted. It's not to penalize you; it's there to point out to others (and especially the person who asked the question) that your answer isn't going to help them. The reputation system here is carefully set up to make it almost impossible to really hurt someone with downvotes.
Mason Wheeler
@Mason: Thanks. Actually I am happy to hear that the reputation system is really informative about the actual answers.
Muhammad Alkarouri
A: 

I suspect you're using Delphi 2009 or Delphi 2010. Those version use Unicode strings, so the Length function tells the number of characters in a string, but not the number bytes. The wm_CopyData message needs to know the number of bytes to send. Multiply the character count by two, or SizeOf(WideChar).

CData.lpData := PWideChar(S);
CData.cbData := (1+Length(S)) * SizeOf(WideChar);
Rob Kennedy
This will work as long as you don't try to reuse the code in a non-unicode version. If you do that, it wil break. Better to use `PChar` and `sizeof(char)` and let the compiler take care of the interpretation.
Mason Wheeler
I'd prefer to make it explicit in this case because we're dealing with interprocess communication. Both sides need to agree regarding the size and format of the payload. If one side is compiled in Unicode mode and the other isn't, my code will fail to compile, hinting that something's wrong, whereas code that uses PChar will compile find on both sides, but the communication will fail at run time.
Rob Kennedy