Problems with ActiveX: WIA flatbed scanner lazarus / 34-Windows ---------------------------------------------------------------------- BigChimp · 2014-04-04 10:28 Hi guys, ActiveX newbie (in Lazarus at least). Using FPC 2.7.1 as well as 2.6.4, have tried to slap together support for scanning from WIA scanners... and failed. Project attached, including converted WIA 2 (Vista+) headers. Code runs into a SIGSEGV on the line indicated: Code: [Select]procedure TLocalWIAScanner.Scan; // Adapted from // http://stackoverflow.com/questions/721948/delphi-twain-issue-help var   i:integer;   Scanner: Device;   Picture: IItem;   Image: OleVariant;   AImage: IImageFile;   ReturnString: string; begin   try     // List of devices is a 1 based array     showmessage('number of devices: '+inttostr(FDevMgr.DeviceInfos.Count));     for i:=1 to FDevMgr.DeviceInfos.Count do     begin       // sigsegv happens in line below===============================>>>>>>       ReturnString:=utf8encode(FDevMgr.DeviceInfos[POleVariant(i)].Pro perties[POleVariant('Name')].get_Value); I've also tried defining an OLEVariant with value Name in advance and passing the address to the Properties call but no change. All suggestions welcome - thanks, BigChimp ---------------------------------------------------------------------- marcov · 2014-04-04 16:11 I find the polevariant(i) funny. That is typecasting an integer to a pointer. Declare an olevariant for i, assign i to it on every iteration, and then pass that (or the pointer) in the crashing line. ---------------------------------------------------------------------- BigChimp · 2014-04-04 16:21 Well, I found it funny as well (after some forum searching) I have also tried this Code: [Select]var   i:integer;   Scanner: Device;   Picture: IItem;   Image: OleVariant;   InVar: OLEVariant;   AImage: IImageFile;   ReturnString: string; begin   try     // List of devices is a 1 based array     showmessage('number of devices: '+inttostr(FDevMgr.DeviceInfos.Count));     for i:=1 to FDevMgr.DeviceInfos.Count do     begin       InVar:=i;       // sigsegv happens in line below===============================>>>>>>       ReturnString:=utf8encode(FDevMgr.DeviceInfos[@InVar].Properties[ POleVariant('Name')].get_Value);       showmessage('Device: '+ReturnString); with a similar error (see attachment) Perhaps I should also declare a separate OLEVariant for 'Name' and assign that to pass its address to the Properties call? ---------------------------------------------------------------------- marcov · 2014-04-04 16:45 Quote from: BigChimp on April 04, 2014, 06:21:21 pmPerhaps I should also declare a separate OLEVariant for 'Name' and assign that to pass its address to the Properties call? Yes. Maybe there are shortcuts to do it inline, but in doubt, do it by the book. ---------------------------------------------------------------------- BigChimp · 2014-04-04 16:54 Thanks. Ah, discovering the book as we go along This works better - corrected Type and Name capitalization: Code: [Select]var   i:integer;   Scanner: Device;   Picture: IItem;   Image: OleVariant;   InVar: OLEVariant;   StringVar: OleVariant;   OutVar: OLEVariant;   AImage: IImageFile;   ReturnString: string; begin   try     // List of devices is a 1 based array     showmessage('number of devices: '+inttostr(FDevMgr.DeviceInfos.Count));     for i:=1 to FDevMgr.DeviceInfos.Count do     begin       InVar:=i;       // Only check scanners (ignore cameras etc)       StringVar:='Type';       OutVar:=FDevMgr.DeviceInfos[@InVar].Properties[@StringVar].get_Value; //Const ScannerDeviceType = $0000000000000001;       if OutVar=ScannerDeviceType then       begin         StringVar:='Name';         ReturnString:=utf8encode(FDevMgr.DeviceInfos[@InVar].Propertie s[@StringVar].get_Value);         showmessage('Device: '+ReturnString);       end       else       begin         showmessage('found a device but it is not a scanner');         OutVar:=FDevMgr.DeviceInfos[@InVar].Properties[@StringVar].get_Value;         showmessage('got device type '+utf8encode(OutVar));       end;     end; However,  if OutVar=ScannerDeviceType then goes wrong - first device is a scanner but not detected as such: OutVar contains 65537 when evaluated as VINTEGER in the debugger - what is the "by the book" conversion to compare to a constant? Convert it to word or something? Sounds a bit arbitrary... ---------------------------------------------------------------------- marcov · 2014-04-04 17:06 Quote from: BigChimp on April 04, 2014, 06:54:13 pm //Const ScannerDeviceType = $0000000000000001;   However,  if OutVar=ScannerDeviceType then goes wrong - first device is a scanner but not detected as such: OutVar contains 65537 when evaluated as VINTEGER in the debugger - what is the "by the book" conversion to compare to a constant? Convert it to word or something? Sounds a bit arbitrary... 65537 mod (2**16)=1. IOW it sounds like a 16-bits value is interpreted as a 32-bit value leading to an extra bit? Or maybe only the lower bits of the value are the scanner type? ---------------------------------------------------------------------- BigChimp · 2014-04-05 10:22 Thanks for the help so far! Well, the importer defined the const as longword (4 byte) value... MSDN doesn't mention the type is used for anything else so I wouldn't expect to need to only consider the lower bits... http://msdn.microsoft.com/en- us/library/windows/desktop/ms630792%28v=vs.85%29.aspx Forcing the result to be seen as a word value did work Code: [Select]      // Apparently need to force result to 4 bytes to compare to constant:       if word(OutVar)=ScannerDeviceType then ... and now am able to get the scanner name as well using widestring instead of just string: Code: [Select]procedure TLocalWIAScanner.Scan; // Adapted from // http://stackoverflow.com/questions/721948/delphi-twain-issue-help var   i:integer;   Scanner: Device;   Picture: IItem;   Image: OleVariant;   InVar: OLEVariant;   StringVar: OleVariant;   OutVar: OLEVariant;   AImage: IImageFile;   ReturnString: widestring; begin   try     // List of devices is a 1 based array     showmessage('number of devices: '+inttostr(FDevMgr.DeviceInfos.Count));     for i:=1 to FDevMgr.DeviceInfos.Count do     begin       InVar:=i;       // Only check scanners (ignore cameras etc)       StringVar:='Type';       OutVar:=FDevMgr.DeviceInfos[@InVar].Properties[@StringVar].get_Value;       // Apparently need to force result to 4 bytes to compare to constant:       if word(OutVar)=ScannerDeviceType then       begin         StringVar:='Name';         if FDevMgr.DeviceInfos[@InVar].Properties.Exists(StringVar) then         begin           ReturnString:=FDevMgr.DeviceInfos[@InVar].Properties[@String Var].get_Value;           showmessage('Device: '+utf8encode(ReturnString));         end         else         begin           ShowMessage('Name property does not exist. Aborting.');           exit;         end;       end       else       begin         showmessage('found a device but it is not a scanner');         OutVar:=FDevMgr.DeviceInfos[@InVar].Properties[@StringVar].get_Value;         showmessage('got device type '+utf8encode(OutVar));       end;     end;     //=========< code works until here - will need to fix stuff below >==============     // This takes the first device. Todo: Figure out first scanner (adapt code above)     Scanner:=FDevMgr.DeviceInfos.Item[POleVariant(1)].Connect;     // to do: figure out which command scans     //reference: wia item property constants - grayscale deskew etc     //http://msdn.microsoft.com/en-us/library/ms630196%28v=VS.85%29     Picture := Scanner.ExecuteCommand(Scanner.Commands.Item[1].CommandID);     { todo: add scan selection dialog:     lDialog.ShowAcquireImage(WIA_TLB.ScannerDeviceType,WIA_TLB.Graysca leIntent,WIA_TLB.MinimizeSize,            jpegFormat,false,false,false);     }     //Transfer as JPG if scanner supports it. todo: change to bmp or tiff     Image := Picture.Transfer(Picture.Formats.Item[1]);     { todo: image may need to be converted to bmp/tiff     http://msdn.microsoft.com/en- us/library/ms630826%28v=VS.85%29.aspx#SharedSample002     }     //Save the image     {todo: replace with in memory manipulation     IImageFile has a property FileData with provides access to the binary image data, via IVector.BinaryData     via vector binarydata - an array of bytes     http://msdn.microsoft.com/en- us/library/windows/desktop/ms630518%28v=vs.85%29.aspx     }     AImage := IImageFile(Image);     AImage.SaveFile('wiaimage.' + AImage.FileExtension);   except     //for now     //to do: debug     on E: Exception do     begin       writeln(stderr,'Error scanning locally: '+E.Message);     end;   end; end; Will report when I hit more problems or have got everything working