Search code examples
delphidelphi-2007delphi-10.1-berlin

Delphi 2007->10.1 Berlin Port: Solving E2251 Ambiguious overloaded call to StrLen


Background: Porting my code to Delphi 10.1 Berlin and working through the third party libraries. Some are no longer available so I will try to fix the code...

The following code (passing params from one instance of a program to another) raises E2251 Ambiguious overloaded call to StrLen. I understand why, I just don't know the best way to resolve it.

type
  PInstInfo = ^TInstInfo;
  TInstInfo = packed record
    FirstInstanceWnd:HWND;
    ParamCount:Integer;
    Params:Array[0..MAX_PARAMS-1, 0..MAX_PARAM_SIZE] of Char;
  end;

// Memory is filled with:
 lpInfo^.ParamCount:=ParamCount;
 if lpInfo^.ParamCount>MAX_PARAMS then
  lpInfo^.ParamCount:=MAX_PARAMS;
 for i:=0 to lpInfo^.ParamCount-1 do
 begin
  tempStr:=ParamStr(i+1);
  if length(tempStr)>MAX_PARAM_SIZE then
   setLength(tempStr,MAX_PARAM_SIZE);
  StrCopy(@(lpInfo^.Params[i,0]),PChar(tempStr));
 end;
 // and notify the first instance
 PostMessage(lpInfo^.FirstInstanceWnd, MSG_2ND_INSTANCE, 0, 0);



// And read using:
   if lpInfo <> nil then
   try
    // get Parameters
    params:=TStringList.Create;
    try
     for i:=0 to lpInfo^.ParamCount-1 do
     begin
      SetString(tempStr,
       PChar(@(lpInfo^.Params[i,0])),
       StrLen(@(lpInfo^.Params[i,0])));  <--- E2251 Ambiguious overloaded call to StrLen
      params.Add(tempStr);
     end;
     InstanceStarted(params);
    finally
     params.Free;
    end;

Thanks


Solution

  • By default, the @ address operator produces an untyped pointer. There are two overloaded versions of StrLen(), one that takes a PAnsiChar and one that takes a PWideChar. An untyped pointer can be passed to both overloads, thus the ambiguity. The PWideChar overload did not exist in Delphi 2007, which is why the code compiled before.

    To fix the code, you will have to either:

    1. use the {$TYPEDADDRESS ON} or {$T+} compiler directive to enable Type-checked pointers so taking the address of a Char variable using the @ operator will produce a typed PChar pointer instead of an untyped pointer.

      {$TYPEDADDRESS ON}
      SetString(tempStr,
        @(lpInfo^.Params[i,0]),
        StrLen(@(lpInfo^.Params[i,0])));
      
    2. use the same type-cast you use in the 2nd parameter of SetString():

      SetString(tempStr,
        PChar(@(lpInfo^.Params[i,0])),
        StrLen(PChar(@(lpInfo^.Params[i,0]))));
      
    3. Get rid of the calls to SetString() and StrLen(), since a null-terminated character pointer can be assigned directly to a String variable:

      tempStr := PChar(@(lpInfo^.Params[i,0]));
      

      {$TYPEDADDRESS ON}
      tempStr := @(lpInfo^.Params[i,0]);
      

    With that said, do be aware that the Char data type changed from Ansi to Unicode in D2009, so this code will only work when sending the parameters to Unicode versions of your app, not to Ansi versions. If you need to continue supporting older versions of your app, you should define a different window message for passing Unicode parameters, and then have your Unicode app support both messages for receiving, and analyze the target HWND to decide which message to use for sending.