Visual Studio - Get the startup project programmatically - c#

I'm building a .Net application that should import a VS2010/2008 solution and find the start up project by reading the .suo file. I looked at How do I programmatically find out the Action of each StartUp Project in a solution? but the solution mentioned works only if I select Multiple Startup Projects in the Solution Properties window. My solution does not have multiple startup project. Is there a way to find the startup project when the solution has only 1 startup proj?
Here's the code
public static class StartUpProjectHelper
{
public static FileInfo GetStartUpProject(FileInfo solutionFile)
{
FileInfo startUpProject = null;
string suoFile = solutionFile.FullName.Substring(0, solutionFile.FullName.Length - 4) + ".suo";
string guid = null;
bool found = false;
foreach (var kvp in ReadStartupOptions(suoFile))
{
if (((kvp.Value & 1) != 0 || (kvp.Value & 2) != 0) && !found)
{
guid = kvp.Key.ToString();
found = true;
}
}
if (!string.IsNullOrEmpty(guid))
{
string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');
startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
}
return startUpProject;
}
public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
{
string projectName = null;
using (var reader = new StreamReader(solutionFile.FullName))
{
string line;
bool found = false;
while ((line = reader.ReadLine()) != null && !found)
{
// sample format
//Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Saltus.digiTICKET.laptop", "digiTICKET.laptop\Saltus.digiTICKET.laptop.csproj",
//"{236D51A1-DEB7-41C3-A4C1-1D16D0A85382}" EndProject
if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
{
projectName = line.Split(',')[1].Split(',')[0];
found = true;
}
}
}
return projectName;
}
//// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
public static IDictionary<Guid, int> ReadStartupOptions(string filePath)
{
if (filePath == null)
{
throw new InvalidOperationException("No file selected");
}
// look for this token in the file
const string token = "dwStartupOpt\0=";
byte[] tokenBytes = Encoding.Unicode.GetBytes(token);
var dic = new Dictionary<Guid, int>();
byte[] bytes;
using (var stream = new MemoryStream())
{
ExtractStream(filePath, "SolutionConfiguration", stream);
bytes = stream.ToArray();
}
int i = 0;
do
{
bool found = true;
for (int j = 0; j < tokenBytes.Length; j++)
{
if (bytes[i + j] != tokenBytes[j])
{
found = false;
break;
}
}
if (found)
{
// back read the corresponding project guid
// guid is formatted as {guid}
// len to read is Guid length* 2 and there are two offset bytes between guid and startup options token
var guidBytes = new byte[38*2];
Array.Copy(bytes, i - guidBytes.Length - 2, guidBytes, 0, guidBytes.Length);
var guid = new Guid(Encoding.Unicode.GetString(guidBytes));
// skip VT_I4
int options = BitConverter.ToInt32(bytes, i + tokenBytes.Length + 2);
dic[guid] = options;
}
i++;
} while (i < bytes.Length);
return dic;
}
public static void ExtractStream(string filePath, string streamName, Stream output)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
if (streamName == null)
throw new ArgumentNullException("streamName");
if (output == null)
throw new ArgumentNullException("output");
IStorage storage;
int hr = StgOpenStorage(filePath, null, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero, 0, out storage);
if (hr != 0)
throw new Win32Exception(hr);
try
{
IStream stream;
hr = storage.OpenStream(streamName, IntPtr.Zero, STGM.READ | STGM.SHARE_EXCLUSIVE, 0, out stream);
if (hr != 0)
throw new Win32Exception(hr);
int read = 0;
IntPtr readPtr = Marshal.AllocHGlobal(Marshal.SizeOf(read));
try
{
var bytes = new byte[0x1000];
do
{
stream.Read(bytes, bytes.Length, readPtr);
read = Marshal.ReadInt32(readPtr);
if (read == 0)
break;
output.Write(bytes, 0, read);
} while (true);
}
finally
{
Marshal.FreeHGlobal(readPtr);
Marshal.ReleaseComObject(stream);
}
}
finally
{
Marshal.ReleaseComObject(storage);
}
}
[DllImport("ole32.dll")]
private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
out IStorage ppstgOpen);
#region Nested type: IStorage
[ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IStorage
{
void Unimplemented0();
[PreserveSig]
int OpenStream([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr reserved1, STGM grfMode,
uint reserved2, out IStream ppstm);
// other methods not declared for simplicity
}
#endregion
#region Nested type: STGM
[Flags]
private enum STGM
{
READ = 0x00000000,
SHARE_DENY_WRITE = 0x00000020,
SHARE_EXCLUSIVE = 0x00000010,
// other values not declared for simplicity
}
#endregion
}
//// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut``

Here's the solution. It is mostly based on #SimonMourier post, but instead of searching for "dwStartupOpt\0=", I'm looking for "StartupProject\0=&\0". This token occurs only once in the .suo file and is followed by the startup project's GUID.
public static FileInfo GetStartUpProject(FileInfo solutionFile)
{
FileInfo startUpProject = null;
string projectName = Path.GetFileNameWithoutExtension(solutionFile.FullName);
FileInfo suoFileInfo = new FileInfo(Path.Combine(solutionFile.Directory.FullName, string.Format(projectName + "{0}", ".suo")));
string guid = ReadStartupOptions(suoFileInfo.FullName).ToString();
if (!string.IsNullOrEmpty(guid))
{
string projectname = GetProjectNameFromGuid(solutionFile, guid).Trim().TrimStart('\"').TrimEnd('\"');
startUpProject = new FileInfo(Path.Combine(solutionFile.DirectoryName, projectname));
}
return startUpProject;
}
public static string GetProjectNameFromGuid(FileInfo solutionFile, string guid)
{
string projectName = null;
using (var reader = new StreamReader(solutionFile.FullName))
{
string line;
bool found = false;
while ((line = reader.ReadLine()) != null && !found)
{
if ((line.IndexOf(guid.ToUpper()) > -1) && line.Contains(",") && line.Contains("="))
{
projectName = line.Split(',')[1].Split(',')[0];
found = true;
}
}
}
return projectName;
}
// from https://stackoverflow.com/questions/8817693/how-do-i-programmatically-find-out-the-action-of-each-startup-project-in-a-solut
public static Guid ReadStartupOptions(string filePath)
{
Guid guid = new Guid();
if (filePath == null)
{
throw new InvalidOperationException("No file selected");
}
const string token = "StartupProject\0=&\0";
byte[] tokenBytes = Encoding.Unicode.GetBytes(token);
byte[] bytes;
using (var stream = new MemoryStream())
{
ExtractStream(filePath, "SolutionConfiguration", stream);
bytes = stream.ToArray();
}
var guidBytes = new byte[36 * 2];
for (int i2 = 0; i2 < bytes.Length; i2++)
{
if (bytes.Skip(i2).Take(tokenBytes.Length).SequenceEqual(tokenBytes))
{
Array.Copy(bytes, i2 + tokenBytes.Length + 2, guidBytes, 0, guidBytes.Length);
guid = new Guid(Encoding.Unicode.GetString(guidBytes));
break;
}
}
return guid;
}
public static void ExtractStream(string filePath, string streamName, Stream output)
{
if (filePath == null)
throw new ArgumentNullException("filePath");
if (streamName == null)
throw new ArgumentNullException("streamName");
if (output == null)
throw new ArgumentNullException("output");
IStorage storage;
int hr = StgOpenStorage(filePath, null, STGM.READ | STGM.SHARE_DENY_WRITE, IntPtr.Zero, 0, out storage);
if (hr != 0)
throw new Win32Exception(hr);
try
{
IStream stream;
hr = storage.OpenStream(streamName, IntPtr.Zero, STGM.READ | STGM.SHARE_EXCLUSIVE, 0, out stream);
if (hr != 0)
throw new Win32Exception(hr);
int read = 0;
IntPtr readPtr = Marshal.AllocHGlobal(Marshal.SizeOf(read));
try
{
var bytes = new byte[0x1000];
do
{
stream.Read(bytes, bytes.Length, readPtr);
read = Marshal.ReadInt32(readPtr);
if (read == 0)
break;
output.Write(bytes, 0, read);
} while (true);
}
finally
{
Marshal.FreeHGlobal(readPtr);
Marshal.ReleaseComObject(stream);
}
}
finally
{
Marshal.ReleaseComObject(storage);
}
}
[DllImport("ole32.dll")]
private static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)] string pwcsName,
IStorage pstgPriority, STGM grfMode, IntPtr snbExclude, uint reserved,
out IStorage ppstgOpen);
#region Nested type: IStorage
[ComImport, Guid("0000000b-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IStorage
{
void Unimplemented0();
[PreserveSig]
int OpenStream([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, IntPtr reserved1, STGM grfMode,
uint reserved2, out IStream ppstm);
// other methods not declared for simplicity
}
#endregion
#region Nested type: STGM
[Flags]
private enum STGM
{
READ = 0x00000000,
SHARE_DENY_WRITE = 0x00000020,
SHARE_EXCLUSIVE = 0x00000010,
// other values not declared for simplicity
}
#endregion

Related

strings breaking coming back from unmanaged code C++

I'm creating a wrapper for an unmanaged C++ function to be called using C#.
The function returns a vector of structs.
When returning from the function, the strings are all ok, but after the 'return' to the wrapper, the strings break returning weird characters.
The weird thing is, if I call it a second time, without closing Visual Studio, it works!
I have saved all the files as Unicode UTF-8, set Unicode as the character set on the Visual Studio project, defined UNICODE and _UNICODE, but still the problem persists.
This is the unmanaged struct:
typedef struct SessionEnumOutput {
SessionEnumOutput() {};
wchar_t *UserName;
wchar_t *SessionName;
WtsSessionState SessionState;
}SessionEnumOutput, *PSessionEnumOutput;
It being built on the unmanaged function:
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
if (innerSes.State == WTSActive)
{
wchar_t *sessionName;
wchar_t *sessUserName;
SessionEnumOutput outObj;
if (innerSes.pUserName == NULL) { sessUserName = L"System"; }
else { sessUserName = innerSes.pUserName; }
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
The managed wrapper:
ref class wSessionEnumOutput
{
public:
String^ UserName;
String^ SessionName;
wWtsSessionState SessionState;
};
List<wSessionEnumOutput^>^ GetEnumeratedSession(String^ computerName, bool onlyActive, bool excludeSystemSessions)
{
pin_ptr<const wchar_t> wName = PtrToStringChars(computerName);
List<wSessionEnumOutput^>^ output = gcnew List<wSessionEnumOutput^>();
vector<Unmanaged::SessionEnumOutput> *result = new vector<Unmanaged::SessionEnumOutput>;
*result = ptr->GetEnumeratedSession((LPWSTR)wName, onlyActive, excludeSystemSessions);
for (size_t it = 0; it < result->size(); it++)
{
Unmanaged::SessionEnumOutput single = result->at(it);
wSessionEnumOutput^ inner = gcnew wSessionEnumOutput();
inner->UserName = Marshal::PtrToStringUni((IntPtr)single.UserName);
inner->SessionName = Marshal::PtrToStringUni((IntPtr)single.SessionName);
inner->SessionState = (wWtsSessionState)single.SessionState;
output->Add(inner);
}
I can see the strings broken at *vectorUnmanaged::SessionEnumOutput result = new vectorUnmanaged::SessionEnumOutput;
I have created a test console to call the C# function twice to analyze the heap:
List<Managed.wSessionEnumOutput> thing = Utilities.GetComputerSession();
Console.WriteLine("###################################################");
for (int i = 0; i < thing.Count; i++)
{
Console.WriteLine("User name: " + thing[i].UserName);
Console.WriteLine("Session name: " + thing[i].SessionName);
Console.WriteLine("Session state: " + thing[i].SessionState);
Console.WriteLine("###################################################");
}
Console.WriteLine("\n");
thing = Utilities.GetComputerSession();
Console.WriteLine("###################################################");
for (int i = 0; i < thing.Count; i++)
{
Console.WriteLine("User name: " + thing[i].UserName);
Console.WriteLine("Session name: " + thing[i].SessionName);
Console.WriteLine("Session state: " + thing[i].SessionState);
Console.WriteLine("###################################################");
}
The difference is, on the second call, I can see Unicode and UTF-8 decoders loaded on the heap.
On the first call, they are not there.
here are both call's results:
I'm not a developer, just a curious system administrator, so pardon my coding habilities.
What am I missing?
EDIT:
Function definition:
vector<Unmanaged::SessionEnumOutput> Unmanaged::GetEnumeratedSession(
LPWSTR computerName = NULL,
BOOL onlyActive = 0,
BOOL excludeSystemSessions = 0
)
{
HANDLE session;
BOOL enumResult;
DWORD pCount = 0;
DWORD pLevel = 1;
vector<SessionEnumOutput> output;
PWTS_SESSION_INFO_1 sessionInfo = (PWTS_SESSION_INFO_1)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WTS_SESSION_INFO_1));
if (computerName != NULL)
{
session = WTSOpenServer(computerName);
if (session == NULL) { goto END; }
}
else { session = WTS_CURRENT_SERVER_HANDLE; }
enumResult = WTSEnumerateSessionsEx(session, &pLevel, 0, &sessionInfo, &pCount);
if (enumResult == 0) { goto END; }
switch (onlyActive)
{
case 1:
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
if (innerSes.State == WTSActive)
{
wchar_t *sessionName;
wchar_t *sessUserName;
SessionEnumOutput outObj;
if (innerSes.pUserName == NULL) { sessUserName = L"System"; }
else { sessUserName = innerSes.pUserName; }
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
break;
default:
if (excludeSystemSessions == 0)
{
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
wchar_t* sessionName;
wchar_t* sessUserName;
SessionEnumOutput outObj;
if (innerSes.pUserName == NULL) { sessUserName = L"System"; }
else { sessUserName = innerSes.pUserName; }
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
else
{
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
wstring sessUserName;
if (innerSes.pUserName == NULL) { sessUserName = L""; }
else { sessUserName = innerSes.pUserName; }
if (sessUserName.length() > 0)
{
wchar_t *innerUser = (wchar_t*)sessUserName.c_str();
wchar_t *sessionName;
SessionEnumOutput outObj;
WTS_SESSION_INFO_1 innerSes = sessionInfo[i];
if (innerSes.pSessionName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pSessionName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = innerUser;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
}
}
}
break;
}
END:
if (session != NULL) { WTSCloseServer(session); }
if (pCount > 0) { WTSFreeMemoryEx(WTSTypeSessionInfoLevel1, sessionInfo, pCount); }
return output;
}
Solved the mystery!
Mr. CharlieFace on the comments posted another question where they are discussing the unespected behavior of the function WTSEnumerateSessionsEx.
Unfortunately, this is an issue happening on Windows for some time now.
I've followed the approach of calling WTSEnumerateSessions and then WTSQuerySessionInformation to get the user name.
Fragment:
PWTS_SESSION_INFO sessionInfo = (PWTS_SESSION_INFO)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WTS_SESSION_INFO));
if (computerName != NULL)
{
session = WTSOpenServer(computerName);
if (session == NULL) { goto END; }
}
else { session = WTS_CURRENT_SERVER_HANDLE; }
enumResult = WTSEnumerateSessions(session, 0, 1, &sessionInfo, &pCount);
if (enumResult == 0) { goto END; }
switch (onlyActive)
{
case 1:
for (DWORD i = 0; i < pCount; i++)
{
WTS_SESSION_INFO innerSes = sessionInfo[i];
if (innerSes.State == WTSActive)
{
wchar_t *sessionName;
wchar_t *sessUserName;
LPWSTR ppBuffer;
DWORD pBytesReturned;
BOOL thisResult;
thisResult = WTSQuerySessionInformation(session, innerSes.SessionId, WTSUserName, &ppBuffer, &pBytesReturned);
if (thisResult == 0) { goto END; }
if (ppBuffer == NULL) { sessUserName = L"System"; }
else { sessUserName = ppBuffer; }
if (innerSes.pWinStationName == NULL) { sessionName = L""; }
else { sessionName = innerSes.pWinStationName; }
Unmanaged::SessionEnumOutput inner;
inner.UserName = sessUserName;
inner.SessionName = sessionName;
inner.SessionState = (WtsSessionState)innerSes.State;
output.push_back(inner);
WTSFreeMemory(&ppBuffer);
}
}
break;
Look how pretty that is!
Sources:
Why does WTSFreeMemoryExA always return ERROR_INVALID_PARAMETER when passed a WTSTypeClass of WTSTypeSessionInfoLevel1?
Memory leak issues with Windows API call - Delphi
Thank you very much for the help!
As I mentioned, there is an outstanding bug involving the WTS functions when used with the ANSI versions. Instead you should use the Unicode versions.
See Why does WTSFreeMemoryExA always return ERROR_INVALID_PARAMETER when passed a WTSTypeClass of WTSTypeSessionInfoLevel1?
I think using a C++/CLI wrapper for this to be able to use in C# is overkill. You should be able to do this using standard PInvoke marshalling in C#.
It's best not to rely on BestFitMapping, and instead specify the function names explicitly.
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern IntPtr WTSOpenServerExW (string pServerName);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern void WTSCloseServer(IntPtr hServer);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern bool WTSEnumerateSessionsExW(
IntPtr hServer,
ref int pLevel,
int Filter,
out IntPtr ppSessionInfo,
out int pCount
);
[DllImport("Wtsapi32.dll", CharSet = CharSet.Unicode, BestFitMapping = false, SetLastError = true)]
static extern bool WTSFreeMemoryExW(
WTS_TYPE_CLASS WTSTypeClass,
IntPtr pMemory,
int NumberOfEntries
);
enum WTS_TYPE_CLASS
{
WTSTypeProcessInfoLevel0,
WTSTypeProcessInfoLevel1,
WTSTypeSessionInfoLevel1
}
enum WtsSessionState
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct WTS_SESSION_INFO
{
public int ExecEnvId;
public WtsSessionState State;
public int SessionId;
[MarshalAs(UnmanagedType.LPTStr)]
public string pSessionName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pHostName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pUserName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pDomainName;
[MarshalAs(UnmanagedType.LPTStr)]
public string pFarmName;
}
List<SessionEnumOutput> GetEnumeratedSession(
string computerName = null,
bool onlyActive = false,
bool excludeSystemSessions = false
)
{
IntPtr server = default;
IntPtr sessionInfo = default;
int pCount = default;
List<SessionEnumOutput> output = new List<SessionEnumOutput>();
if (computerName != null)
{
server = WTSOpenServerExW(computerName);
if (server == IntPtr.Zero || server == new IntPtr(-1))
throw new Exception("Invalid computer name");
}
try
{
int pLevel = 1;
if (!WTSEnumerateSessionsExW(server, ref pLevel, 0, out sessionInfo, out pCount))
throw new Win32Exception(Marshal.GetLastWin32Error());
for (var i = 0; i < pCount; i++)
{
WTS_SESSION_INFO innerSes = Marshal.PtrToStructure<WTS_SESSION_INFO>(sessionInfo + i * Marshal.SizeOf<WTS_SESSION_INFO>());
if (onlyActive && innerSes.State != WtsSessionState.WTSActive
|| excludeSystemSessions && innerSes.pSessionName == null)
continue;
SessionEnumOutput inner = new SessionEnumOutput
{
UserName = innerSes.pUserName ?? "System",
SessionName = innerSes.pSessionName ?? "",
SessionState = innerSes.State,
};
output.Add(inner);
};
}
finally
{
if (sessionInfo != default)
WTSFreeMemoryExW(WTS_TYPE_CLASS.WTSTypeSessionInfoLevel1, sessionInfo, pCount);
if (server != default)
WTSCloseServer(server);
}
return output;
}
Note the use of a finally to enure memory is freed correctly.
There is also a huge amount of duplicated code. I have cleaned up the rest of the function significantly.

FFmpeg.swr_convert: audio to raw 16 bit pcm, to be used with xna SoundEffect. Audio cuts out when i convert

I want to resample mkv(vp8/ogg) and also raw 4 bit adpcm to raw 16bit pcm byte[] to be loaded into SoundEffect from xna library. So I can play it out while I'm using other code to display the frames (the video side is working).
I can read a 16 bit wav file and play it. But when I goto resample something it doesn't play 100%. One file is 3 mins and 15 secs. I only get 13 sec and 739 ms before it quits playing. I have been learning to do this by finding code samples in c++ and correcting it to work in c# using ffmpeg.autogen.
the below is my best attempt at resampling.
int nb_samples = Frame->nb_samples;
int output_nb_samples = nb_samples;
int nb_channels = ffmpeg.av_get_channel_layout_nb_channels(ffmpeg.AV_CH_LAYOUT_STEREO);
int bytes_per_sample = ffmpeg.av_get_bytes_per_sample(AVSampleFormat.AV_SAMPLE_FMT_S16) * nb_channels;
int bufsize = ffmpeg.av_samples_get_buffer_size(null, nb_channels, nb_samples,
AVSampleFormat.AV_SAMPLE_FMT_S16, 1);
byte*[] b = Frame->data;
fixed (byte** input = b)
{
byte* output = null;
ffmpeg.av_samples_alloc(&output, null,
nb_channels,
nb_samples,
(AVSampleFormat)Frame->format, 0);//
// Buffer input
Ret = ffmpeg.swr_convert(Swr, &output, output_nb_samples / 2, input, nb_samples);
CheckRet();
WritetoMs(output, 0, Ret * bytes_per_sample);
output_nb_samples -= Ret;
// Drain buffer
while ((Ret = ffmpeg.swr_convert(Swr, &output, output_nb_samples, null, 0)) > 0)
{
CheckRet();
WritetoMs(output, 0, Ret * bytes_per_sample);
output_nb_samples -= Ret;
}
}
I changed that all to this but it cuts off sooner.
Channels = ffmpeg.av_get_channel_layout_nb_channels(OutFrame->channel_layout);
int nb_channels = ffmpeg.av_get_channel_layout_nb_channels(ffmpeg.AV_CH_LAYOUT_STEREO);
int bytes_per_sample = ffmpeg.av_get_bytes_per_sample(AVSampleFormat.AV_SAMPLE_FMT_S16) * nb_channels;
if((Ret = ffmpeg.swr_convert_frame(Swr, OutFrame, Frame))>=0)
WritetoMs(*OutFrame->extended_data, 0, OutFrame->nb_samples * bytes_per_sample);
CheckRet();
Both code use a function to set Swr it runs one time after the first frame is decoded.
private void PrepareResampler()
{
ffmpeg.av_frame_copy_props(OutFrame, Frame);
OutFrame->channel_layout = ffmpeg.AV_CH_LAYOUT_STEREO;
OutFrame->format = (int)AVSampleFormat.AV_SAMPLE_FMT_S16;
OutFrame->sample_rate = Frame->sample_rate;
OutFrame->channels = 2;
Swr = ffmpeg.swr_alloc();
if (Swr == null)
throw new Exception("SWR = Null");
Ret = ffmpeg.swr_config_frame(Swr, OutFrame, Frame);
CheckRet();
Ret = ffmpeg.swr_init(Swr);
CheckRet();
Ret = ffmpeg.swr_is_initialized(Swr);
CheckRet();
}
This is where I take the output and put it in the sound effect
private void ReadAll()
{
using (Ms = new MemoryStream())
{
while (true)
{
Ret = ffmpeg.av_read_frame(Format, Packet);
if (Ret == ffmpeg.AVERROR_EOF)
break;
CheckRet();
Decode();
}
if (Ms.Length > 0)
{
se = new SoundEffect(Ms.ToArray(), 0, (int)Ms.Length, OutFrame->sample_rate, (AudioChannels)Channels, 0, 0);
//se.Duration; Stream->duration;
see = se.CreateInstance();
see.Play();
}
}
}
I found some code in C++ FFmpeg distorted sound when converting audio adapted it to c#. Slowly tried bits of it in my program. Turns out my decoder was doing something wrong. So all the attempts at resampling or encoding were going to fail.
using FFmpeg.AutoGen;
using System;
using System.IO;
namespace ConsoleApp1
{
//adapted using code from https://stackoverflow.com/questions/32051847/c-ffmpeg-distorted-sound-when-converting-audio?rq=1
public unsafe class Program
{
public static AVStream* in_audioStream { get; private set; }
static unsafe void die(string str)
{
throw new Exception(str);
}
private static unsafe AVStream* add_audio_stream(AVFormatContext* oc, AVCodecID codec_id, int sample_rate = 44100)
{
AVCodecContext* c;
AVCodec* encoder = ffmpeg.avcodec_find_encoder(codec_id);
AVStream* st = ffmpeg.avformat_new_stream(oc, encoder);
if (st == null)
{
die("av_new_stream");
}
c = st->codec;
c->codec_id = codec_id;
c->codec_type = AVMediaType.AVMEDIA_TYPE_AUDIO;
/* put sample parameters */
c->bit_rate = 64000;
c->sample_rate = sample_rate;
c->channels = 2;
c->sample_fmt = encoder->sample_fmts[0];
c->channel_layout = ffmpeg.AV_CH_LAYOUT_STEREO;
// some formats want stream headers to be separate
if ((oc->oformat->flags & ffmpeg.AVFMT_GLOBALHEADER) != 0)
{
c->flags |= ffmpeg.AV_CODEC_FLAG_GLOBAL_HEADER;
}
return st;
}
private static unsafe void open_audio(AVFormatContext* oc, AVStream* st)
{
AVCodecContext* c = st->codec;
AVCodec* codec;
/* find the audio encoder */
codec = ffmpeg.avcodec_find_encoder(c->codec_id);
if (codec == null)
{
die("avcodec_find_encoder");
}
/* open it */
AVDictionary* dict = null;
ffmpeg.av_dict_set(&dict, "strict", "+experimental", 0);
int res = ffmpeg.avcodec_open2(c, codec, &dict);
if (res < 0)
{
die("avcodec_open");
}
}
public static int DecodeNext(AVCodecContext* avctx, AVFrame* frame, ref int got_frame_ptr, AVPacket* avpkt)
{
int ret = 0;
got_frame_ptr = 0;
if ((ret = ffmpeg.avcodec_receive_frame(avctx, frame)) == 0)
{
//0 on success, otherwise negative error code
got_frame_ptr = 1;
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
{
//AVERROR(EAGAIN): input is not accepted in the current state - user must read output with avcodec_receive_packet()
//(once all output is read, the packet should be resent, and the call will not fail with EAGAIN)
ret = Decode(avctx, frame, ref got_frame_ptr, avpkt);
}
else if (ret == ffmpeg.AVERROR_EOF)
{
die("AVERROR_EOF: the encoder has been flushed, and no new frames can be sent to it");
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EINVAL))
{
die("AVERROR(EINVAL): codec not opened, refcounted_frames not set, it is a decoder, or requires flush");
}
else if (ret == ffmpeg.AVERROR(ffmpeg.ENOMEM))
{
die("Failed to add packet to internal queue, or similar other errors: legitimate decoding errors");
}
else
{
die("unknown");
}
return ret;
}
public static int Decode(AVCodecContext* avctx, AVFrame* frame, ref int got_frame_ptr, AVPacket* avpkt)
{
int ret = 0;
got_frame_ptr = 0;
if ((ret = ffmpeg.avcodec_send_packet(avctx, avpkt)) == 0)
{
//0 on success, otherwise negative error code
return DecodeNext(avctx, frame, ref got_frame_ptr, avpkt);
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
{
die("input is not accepted in the current state - user must read output with avcodec_receive_frame()(once all output is read, the packet should be resent, and the call will not fail with EAGAIN");
}
else if (ret == ffmpeg.AVERROR_EOF)
{
die("AVERROR_EOF: the decoder has been flushed, and no new packets can be sent to it (also returned if more than 1 flush packet is sent");
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EINVAL))
{
die("codec not opened, it is an encoder, or requires flush");
}
else if (ret == ffmpeg.AVERROR(ffmpeg.ENOMEM))
{
die("Failed to add packet to internal queue, or similar other errors: legitimate decoding errors");
}
else
{
die("unknown");
}
return ret;//ffmpeg.avcodec_decode_audio4(fileCodecContext, audioFrameDecoded, &frameFinished, &inPacket);
}
public static int DecodeFlush(AVCodecContext* avctx, AVPacket* avpkt)
{
avpkt->data = null;
avpkt->size = 0;
return ffmpeg.avcodec_send_packet(avctx, avpkt);
}
public static int EncodeNext(AVCodecContext* avctx, AVPacket* avpkt, AVFrame* frame, ref int got_packet_ptr)
{
int ret = 0;
got_packet_ptr = 0;
if ((ret = ffmpeg.avcodec_receive_packet(avctx, avpkt)) == 0)
{
got_packet_ptr = 1;
//0 on success, otherwise negative error code
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
{
//output is not available in the current state - user must try to send input
return Encode(avctx, avpkt, frame, ref got_packet_ptr);
}
else if (ret == ffmpeg.AVERROR_EOF)
{
die("AVERROR_EOF: the encoder has been fully flushed, and there will be no more output packets");
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EINVAL))
{
die("AVERROR(EINVAL) codec not opened, or it is an encoder other errors: legitimate decoding errors");
}
else
{
die("unknown");
}
return ret;//ffmpeg.avcodec_encode_audio2(audioCodecContext, &outPacket, audioFrameConverted, &frameFinished)
}
public static int Encode(AVCodecContext* avctx, AVPacket* avpkt, AVFrame* frame, ref int got_packet_ptr)
{
int ret = 0;
got_packet_ptr = 0;
if ((ret = ffmpeg.avcodec_send_frame(avctx, frame)) == 0)
{
//0 on success, otherwise negative error code
return EncodeNext(avctx, avpkt, frame, ref got_packet_ptr);
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EAGAIN))
{
die("input is not accepted in the current state - user must read output with avcodec_receive_packet() (once all output is read, the packet should be resent, and the call will not fail with EAGAIN)");
}
else if (ret == ffmpeg.AVERROR_EOF)
{
die("AVERROR_EOF: the decoder has been flushed, and no new packets can be sent to it (also returned if more than 1 flush packet is sent");
}
else if (ret == ffmpeg.AVERROR(ffmpeg.EINVAL))
{
die("AVERROR(ffmpeg.EINVAL) codec not opened, refcounted_frames not set, it is a decoder, or requires flush");
}
else if (ret == ffmpeg.AVERROR(ffmpeg.ENOMEM))
{
die("AVERROR(ENOMEM) failed to add packet to internal queue, or similar other errors: legitimate decoding errors");
}
else
{
die("unknown");
}
return ret;//ffmpeg.avcodec_encode_audio2(audioCodecContext, &outPacket, audioFrameConverted, &frameFinished)
}
public static int EncodeFlush(AVCodecContext* avctx)
{
return ffmpeg.avcodec_send_frame(avctx, null);
}
public static void Main(string[] argv)
{
//ffmpeg.av_register_all();
if (argv.Length != 2)
{
//fprintf(stderr, "%s <in> <out>\n", argv[0]);
return;
}
// Allocate and init re-usable frames
AVCodecContext* fileCodecContext, audioCodecContext;
AVFormatContext* formatContext, outContext;
AVStream* out_audioStream;
SwrContext* swrContext;
int streamId;
// input file
string file = argv[0];
int res = ffmpeg.avformat_open_input(&formatContext, file, null, null);
if (res != 0)
{
die("avformat_open_input");
}
res = ffmpeg.avformat_find_stream_info(formatContext, null);
if (res < 0)
{
die("avformat_find_stream_info");
}
AVCodec* codec;
res = ffmpeg.av_find_best_stream(formatContext, AVMediaType.AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
if (res < 0)
{
return; // die("av_find_best_stream");
}
streamId = res;
fileCodecContext = ffmpeg.avcodec_alloc_context3(codec);
AVCodecParameters* cp = null;
ffmpeg.avcodec_parameters_to_context(fileCodecContext, formatContext->streams[streamId]->codecpar);
res = ffmpeg.avcodec_open2(fileCodecContext, codec, null);
if (res < 0)
{
die("avcodec_open2");
}
in_audioStream = formatContext->streams[streamId];
// output file
//string outfile = Path.Combine(Path.GetTempPath(), $"{Path.GetFileNameWithoutExtension(argv[0])}.pcm");
//AVOutputFormat* fmt = fmt = ffmpeg.av_guess_format("s16le", null, null);
string outfile = argv[1];
AVOutputFormat * fmt = fmt = ffmpeg.av_guess_format(null, outfile, null);
if (fmt == null)
{
die("av_guess_format");
}
outContext = ffmpeg.avformat_alloc_context();
outContext->oformat = fmt;
out_audioStream = add_audio_stream(outContext, fmt->audio_codec, in_audioStream->codec->sample_rate);
open_audio(outContext, out_audioStream);
out_audioStream->time_base = in_audioStream->time_base;
res = ffmpeg.avio_open2(&outContext->pb, outfile, ffmpeg.AVIO_FLAG_WRITE, null, null);
if (res < 0)
{
die("url_fopen");
}
ffmpeg.avformat_write_header(outContext, null);
AVCodec* ocodec;
res = ffmpeg.av_find_best_stream(outContext, AVMediaType.AVMEDIA_TYPE_AUDIO, -1, -1, &ocodec, 0);
audioCodecContext = ffmpeg.avcodec_alloc_context3(ocodec);
ffmpeg.avcodec_parameters_to_context(audioCodecContext, out_audioStream->codecpar);
res = ffmpeg.avcodec_open2(audioCodecContext, ocodec, null);
if (res < 0)
{
die("avcodec_open2");
}
// resampling
swrContext = ffmpeg.swr_alloc();
ffmpeg.av_opt_set_channel_layout(swrContext, "in_channel_layout", (long)fileCodecContext->channel_layout, 0);
ffmpeg.av_opt_set_channel_layout(swrContext, "out_channel_layout", (long)audioCodecContext->channel_layout, 0);
ffmpeg.av_opt_set_int(swrContext, "in_sample_rate", fileCodecContext->sample_rate, 0);
ffmpeg.av_opt_set_int(swrContext, "out_sample_rate", audioCodecContext->sample_rate, 0);
ffmpeg.av_opt_set_sample_fmt(swrContext, "in_sample_fmt", fileCodecContext->sample_fmt, 0);
ffmpeg.av_opt_set_sample_fmt(swrContext, "out_sample_fmt", audioCodecContext->sample_fmt, 0);
res = ffmpeg.swr_init(swrContext);
if (res < 0)
{
die("swr_init");
}
AVFrame* audioFrameDecoded = ffmpeg.av_frame_alloc();
if (audioFrameDecoded == null)
{
die("Could not allocate audio frame");
}
audioFrameDecoded->format = (int)fileCodecContext->sample_fmt;
audioFrameDecoded->channel_layout = fileCodecContext->channel_layout;
audioFrameDecoded->channels = fileCodecContext->channels;
audioFrameDecoded->sample_rate = fileCodecContext->sample_rate;
AVFrame* audioFrameConverted = ffmpeg.av_frame_alloc();
if (audioFrameConverted == null)
{
die("Could not allocate audio frame");
}
audioFrameConverted->nb_samples = audioCodecContext->frame_size;
audioFrameConverted->format = (int)audioCodecContext->sample_fmt;
audioFrameConverted->channel_layout = audioCodecContext->channel_layout;
audioFrameConverted->channels = audioCodecContext->channels;
audioFrameConverted->sample_rate = audioCodecContext->sample_rate;
if (audioFrameConverted->nb_samples <= 0)
{
audioFrameConverted->nb_samples = 32;
}
AVPacket inPacket;
ffmpeg.av_init_packet(&inPacket);
inPacket.data = null;
inPacket.size = 0;
int frameFinished = 0;
for (; ; )
{
if (ffmpeg.av_read_frame(formatContext, &inPacket) < 0)
{
break;
}
if (inPacket.stream_index == streamId)
{
int len = Decode(fileCodecContext, audioFrameDecoded, ref frameFinished, &inPacket);
if (len == ffmpeg.AVERROR_EOF)
{
break;
}
if (frameFinished != 0)
{
// Convert
byte* convertedData = null;
if (ffmpeg.av_samples_alloc(&convertedData,
null,
audioCodecContext->channels,
audioFrameConverted->nb_samples,
audioCodecContext->sample_fmt, 0) < 0)
{
die("Could not allocate samples");
}
int outSamples = 0;
fixed (byte** tmp = (byte*[])audioFrameDecoded->data)
{
outSamples = ffmpeg.swr_convert(swrContext, null, 0,
//&convertedData,
//audioFrameConverted->nb_samples,
tmp,
audioFrameDecoded->nb_samples);
}
if (outSamples < 0)
{
die("Could not convert");
}
for (; ; )
{
outSamples = ffmpeg.swr_get_out_samples(swrContext, 0);
if ((outSamples < audioCodecContext->frame_size * audioCodecContext->channels) || audioCodecContext->frame_size == 0 && (outSamples < audioFrameConverted->nb_samples * audioCodecContext->channels))
{
break; // see comments, thanks to #dajuric for fixing this
}
outSamples = ffmpeg.swr_convert(swrContext,
&convertedData,
audioFrameConverted->nb_samples, null, 0);
int buffer_size = ffmpeg.av_samples_get_buffer_size(null,
audioCodecContext->channels,
audioFrameConverted->nb_samples,
audioCodecContext->sample_fmt,
0);
if (buffer_size < 0)
{
die("Invalid buffer size");
}
if (ffmpeg.avcodec_fill_audio_frame(audioFrameConverted,
audioCodecContext->channels,
audioCodecContext->sample_fmt,
convertedData,
buffer_size,
0) < 0)
{
die("Could not fill frame");
}
AVPacket outPacket;
ffmpeg.av_init_packet(&outPacket);
outPacket.data = null;
outPacket.size = 0;
if (Encode(audioCodecContext, &outPacket, audioFrameConverted, ref frameFinished) < 0)
{
die("Error encoding audio frame");
}
//outPacket.flags |= ffmpeg.AV_PKT_FLAG_KEY;
outPacket.stream_index = out_audioStream->index;
//outPacket.data = audio_outbuf;
outPacket.dts = audioFrameDecoded->pkt_dts;
outPacket.pts = audioFrameDecoded->pkt_pts;
ffmpeg.av_packet_rescale_ts(&outPacket, in_audioStream->time_base, out_audioStream->time_base);
if (frameFinished != 0)
{
if (ffmpeg.av_interleaved_write_frame(outContext, &outPacket) != 0)
{
die("Error while writing audio frame");
}
ffmpeg.av_packet_unref(&outPacket);
}
}
}
}
}
EncodeFlush(audioCodecContext);
DecodeFlush(fileCodecContext, &inPacket);
ffmpeg.swr_close(swrContext);
ffmpeg.swr_free(&swrContext);
ffmpeg.av_frame_free(&audioFrameConverted);
ffmpeg.av_frame_free(&audioFrameDecoded);
ffmpeg.av_packet_unref(&inPacket);
ffmpeg.av_write_trailer(outContext);
ffmpeg.avio_close(outContext->pb);
ffmpeg.avcodec_close(fileCodecContext);
ffmpeg.avcodec_free_context(&fileCodecContext);
ffmpeg.avformat_close_input(&formatContext);
return;
}
}
}

How to print PDF to ZPL (Zebra Printers) using c#?

ZEBRA PRINTERS uses the native commands called ZPL programming language, printing PDF to the printers usually do not work, the best solution for printing using C# is?
I created this question and answer as I could not find an effective solution on the Internet, and this one will help a lot of people with this issue
The best solution below:
Requirement: Install the free library Ghostscript 32 bits in the computer
https://www.ghostscript.com/download.html
Main methods: pdfbase64 to ZPL or Stream pdf to ZPL
public static List<string> ZplFromPdf(string pdfBase64, int dpi = 300)
{
return ZplFromPdf(new MemoryStream(Convert.FromBase64String(pdfBase64)), new Size(0,0), dpi);
}
public static List<string> ZplFromPdf(Stream pdf, Size size, int dpi = 300)
{
var zpls = new List<string>();
if (size == new Size(0, 0))
{
size = new Size(812, 1218);
}
using (var rasterizer = new GhostscriptRasterizer())
{
rasterizer.Open(pdf);
var images = new List<Image>();
for (int pageNumber = 1; pageNumber <= rasterizer.PageCount; pageNumber++)
{
var bmp = new Bitmap(rasterizer.GetPage(dpi, dpi, pageNumber), size.Width, size.Height);
var zpl = new StringBuilder();
zpl.Append(ZPLHelper.GetGrfStoreCommand("R:LBLRA2.GRF", bmp));
zpl.Append("^XA^FO0,0^XGR:LBLRA2.GRF,1,1^FS^XZ");
zpl.Append("^XA^IDR:LBLRA2.GRF^FS^XZ");
zpls.Add(zpl.ToString());
}
return zpls;
}
}
Core methods
public class ZPLHelper
{
static Regex regexFilename = new Regex("^[REBA]:[A-Z0-9]{1,8}\\.GRF$");
public static bool PrintLabelBase64Image(string printerName, string base64Image, string jobName = "label")
{
try
{
var bmpLabel = Base64ToBitmap(base64Image);
var baseStream = new MemoryStream();
var tw = new StreamWriter(baseStream, Encoding.UTF8);
tw.WriteLine(GetGrfStoreCommand("R:LBLRA2.GRF", bmpLabel));
tw.WriteLine(GetGrfPrintCommand("R:LBLRA2.GRF"));
tw.WriteLine(GetGrfDeleteCommand("R:LBLRA2.GRF"));
tw.Flush();
baseStream.Position = 0;
var gdipj = new GdiPrintJob(printerName, GdiPrintJobDataType.Raw, jobName, null);
gdipj.WritePage(baseStream);
gdipj.CompleteJob();
return true;
}
catch (Exception)
{
return false;
}
}
public static bool PrintLabelZpl(string printerName, string zplCommand, string jobName = "label")
{
var baseStream = new MemoryStream();
var tw = new StreamWriter(baseStream, Encoding.UTF8);
tw.WriteLine(zplCommand);
tw.Flush();
baseStream.Position = 0;
var gdiJob = new GdiPrintJob(printerName, GdiPrintJobDataType.Raw, jobName, null);
gdiJob.WritePage(baseStream);
gdiJob.CompleteJob();
return true;
}
private static Bitmap Base64ToBitmap(string base64Image)
{
Image image;
using (var ms = new MemoryStream(Convert.FromBase64String(base64Image)))
{
image = Image.FromStream(ms);
}
return new Bitmap(image);
}
public static string GetGrfStoreCommand(string filename, Bitmap bmpSource)
{
if (bmpSource == null)
{
throw new ArgumentNullException("bmpSource");
}
validateFilename(filename);
var dim = new Rectangle(Point.Empty, bmpSource.Size);
var stride = ((dim.Width + 7) / 8);
var bytes = stride * dim.Height;
using (var bmpCompressed = bmpSource.Clone(dim, PixelFormat.Format1bppIndexed))
{
var result = new StringBuilder();
result.AppendFormat("^XA~DG{2},{0},{1},", stride * dim.Height, stride, filename);
byte[][] imageData = GetImageData(dim, stride, bmpCompressed);
byte[] previousRow = null;
foreach (var row in imageData)
{
appendLine(row, previousRow, result);
previousRow = row;
}
result.Append(#"^FS^XZ");
return result.ToString();
}
}
public static string GetGrfDeleteCommand(string filename)
{
validateFilename(filename);
return string.Format("^XA^ID{0}^FS^XZ", filename);
}
public static string GetGrfPrintCommand(string filename)
{
validateFilename(filename);
return string.Format("^XA^FO0,0^XG{0},1,1^FS^XZ", filename);
}
private static void validateFilename(string filename)
{
if (!regexFilename.IsMatch(filename))
{
throw new ArgumentException("Filename must be in the format "
+ "R:XXXXXXXX.GRF. Drives are R, E, B, A. Filename can "
+ "be alphanumeric between 1 and 8 characters.", "filename");
}
}
unsafe private static byte[][] GetImageData(Rectangle dim, int stride, Bitmap bmpCompressed)
{
byte[][] imageData;
var data = bmpCompressed.LockBits(dim, ImageLockMode.ReadOnly, PixelFormat.Format1bppIndexed);
try
{
byte* pixelData = (byte*)data.Scan0.ToPointer();
byte rightMask = (byte)(0xff << (data.Stride * 8 - dim.Width));
imageData = new byte[dim.Height][];
for (int row = 0; row < dim.Height; row++)
{
byte* rowStart = pixelData + row * data.Stride;
imageData[row] = new byte[stride];
for (int col = 0; col < stride; col++)
{
byte f = (byte)(0xff ^ rowStart[col]);
f = (col == stride - 1) ? (byte)(f & rightMask) : f;
imageData[row][col] = f;
}
}
}
finally
{
bmpCompressed.UnlockBits(data);
}
return imageData;
}
private static void appendLine(byte[] row, byte[] previousRow, StringBuilder baseStream)
{
if (row.All(r => r == 0))
{
baseStream.Append(",");
return;
}
if (row.All(r => r == 0xff))
{
baseStream.Append("!");
return;
}
if (previousRow != null && MatchByteArray(row, previousRow))
{
baseStream.Append(":");
return;
}
byte[] nibbles = new byte[row.Length * 2];
for (int i = 0; i < row.Length; i++)
{
nibbles[i * 2] = (byte)(row[i] >> 4);
nibbles[i * 2 + 1] = (byte)(row[i] & 0x0f);
}
for (int i = 0; i < nibbles.Length; i++)
{
byte cPixel = nibbles[i];
int repeatCount = 0;
for (int j = i; j < nibbles.Length && repeatCount <= 400; j++)
{
if (cPixel == nibbles[j])
{
repeatCount++;
}
else
{
break;
}
}
if (repeatCount > 2)
{
if (repeatCount == nibbles.Length - i
&& (cPixel == 0 || cPixel == 0xf))
{
if (cPixel == 0)
{
if (i % 2 == 1)
{
baseStream.Append("0");
}
baseStream.Append(",");
return;
}
else if (cPixel == 0xf)
{
if (i % 2 == 1)
{
baseStream.Append("F");
}
baseStream.Append("!");
return;
}
}
else
{
baseStream.Append(getRepeatCode(repeatCount));
i += repeatCount - 1;
}
}
baseStream.Append(cPixel.ToString("X"));
}
}
private static string getRepeatCode(int repeatCount)
{
if (repeatCount > 419)
throw new ArgumentOutOfRangeException();
int high = repeatCount / 20;
int low = repeatCount % 20;
const string lowString = " GHIJKLMNOPQRSTUVWXY";
const string highString = " ghijklmnopqrstuvwxyz";
string repeatStr = "";
if (high > 0)
{
repeatStr += highString[high];
}
if (low > 0)
{
repeatStr += lowString[low];
}
return repeatStr;
}
private static bool MatchByteArray(byte[] row, byte[] previousRow)
{
for (int i = 0; i < row.Length; i++)
{
if (row[i] != previousRow[i])
{
return false;
}
}
return true;
}
}
internal static class NativeMethods
{
#region winspool.drv
#region P/Invokes
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool OpenPrinter(string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern UInt32 StartDocPrinter(IntPtr hPrinter, Int32 level, IntPtr di);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool WritePrinter(
// 0
IntPtr hPrinter,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] byte[] pBytes,
// 2
UInt32 dwCount,
out UInt32 dwWritten);
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
internal struct DOC_INFO_1
{
[MarshalAs(UnmanagedType.LPWStr)]
public string DocName;
[MarshalAs(UnmanagedType.LPWStr)]
public string OutputFile;
[MarshalAs(UnmanagedType.LPWStr)]
public string Datatype;
}
#endregion
#endregion
}
/// <summary>
/// Represents a print job in a spooler queue
/// </summary>
public class GdiPrintJob
{
IntPtr PrinterHandle;
//IntPtr DocHandle;
/// <summary>
/// The ID assigned by the print spooler to identify the job
/// </summary>
public UInt32 PrintJobID { get; private set; }
/// <summary>
/// Create a print job with a enumerated datatype
/// </summary>
/// <param name="PrinterName"></param>
/// <param name="dataType"></param>
/// <param name="jobName"></param>
/// <param name="outputFileName"></param>
public GdiPrintJob(string PrinterName, GdiPrintJobDataType dataType, string jobName, string outputFileName)
: this(PrinterName, translateType(dataType), jobName, outputFileName)
{
}
/// <summary>
/// Create a print job with a string datatype
/// </summary>
/// <param name="PrinterName"></param>
/// <param name="dataType"></param>
/// <param name="jobName"></param>
/// <param name="outputFileName"></param>
public GdiPrintJob(string PrinterName, string dataType, string jobName, string outputFileName)
{
if (string.IsNullOrWhiteSpace(PrinterName))
throw new ArgumentNullException("PrinterName");
if (string.IsNullOrWhiteSpace(dataType))
throw new ArgumentNullException("PrinterName");
IntPtr hPrinter;
if (!NativeMethods.OpenPrinter(PrinterName, out hPrinter, IntPtr.Zero))
throw new Win32Exception();
this.PrinterHandle = hPrinter;
NativeMethods.DOC_INFO_1 docInfo = new NativeMethods.DOC_INFO_1()
{
DocName = jobName,
Datatype = dataType,
OutputFile = outputFileName
};
IntPtr pDocInfo = Marshal.AllocHGlobal(Marshal.SizeOf(docInfo));
RuntimeHelpers.PrepareConstrainedRegions();
try
{
Marshal.StructureToPtr(docInfo, pDocInfo, false);
UInt32 docid = NativeMethods.StartDocPrinter(hPrinter, 1, pDocInfo);
if (docid == 0)
throw new Win32Exception();
this.PrintJobID = docid;
}
finally
{
Marshal.FreeHGlobal(pDocInfo);
}
}
/// <summary>
/// Write the data of a single page or a precomposed PCL document
/// </summary>
/// <param name="data"></param>
public void WritePage(Stream data)
{
if (data == null)
throw new ArgumentNullException("data");
if (!data.CanRead && !data.CanWrite)
throw new ObjectDisposedException("data");
if (!data.CanRead)
throw new NotSupportedException("stream is not readable");
if (!NativeMethods.StartPagePrinter(this.PrinterHandle))
throw new Win32Exception();
byte[] buffer = new byte[0x14000]; /* 80k is Stream.CopyTo default */
uint read = 1;
while ((read = (uint)data.Read(buffer, 0, buffer.Length)) != 0)
{
UInt32 written;
if (!NativeMethods.WritePrinter(this.PrinterHandle, buffer, read, out written))
throw new Win32Exception();
if (written != read)
throw new InvalidOperationException("Error while writing to stream");
}
if (!NativeMethods.EndPagePrinter(this.PrinterHandle))
throw new Win32Exception();
}
/// <summary>
/// Complete the current job
/// </summary>
public void CompleteJob()
{
if (!NativeMethods.EndDocPrinter(this.PrinterHandle))
throw new Win32Exception();
}
#region datatypes
private readonly static string[] dataTypes = new string[]
{
// 0
null,
"RAW",
// 2
"RAW [FF appended]",
"RAW [FF auto]",
// 4
"NT EMF 1.003",
"NT EMF 1.006",
// 6
"NT EMF 1.007",
"NT EMF 1.008",
// 8
"TEXT",
"XPS_PASS",
// 10
"XPS2GDI"
};
private static string translateType(GdiPrintJobDataType type)
{
return dataTypes[(int)type];
}
#endregion
}
public enum GdiPrintJobDataType
{
Unknown = 0,
Raw = 1,
RawAppendFF = 2,
RawAuto = 3,
NtEmf1003 = 4,
NtEmf1006 = 5,
NtEmf1007 = 6,
NtEmf1008 = 7,
Text = 8,
XpsPass = 9,
Xps2Gdi = 10
}
You might be interested in my NuGet package for converting PDF files into ZPL code.
The official Zebra SDK might contain a conversion but I haven't checked (not sure about the licensing here).
Adapting your previous code for the first page would be
public static string ZplFromPdf(string pdfBase64, int dpi = 300)
{
return PDFtoZPL.Conversion.ConvertPdfPage(pdfBase64, dpi: dpi);
}
Disclaimer: I ran into the same problem, stitched together ZPL code found on the internet and bundled it into a tiny .NET API.

C# Easyhook Winsock WS2_32.dll,connect hook Socks5

I'm trying to hook the winsock connect function and route the TCP connection through socks5 proxy /w auth.
This works if the socket is a blocking socket, but while using firefox ( nonblocking sockets ) I get a lot of 10035, 10022 winsock Errors.
How can i determine if it's a nonblocking / blocking socket?
I would really appreciate any hints or ideas to achieve the functionality to hook the wsock connect function and route tcp traffic through a socks5 server.
I can put the demo application on github if anybody wants to test it. ( Works with any version of firefox )
Edit1: https://github.com/duketwo/WinsockConnectHookSocks5/
( You have to edit the proxy information in WSockConnectHook/HookManager.cs and the path of firefox in Injector/MainForm.cs )
Edit2: It's easyhook which is causing the trouble, anything after the original function call doesn't work properly.
Edit3: Seems like i got it working with many flaws, in fact it is required differentiate between nonblocking sockets and blocking sockets. Any ideas how to achieve this?
Edit4: Windows doesn't offer any method to retrieve the blocking-attribute of a socket, so I might have to hook the ioctlsocket function to keep track of the blocking status of the sockets.
Thanks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using EasyHook;
using System.IO;
using System.Windows.Forms;
namespace WSockConnectHook
{
public class WinSockConnectController : IDisposable, IHook
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi, SetLastError = true)]
private delegate int WinsockConnectDelegate(IntPtr s, IntPtr addr, int addrsize);
[DllImport("WS2_32.dll", SetLastError = true)]
public static extern int connect(IntPtr s, IntPtr addr, int addrsize);
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct sockaddr_in
{
public const int Size = 16;
public short sin_family;
public ushort sin_port;
public struct in_addr
{
public uint S_addr;
public struct _S_un_b
{
public byte s_b1, s_b2, s_b3, s_b4;
}
public _S_un_b S_un_b;
public struct _S_un_w
{
public ushort s_w1, s_w2;
}
public _S_un_w S_un_w;
}
public in_addr sin_addr;
}
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern int WSAGetLastError();
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern void WSASetLastError(int set);
[DllImport("Ws2_32.dll", CharSet = CharSet.Ansi)]
public static extern uint inet_addr(string cp);
[DllImport("Ws2_32.dll")]
public static extern ushort htons(ushort hostshort);
[DllImport("ws2_32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr socket(short af, short socket_type, int protocol);
[DllImport("Ws2_32.dll")]
public static extern int send(IntPtr s, IntPtr buf, int len, int flags);
[DllImport("Ws2_32.dll")]
public static extern int recv(IntPtr s, IntPtr buf, int len, int flags);
[DllImport("ws2_32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int closesocket(IntPtr s);
[DllImport("Ws2_32.dll")]
public static extern ushort ntohs(ushort netshort);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern void SetLastError(int errorCode);
private string _name;
private LocalHook _hook;
public bool Error { get; set; }
public string Name { get; set; }
private string proxyIp, proxyPort, proxyUser, proxyPass;
public WinSockConnectController(IntPtr address, string proxyIp, string proxyPort, string proxyUser, string proxyPass)
{
this.Name = typeof(WinSockConnectController).Name;
this.proxyIp = proxyIp;
this.proxyPort = proxyPort;
this.proxyUser = proxyUser;
this.proxyPass = proxyPass;
try
{
_name = string.Format("WinsockHook_{0:X}", address.ToInt32());
_hook = LocalHook.Create(address, new WinsockConnectDelegate(WinsockConnectDetour), this);
_hook.ThreadACL.SetExclusiveACL(new Int32[] { 1 });
}
catch (Exception)
{
this.Error = true;
}
}
private object wSockLock = new object();
private int WinsockConnectDetour(IntPtr s, IntPtr addr, int addrsize)
{
lock (wSockLock)
{
// retrieve remote ip
sockaddr_in structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
string remoteIp = new System.Net.IPAddress(structure.sin_addr.S_addr).ToString();
ushort remotePort = ntohs(structure.sin_port);
HookManager.Log("Ip: " + remoteIp + " Port: " + remotePort.ToString() + " Addrsize: " + addrsize);
if (!proxyIp.Equals(""))
//if (!proxyIp.Equals(""))
{
// connect to socks5 server
SetAddr(s, addr, proxyIp, proxyPort);
var result = Connect(s, addr, addrsize);
if (result == -1)
return -1;
// send socks 5 request
IntPtr socksProtocolRequest = SetUpSocks5Request();
result = send(s, socksProtocolRequest, 4, 0);
if (result == -1)
return -1;
// retrieve server repsonse
var response = Recieve(s, 2);
if (response == IntPtr.Zero)
return -1;
byte[] recvBytes = new byte[2] { Marshal.ReadByte(response), Marshal.ReadByte(response, 1) };
if (recvBytes[1] == 255)
{
HookManager.Log("No authentication method was accepted by the proxy server");
return -1;
}
if (recvBytes[0] != 5)
{
HookManager.Log("No SOCKS5 proxy");
return -1;
}
// if auth request response, send authenicate request
if (recvBytes[1] == 2)
{
int length = 0;
var authenticateRequest = SetUpAuthenticateRequest(proxyUser, proxyPass, out length);
result = Send(s, authenticateRequest, length);
response = Recieve(s, 2);
if (response == IntPtr.Zero)
return -1;
recvBytes = new byte[2] { Marshal.ReadByte(response), Marshal.ReadByte(response, 1) };
if (recvBytes[1] != 0)
{
HookManager.Log("Proxy: incorrect username/password");
return -1;
}
}
// request bind with server
var bindRequest = SetUpBindWithRemoteHost(remoteIp, remotePort);
result = Send(s, bindRequest, 10);
if (result == -1)
return -1;
// response
response = Recieve(s, 10);
if (response == IntPtr.Zero)
return -1;
if (!VerifyBindResponse(response))
return -1;
// success
WSASetLastError(0);
SetLastError(0);
// clean memory
foreach (var ptr in allocatedMemory)
Marshal.FreeHGlobal(ptr);
allocatedMemory.Clear();
return 0;
}
else
{
var result = connect(s, addr, addrsize);
return result;
}
}
}
private int Connect(IntPtr socket, IntPtr addr, int addrsize)
{
var result = connect(socket, addr, addrsize);
while (result == -1)
{
var errorcode = WSAGetLastError();
HookManager.Log("Error: " + errorcode);
if (errorcode == 10056)
break;
if (errorcode == 10037)
break;
if (errorcode != 10035 && errorcode != 10037)
return -1;
//flag = 1;
result = connect(socket, addr, addrsize);
}
return result;
}
private int Send(IntPtr socket, IntPtr buf, int len)
{
var result = send(socket, buf, len, 0);
while (result == -1)
{
var errorcode = WSAGetLastError();
HookManager.Log("Error: " + errorcode);
if (errorcode == 10056)
break;
if (errorcode == 10037)
break;
if (errorcode != 10035 && errorcode != 10037)
return -1;
result = send(socket, buf, 4, 0);
}
return result;
}
private List<IntPtr> allocatedMemory = new List<IntPtr>();
private IntPtr Recieve(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error2: " + WSAGetLastError());
return IntPtr.Zero;
}
return buffer;
}
private IntPtr RecieveAuth(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error3: " + WSAGetLastError());
return IntPtr.Zero; ;
}
if (result == 0)
return buffer;
if (result != 2)
{
HookManager.Log("Proxy: Bad response from server");
return IntPtr.Zero;
}
return buffer;
}
private IntPtr RecieveBind(IntPtr socket, int len)
{
var buffer = Marshal.AllocHGlobal(len);
allocatedMemory.Add(buffer);
var result = recv(socket, buffer, len, 0);
if (result == -1)
{
HookManager.Log("Error3: " + WSAGetLastError());
return IntPtr.Zero; ;
}
if (result == 0)
return buffer;
if (result != 10)
{
HookManager.Log("Proxy: Bad response from server");
return IntPtr.Zero;
}
return buffer;
}
private void SetAddr(IntPtr socket, IntPtr addr, string ip, string port)
{
sockaddr_in structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
string originalip = new System.Net.IPAddress(structure.sin_addr.S_addr).ToString();
ushort originalport = ntohs(structure.sin_port);
structure.sin_addr.S_addr = inet_addr(ip);
structure.sin_port = htons(Convert.ToUInt16(port));
Marshal.StructureToPtr(structure, addr, true);
structure = (sockaddr_in)Marshal.PtrToStructure(addr, typeof(sockaddr_in));
}
private IntPtr SetUpSocks5Request()
{
var initialRequest = Marshal.AllocHGlobal(4);
Marshal.WriteByte(initialRequest, Convert.ToByte(5));
Marshal.WriteByte(initialRequest + 1, Convert.ToByte(2));
Marshal.WriteByte(initialRequest + 2, Convert.ToByte(0));
Marshal.WriteByte(initialRequest + 3, Convert.ToByte(2));
return initialRequest;
}
private IntPtr SetUpAuthenticateRequest(string username, string password, out int index)
{
index = 0;
var size = 3 + Encoding.Default.GetBytes(username).Length + Encoding.Default.GetBytes(password).Length;
var authenticateBuffer = Marshal.AllocHGlobal(size);
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(1));
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(username.Length));
byte[] rawBytes;
if (username.Length > 0)
{
rawBytes = Encoding.Default.GetBytes(username);
for (int i = 0; i < rawBytes.Length; i++)
{
Marshal.WriteByte(authenticateBuffer + index++, rawBytes[i]);
}
}
Marshal.WriteByte(authenticateBuffer + index++, Convert.ToByte(password.Length));
if (password.Length > 0)
{
rawBytes = Encoding.Default.GetBytes(password);
for (int i = 0; i < rawBytes.Length; i++)
{
Marshal.WriteByte(authenticateBuffer + index++, rawBytes[i]);
}
}
return authenticateBuffer;
}
private IntPtr SetUpBindWithRemoteHost(string eveIP, ushort evePort)
{
var bindWithEveBuffer = Marshal.AllocHGlobal(10);
var iplist = eveIP.Split('.').ToList();
byte[] portbyte = BitConverter.GetBytes(evePort).Reverse().ToArray();
byte[] newbyte = new byte[2];
int indexy = 0;
foreach (var byty in portbyte)
{
newbyte[indexy] = byty;
indexy++;
}
// bind with remote server
Marshal.WriteByte(bindWithEveBuffer, Convert.ToByte(5));
Marshal.WriteByte(bindWithEveBuffer + 1, Convert.ToByte(1));
Marshal.WriteByte(bindWithEveBuffer + 2, Convert.ToByte(0));
Marshal.WriteByte(bindWithEveBuffer + 3, Convert.ToByte(1));
Marshal.WriteByte(bindWithEveBuffer + 4, Convert.ToByte(iplist[0]));
Marshal.WriteByte(bindWithEveBuffer + 5, Convert.ToByte(iplist[1]));
Marshal.WriteByte(bindWithEveBuffer + 6, Convert.ToByte(iplist[2]));
Marshal.WriteByte(bindWithEveBuffer + 7, Convert.ToByte(iplist[3]));
Marshal.WriteByte(bindWithEveBuffer + 8, newbyte[0]);
Marshal.WriteByte(bindWithEveBuffer + 9, newbyte[1]);
return bindWithEveBuffer;
}
private bool VerifyBindResponse(IntPtr buffer)
{
var recvBytes = new byte[10] { Marshal.ReadByte(buffer), Marshal.ReadByte(buffer, 1), Marshal.ReadByte(buffer, 2), Marshal.ReadByte(buffer, 3), Marshal.ReadByte(buffer, 4), Marshal.ReadByte(buffer, 5), Marshal.ReadByte(buffer, 6), Marshal.ReadByte(buffer, 7), Marshal.ReadByte(buffer, 8), Marshal.ReadByte(buffer, 9) };
if (recvBytes[1] != 0)
{
if (recvBytes[1] == 1)
HookManager.Log("General failure");
if (recvBytes[1] == 2)
HookManager.Log("connection not allowed by ruleset");
if (recvBytes[1] == 3)
HookManager.Log("network unreachable");
if (recvBytes[1] == 4)
HookManager.Log("host unreachable");
if (recvBytes[1] == 5)
HookManager.Log("connection refused by destination host");
if (recvBytes[1] == 6)
HookManager.Log("TTL expired");
if (recvBytes[1] == 7)
HookManager.Log("command not supported / protocol error");
if (recvBytes[1] == 8)
HookManager.Log("address type not supported");
HookManager.Log("Proxy: Connection error binding eve server");
return false;
}
return true;
}
public void Dispose()
{
if (_hook == null)
return;
_hook.Dispose();
_hook = null;
}
}
}

C# PInvoke VerQueryValue returns back OutOfMemoryException?

Below is the code sample which I got from online resource but it's suppose to work with fullframework, but when I try to build it using C# smart device, it throws exception saying it's out of memory. Does anybody know how can I fix it to use on compact? the out of memory exception when I make the second call to VerQueryValue which is the last one.
thanks,
[DllImport("coredll.dll")]
public static extern bool VerQueryValue(byte[] buffer, string subblock, out IntPtr blockbuffer, out uint len);
[DllImport("coredll.dll")]
public static extern bool VerQueryValue(byte[] pBlock, string pSubBlock, out string pValue, out uint len);
//
private static void GetAssemblyVersion()
{
string filename = #"\Windows\MyLibrary.dll";
if (File.Exists(filename))
{
try {
int handle = 0;
Int32 size = 0;
size = GetFileVersionInfoSize(filename, out handle);
if (size > 0)
{
bool retValue;
byte[] buffer = new byte[size];
retValue = GetFileVersionInfo(filename, handle, size, buffer);
if (retValue == true)
{
bool success = false;
IntPtr blockbuffer = IntPtr.Zero;
uint len = 0;
//success = VerQueryValue(buffer, "\\", out blockbuffer, out len);
success = VerQueryValue(buffer, #"\VarFileInfo\Translation", out blockbuffer, out len);
if(success)
{
int p = (int)blockbuffer;
//Reads a 16-bit signed integer from unmanaged memory
int j = Marshal.ReadInt16((IntPtr)p);
p += 2;
//Reads a 16-bit signed integer from unmanaged memory
int k = Marshal.ReadInt16((IntPtr)p);
string sb = string.Format("{0:X4}{1:X4}", j, k);
string spv = #"\StringFileInfo\" + sb + #"\ProductVersion";
string versionInfo;
VerQueryValue(buffer, spv, out versionInfo, out len);
}
}
}
}
catch (Exception err)
{
string error = err.Message;
}
}
}
After adding these two statements:
Int32 dwVerMinor = j & 0xffff;
Int32 dwVerBuild = k & 0xffff;
it's able to retrieve the DLL version.
Here's an implementation:
using DWORD = System.UInt32;
public static class NativeFile
{
public struct NativeFileInfo
{
public Version Version;
public NameValueCollection StringTable;
}
public unsafe static NativeFileInfo GetFileInfo(string path)
{
if (!File.Exists(path))
{
throw new FileNotFoundException();
}
IntPtr handle;
var size = GetFileVersionInfoSize(path, out handle);
var buffer = Marshal.AllocHGlobal(size);
try
{
if (!GetFileVersionInfo(path, handle, size, buffer))
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
IntPtr pVersion;
int versionLength;
VerQueryValue(buffer, “\”, out pVersion, out versionLength);
var versionInfo = (VS_FIXEDFILEINFO)Marshal.PtrToStructure(pVersion, typeof(VS_FIXEDFILEINFO));
var version = new Version((int)versionInfo.dwFileVersionMS >> 16,
(int)versionInfo.dwFileVersionMS & 0xFFFF,
(int)versionInfo.dwFileVersionLS >> 16,
(int)versionInfo.dwFileVersionLS & 0xFFFF);
// move to the string table and parse
var pStringTable = ((byte*)pVersion.ToPointer()) + versionLength;
var strings = ParseStringTable(pStringTable, size – versionLength);
return new NativeFileInfo
{
Version = version,
StringTable = strings
};
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
private unsafe static NameValueCollection ParseStringTable(byte* pStringTable, int length)
{
NameValueCollection nvc = new NameValueCollection();
byte* p = pStringTable;
short stringFileInfoLength = (short)*p;
byte* end = pStringTable + length;
p += (2 + 2 + 2); // length + valuelength + type
// verify key
var key = Marshal.PtrToStringUni(new IntPtr(p), 14);
if (key != "StringFileInfo") throw new ArgumentException();
// move past the key to the first string table
p += 30;
short stringTableLength = (short)*p;
p += (2 + 2 + 2); // length + valuelength + type
// get locale info
key = Marshal.PtrToStringUni(new IntPtr(p), 8);
// move to the first string
p += 18;
while (p < end)
{
short stringLength = (short)*p;
p += 2;
short valueChars = (short)*p;
p += 2;
short type = (short)*p;
p += 2;
if (stringLength == 0) break;
if ((valueChars == 0) || (type != 1))
{
p += stringLength;
continue;
}
var keyLength = stringLength – (valueChars * 2) – 6;
key = Marshal.PtrToStringUni(new IntPtr(p), keyLength / 2).TrimEnd(”);
p += keyLength;
var value = Marshal.PtrToStringUni(new IntPtr(p), valueChars).TrimEnd(”);
p += valueChars * 2;
if ((int)p % 4 != 0) p += 2;
nvc.Add(key, value);
}
return nvc;
}
private const string COREDLL = "coredll.dll";
[DllImport(COREDLL, SetLastError = true)]
private static extern int GetFileVersionInfoSize(string lptstrFilename, out IntPtr lpdwHandle);
[DllImport(COREDLL, SetLastError = true)]
private static extern bool GetFileVersionInfo(string lptstrFilename, IntPtr dwHandle, int dwLen, IntPtr lpData);
[DllImport(COREDLL, SetLastError = true)]
private static extern bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out int puLen);
[StructLayout(LayoutKind.Sequential)]
private struct VS_FIXEDFILEINFO
{
public DWORD dwSignature;
public DWORD dwStrucVersion;
public DWORD dwFileVersionMS;
public DWORD dwFileVersionLS;
public DWORD dwProductVersionMS;
public DWORD dwProductVersionLS;
public DWORD dwFileFlagsMask;
public DWORD dwFileFlags;
public FileOS dwFileOS;
public FileType dwFileType;
public DWORD dwFileSubtype;
public DWORD dwFileDateMS;
public DWORD dwFileDateLS;
};
public enum FileOS : uint
{
Unknown = 0x00000000,
DOS = 0x00010000,
OS2_16 = 0x00020000,
OS2_32 = 0x00030000,
NT = 0x00040000,
WindowsCE = 0x00050000,
}
public enum FileType : uint
{
Unknown = 0x00,
Application = 0x01,
DLL = 0x02,
Driver = 0x03,
Font = 0x04,
VXD = 0x05,
StaticLib = 0x07
}
}
And an example of usage:
class Program
{
static void Main(string[] args)
{
string target = “\FlashFX Disk\ARMv4i\conmanclient2.exe”;
var version = NativeFile.GetFileInfo(target);
Console.WriteLine(string.Format(“File: { 0}”, Path.GetFileName(target)));
Console.WriteLine(string.Format(“Version: { 0}”, version.Version.ToString(4)));
foreach (var key in version.StringTable.AllKeys)
{
Console.WriteLine(string.Format(“{ 0}: { 1}”, key, version.StringTable[key]));
}
Console.ReadLine();
}

Categories