Jump to content
Dave Nottage

Citrix Virtual Channel DLL problem

Recommended Posts

Not sure exactly which forum this fits in, so I've posted this here. Some background:

 

Citrix allows developers to create their own "Virtual Channel" DLLs using their SDK:

 

  https://www.citrix.com/community/citrix-developer/xenapp-xendesktop/virtual-channel-sdk.html

 

Originally, I attempted to produce such a DLL purely in Delphi, however I was not able to make Citrix make the required calls to the DLL, so (long story short), I created a "conduit" DLL in C that loads a Delphi DLL and redirects all the required calls to it.

 

Over time, I have built functionality into the Delphi DLL, and it has all been working fine; that is up until I attempted to add support for scanning using DelphiTwain:

 

  http://kluug.net/delphitwain.php

 

The problem now: The mere fact that the DelphiTwain code is linked in to the DLL causes it to crash within Citrix. I am not creating any of the classes, and therefore none of the actual DelphiTwain code actually executes - there are no class constructors or unit initialization code, unless there is something implicit that I don't know about.  Regardless of all that, the DLL actually successfully loads. There is a certain sequence of calls that Citrix makes when loading the DLL, and I have logging to show when these events occur. It is only when it reaches a certain point that the "crash" occurs, which for those who are familiar with Citrix Virtual Channels, happens somewhere between the call to DriverOpen and DriverSetInformation.

 

I have spent a fair amount of time attempting to track down the issue, including producing a "cut down" version of DelphiTwain in order to isolate what code is being included that causes the "crash". There appears to be no rhyme or reason as to why certain parts of the non-executing code would affect it in this way, so I'm reaching out here in case someone is able to shed some light on it. As an example, recently I discovered for this section of code:

function TTwainSource.GetEnumerationValue(Capability: TW_UINT16; var ItemType: TW_UINT16; var List: TGetCapabilityList; var Current, Default: Integer;
  Mode: TRetrieveCap; MemHandle: HGLOBAL): TCapabilityRet;
var
  EnumV: pTW_ENUMERATION;
  ItemSize: Integer;
  Data: PAnsiChar; //ccc
  CurItem: Integer;
  Value: string;
  Container: TW_UINT16;
begin
  {Call method to get the memory to the return}
  if MemHandle = 0 then
    Result := GetCapabilityRec(Capability, MemHandle, Mode, Container)
  else
  begin
    Result := crSuccess;
    Container := TWON_ENUMERATION;
  end;

  if (Result = crSuccess) and (Container <> TWON_ENUMERATION) then
  begin
    Result := crInvalidContainer;
    GlobalFree(MemHandle);
    Exit;
  end;

  {If result was sucessfull and memory was allocated}
  if (Result = crSuccess) then
  begin
    {Obtain structure pointer}
    EnumV := GlobalLock(MemHandle);

    {Fill return properties}
    Current := EnumV^.CurrentIndex;
    Default := EnumV^.DefaultIndex;
    ItemType := EnumV^.ItemType;

    {Prepare to list items}
    // ItemSize := TWTypeSize(ItemType);  //!!!!!!!! Citrix crash (non-running code)
    Data := @EnumV^.ItemList[0];
    SetLength(List, EnumV^.NumItems);

    {Copy items}
    for CurItem := 0 to EnumV^.NumItems - 1 do
    begin
      {Obtain this item}
//      GetItem(Value, ItemType, Data);
      // Value := GetItemValue(ItemType, Data);
//      List[CurItem] := Value;
      {Move memory to the next}
      inc(Data, ItemSize);
    end;

    {Unlock memory and unallocate}
    GlobalUnlock(MemHandle);
    GlobalFree(MemHandle);
  end {if (Result = crSuccess)}
end;

As can be seen by my comment, having the line that calls TWTypeSize causes a "crash", whereas commenting it out does not. TWTypeSize looks like this:

 

function TWTypeSize(TypeName: TW_UINT16): Integer;
begin
  {Test the type to return the size}
  case TypeName of
    TWTY_INT8:
      Result := sizeof(TW_INT8);
    TWTY_UINT8:
      Result := sizeof(TW_UINT8);
    TWTY_INT16:
      Result := sizeof(TW_INT16);
    TWTY_UINT16:
      Result := sizeof(TW_UINT16);
    TWTY_INT32:
      Result := sizeof(TW_INT32);
    TWTY_UINT32:
      Result := sizeof(TW_UINT32);
    TWTY_FIX32:
      Result := sizeof(TW_FIX32);
    TWTY_FRAME:
      Result := sizeof(TW_FRAME);
    TWTY_STR32:
      Result := sizeof(TW_STR32);
    TWTY_STR64:
      Result := sizeof(TW_STR64);
    TWTY_STR128:
      Result := sizeof(TW_STR128);
    TWTY_STR255:
      Result := sizeof(TW_STR255);
    //npeter: the following types were not implemented
    //especially the bool caused problems
    TWTY_BOOL:
      Result := sizeof(TW_BOOL);
    TWTY_UNI512:
      Result := sizeof(TW_UNI512);
    TWTY_STR1024:
      Result := sizeof(TW_STR1024);
  else
    Result := 0;
  end {case}
end;

Again, this is code that is *never* even executed, because none of the classes from DelphiTwain are even created. Sadly, Citrix's diagnostics are insufficient to discover what the problem is.

 

I'm hoping that someone might have an idea of why this might happen.

 

 

Share this post


Link to post

Wild guess: Does it automatically load other DLLs? And do those DLLs maybe load other DLLs as well?

I had similar problems with OpenCV DLLs where the DLL I actually loaded needed another one that wasn't there. That crashed my program without any meaningful error message.

Share this post


Link to post
1 hour ago, dummzeuch said:

Wild guess: Does it automatically load other DLLs? And do those DLLs maybe load other DLLs as well?

I had similar problems with OpenCV DLLs where the DLL I actually loaded needed another one that wasn't there. That crashed my program without any meaningful error message.

No, the DelphiTwain code uses dynamic binding, and only attempts to load the Twain DLL when the Delphi Twain object is created, which is not even happening yet. 

 

Regardless, why would the Citrix VC load sequence succeed with that line of code (for example) commented out (when the routine is not even executed at all - I have had log statements inserted before as a sanity check), and not succeed if it isn't?

Edited by Dave Nottage

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

×