Jump to content
Mark Williams

SendMessage From DLL

Recommended Posts

I have a Word Add-In created with Add-In Express, although that's probably irrelevant.

 

The Add-In can only be used for word docs created via a certain desktop app (MyAPP).

 

When MyAPP creates a Word document it stores a unique identifier for that Word document and also the handle of MyAPP.

 

I am trying to send information from the Add-In DLL back to MyApp using SendMessage and WM_COPYDATA.

 

I have the following types declared in both the DLL and MyAPP:

type TWordFunction = (wfNone, wfEdit, wfSave, wfLink, wfMasterWindow, wfBundle);

type
  TWordActionRec = Record
    wf:TWordFunction;
    wordID:THandle;
end;

Within the DLL:

procedure TAddInModule.DefaultButtonAction(wordFunction : TWordFunction);
var
    WAR : TWordActionRec;
    copyDataStruct : TCopyDataStruct;
    hdl : THandle;
begin
  hdl := GetViewerHandle;
  WAR.wf := WordFunction;
  WAR.wordID := GetWordIdentifier;
  copyDataStruct.dwData := 0;
  copyDataStruct.cbData := SizeOf(WAR);
  copyDataStruct.lpData := @WAR;
  WinAPI.Windows.SendMessage(hdl, WM_COPYDATA, 0, Integer(@copyDataStruct));
end;

The above runs without triggering any error and the hdl parameter is correct for MyAPP.

 

Within MyAPP:

procedure TForm1.WMCopyData(var Msg: TWMCopyData);
var
  WAR : TWordActionRec;
begin
  WAR := TWordActionRec(Msg.CopyDataStruct.lpData^);  
  ListBox1.Items.Add(WAR.wordID.ToString);
  Case WAR.wf of
    wfNone: ListBox2.Items.Add('None');
    wfEdit: ListBox2.Items.Add('Edit');
    wfSave: ListBox2.Items.Add('Save');
    wfLink: ListBox2.Items.Add('Link');
    wfMasterWindow: ListBox2.Items.Add('MW');
    wfBundle: ListBox2.Items.Add('Bundle');
  End;
end;

However, the DLL's message is never received by MyApp.

 

If I take the code from the DDL and add it to a standard executable it works as expected. 

 

I have Googled to see if there is any reason why SendMessage would not work within a DLL and as far as I can see there shouldn't be. 

 

Is there anything wrong with my code or is this perhaps an Add-IN Express specific issue?

Share this post


Link to post

I highly doubt that wordID or hdl are really THandle. The latter should be HWND. Not sure what the former should be. THandle represents a kernel handle. It's something that you would call CloseHandle on.

 

As for your problem, hard to know. Could be UIPI. You don't do any error checking when you call SendMessage. You need to add some trace debugging.

Share this post


Link to post

Word ID is a randomly generated integer value. It's not intended to be used as a handle. Just a way of identifying which Word window my app is communicating with. 

 

My app passes its own handle to be stored in the word document as follows:

 

Quote

WordApp.ActiveDocument.Variables.Add('ViewerHandle', Handle);

Within a word document the variables are stored as a wideString. To get the handle of my app from within the DLL I call this function which queries the Add In's host word window:

Result:=0;
val := Doc.Variables.Item(i).Value;
tryStrToInt(val, hdl);
  Result := Hdl;

Result is a THandle.

 

Debugging a 64 bit AddIn is a bit problematic. So I have not looked into it yet. However, I have written to a log file to output what is happening at the various stages. I know that the hdl being passed to SendMessage is the correct hdl of my app. Also GetLastError returns success after SendMessage (if that means anything!)

 

On My app I show the value of its handle on a label when it is running. Let's say its 12345678.

 

If I create another Delphi executable and call

 

WinAPI.Windows.SendMessage(12345678, WM_COPYDATA, 0, Integer(@copyDataStruct));

That works as expected.

 

If I do the same in my Add In DLL (ie substitute the hdl var with the Longint value of my app's handle) it doesn't work.

 

Share this post


Link to post
Guest

First

6 hours ago, Mark Williams said:

Integer(@copyDataStruct)

Using Integer is dangerous here, though it is not the problem.

 

Second :

It is long shot but it might be the cause, check the permission and security setting, i have no idea what Word do have, but from the following

1) "When a message is blocked by UIPI the last error, retrieved with GetLastError, is set to 5 (access denied)." from SendMessage documentation

2) from detailed Remy answer on SO https://stackoverflow.com/questions/40122964/cross-process-postmessage-uipi-restrictions-and-uiaccess-true

3) Windows does have message filter, in case it is Word then Word process can and might be utilizing such filter

 

i can conclude that security and permission might be in play here, right ? 

 

so i would recommend :

Disable Antivirus's if there any to see if they are interacting here, there is few antivirus that block WM_COPYDATA when send to different process as extra protection, because they can.

Check what does Word/Office in security settings have.

Try to send different (innocent) message to see if it will be delivered at all.

Is that WAR is yours ( dll allocated ) or Word's, may be Word can block different process from reading it process memory as security measure, in this case filtering out WM_COPYDATA, so if you copied the data first, by copy i mean copied the that buffer, but first you might test your dll sending it owns ( any temporary data) using WM_COPYDATA, this step too shed some light on this.

Share this post


Link to post
1 hour ago, Kas Ob. said:

 

i can conclude that security and permission might be in play here, right ? 

It might be a UIPI issues as flagged by @David Heffernan initially and missed by me! But I don't think so. However not dealt with UIPI issues before so I may be handling it incorrectly,

 

Whilst Word runs at a low level I think the DLL Add-In will be at the same level as my receiving app. Also, as you pointed out if there is an UIPI error then GetLastError is set to 5. My SendMessage is reporting success.

 

However I have tried to incorporate ChangeWindowFilterEx within my receiving app to see if that makes any difference.

 

type
  TChangeFilterStruct = packed record
    cbSize: DWORD;
    ExtStatus: DWORD;
  end;
  PChangeFilterStruct = ^TChangeFilterStruct;

  function ChangeWindowMessageFilterEx(hWnd: HWND; Message: UINT; Action: DWORD;
  ChangeFilterStruct: PChangeFilterStruct): Bool; stdcall; external 'user32.dll';

const
  MSGFLT_ALLOW = 1;
  MSGFLT_DISALLOW = 2;
  MSGFLT_RESET = 0;

{In formcreate:}

ChangeWindowMessageFilterEx(Handle, WM_COPYDATA, MSGFLT_ALLOW, nil);

If I am implementing this correctly there doesn't appear to be a UIPI error. 

Share this post


Link to post
1 hour ago, David Heffernan said:

Wait a minute. If its a 64 bit add in then why are you casting the pointer to a 32 bit integer? 

Ah ha! The penny at last drops. The dangers of copying code that works in 32 bit, but no longer in 64 bit.

 

My DLL is 64bit, but when I created a test executable to try and send the message I must have compiled it as 32 bit rather than 64 so it worked.

 

The SendMessage needed to be:

WinAPI.Windows.SendMessage(Hdl, WM_COPYDATA, 0, LPARAM(@copyDataStruct));

That was the only reason the message was not getting through and had I realised this 48 hours ago I might have some hair left. 

 

It's time for that Lockdown drink! I will raise a glass to you

  • Like 1

Share this post


Link to post

BTW should have noted there was no need for ChangeWindowMessageFilterEx, but good to know about it.

Edited by Mark Williams

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×